Una Incompatibilidad en los Contratos Inteligentes de Ethereum Amenaza el Ecosistema dApp
Autor: Yu Guo, Fundador de Laboratorios SECBIT.
Hay una gran cantidad de contratos inteligentes siendo desplegados en la plataforma Ethereum los cuales han funcionado sin problema por meses, pero el equipo de SECBIT ha encontrado mas de 1995 contratos con problemas de incompatibilidad lo que podría hacer que las transacciones en las plataformas de cambio de criptomonedas sean revertidas (ver Referencias #3 y #4), funciones a las cuales les falta el valor de retorno como transfer(), transferFrom() o approve(). Este es el link a un ¨issue¨ hecho en GitHub: https://github.com/ethereum/solidity/issues/4116. Se puede verificar facilmente la incompatibilidad luego de actualizar a la version 0.4.22. Si no se maneja bien, intercambios involucrando a estos contratos serán revertidos, causando que miles de millones de dólares se encontrasen atascados en ellos. Sin embargo, muchas personas no están al tanto de este problema.
Tokens ERC20 Afectadas
De acuerdo a las estadisticas del equipo SECBIT, un total de 1995 contratos sufren de incompatibilidad en su código fuente con la versión 0.4.22 de Solidity, aquí hay una lista de tokens en el top 100 de capitalización de mercado las cuales sufren de esta incompatibilidad:
Todas las estadísticas fueron recolectadas el 8 de Junio, 2018 del sitio web coinmarketcap. El equipo de SECBIT publicó una lista con todos los contratos incompatibles. Este cuadro solo esta destinado a ser usado como referencia por los desarrolladores, por favor fíjense en todos los contratos inteligentes y eviten la incompatibilidad, especialmente si está trabajando con alguno de ellos.
Equipo de Desarrollo y Plantilla Oficial
No podemos simplemente culpar a los equipos de desarrollo del contrato por el problema de incompatibilidad, debido a que ellos siguen la orientación oficial estrictamente. Luego de inspeccionar cuidadosamente muestras de los contratos incompatibles, el equipo SECBIT descubrió que la mayoría de los contratos seguían dos ejemplos oficiales: OpenZeppelin (292 contratos) y and Sitio web oficial de Ethereum (1703 contratos). Resultó que OpenZeppelin tenía este problema desde un ¨commit¨ el 21 de Marzo, 2017 hasta que fue solucionado el 13 de Julio, 2017. on March 21, 2017 until they patched it in a commit on July 13, 2017. El repositorio GitHub de Ethereum no lo solucionó hasta el 7 de junio, 2018. U miembro del equipo SECBIT realizó un ¨pull request¨y la misma fue aceptada. Si accidentalmente siguió estos ejemplos oficiales entre los periodos antes mencionados, por favor revise sus funciones tan pronto le sea posible.
Especificación EIP20
¿Por qué las versiones oficiales no son compatibles con el compilador de Solidity luego de ser actualizadas? El equipo SECBIT encontró que las especificaciones EIP20 no indican nada sobre los valores de retorno, causando de esta forma confusiones y contradicciones. Ahora hay 3 casos de transfer()
:
- Retornar
false
al fallar revert
al fallar- Aunque
transfer()
tenga éxito, revertir debido a un valor de retorno faltante
Si esta confusión permanece sin ser solucionada, los desarrolladores no podrán establecer un estándar para la creación de contratos inteligentes y los contratos no inteligentes traerán muchos problemas al ser llamados de forma externa. ¿Podemos señalar al EIP20 como responsable? La respuesta es no. Realmente, debates acerca de cuál debe ser el valor de retorno de transfer()
empezaron hace muchos años. Algunas personas votaron por retornar falso mientras que otras pensaron que revertir es mejor. Hasta ahora, ninguna conclusión ha sido hecha en las especificaciones EIP20 pero revertir se ha vuelto mas común hoy en día.
Compilador de Solidity y Máquina Virtual de Ethereum (EVM)
Dejando a un lado esta discusión un miembro de nuestro equipo de desarrollo, Christian Reitwiessner piensa que hay muy buenas razones para revisar el valor de retorno, hasta podría llevar a nuevas incompatibilidades. Observe una función de ensamblaje en la EVM: call(g, a, v, in, insize, out, outsize)
, todas los contratos en las direcciones a
con la entrada mem[in..(in+insize)]
proveyendo g
gas y v
wei, área de sálidamem[out..(out+outsize)]
returnando 0 en error (por ejemplo cuando no hay gas) y 1 cuando tiene éxito.
Después de Solidity 0.4.22, la introducción de opcode returndatasize
habilitó la comprobación del valor de retorno leído de la función, en lugar de datos basura de una dirección de memoria específica. Sin la sentencia de retorno para pasar la verificación returndatasize
, las funciones arrojarían una excepción, revirtiendo así todas las transacciones en el contrato: no hay un valor de retorno, por lo tanto, returndatasize
es cero, más pequeño que un booleano —true
ofalse
.
Esta verificación, generalmente, es útil, porque ahora tenemos medios para verificar los datos devueltos que queremos en primer lugar, no algunos datos aleatorios sin sentido. De hecho, el opcode fue propuesto hace mucho tiempo — el Hard Fork de Byzantium, octubre de 2017. Ethereum tomó muchas ideas del EIP, incluyendo revert
y returndatasize
en EVM. Finalmente, el opcode entra en efecto en Solidity 0.4.22. Con esta mejora, podemos evitar el problema DAPP mencionado anteriormente, por lo que adoptar la actualización es una buena práctica, con un pequeño sacrificio de tiempo para solucionar la incompatibilidad.
Soluciones Propuestas
El equipo SECBIT tiene varios consejos para evitar la incompatibilidad:
- Volver a implementar el contrato.
- Los desarrolladores de DAPP utilizan una función de llamada segura para acceder a los contratos Tokens incompatibles.
- El hard fork de Ethereum.
Gracias al artículo de Lukas Cremer. Aunque el primer método parece estar bien, se necesita mucho trabajo para volver a implementar miles de contratos. La tercera forma es teóricamente práctica, mientras que todos son relevantes y mucha discusión está por llegar. El equipo SECBIT ofrece ahora la solución más racional — la segunda y, ya hemos publicado el código en GitHub: https://github.com/sec-bit/badERC20Fix/blob/master/badERC20Fix.sol, aquí hay una captura de pantalla de nuestra solución transfer()
():
Gracias a la solución de Brendan Chou, el equipo SECBIT completó el programa, refiriéndose a parte de su código. Además, actualizamos el código con algunas mejoras menores:
- Obtener la firma de la función utilizando
keccak256()
— el código de Brendan Chou utilizó una interfaz sin valor de retorno:function transfer(address to, uint256 value) external
. Esto trae otro problema de compatibilidad de ‘no retorno’, por lo que cambiamos el método. - Agregar la solución del valor de retorno perdido en
transferFrom()
. - Agregar la solución del valor de retorno perdido en
approve()
.
Conclusión
Ethereum aún está en crecimiento robusto, por lo que podemos enfrentar muchos cambios en el futuro. La incompatibilidad del valor de retorno tiene una larga historia y todos deberían trabajar juntos para una mejor comunicación. Sin embargo, ha pasado alrededor de un mes desde que surgió el problema; muchos desarrolladores DAPP y de contratos inteligentes aún no lo saben. El laboratorio SECBIT sostiene que la comunidad Ethereum debe construir lazos más fuertes en tecnología y mejorar la conciencia de seguridad.
Referencias
- Repositorio oficial de Ethereum en GitHub: https://github.com/ethereum/ethereum-org/blob/master/solidity/token-advanced.sol#L85
- Especificación EIP20: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
- Explicando las reversiones inesperadas empezando con Solidity 0.4.22: https://medium.com/@chris_77367/explaining-unexpected-reverts-starting-with-solidity-0-4-22-3ada6e82308c
- Bug de valor de retorno perdido — al menos 130 Tokens afectadas: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
- Issue en el GitHub de Solidity: https://github.com/ethereum/solidity/issues/4116
- Página oficial de Ethereum: https://ethereum.org/token
- Código de Brendan Chou: https://gist.github.com/BrendanChou/88a2eeb80947ff00bcf58ffdafeaeb61
Feedback
Para mayor información, por favor contáctanos a través de info@secbit.io.
Traducido por Carlos B
Para acceder al artículo original, visítanos en la página oficial de blogs en Inglés de Loopring. Para información al día síguenos en nuestras redes:
⭑ Twitter: twitter.com/loopringorg
⭑ Reddit: reddit.com/r/loopringorg
⭑ Telegram: t.me/loopring_es
⭑ Email: foundation@loopring.org