NUEVO: Helius adquiere Light Protocol
Versionado de transacciones de Solana: legacy, v0 y v1
Blog/Fundamentos

Versionado de transacciones de Solana: legacy, v0 y v1

InvestigadorLostin en X
23 min de lectura

Breve historia de los formatos de transacción de Solana

Solana tiene tres formatos de transacción: legacy, v0 y v1. A grandes rasgos, todos cumplen el mismo propósito. Es decir, agrupan firmas, cuentas e instrucciones para que los validadores las ejecuten en programas en cadena.

Lo que ha cambiado con el tiempo es el formato de transmisión, es decir, cómo se serializan y transmiten esos elementos por la red.

Cada formato nuevo se introdujo para abordar las limitaciones que surgieron a medida que las aplicaciones de Solana se volvieron más complejas. Al mismo tiempo, se mantuvo la compatibilidad con versiones anteriores para que los formatos antiguos siguieran funcionando.

Los tres formatos coexisten en la red y los desarrolladores pueden usar el que mejor se adapte a la transacción que están creando.

Transacciones legacy

El formato original se conoce como legacy porque es anterior al versionado de transacciones y no contiene ningún identificador de versión.

Su tamaño máximo de 1,232 bytes se remonta al diseño de red original de Solana: las transacciones se dimensionaron para ajustarse a la MTU mínima de IPv6 de 1,280 bytes, lo que dejaba 1,232 bytes después de la sobrecarga de red.

Esto funcionaba bien para transacciones pequeñas, pero el espacio se volvió cada vez más restrictivo a medida que crecían las aplicaciones. Cada dirección de cuenta incluida directamente en una transacción ocupa 32 bytes, mientras que cada firma Ed25519 ocupa otros 64 bytes.

Por lo tanto, en la práctica, las transacciones con muchas cuentas podían agotar el límite de bytes mucho antes de alcanzar el límite de bloqueo de cuentas del entorno de ejecución.

Transacción v0

El primer intento de aliviar esta presión fue la transacción v0, que se activó en mainnet-beta en octubre de 2022. En lugar de aumentar el límite de 1,232 bytes, v0 introdujo las tablas de consulta de direcciones (ALT).

Una ALT almacena direcciones de cuentas en cadena, lo que permite que una transacción v0 haga referencia a ellas mediante índices de un byte en lugar de repetir cada clave pública de 32 bytes. Esto permite que las transacciones alcancen el límite de 64 cuentas del entorno de ejecución sin superar la restricción existente sobre el tamaño de los paquetes.

v0 fue una extensión incremental del formato legacy: agregó el prefijo de versión 0x80 y una sección de consulta de direcciones, pero conservó la misma codificación básica de mensajes e instrucciones.

La introducción del versionado también estableció un marco en el que los nuevos formatos de transacción podían coexistir con las transacciones legacy en lugar de reemplazarlas.

Las ALT resolvieron el problema inmediato del tamaño de las cuentas, pero introdujeron nuevas complejidades. Las aplicaciones deben crear y mantener tablas de consulta en cadena, los validadores deben resolver sus entradas antes de la ejecución, y los servicios RPC y los indexadores necesitan las direcciones resueltas para reconstruir la transacción completa.

La decodificación histórica también se vuelve mucho más difícil si la tabla de consulta a la que hace referencia una transacción sin procesar ya se cerró. Por lo tanto, v0 comprimió eficazmente la lista de cuentas, pero lo hizo al introducir una dependencia externa en cadena en la representación de transmisión de la transacción.

Las ALT fueron adoptadas ampliamente por los desarrolladores de Solana. Una investigación realizada poco antes de la introducción de las transacciones v1 determinó que cerca del 62 % de las transacciones v0 hacen referencia al menos a una ALT.

Adaptación de las comisiones de prioridad

Las comisiones de prioridad también se introdujeron en Solana en 2022, antes de que las transacciones v0 se activaran en mainnet, aunque para entonces el formato v0 ya estaba diseñado. En lugar de rediseñar cualquiera de los formatos de transacción, Solana agregó la configuración de comisiones mediante el Compute Budget Program existente. Las transacciones podían incluir instrucciones como SetComputeUnitLimit y SetComputeUnitPrice, lo que permitía a los usuarios solicitar un presupuesto de cómputo y adjuntar una comisión adicional para obtener prioridad en la programación. Esto funcionaba igual de bien para las transacciones legacy y v0, a la vez que evitaba un mecanismo de comisiones independiente para cada formato.

Esta fue una solución pragmática y compatible con versiones anteriores, pero no especialmente elegante. Las comisiones de prioridad y los límites de cómputo son en realidad metadatos a nivel de transacción. Sin embargo, legacy y v0 no tenían campos específicos para ellos, por lo que se incorporaron posteriormente al flujo de instrucciones como llamadas a programas. Esto significa que la transacción debe hacer referencia al Compute Budget Program, la configuración consume bytes de instrucciones y los validadores deben inspeccionar esas instrucciones durante la ingesta de la transacción para recuperar los ajustes de comisiones y recursos.

Por lo tanto, v0 no fue un rediseño amplio del formato de transacción de Solana. Su objetivo principal era resolver el cuello de botella de las direcciones de cuentas mediante tablas de consulta de direcciones, manteniendo intacta la mayor parte de la estructura de mensajes legacy. Como las instrucciones de Compute Budget ya ofrecían una forma compatible de admitir comisiones de prioridad en ambos formatos, había pocos motivos para ampliar aún más el alcance de v0.

Transacciones de mayor tamaño

Con el tiempo, Solana migró la ingesta de transacciones de UDP a QUIC, cuyos flujos ya no están limitados a una sola carga útil del tamaño de una MTU. Por lo tanto, el límite original de 1,232 bytes dejó de ser un requisito de transporte. En 2025 se propusieron dos SIMD para introducir un nuevo formato v1. SIMD-0296 aumentó el tamaño máximo de una transacción v1 a 4,096 bytes, aproximadamente 3.3 veces el límite anterior, mientras que SIMD-0385 definió un formato de transmisión completamente nuevo, diseñado en torno a ese espacio adicional y a una ingesta más eficiente por parte de los validadores.

El espacio adicional permite que v1 elimine por completo las ALT. El límite de 64 cuentas con direcciones de 32 bytes requiere como máximo 2,048 bytes, por lo que la lista completa de cuentas puede incluirse directamente.

El análisis de Solana Foundation sugiere que el impacto de insertar todas las direcciones es moderado al actualizar la mayoría de las transacciones v0 existentes: el 50 % crece menos de 420 bytes y el 90 % menos de 1,400 bytes. Esto deja un margen considerable dentro del límite de 4,096 bytes de v1, incluso para transacciones que usan muchas ALT.

Más importante aún, v1 no es simplemente una transacción v0 más grande. Reorganiza considerablemente el formato de transmisión: las firmas pasan al final, la configuración de recursos sale de las instrucciones de Compute Budget y pasa a una sección de configuración de la transacción, y las cargas útiles de instrucciones de longitud variable se separan de los encabezados de ancho fijo. Estos cambios hacen que a los validadores les resulte más económico y sencillo analizar y priorizar las transacciones.

El mayor límite de 4,096 bytes también habilita cargas de trabajo para las que la compresión de direcciones por sí sola nunca podría ser útil. Las pruebas de conocimiento cero, las multifirmas anidadas, las firmas Winternitz sin truncar, las firmas BLS y otras operaciones criptográficas pueden requerir cientos o miles de bytes de datos de instrucciones. Por ejemplo, ahora los saldos confidenciales de Token-2022 pueden construirse como una única transacción v1 atómica, en lugar de dividirse entre varias transacciones.

Cabe destacar que el formato v1 inicial no aumenta los límites de cómputo, firmas, cantidad de instrucciones ni 64 cuentas de Solana. Su función principal es eliminar el cuello de botella del tamaño y rediseñar la representación de la transacción durante la transmisión.

SIMD-0596, propuesta por Anza, aumentaría de 64 a 96 el límite de bloqueo de cuentas de v1. Esto abarcaría todas las cuentas referenciadas por la transacción, incluidos los firmantes, los ID de programas y las cuentas de escritura y solo lectura. Al momento de escribir este artículo, la propuesta sigue en revisión. Las transacciones legacy y v0 mantendrían el límite de 64.

Especificaciones y comparación de formatos

LímiteLegacyv0v1
Tamaño máximo1,232 bytes1,232 bytes4,096 bytes
Direcciones de cuentas~32, limitadas por el tamaño64, mediante tablas de consulta64, insertadas directamente
Tablas de consulta de direccionesNo compatiblesCompatiblesNo compatibles
Direcciones duplicadaspermitidaspermitidasrechazadas
PrefijoNinguno0x800x81
Comisión de prioridadInstrucción SetComputeUnitPrice, microlamports por CUInstrucción SetComputeUnitPrice, microlamports por CUconfig.priorityFeeLamports, lamports totales
Límite de unidades de cómputoInstrucción SetComputeUnitLimitInstrucción SetComputeUnitLimitconfig.computeUnitLimit
Tamaño del heapInstrucción RequestHeapFrameInstrucción RequestHeapFrameconfig.heapSize
Límite de cuentas cargadasInstrucción SetLoadedAccountsDataSizeLimitInstrucción SetLoadedAccountsDataSizeLimitconfig.loadedAccountsDataSizeLimit

Especificación de las transacciones legacy

Una transacción legacy serializada comienza con un conteo de firmas compact-u16, seguido de las firmas Ed25519 de 64 bytes.

A continuación viene el mensaje, que contiene el encabezado de tres bytes, la lista de cuentas con un prefijo de longitud compacta, el blockhash reciente de 32 bytes y el arreglo de instrucciones compiladas con un prefijo de longitud compacta.

Las transacciones legacy no tienen prefijo de versión.

Código
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 = InstructionDataLength

Una característica clave es que cada CompiledInstruction se serializa por completo antes de que comience la siguiente instrucción.

Los arreglos de índices de cuentas y datos tienen una longitud variable, y los prefijos compact-u16 definen sus tamaños.

Especificación de la transacción v0

La transacción v0 conserva casi todo el formato de transmisión legacy. La transacción sigue comenzando con el conteo de firmas y las firmas, pero el mensaje comienza con el prefijo de versión 0x80.

Luego, el mensaje usa el mismo encabezado de tres bytes, la lista de cuentas estáticas, el blockhash y el formato de instrucciones compiladas que legacy, antes de agregar una sección de tablas de consulta de direcciones.

Esto permite que las transacciones v0 carguen cuentas adicionales mediante ALT sin superar el límite de 1,232 bytes por transacción.

Código
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 table

Es importante distinguir entre las claves de cuentas estáticas y las direcciones cargadas. Solo las claves estáticas se serializan directamente en el mensaje. Las direcciones referenciadas mediante ALT se resuelven durante la ejecución y se agregan a la lista efectiva de cuentas.

Para una transacción v0 que usa ALT, el validador primero debe cargar cada tabla de consulta referenciada desde Bank y AccountsDB, verificar que la cuenta sea una tabla de consulta de direcciones válida, deserializar su estado, convertir los índices solicitados en direcciones completas de 32 bytes y, después, combinar esas direcciones con las claves de cuentas estáticas de la transacción. Solo después de este paso de resolución, el validador dispone de la lista completa de cuentas necesaria para determinar los bloqueos de lectura y escritura y los conflictos con otras transacciones.

Especificación de la transacción v1

Una transacción v1 serializada comienza con el byte de versión 0x81. Le siguen un encabezado de mensaje de tres bytes al estilo legacy, una máscara de configuración de transacción de 32 bits, un especificador de vigencia de 32 bytes, los conteos de instrucciones y direcciones, el arreglo completo de direcciones de 32 bytes, los valores de configuración, los encabezados de instrucciones de tamaño fijo, las cargas útiles contiguas de las instrucciones y, finalmente, las firmas.

Código
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.NumRequiredSignatures

Por lo tanto, v1 difiere de los formatos anteriores en varios aspectos fundamentales. El identificador de versión se encuentra en el byte cero, las firmas pasan al final y ya no necesitan su propio prefijo de longitud, los conteos de cuentas e instrucciones se convierten en valores u8 de ancho fijo, la configuración de recursos se codifica directamente en la transacción y los encabezados de instrucciones de tamaño fijo se separan de sus cargas útiles de longitud variable. A diferencia de legacy y v0, v1 no usa tablas de consulta de direcciones.

La transacción v1 reemplaza las instrucciones de configuración del Compute Budget Program por campos en el encabezado de la transacción. El TransactionConfigMask inicial puede declarar lo siguiente para la transacción:

  • comisión de prioridad total en lamports
  • límite de unidades de cómputo
  • límite del tamaño de los datos de cuentas cargadas
  • tamaño de heap solicitado

Cada bit establecido identifica un valor de configuración adjunto de cuatro bytes. La comisión de prioridad de 64 bits ocupa dos posiciones de la máscara. La máscara está diseñada para poder ampliarse en versiones futuras de las transacciones.

Transacción de ejemplo

Para contrastar los distintos formatos de transacción de Solana, analizaremos el ejemplo útil más sencillo: transferir SOL de una dirección a otra.

Nuestra transacción de ejemplo tiene un solo firmante (es decir, el remitente) e invoca una vez al System Program para transferir 1,000 lamports al destinatario. Hace referencia a tres direcciones: la del remitente, la del destinatario y la del System Program.

Transacciones legacy y v0

Las transacciones legacy y v0 usan casi la misma distribución de nivel superior. Ambas comienzan con la cantidad de firmas, seguida inmediatamente de las propias firmas. Después viene el mensaje de la transacción. Para una transferencia con un único firmante, el primer byte es 01, que especifica una sola firma, seguido de la firma Ed25519 de 64 bytes del remitente.

El mensaje contiene un encabezado de tres bytes, direcciones de cuentas, un blockhash reciente (el especificador de vigencia) e instrucciones compiladas.

El remitente es la dirección 0, el destinatario es la dirección 1 y el System Program es la dirección 2. La instrucción de transferencia hace referencia al programa en el índice 2 y pasa al programa los índices de cuentas 00 01.

Los formatos legacy y v0 solo difieren ligeramente:

  • Un mensaje legacy comienza directamente con su encabezado de mensaje de tres bytes, mientras que un mensaje v0 comienza con el byte de versión 0x80, seguido del encabezado.
  • v0 también agrega una sección de tablas de consulta de direcciones después de las instrucciones. Nuestro ejemplo sencillo no usa una tabla de consulta, por lo que esta sección consta de un solo byte 00 que indica cero consultas.

Esto hace que nuestra transacción de transferencia de SOL de ejemplo ocupe 215 bytes como transacción legacy y 217 bytes como v0. v0 agrega 1 byte para el prefijo de versión y 1 byte para el arreglo vacío de tablas de consulta. El resto de la transacción es idéntico.

El remitente solo firma el mensaje. En nuestro ejemplo legacy, esto significa que la firma abarca los bytes 65–214. En v0, abarca los bytes 65–216 y comienza con el prefijo de versión del mensaje 0x80.

La siguiente tabla detalla la distribución de bytes de nuestra transferencia de SOL de ejemplo entre dos direcciones, con el formato de transacción v0.

DesplazamientoTamañoCampoValor de ejemploDescripción
01 BConteo de firmas01Una firma; compact-u16
1–6464 BFirma 0<Ed25519 signature>Firma del remitente
651 BPrefijo de versión80Mensaje versionado, versión 0
66–683 BEncabezado del mensaje01 00 011 firmante requerido, 0 firmadas de solo lectura, 1 sin firmar de solo lectura
691 BConteo de cuentas estáticas03Tres cuentas insertadas directamente
70–10132 BCuenta 0<sender pubkey>Remitente/pagador de comisiones; firmante con permiso de escritura
102–13332 BCuenta 1<recipient pubkey>Destinatario; sin firma y con permiso de escritura
134–16532 BCuenta 211111111111111111111111111111111System Program; sin firma y de solo lectura
166–19732 BBlockhash reciente<recent blockhash>Vigencia de la transacción
1981 BConteo de instrucciones01Una instrucción
1991 BÍndice del ID del programa02System Program
2001 BConteo de cuentas de la instrucción02Dos cuentas de la instrucción
201–2022 BÍndices de cuentas00 01Remitente, destinatario
2031 BLongitud de los datos0C12 bytes
204–2074 BDiscriminador de transferencia02 00 00 00SystemInstruction::Transfer
208–2158 BLamportsE8 03 00 00 00 00 00 001,000 lamports
2161 BConteo de consultas de tablas de direcciones00Ninguna consulta ALT
Total217 B

Configuraciones de recursos

En las transacciones legacy y v0, las configuraciones de recursos se expresan como instrucciones para el Compute Budget Program. Entre las más comunes se encuentran SetComputeUnitLimit y SetComputeUnitPrice. SetLoadedAccountsDataSizeLimit y RequestHeapFrame también se usan cuando son necesarias.

En nuestro ejemplo de transacción mínima, esas instrucciones no están presentes. En su lugar, el entorno de ejecución proporciona valores predeterminados.

Si no se establece un precio por unidad de cómputo, no hay comisión de prioridad. El límite de datos de cuentas cargadas adopta de forma predeterminada el máximo del entorno de ejecución.

La ineficiencia que supone para los validadores tener que examinar la lista de instrucciones en busca de instrucciones de Compute Budget durante la ingesta de transacciones es una de las motivaciones del formato v1 actualizado, donde esta información se traslada a TransactionConfigMask y ConfigValues.

Para empezar, representar la configuración a nivel de transacción como instrucciones también implica cierta sobrecarga inherente. Legacy y v0 no tenían campos específicos para el cómputo solicitado ni para las comisiones de prioridad, por lo que estos ajustes se incorporaron posteriormente mediante el Compute Budget Program: la transacción debe hacer referencia al ID de su programa, cada ajuste ocupa espacio en la lista de instrucciones y la invocación del programa no realiza ningún trabajo de la aplicación, aunque sigue consumiendo cómputo.

En cambio, v1 asigna a estos valores un lugar nativo en el formato de transmisión. Esto evita la sobrecarga adicional de las instrucciones y hace que la configuración de recursos sea más económica y fácil de inspeccionar para los validadores.

Transacción v1

En lugar de comenzar con un conteo de firmas y las firmas, una transacción v1 comienza directamente con el byte de versión 0x81.

A continuación viene el encabezado de tres bytes, seguido de un nuevo TransactionConfigMask de cuatro bytes, el especificador de vigencia (normalmente un blockhash, aunque también podría ser un nonce), los conteos de instrucciones y direcciones, las direcciones de cuentas, los valores de configuración, las instrucciones y, finalmente, las firmas.

DesplazamientoTamañoCampoValor de ejemploDescripción
01 BByte de versión81Transacción v1
1–33 BEncabezado legacy01 00 011 firma requerida, 0 cuentas firmadas de solo lectura, 1 cuenta sin firmar de solo lectura
4–74 BMáscara de configuración de la transacción0C 00 00 000x0000000C: bits 2 y 3 establecidos, que especifican dos valores de configuración de 4 bytes
8–3932 BEspecificador de vigencia<recent blockhash>Blockhash reciente (o nonce)
401 BCantidad de instrucciones011 instrucción del System Program
411 BCantidad de direcciones03Remitente, destinatario, System Program
42–7332 BDirección 0<sender pubkey>Remitente, pagador de comisiones, firmante con permiso de escritura
74–10532 BDirección 1<recipient pubkey>Destinatario, cuenta sin firma y con permiso de escritura
106–13732 BDirección 211111111111111111111111111111111System Program, de solo lectura, sin firma
138–1414 BValor de configuración: límite de unidades de cómputo10 27 00 0010,000 CU como u32 little-endian (corresponde al bit 2 de la máscara)
142–1454 BValor de configuración: límite del tamaño de los datos de cuentas cargadas00 00 01 0065,536 bytes/64 KiB como u32 little-endian (corresponde al bit 3 de la máscara)
146–1494 BInstrucción: encabezado02 02 0C 00Índice de programa 2, 2 cuentas, 12 bytes de datos
150–1512 BInstrucción: índices de cuentas00 01Dirección 0 = remitente, dirección 1 = destinatario
152–1554 BInstrucción: discriminador de transferencia02 00 00 00SystemInstruction::Transfer
156–1638 BInstrucción: lamportsp. ej., E8 03 00 00 00 00 00 001,000 lamports como u64 little-endian
164–22764 BFirma 0<Ed25519 signature>Firma del remitente sobre los bytes 0–155
Total228 B

Verificación de firmas

La verificación de firmas es una de las primeras operaciones que se realizan cuando una transacción llega a un validador. Las transacciones con firmas no válidas deben filtrarse y descartarse antes de llegar al planificador. Podría parecer que colocar las firmas detrás de un mensaje de longitud variable dificulta el acceso a ellas, pero en la práctica no es así.

Antes de realizar la costosa verificación Ed25519, el validador solo necesita identificar dónde comienza y termina cada parte de la transacción. El formato v1 está diseñado para que esto sea económico y determinista.

El primer byte, 0x81, identifica inmediatamente la transacción como v1. El encabezado siguiente contiene num_required_signatures, por lo que el validador sabe desde el principio cuántas firmas de 64 bytes debe encontrar.

Luego, el analizador TransactionView de Agave avanza por la transacción mientras registra los desplazamientos. Lee los campos de tamaño fijo y usa NumAddresses para omitir el arreglo de direcciones.

Usa la máscara de configuración para determinar el tamaño de la sección de configuración y lee cada encabezado de instrucción de 4 bytes para conocer el tamaño de la carga útil correspondiente.

Una vez que el analizador supera la carga útil de la última instrucción, registra su posición actual como el desplazamiento de las firmas. A partir de esa posición, Agave espera exactamente num_required_signatures × 64 bytes.

La implementación almacena un SignatureFrame que contiene la cantidad de firmas y el desplazamiento en bytes de la primera firma del paquete. El mensaje firmado de la transacción es el intervalo de bytes que va desde el inicio del mensaje v1 hasta el desplazamiento de firma registrado. Así, el validador puede realizar la costosa verificación Ed25519 sin ejecutar la transacción ni cargar cuentas.

v1 también prohíbe los bytes adicionales después del arreglo de firmas. Cuando el analizador llega a las firmas, los bytes restantes tienen un tamaño inequívoco determinado por el conteo de firmas. SIMD-0385 exige exactamente una firma de 64 bytes por cada firmante requerido y ningún dato después de la última firma.

Análisis mejorado de instrucciones

La distribución de la transacción v1 facilita el análisis de las instrucciones. Para localizar una instrucción posterior en transacciones legacy y v0, el analizador debe recorrer las instrucciones anteriores, decodificar sus longitudes y omitir cada carga útil de longitud variable.

v1 separa los encabezados de instrucciones de ancho fijo de las cargas útiles de longitud variable. Cada instrucción aporta primero un encabezado de cuatro bytes que contiene el índice de la cuenta del programa, la cantidad de cuentas de la instrucción y la longitud de los datos de la instrucción. Estos encabezados se agrupan antes de los índices de cuentas y las cargas útiles de datos. Esto proporciona desde el principio una descripción compacta de cada instrucción. Reduce el costo de delimitar y omitir la sección de instrucciones, y ayuda a determinar el desplazamiento del arreglo de firmas final sin analizar los datos de las instrucciones.

La máscara de configuración de la transacción

La otra incorporación importante del formato v1 es el TransactionConfigMask de cuatro bytes.

Las transacciones legacy y v0 configuran los recursos mediante instrucciones para el Compute Budget Program. Por ejemplo, una transacción puede contener instrucciones SetComputeUnitLimit, SetComputeUnitPrice o SetLoadedAccountsDataSizeLimit. Un validador que necesite conocer los requisitos de recursos o la prioridad de la transacción debe localizar e interpretar estas instrucciones. La transacción v1 saca esta información del flujo de instrucciones y la integra en el propio formato de la transacción.

La máscara es un campo de bits little-endian de 32 bits. Cada bit establecido corresponde a una palabra de cuatro bytes en la sección ConfigValues que aparece más adelante en la transacción:

  • Bits 0 y 1: comisión de prioridad total en lamports (codificada como un u64 little-endian de 8 bytes)
  • Bit 2: límite de unidades de cómputo (un u32 de 4 bytes)
  • Bit 3: límite del tamaño de los datos de cuentas cargadas (un u32 de 4 bytes)
  • Bit 4: tamaño de heap solicitado (un u32 de 4 bytes)

Nota: La especificación inicial de v1 solo asigna significado a los bits 0–4. Los bits restantes no están asignados actualmente, lo que deja espacio para futuros campos de configuración a nivel de transacción.

La máscara funciona como un esquema compacto que indica al analizador qué campos existen y cuántos bytes debe esperar. Por ejemplo, nuestra sencilla transferencia de SOL necesita un límite de unidades de cómputo y otro para el tamaño de los datos de cuentas cargadas, pero no necesita una comisión de prioridad ni un tamaño de heap personalizado.

Como estos dos bits están establecidos, después del arreglo de direcciones de la transacción aparecen exactamente dos valores de configuración de 4 bytes. En nuestro caso, solicitan un límite de 20,000 CU y un límite de 64 KiB para los datos de cuentas cargadas.

La máscara indica al validador que la primera configuración de 4 bytes pertenece al bit 2 (el límite de unidades de cómputo) y la segunda al bit 3 (el límite de datos de cuentas cargadas).

En las transacciones legacy y v0, omitir una instrucción de límite de unidades de cómputo proporciona a la transacción un presupuesto de cómputo implícito. v1 no usa esos mismos valores predeterminados. Un límite de unidades de cómputo sin establecer se resuelve como cero, al igual que un límite del tamaño de los datos de cuentas cargadas sin establecer. Solo el heap conserva un valor predeterminado de 32 KiB. Por lo tanto, una transacción v1 debe solicitar explícitamente los recursos que necesita.

Trasladar estos ajustes a un área de configuración bien definida proporciona a los validadores acceso directo a la información que necesitan durante la ingesta de transacciones, en lugar de obligarlos a buscarla en las instrucciones. También significa que las instrucciones del Compute Budget Program ya no configuran las transacciones v1. Si se incluyen, se ignoran y se procesan como instrucciones no-op, aunque siguen consumiendo unidades de cómputo.

Esto también ahorra espacio. En las transacciones legacy/v0, agregar instrucciones de Compute Budget generalmente requiere incluir en la lista de cuentas la dirección de 32 bytes del Compute Budget Program, además de las propias instrucciones serializadas.

Conclusión

Los tres formatos de transacción de Solana reflejan la evolución de la red. Legacy estableció el modelo original, v0 lo amplió con ALT para admitir conjuntos de cuentas más grandes dentro del límite de 1,232 bytes y v1 replantea el formato de transmisión de una forma más profunda, con transacciones más grandes, análisis más sencillo, direcciones insertadas directamente y configuración nativa de recursos.

Los tres siguen siendo válidos, pero cada uno representa una etapa distinta del desarrollo de Solana. En conjunto, muestran cómo se adaptó el formato de transacción a medida que evolucionaron las aplicaciones de la red, los mercados de comisiones y los requisitos de los validadores.

Recursos adicionales

Suscríbete a Helius

Mantente al día con las novedades del desarrollo en Solana y recibe actualizaciones cuando publiquemos

Imagen ampliada