Ethereum ofrece dos familias distintas de RPC para el rastreo de ejecución: el espacio de nombres 'trace' (trace_transaction, trace_call, trace_block) y el espacio de nombres 'debug' (debug_traceTransaction, debug_traceCall). Se diferencian en el formato de salida, la selección de trazadores y el soporte del cliente. Elige trace_transaction para rastreos estructurados al estilo OpenEthereum con razones de reversión integradas y diferencias de estado; elige debug_traceTransaction con callTracer para una salida flexible basada en trazadores. Ambos re-ejecutan la EVM contra el estado histórico, lo que los hace mucho más pesados que eth_call. La disponibilidad y el costo varían según el proveedor, así que siempre verifica en tu endpoint.
Respuesta Directa: ¿Qué RPC de Rastreo Deberías Usar?
Cuando necesitas entender qué sucedió dentro de una transacción minada en Ethereum—por qué se revirtió, qué llamadas internas se hicieron, cómo se consumió el gas—tienes dos familias principales de RPC: los métodos trace_* (del módulo de rastreo de OpenEthereum/Parity, ahora soportados por varios clientes) y los métodos debug_* (del espacio de nombres debug de Geth). La respuesta corta: usa trace_transaction cuando quieras un rastreo estructurado y definido con un formato consistente (acción/resultado) y razones de reversión integradas; usa debug_traceTransaction con un trazador como callTracer cuando necesites flexibilidad en el formato de salida o acceso a detalles a nivel de opcode. Para simular una transacción aún no enviada, usa trace_call (con anulaciones de estado opcionales) o debug_traceCall. Ambas familias re-ejecutan la EVM, por lo que son computacionalmente costosas y a menudo restringidas por los proveedores. Siempre verifica qué métodos soporta tu endpoint antes de construir un pipeline.
Este artículo explica el mecanismo detrás de ambas familias, compara sus salidas y proporciona un script reproducible en Node.js para probar tu propio endpoint. Para una visión más amplia de los RPC de Ethereum, consulta el centro de aprendizaje de OnFinality y la página de la red Ethereum.
Cómo Funciona el Rastreo de EVM Internamente
Tanto trace_transaction como debug_traceTransaction funcionan re-ejecutando la transacción objetivo en una instancia de EVM contra un estado específico. Para una transacción minada, ese estado es el estado histórico en el bloque donde se incluyó la transacción—esto requiere un nodo de archivo o un nodo con suficiente estado histórico. Para trace_call y debug_traceCall, el estado es la cabeza actual (o un bloque especificado) más anulaciones de estado opcionales que te permiten modificar saldos, código o almacenamiento antes de la ejecución.
Durante la re-ejecución, la EVM registra cada opcode ejecutado, cada llamada de mensaje interno, creación de contratos y cambio de estado. La diferencia radica en cómo se empaquetan esos datos brutos. El espacio de nombres debug_* devuelve la salida de un trazador—un fragmento de código que observa la ejecución de la EVM. El trazador predeterminado es el registrador de estructura, que produce un registro verboso opcode por opcode. Más comúnmente, especificas callTracer para obtener un árbol de llamadas estructurado, o prestateTracer para capturar el estado previo a la ejecución. El espacio de nombres trace_*, por otro lado, tiene un esquema de salida fijo definido por la especificación de rastreo de OpenEthereum: cada rastreo es un objeto con action, result, subtraces y traceAddress, que cubre operaciones de llamada, creación y suicidio. También ofrece métodos como trace_filter para recuperar rastreos por dirección o rango de bloques, e incluye revertReason y stateDiff en algunas implementaciones.
Debido a que el rastreo re-ejecuta la EVM, es significativamente más pesado que un simple eth_call. El costo exacto depende de la complejidad de la transacción y del trazador utilizado. Muchos proveedores deshabilitan el rastreo por defecto o aplican límites de velocidad estrictos y tiempos de espera más largos. Siempre consulta la documentación de tu proveedor—por ejemplo, las páginas de precios de RPC y servicio de API describen los niveles típicos. Para una inmersión más profunda en las anulaciones de estado, consulta nuestra guía sobre anulaciones de estado y simulación en eth_call.
Comparando las Salidas de trace_transaction y debug_traceTransaction
Para ilustrar la diferencia, considera una transacción simple que transfiere ETH y llama a un contrato. trace_transaction devuelve un array de objetos de rastreo, cada uno con un type (call, create, suicide), action (from, to, value, gas, input) y result (output, gasUsed). También incluye traceAddress para representar el árbol de llamadas. Aquí hay un ejemplo simplificado:
En contraste, debug_traceTransaction con callTracer devuelve un objeto JSON anidado que representa el árbol de llamadas, con campos como type, from, to, value, gas, gasUsed, input, output y calls (un array de llamadas hijas). No incluye traceAddress porque el anidamiento en sí codifica la estructura. Ejemplo:
El espacio de nombres trace_* también ofrece trace_filter para consultar rastreos por dirección o rango de bloques, lo cual es útil para indexadores. El espacio de nombres debug_* no tiene un filtro directo; debes rastrear bloques o transacciones individuales. Para una comparación completa del soporte de clientes, consulta la documentación oficial de la API debug de Geth y la documentación de la API trace de Erigon. La disponibilidad varía según el cliente y el endpoint, así que siempre verifica.
[
{
"action": {
"callType": "call",
"from": "0x...",
"gas": "0x7a120",
"input": "0x...",
"to": "0x...",
"value": "0x0"
},
"result": {
"gasUsed": "0x5208",
"output": "0x"
},
"subtraces": 1,
"traceAddress": [],
"type": "call"
},
{
"action": {
"callType": "call",
"from": "0x...",
"gas": "0x...",
"input": "0x...",
"to": "0x...",
"value": "0x0"
},
"result": {
"gasUsed": "0x...",
"output": "0x..."
},
"subtraces": 0,
"traceAddress": [0],
"type": "call"
}
]
{
"type": "CALL",
"from": "0x...",
"to": "0x...",
"value": "0x0",
"gas": "0x7a120",
"gasUsed": "0x5208",
"input": "0x...",
"output": "0x",
"calls": [
{
"type": "CALL",
"from": "0x...",
"to": "0x...",
"value": "0x0",
"gas": "0x...",
"gasUsed": "0x...",
"input": "0x...",
"output": "0x..."
}
]
}Cuándo Usar trace_call vs debug_traceCall
trace_call y debug_traceCall se utilizan para simular una transacción sin enviarla a la red. Aceptan un objeto de transacción (from, to, gas, gasPrice, value, data) y un número de bloque o etiqueta opcional. La ventaja clave de trace_call es que devuelve un rastreo estructurado similar a trace_transaction, y soporta anulaciones de estado a través del parámetro stateOverrides (en algunas implementaciones). Esto es ideal para simular una interacción con un contrato para estimar gas, verificar reversiones o inspeccionar llamadas internas antes de transmitir.
debug_traceCall es el equivalente en el espacio de nombres debug. También acepta un objeto de transacción y un argumento de trazador. Con callTracer, devuelve la misma estructura de árbol de llamadas que debug_traceTransaction. La elección entre ellos a menudo se reduce a qué espacio de nombres soporta tu proveedor. Algunos proveedores solo exponen uno. Por ejemplo, si estás usando un nodo Geth, debug_traceCall está disponible; si estás usando un endpoint compatible con OpenEthereum, trace_call es el camino a seguir.
Al simular, también puedes usar eth_call con anulaciones de estado, pero eso solo devuelve la salida o la razón de reversión, no el árbol de llamadas interno. Para una guía detallada sobre anulaciones de estado, consulta anulaciones de estado y simulación en eth_call.
Ejemplo Reproducible: Probando tu Endpoint con Node.js
El siguiente script en Node.js te permite probar qué métodos de rastreo soporta tu endpoint RPC y comparar las salidas de trace_transaction y debug_traceTransaction en una transacción real. También ejecuta una simulación de trace_call con una anulación de estado. Necesitarás un entorno Node.js con la biblioteca axios instalada (npm install axios). Reemplaza YOUR_RPC_URL con la URL de tu endpoint y, opcionalmente, proporciona un hash de transacción y una dirección de contrato para la prueba de anulación de estado.
El script realiza tres solicitudes: (1) trace_transaction en un hash dado, (2) debug_traceTransaction con callTracer en el mismo hash, y (3) trace_call con una transferencia simple a una dirección de contrato, usando una anulación de estado para establecer el saldo del contrato. Imprime los resultados y un resumen de qué métodos tuvieron éxito. Ten en cuenta que si un método no es soportado, el nodo devolverá un error; el script lo captura y lo informa.
Ejecuta el script y completa la tabla de resultados a continuación. Esto te ayudará a entender qué soporta tu endpoint y la forma de las respuestas.
const axios = require('axios');
const RPC_URL = 'YOUR_RPC_URL'; // p. ej., https://mainnet.example.com
const TX_HASH = '0x...'; // reemplaza con un hash de transacción real
const CONTRACT_ADDRESS = '0x...'; // reemplaza con una dirección de contrato para la anulación de estado
async function rpcCall(method, params) {
const response = await axios.post(RPC_URL, {
jsonrpc: '2.0',
id: 1,
method,
params
});
if (response.data.error) {
throw new Error(response.data.error.message);
}
return response.data.result;
}
async function main() {
// 1. trace_transaction
try {
const trace = await rpcCall('trace_transaction', [TX_HASH]);
console.log('trace_transaction tuvo éxito. Número de rastreos:', trace.length);
console.log('Primer tipo de rastreo:', trace[0]?.type);
} catch (e) {
console.log('trace_transaction falló:', e.message);
}
// 2. debug_traceTransaction con callTracer
try {
const debug = await rpcCall('debug_traceTransaction', [TX_HASH, { tracer: 'callTracer' }]);
console.log('debug_traceTransaction tuvo éxito. Tipo de nivel superior:', debug.type);
console.log('Número de llamadas:', debug.calls ? debug.calls.length : 0);
} catch (e) {
console.log('debug_traceTransaction falló:', e.message);
}
// 3. trace_call con anulación de estado
try {
const tx = {
from: '0x0000000000000000000000000000000000000000',
to: CONTRACT_ADDRESS,
value: '0x0',
data: '0x' // cambia a una llamada de función si es necesario
};
const overrides = {
[CONTRACT_ADDRESS]: {
balance: '0xde0b6b3a7640000' // 1 ETH
}
};
const result = await rpcCall('trace_call', [tx, ['trace'], overrides]);
console.log('trace_call tuvo éxito. Salida:', result.output);
console.log('Gas usado:', result.gasUsed);
} catch (e) {
console.log('trace_call falló:', e.message);
}
}
main();Tabla de Resultados: Completa el Comportamiento de tu Endpoint
Usa esta tabla para registrar el resultado de cada método en tu endpoint objetivo. Esta es una herramienta de diagnóstico, no un punto de referencia.
- trace_transaction – ¿Soportado? (Sí/No) – Mensaje de error si lo hay – Número de rastreos devueltos – Primer tipo de rastreo
- debug_traceTransaction (callTracer) – ¿Soportado? (Sí/No) – Mensaje de error si lo hay – Tipo de nivel superior – Número de llamadas hijas
- trace_call (con anulación de estado) – ¿Soportado? (Sí/No) – Mensaje de error si lo hay – Salida (primeros 10 bytes) – Gas usado
- Notas – Cualquier límite de velocidad, tiempo de espera o requisitos especiales observados
Solución de Problemas Comunes en el Rastreo
Cuando el rastreo falla, el mensaje de error a menudo señala la causa raíz. Aquí hay problemas comunes y cómo solucionarlos:
- Método no encontrado – El endpoint no soporta el espacio de nombres
trace_*odebug_*. Consulta la documentación de tu proveedor o cambia a un nodo que lo soporte. Algunos proveedores ofrecen endpoints separados para el rastreo. - Estado histórico no disponible – Rastrear una transacción antigua requiere datos de nodo de archivo. Si obtienes un error como 'nodo trie faltante' o 'encabezado no encontrado', tu nodo puede ser un nodo completo sin datos de archivo. Usa un endpoint de archivo o un proveedor que ofrezca rastreos históricos.
- Tiempo de espera agotado – El rastreo es lento. Si tu solicitud agota el tiempo, intenta con un trazador más específico (por ejemplo,
callTraceren lugar del registrador de estructura predeterminado), o usa un proveedor con tiempos de espera más largos. Consulta nuestra guía sobre tiempos de espera y reintentos de RPC en Ethereum. - Límite de velocidad – Los proveedores a menudo limitan la velocidad de los métodos de rastreo de manera más agresiva. Si alcanzas los límites de velocidad, considera agrupar solicitudes o usar un endpoint de rastreo dedicado. Consulta precios de RPC para conocer los límites típicos.
- Nombre de trazador inválido – Al usar
debug_traceTransaction, asegúrate de que el nombre del trazador sea soportado por tu cliente. Los trazadores comunes incluyencallTracer,prestateTracer,4byteTraceryopcodeLogger. Consulta la documentación de Geth para obtener una lista. - Formato de anulación de estado – En
trace_call, el parámetrostateOverridesdebe ser un objeto claveado por dirección, con cada valor que contenga campos opcionalesbalance,code,nonceostate. Un formato incorrecto puede causar errores.
Limitaciones y Compensaciones
El rastreo es una operación poderosa pero costosa. Puede consumir una cantidad significativa de CPU y E/S, especialmente para transacciones complejas o bloques grandes. Los proveedores a menudo deshabilitan el rastreo por defecto o lo ofrecen como una característica premium. Siempre consulta la documentación de tu endpoint específico. Por ejemplo, algunos proveedores solo soportan debug_traceTransaction en bloques recientes, mientras que otros requieren que uses un nodo de archivo dedicado.
El formato de salida también varía entre clientes. Mientras que el espacio de nombres trace_* busca consistencia con la especificación de OpenEthereum, hay diferencias sutiles en los nombres de los campos y la disponibilidad de revertReason o stateDiff. De manera similar, los trazadores debug_* pueden producir estructuras diferentes dependiendo de la versión del cliente. Siempre valida tu lógica de análisis contra las respuestas reales.
Para sistemas de producción, considera almacenar en caché los resultados de rastreo si necesitas reproducir la misma transacción varias veces. También ten en cuenta el tamaño de la carga útil: un rastreo para una transacción compleja puede tener varios megabytes, lo que puede afectar el rendimiento de la red. Usa filtros o trazadores que limiten la salida cuando sea posible.
Si estás construyendo un indexador o un pipeline de análisis, podrías preferir trace_filter para recuperar rastreos para una dirección o rango de bloques específico, pero este método no está disponible en todos los clientes. Para más información sobre monitoreo y salud del endpoint, consulta Monitoreo de endpoints RPC y salud del nodo.
Próximos Pasos y Lecturas Adicionales
Ahora que entiendes la diferencia entre los espacios de nombres trace y debug, puedes elegir el método adecuado para tu caso de uso. Para profundizar tu conocimiento, explora los siguientes recursos:
- Elegir un nodo RPC de Ethereum (Asistente de RPC) – Aprende a seleccionar un nodo que soporte el rastreo.
- Decodificar razones de reversión y errores personalizados – Usa el rastreo para extraer razones de reversión de transacciones fallidas.
- Anulaciones de estado y simulación en eth_call – Combina anulaciones de estado con rastreo para simulaciones potentes.
- Tiempos de espera y reintentos de RPC en Ethereum – Maneja solicitudes de rastreo lentas con elegancia.
- Monitoreo de endpoints RPC y salud del nodo – Vigila el rendimiento de tu endpoint.
Para referencias autorizadas, consulta la documentación de APIs de ejecución de Ethereum y la documentación del espacio de nombres debug de Geth. Si estás usando un proveedor, consulta siempre su documentación específica de rastreo, ya que el soporte y la salida pueden variar.