paint-brush
Comment perdre 13 $ des fonds des utilisateurs (en tant que développeur Blockchain)par@msokola
579 lectures
579 lectures

Comment perdre 13 $ des fonds des utilisateurs (en tant que développeur Blockchain)

par Matéush7m2022/12/20
Read on Terminal Reader

Trop long; Pour lire

Les types de données sont importants et les négliger entraîne de graves conséquences. Les langages modernes comme JavaScript et Python utilisent le "duck typing" pour déterminer le type de variable. Dans la plupart des cas, votre interprète de langue gérera les types de manière correcte. Le problème commence lorsque votre programme doit exécuter des équations (même simples) sur de grands nombres.
featured image - Comment perdre 13 $ des fonds des utilisateurs (en tant que développeur Blockchain)
Matéush HackerNoon profile picture
0-item

Le gouvernement dit que nous ne sommes pas en récession, mais en même temps, nous entendons parler de la montée en flèche de l'inflation, des augmentations des taux d'intérêt et des licenciements dans presque tous les secteurs de l'économie.


Bien que la crypto et TradFi soient les plus durement touchés, de nombreuses entreprises construisent encore leurs jetons, protocoles et produits DeFi. Êtes-vous un d'entre eux?


Aujourd'hui, je vais parler des types de données, et attendez. J'ai quelque chose de très important à dire. Vous pourriez me voir comme un professeur de plus de 60 ans du MIT torturant des étudiants avec des conférences sur des sujets qui n'ont plus d'importance. Mais ce n'est pas vrai.


Les types de données sont toujours importants et les négliger entraîne de graves conséquences. Je vais essayer de passer brièvement en revue tous les problèmes potentiels et de les résoudre afin que vous ne trouviez pas les 8 minutes que vous passez à lire cet article perdues.


Les langages modernes comme JavaScript et Python utilisent le "type de canard" pour déterminer le type de variable. Si nous attribuons ce genre de formule a = 2 + 2 à une variable, l'interpréteur de langage sait qu'il s'agit de nombres, et il effectuera des opérations mathématiques sur ce littéral.


Le typage canard peut s'expliquer par cette phrase : « Si ça marche comme un canard et ça cancane comme un canard, alors ça doit être un canard ». Lorsque vous regardez de plus près le sens de celui-ci - cela prend tout son sens. Si le littéral contient des lettres et des chiffres, il doit s'agir d'une chaîne, et c'est clair. Mais que se passe-t-il s'il y a des chiffres ?


Est-ce un boolean , un integer , un decimal , float ou une date . Dans la plupart des cas, votre interprète de langue gérera les types de la bonne manière. Le problème commence lorsque votre programme doit exécuter des équations (même simples) sur de grands nombres.


« S'il marche comme un canard et qu'il cancane comme un canard, alors ce doit être un canard », n'est-ce pas ? En fait, ce n'est pas le cas.


Dénominations Ethereum en bref

Dans les paragraphes suivants, je fais référence aux dénominations communes d'Ethereum - wei et gwei . Permettez-moi de vous les présenter brièvement afin que nous parlions le langage commun.


La plus petite dénomination est 1 wei , et 1 éther est égal à 1 000 000 000 000 000 000 Wei (18 zéros) . Je répète - 18 zéros. Il est difficile de se concentrer sur des chiffres aussi importants, mais ils font une différence et comptent beaucoup.


La prochaine dénomination commune est 1 gwei . 1 éther équivaut à 1 000 000 000 gwei (9 zéros) . Gwei est plus supportable pour les humains - au final, tout le monde veut être millionnaire, non ? (clin d'oeil clin d'oeil)


Résumons-le - 1 éther est égal à :

  • 1 000 000 000 gwei (9 zéros)
  • 1 000 000 000 000 000 000 wei (18 zéros)


Note technique : Ethereum a deux couches - la couche d'exécution et la couche de consensus. La couche d'exécution utilise wei pour représenter les valeurs d'éther, et la couche consensus utilise gwei. Si vous êtes un développeur blockchain, vous devez apprendre à interagir avec les deux.

Exemple concret : conseil de Stakefish et pool MEV

Je suis ingénieur logiciel chez stakefish . Je suis responsable de la construction de notre palette de produits DeFi, et l'une des plus récentes est notre pool de pourboires et de MEV pour Ethereum.


À partir du 15 septembre 2022, tous les validateurs sont éligibles aux conseils de transaction et peuvent participer aux MEV pour gagner des récompenses supplémentaires. Les pourboires de transaction et les MEV sont gagnés lorsque le validateur propose un nouveau bloc.


Nous avons décidé de créer un contrat intelligent qui collecte toutes les récompenses dans un coffre-fort commun et permet à l'utilisateur d'en réclamer sa part. Je n'essaie pas de faire de la publicité pour notre produit, mais j'ai besoin de définir le contexte de cet article.


Si vous êtes plus intéressé par ce produit, vous pouvez en savoir plus ici . Je ne vous vends rien d'autre que mon expérience.


Comme je l'ai mentionné, nous avons un contrat intelligent qui reçoit des conseils de transaction et des récompenses MEV gagnées par les validateurs. Cela signifie que notre contrat intelligent a un solde assez important. Il s'agit de plus de 963 éthers (1,1 million de dollars) et 8 671 validateurs y contribuent.


La partie critique responsable de la synchronisation entre l'exécution d'Ethereum et la couche de consensus est Oracle . C'est un système très important qui nous permet de déterminer quels validateurs contribuent au pool.


L'oracle est écrit en Python, mais il pourrait être écrit en JavaScript - le problème reste inchangé, et je le prouverai bientôt.


Plongeons profondément dans le code !

Pourquoi les types de données sont importants

Le solde du contrat intelligent est maintenant égal à 963 135 554 442 603 402 422 wei (963 éther). Ce nombre n'est pas seulement difficile à comprendre pour les humains mais aussi pour les ordinateurs (les interprètes de langage pour être exact). Vérifions JavaScript :


 const vault_balance = parseInt("963135554442603402422") console.log(vault_balance) // 963135554442603400000 (lost 2422 wei in total)


J'ai seulement coulé le solde de string à int , et il me manque déjà 2422 wei . Nous n'avons encore exécuté aucune équation.


Le solde du contrat intelligent est si élevé grâce aux nombreux validateurs qui y contribuent. Maintenant, calculons maintenant la part moyenne du validateur dans le solde du contrat :


 const vault_balance = parseInt("963135554442603402422") const validator_count = 8671 const avg_validator_contribution = vault_balance / validator_count // 111075487768723730 (lost 7 wei per validator)


La part moyenne est de 0,111 éther. Mais ce montant n'est pas correct - il nous manque en fait 7 wei. C'est 60,697 wei au total (7 wei fois 8671 validateurs). Je montrerai le bon numéro plus tard.


Poursuivons plus loin dans le terrier du lapin des pertes - calculons le montant total des récompenses par validateur donné. Gardez à l'esprit que l'utilisateur devait déposer 32 éthers pour démarrer un validateur, je le déduirai donc du solde du validateur.


Et je prendrai comme exemple un validateur aléatoire contribuant au contrat intelligent qui a un solde de 32,779 éther.


 const vault_balance = parseInt("963135554442603402422") // (lost 2422 wei) const validator_count = 8671 const avg_validator_contribution = vault_balance / validator_count // (lost 7 wei) const initial_deposit = parseInt("32000000000000000000") const validator_balance = parseInt("32779333896000000000") const total_validator_rewards = validator_balance - initial_deposit + avg_validator_contribution // 890409383768723700 (lost 23 wei per validator)


Le total des récompenses gagnées par ce validateur est égal à 0,8904 éther, mais cette valeur n'est pas non plus exacte. À ce moment, nous avons mal compté pour 199,443 wei au total (23 wei fois 8671 validateur). Comme vous pouvez le voir, cette façon de calculer les nombres n'est pas viable.

Qu'est-ce qui ne va pas ?

Il y a deux problèmes avec le code ci-dessus :


  • En JavaScript, la valeur maximale de sécurité pour les entiers est égale à 2^53 - 1 uniquement. Cela signifie qu'il peut gérer jusqu'à 9007199254740991 wei (0,009 éther)


  • Techniquement, nous aurions pu utiliser BigInt mais nous aurions des problèmes de division. On se retrouverait avec des valeurs « flottantes ». Les flotteurs sont à l'origine de tous les maux en finance car ils sont approximatifs. Cela signifie qu'ils perdent en précision. Nous devons utiliser des décimales. (La principale différence entre décimal et float est que décimal stocke la valeur exacte et float se rapproche.)


Si vous avez déjà fait du codage lié à Ethereum en JavaScript, vous devez avoir entendu parler de ethers.js . Cette bibliothèque contient tous les utilitaires nécessaires pour interagir avec la blockchain. Pour résoudre le problème ci-dessus, nous utiliserons l'un des outils appelés BigNumber qui prend en charge les nombres extrêmement grands et gère les décimales de la bonne manière.


Faisons le!


 const vault_balance = BigNumber.from("963135554442603402422") // no loss const validator_count = BigNumber.from(8671) const avg_validator_contribution = vault_balance.div(validator_count) // no loss // 111075487768723723 const initial_deposit = BigNumber.from("32000000000000000000") const validator_balance = BigNumber.from("32779333896000000000") const total_validator_rewards = validator_balance.sub(initial_deposit).add(avg_validator_contribution) // 890409383768723723


Comme vous pouvez le voir, nous nous sommes maintenant retrouvés avec le nombre exact. Comment puis-je savoir que c'est bien le bon numéro ? Je vais répéter le même exercice en Python pour prouver que j'ai raison.

Essayons-le en Python

Python prend en charge les entiers longs, de sorte que les valeurs ne seront pas soudainement coupées comme nous l'avons vu en JavaScript. Malheureusement, il détermine toujours tous les nombres flottants comme float s par défaut :


 vault_balance = int("963135554442603402422") # no loss validator_count = 8671 avg_validator_contribution = vault_balance / validator_count # 111075487768723728 (5 wei too much) initial_deposit = int("32000000000000000000") validator_balance = int("32779333896000000000") total_validator_rewards = validator_balance - initial_deposit + avg_validator_contribution # 890409383768723712 (lost 11 wei)


Vous vous demandez exactement où il a perdu sa précision ? La division avg_validator_contribution en float au lieu de decimal . L'extrait correct ressemblerait à ceci :


 vault_balance = Decimal("963135554442603402422") validator_count = Decimal(8671) avg_validator_contribution = vault_balance / validator_count # 111075487768723723 initial_deposit = Decimal("32000000000000000000") validator_balance = Decimal("32779333896000000000") total_validator_rewards = validator_balance - initial_deposit + avg_validator_contribution # 890409383768723723


Désormais, les valeurs renvoyées par Python et JavaScript sont exactes. Vérifiez par vous-même !


Ces types de pertes sont marginales et peuvent facilement passer inaperçues. Souvent, nous les découvrons lorsqu'ils s'aggravent avec le temps et atteignent un nombre significatif.


De telles situations donnent toujours des maux de tête, non seulement aux développeurs mais aussi à d'autres départements tels que les finances ou le juridique. Vous devriez toujours tester vos formules et ne jamais utiliser de beaux chiffres ronds pour le faire !


Cela signifierait le monde pour moi si vous me suivez sur Twitter . Je concentre mon activité sur le génie logiciel et la blockchain. Je suis en open source pour la plupart de mon travail, vous voudrez peut-être consulter mon GitHub .