Vista previa gratuita10 min de lectura
Reentrancia: el truco más viejo que sigue funcionando
Un contrato que envía dinero antes de actualizar sus propios registros puede recibir el pedido de ese mismo dinero otra vez, en medio del envío. Eso es la reentrancia, la falla detrás del primer desastre famoso en la historia de Ethereum, y sigue encontrando víctimas.
El mecanismo
- Un contrato guarda los saldos de quienes depositan y ofrece una función de retiro.
- El retiro envía los fondos primero y después pone en cero el saldo de quien depositó.
- Enviar fondos a otro contrato le entrega el control al código de ese contrato.
- Ese código llama de nuevo a la función de retiro. El saldo todavía no se puso en cero, así que la comprobación pasa una segunda vez.
- El ciclo se repite hasta que el pool queda vacío.
El arreglo es una regla de orden conocida como comprobaciones, efectos, interacciones: valida primero, actualiza tu propio estado segundo, y habla con cualquier otro al final. Un guard de reentrancia, un candado que rechaza una segunda entrada mientras la primera llamada está abierta, es el cinturón que acompaña a esos tirantes.
Dónde ha pasado
- The DAO, junio de 2016. Una falla de reentrancia drenó alrededor de 3.6 millones de ETH de un fondo de inversión construido sobre Ethereum. La respuesta partió la cadena: la mayoría hizo un fork para revertir el robo, y la cadena que se negó sigue hoy como Ethereum Classic.
- Curve Finance, julio de 2023. La falla no estaba en la lógica propia de Curve sino en versiones específicas del compilador Vyper, donde el guard de reentrancia no funcionaba como estaba escrito. Varios pools fueron drenados, con pérdidas reportadas de alrededor de 70 millones de dólares. El código auditado todavía puede quedar deshecho por las herramientas que lo compilan.
Por qué sobrevive
- Cualquier llamada externa puede entregar el control: hooks de transferencia de tokens, callbacks, y estándares de tokens más nuevos que avisan a quien recibe.
- La reentrancia de solo lectura es más sutil. Quien ataca vuelve a entrar en una función de lectura mientras el estado está actualizado a medias, así que otro protocolo lee un precio o un saldo equivocado y actúa sobre eso.
- La reentrancia entre funciones usa una función distinta que comparte el mismo estado, así que un guard en una sola función no alcanza.
Qué lo previene de verdad
- Comprobaciones, efectos, interacciones, aplicado en todas partes y no solo en el retiro obvio.
- Guards de reentrancia en cada punto de entrada que cambia el estado y hace llamadas externas.
- Tratar cada token como código hostil, porque un contrato de token ejecuta su propia lógica durante una transferencia.
- Pruebas que llaman de vuelta al contrato desde un receptor malicioso, que es la forma de encontrar esto antes del lanzamiento.
Esto es educación, no asesoría financiera. Nada de lo que está aquí es una recomendación para usar o evitar ningún protocolo.