Datos de entrada de Ethereum: cómo interpretar los bytes y la ABI

Los datos de entrada de una transacción de Ethereum son un campo opcional que puede contener datos arbitrarios. En una llamada a función con codificación ABI estándar, esos bytes tienen una estructura reconocible: un selector de función seguido de argumentos codificados. Para interpretar esa estructura hace falta el esquema de la interfaz correspondiente.

Conviene separar tres preguntas: qué bytes hay, cómo los interpreta un esquema concreto y qué conclusiones siguen sin estar demostradas. Una entrada vacía, un selector reconocible y un número descodificado solo responden a una parte de esas preguntas.

Qué contiene el campo de entrada

La documentación de Ethereum sobre transacciones describe los datos de entrada como un campo opcional que admite datos arbitrarios. Esta definición es importante: no exige que cualquier entrada siga la convención de llamadas a funciones de la ABI. Empieza por observar los bytes disponibles antes de asignarles un nombre de función o un tipo de argumento.

Para representar secuencias de bytes, JSON-RPC de Ethereum utiliza texto hexadecimal con el prefijo 0x y dos dígitos hexadecimales por byte. Las convenciones de codificación de JSON-RPC distinguen esta representación, denominada DATA, del formato de las cantidades numéricas. Mantén esa distinción al leer el campo de entrada.

En la representación de secuencias de bytes, 0x significa una secuencia vacía: no hay bytes de datos después del prefijo. No es la codificación de la cantidad numérica cero. Descríbelo como una entrada vacía, en lugar de tratarlo como un número pendiente de descodificar.

Cómo encajan el selector y los argumentos

La especificación de la ABI de contratos de Solidity sitúa el selector de función en los cuatro primeros bytes de los datos de una llamada estándar, también llamados calldata. Si hay argumentos, su codificación comienza en el quinto byte. Esta estructura no implica que todas las funciones necesiten argumentos ni describe cualquier secuencia arbitraria de entrada.

El selector está formado por los cuatro primeros bytes del hash Keccak-256 de la firma canónica de la función. Esa firma contiene el nombre de la función seguido de los tipos de sus parámetros entre paréntesis, separados por comas y sin espacios. Los tipos deben utilizar sus representaciones canónicas en la ABI; los valores concretos de los argumentos no forman parte de esa firma.

Como ejercicio didáctico, imaginemos una función inventada con un único parámetro uint256 cuya firma canónica sea record(uint256). En este supuesto, la firma identifica el nombre de la función y el tipo del parámetro. Un valor concreto, como 6, corresponde a la región de argumentos codificados. Aquí no se calcula ni se atribuye ningún valor de selector a esa función ficticia.

Al anotar una llamada ABI estándar, identifica por separado el selector y la región de argumentos. Así podrás mantener junto a los bytes la descripción de la interfaz que utilizas para interpretarlos.

Por qué hace falta el esquema de la ABI

La codificación ABI no se describe a sí misma. Para descodificarla hace falta un esquema que aporte la estructura y los tipos correspondientes. Una ABI adecuada del contrato proporciona esa información, aunque un archivo JSON concreto no es la única forma de representar los tipos necesarios.

Es fácil pasar por alto este requisito cuando una herramienta muestra un nombre de función conocido. La documentación de transacciones de Ethereum ilustra la ambigüedad de los selectores con 0xa9059cbb: señala varias funciones candidatas conocidas para ese selector. El ejemplo respalda la posibilidad de que existan varias candidatas. No demuestra que todos los selectores tengan varias coincidencias conocidas.

Por tanto, una búsqueda por selector debe seguir tratándose como una identificación de candidatas mientras no se haya establecido la interfaz del contrato de destino. En un ejercicio de lectura, anota de dónde procede el esquema. Si se ha supuesto para el ejemplo, mantén visible esa suposición. Si se desconoce, deja pendiente la interpretación de los tipos, sin presentar una etiqueta plausible como un dato demostrado.

Un argumento hipotético que se descodifica como 6

Considera una ficha de ejercicio hipotética con tres supuestos explícitos: la entrada contiene datos de llamada ABI estándar correctamente formados, el selector corresponde a la función prevista y esa función tiene exactamente un argumento uint256. Después de los cuatro bytes del selector, la ficha proporciona esta palabra de argumento:

0000000000000000000000000000000000000000000000000000000000000006

La palabra contiene 31 bytes de valor cero seguidos de 06. La codificación ABI estándar representa los enteros sin signo admitidos mediante valores de 32 bytes en orden big-endian, con relleno de bytes cero a la izquierda. Esta regla se aplica a esos tipos de enteros en la codificación ABI estándar; no es una regla general para descodificar cualquier tipo de dato o entrada arbitraria.

La deducción depende de los supuestos: el esquema del ejercicio identifica la palabra posterior al selector como un uint256 y, al aplicar la regla de codificación de enteros sin signo, se obtiene el entero 6. La posición del argumento, el esquema de tipos y la regla de codificación respaldan conjuntamente esa interpretación.

Ahora elimina el esquema de la ficha. Como la codificación no se describe a sí misma, la palabra terminada en 06 no basta por sí sola para establecer que su tipo sea uint256. Otras pruebas sobre la interfaz podrían establecerlo, pero los bytes por sí solos no aportan esa premisa. El ejemplo tampoco demuestra que se haya producido o ejecutado ninguna transacción real.

Qué permite concluir una entrada vacía

Una entrada vacía contiene cero bytes, mientras que un selector ABI estándar ocupa cuatro. A partir de esas dos premisas, podemos deducir que una entrada vacía no contiene ningún selector de función ABI estándar. Es una conclusión sobre los bytes disponibles. No determina el tipo de cuenta de destino.

Por separado, la documentación de Ethereum describe una transacción dirigida a una cuenta de contrato como una transacción que ejecuta código del contrato. Esa afirmación general no identifica una vía de ejecución concreta ni demuestra que la ejecución tenga éxito.

Para un segundo ejercicio hipotético, supongamos que el destino es una cuenta de contrato y que la entrada está vacía. La ausencia de bytes de selector y la regla sobre el destino contractual pueden darse a la vez. La deducción es limitada: que no haya selector no implica que no se ejecute código del contrato. Determinar la vía concreta de ejecución o su resultado requeriría más pruebas.

Lista de comprobación para leer los datos de entrada

  1. Identifica la representación. Lee el campo como una secuencia de bytes DATA y distingue su formato del de las cantidades numéricas.
  2. Comprueba si está vacío. Registra 0x como cero bytes de entrada, sin atribuirle un tipo de destino ni un resultado de ejecución.
  3. Establece la convención de codificación. Aplica la estructura de selector y argumentos solo cuando corresponda suponer una llamada a función con codificación ABI estándar.
  4. Anota el esquema. Identifica las pruebas sobre la interfaz o indica claramente los tipos de parámetros supuestos en el ejemplo.
  5. Mantén las reservas sobre los nombres candidatos. Una búsqueda por selector puede ser ambigua y no establece por sí sola la interfaz del destino.
  6. Explica cada deducción. Relaciona el valor descodificado con su posición, su esquema de tipos y su regla de codificación.

Utiliza estas preguntas para interpretar los bytes con precisión. La lista organiza la lectura; no certifica la seguridad de un contrato. La atribución de fuentes identifica la base de la explicación, sin demostrar la seguridad de una interacción concreta.

Este artículo educativo se ha elaborado con asistencia de IA. Sus ejemplos hipotéticos explican la descodificación de datos de entrada y no recomiendan ninguna actuación financiera.

Lectura relacionada: ABI de un contrato de Ethereum: cómo interpretar su interfaz.