Une incompatibilité dans Ethereum Smart Contract Threatening dApp Ecosystem

Shuang Zhao
Protocole Loopring
Published in
6 min readJun 17, 2018

Auteur : Yu Guo, fondateur du SECBIT Lab.

Il y a beaucoup de contrats intelligents déployés sur la plateforme Ethereum et ils fonctionnent bien depuis des mois, mais l’équipe de la SECBIT a constaté que plus de 1995 contrats sont confrontés à une incompatibilité qui pourrait entraîner le retour en arrière des transactions sur les bourses (voir les références #3 et #4) — valeur de retour manquante dans des fonctions comme transfer(), transferFrom() ou approve(). Il y a un lien problématique sur GitHub : https://github.com/ethereum/solidity/issues/4116. On pourrait facilement vérifier l’incompatibilité après la mise à jour de la version 0.4.22 de Solidity. Si elles n’étaient pas bien gérées, les transactions impliquant ces contrats reviendraient, causant des milliards de dollars bloqués dans les contrats. Cependant, beaucoup de gens ne sont pas du tout au courant de ce problem.

Jetons ERC20 affectés

Selon les statistiques de l’équipe de SECBIT, un total de 1995 contrats avec code source n’est pas compatible avec Solidity 0.4.22, voici une liste des fameux jetons classés Top100 en capitalisation boursière qui est incompatible :

jeton incompatible

Toutes les statistiques sont collectées le 08 juin 2018 de coinmarketcap. L’équipe SECBIT a publié une liste de tous les contrats incompatibles. Cette table est uniquement destinée à la référence des développeurs DAPP, veuillez faire attention à tous les contrats intelligents listés et faire de votre mieux pour éviter l’incompatibilité, surtout si vous travaillez avec l’un d’entre eux.

Équipe en développement et modèle officiel

Nous ne pouvons pas simplement blâmer l’équipe de développement du contrat pour le problem incompatible, puisqu’ils ont strictement suivi les directives officielles. Après avoir soigneusement inspecté des échantillons de contrats incompatibles, l’équipe de SECBIT a découvert qu’une grande masse d’entre eux suivait deux exemples officiels : OpenZeppelin (292 d’entre eux) et le site officiel d’Ethereum (1703 d’entre eux). Il s’est avéré qu’OpenZeppelin avait ce problème depuis un commit le 21 mars 2017 jusqu’à ce qu’ils le patchent dans un commit le 13 juillet 2017. Le dépôt Ethereum GitHub ne l’a pas corrigé avant le 7 juin 2018 : Un membre de l’équipe SECBIT a ouvert une demande de retrait et a fusionné. Si vous avez suivi accidentellement deux exemples de codes officiels pendant cette période, veuillez vérifier vos fonctions dès que possible.

Spécifications EIP20

Alors pourquoi les versions officielles ne sont pas compatibles avec le compilateur Solidity après la mise à jour ? L’équipe SECBIT a constaté que la spécification EIP20 n’indique rien au sujet de la valeur de retour, ce qui entraîne confusion et contradiction. Maintenant il y a 3 cas de transfer() :

  1. retourne false en cas d’échec
  2. revert en cas d’échec
  3. bien que le transfer() ait réussi, le retour est dû à une valeur de retour manquante.

Si cette confusion n’est pas résolue, les développeurs n’ont pas pu établir une spécification de contrat intelligent standard et les contrats intelligents non standard apporteraient de plus en plus d’ennuis lorsqu’ils sont appelés de l’extérieur. Pouvons-nous affirmer que EIP20 est entièrement responsable ? La réponse est non. En fait, les débats sur la valeur à transfer() return pourrait être daté d’il y a des années. Certaines personnes votent pour le retour de faux alors que d’autres pensent qu’il vaut mieux revenir en arrière. Jusqu’à présent, aucune conclusion n’a été tirée dans les spécifications EIP20, mais le retour en arrière est plus courant aujourd’hui.

Solidity Compiler et EVM

En dehors de cette discussion, un membre de l’équipe de développement de Solidity, Christian Reitwiessner pense qu’il y a de bonnes raisons à vérifier la valeur de retour, même si cela peut conduire à des incompatibilités. Jetez un coup d’oeil à une fonction d’assemblage EVM : call(g, a, v, in, insize, out, outsize)-contrat d’appel à l’adresse a avec entrée mem[in..(in+insize)] fournissant gaz g et vwei, zone de sortie mem[out..(out+outsize)]renvoie 0 en cas d’erreur (p.ex. plus d’essence) et 1 en cas de succès.

Avant Solidity 0.4.22, le contrat intermédiaire de l’échange s’appelait un contrat avec l’adresse a. L’adresse de l’entrée et de la sortie ont été réglées de la même manière afin d’économiser du gaz. Si la fonction appelée n’a pas de valeur de retour, l’appelant lisait l’entrée car il n’y avait pas de sortie couvrant l’entrée, ainsi le mem[out] contiendrait quelques données de déchets, ce qui pourrait être une valeur non nulle pour true in Solidity. C’est pourquoi transfer() semble normal dans les versions précédentes de Solidity. De plus, si les données des ordures sont nulles, cela signifie qu’elles sont falsealors que le contrat symbolique est transféré avec succès. Le DAPP externe reçoit un faux résultat et le considère comme un échec de transfert, causant de nouvelles vulnérabilités.

Après Solidity 0.4.22, l’introduction de l’opcode returndatasize a permis de vérifier la valeur de retour lue à partir de la fonction plutôt que les données de déchets à partir d’une adresse mémoire spécifique. Sans une phrase de retour pour passer le contrôle returndatasize, les fonctions lancerait une exception et inverserait ainsi toutes les transactions dans le contrat : il n’y a pas de valeur de retour, donc le returndatasize est nul, plus petit qu’un booléen — true ou false.

Cette vérification est généralement utile, parce que nous avons maintenant les moyens de vérifier les données retournées que nous voulons en premier lieu, et non des données aléatoires insignifiantes. En fait, l’opcode a été proposé il y a longtemps — le Byzantium Hard Fork, octobre 2017. Ethereum a pris beaucoup d’idées de l’EIP, y compris revert et returndatasize dans EVM. Enfin, l’opcode entre en vigueur dans Solidity 0.4.22. Par cette amélioration, nous pourrions éviter le problème de DAPP mentionné ci-dessus, donc embrasser la mise à jour est une grande pratique, avec un petit sacrifice de temps pour réparer l’incompatibilité.

Solutions proposées

L’équipe SECBIT a quelques conseils pour éviter l’incompatibilité :

  1. Redéployer le contrat
  2. Les développeurs DAPP utilisent une fonction d’appel sécurisé pour accéder aux contrats de Jetons incompatibles.
  3. Fourchette dure Ethereum

Merci à l’article de Lukas Cremer. Bien que la première méthode semble satisfaisante, il reste beaucoup de travail à faire pour redéployer des milliers de contrats. La troisième moyen est théoriquement pratique, alors que tout le monde est pertinent et que beaucoup de discussions sont encore à venir. L’équipe SECBIT offre maintenant une solution plus rationnelle — la seconde et nous avons déjà publié le code sur GitHub : https://github.com/sec-bit/badERC20Fix/blob/master/badERC20Fix.sol, voici un extrait de notre solution de transfer():

Merci à la solution de Brendan Chou, l’équipe de SECBIT a complété le programme en se référant à une partie de son code. De plus, nous avons mis à jour le code avec quelques améliorations mineures :

  1. Obtenir la signature de la fonction en utilisant keccak256() — Le code de Brendan Chou a utilisé une interface sans valeur de retour : function transfer(address to, uint256 value) external. Ceci apporte un autre problème de compatibilité’no return’, donc nous avons changé la méthode.
  2. Ajouter la solution pour la valeur de retour manquante dans transferFrom().
  3. Ajouter la solution pour la valeur de retour manquante dans approve().

Conclusion

Ethereum n’a pas encore pris de l’ampleur, de sorte que nous pourrions être confrontés à de nombreux changements à l’avenir. L’incompatibilité de la valeur de retour a une longue histoire, et tout le monde devrait travailler ensemble pour une meilleure communication. Cependant, il y a environ un mois depuis que la question a été soulevée, beaucoup de DAPP et de développeurs de contrats intelligents ne sont toujours pas au courant. SECBIT Lab est d’avis que la communauté d’Ethereum devrait créer des liens plus forts dans le domaine de la technologie et améliorer la conscience de la sécurité.

Référence

  1. Ethereum official GitHub repository: https://github.com/ethereum/ethereum-org/blob/master/solidity/token-advanced.sol#L85
  2. EIP20 specification: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
  3. Explaining Unexpected Reverts Starting with Solidity 0.4.22: https://medium.com/@chris_77367/explaining-unexpected-reverts-starting-with-solidity-0-4-22-3ada6e82308c
  4. Missing Return Value Bug — At Least 130 Tokens Affected: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
  5. Solidity’s GitHub Issue: https://github.com/ethereum/solidity/issues/4116
  6. Ethereum Official Website: https://ethereum.org/token
  7. Brendan Chou’s Code: https://gist.github.com/BrendanChou/88a2eeb80947ff00bcf58ffdafeaeb61

Feedback

Pour plus d’informations, veuillez contacter info@secbit.io.

Pour plus d’informations à jour, rejoignez-nous sur les médias sociaux: ⭑Twitter: twitter.com/@loopringorg
⭑ Reddit: reddit.com/r/loopringorg
⭑ Telegram: t.me/loopring_en
⭑ Telegram: t.me/loopring_fr(Français)

--

--