Un nouveau modèle de sécurité des contrats DeFi : focus sur l'invariance des protocoles

Résumé

Ne vous contentez pas d'écrire des instructions require pour des fonctions spécifiques ; écrivez des instructions require pour vos protocoles. Contrôles de conformité des fonctions (exigences)-efficacité (Effets)-interactions (INteractions) + invariance de protocole (Iniants) ou mode FREI-PI peuvent aider votre contrat à être plus sécurisé, car il oblige les développeurs à se concentrer sur la sécurité au niveau des fonctions, Attention également pour les invariants au niveau du protocole.

motivation

En mars 2023, Euler Finance a été piraté et a perdu 200 millions de dollars. Euler Finance est une place de marché de prêt où les utilisateurs peuvent déposer des garanties et emprunter contre celles-ci. Il présente des caractéristiques uniques, en fait, il s'agit d'un marché de prêt comparable à Compound Finance et Aave.

Vous pouvez lire un post-mortem sur ce hack ici. Son contenu principal est l'absence de bilans de santé dans une fonction spécifique, permettant aux utilisateurs de briser l'invariance fondamentale du marché des prêts.

Iniants fondamentaux

Au cœur de la plupart des protocoles DeFi se trouve une immuabilité, une propriété de l'état du programme qui devrait toujours être vraie. Il est également possible d'avoir plusieurs invariants, mais en général, ils sont construits autour d'une idée centrale. Voici quelques exemples:

  • Comme sur le marché des prêts : les utilisateurs ne peuvent prendre aucune mesure pour placer un compte dans une position de garantie dangereuse ou moins sécurisée ("moins sûr" signifie qu'il est déjà en dessous du seuil de sécurité minimum et ne peut donc pas être retiré davantage).
  • Dans AMM DEX : x * y == k, x + y == k, etc.
  • Dans le staking de minage de liquidité : les utilisateurs ne devraient pouvoir retirer que le montant de jetons de staking qu'ils ont déposés.

Ce qui n'allait pas avec Euler Finance n'était pas nécessairement qu'ils ajoutaient des fonctionnalités, n'écrivaient pas de tests ou ne suivaient pas les meilleures pratiques traditionnelles. Ils ont audité la mise à niveau et effectué des tests, mais elle a quand même échappé aux mailles du filet. Le problème central est qu'ils oublient un invariant central du marché des prêts (comme le font les auditeurs !).

*Remarque : Je n'essaie pas de m'en prendre à Euler, c'est une équipe talentueuse, mais il s'agit d'un cas récent. *

Le cœur du problème

Vous pensez probablement "Eh bien, c'est vrai. C'est pourquoi ils ont été piratés ; ils ont oublié une instruction require". Oui et non.

Mais pourquoi oublieraient-ils l'instruction require ?

vérifier - valider - l'interaction n'est pas assez bonne

Un modèle commun recommandé pour les développeurs de solidité est le modèle Contrôles-Effets-Interactions. C'est très utile pour éliminer les bogues liés à la réentrance et augmente souvent la quantité de validation d'entrée qu'un développeur doit faire. _Mais_, il est sujet au problème de ne pas voir la forêt pour les arbres.

Ce qu'il enseigne au développeur, c'est : "J'écris d'abord mon instruction require, puis je fais la validation, puis peut-être que je fais n'importe quelle interaction, alors je suis en sécurité". Le problème est que, le plus souvent, cela devient un mélange de vérifications et d'effets - pas mal, hein ? Les interactions sont toujours définitives, donc la réentrance n'est pas un problème. Mais cela oblige les utilisateurs à se concentrer sur des fonctions plus spécifiques et des transitions d'état individuelles plutôt que sur le contexte global et plus large. Cela signifie que:

Le simple modèle d'interaction vérifier-valider fait oublier aux développeurs les invariants de base de leurs protocoles.

C'est toujours un excellent modèle pour les développeurs, mais l'invariance du protocole doit toujours être assurée (sérieusement, vous devriez toujours utiliser CEI !).

Faites-le bien : mode FREI-PI

Prenons par exemple cet extrait du contrat SoloMargin de dYdX (code source), qui est un marché de prêt et un contrat de négociation à effet de levier. Ceci est un bon exemple de ce que j'appelle le modèle Function Requirements-Effects-Interactions + Protocol Iniants, ou le modèle FREI-PI.

En tant que tel, je pense qu'il s'agit du seul marché des prêts sur le marché des prêts de démarrage qui ne présente aucune vulnérabilité liée au marché. Compound et Aave n'ont pas de problèmes directs, mais leurs fourches en ont. Et bZx a été piraté plusieurs fois.

Examinez le code ci-dessous et notez les abstractions suivantes :

  1. Vérifiez les paramètres d'entrée (_verifyInputs).
  2. Action (conversion de données, manipulation d'état)
  3. Vérifiez l'état final (_verifyFinalState).

Les vérifications-effets-interactions habituelles sont toujours effectuées. Il convient de noter que l'interaction de vérification-validation avec des vérifications supplémentaires n'est pas équivalente à FREI-PI - elles sont similaires mais servent des objectifs fondamentalement différents. Par conséquent, les développeurs doivent les considérer comme différents : FREI-PI, en tant qu'abstraction supérieure, vise la sécurité du protocole, tandis que CEI vise la sécurité fonctionnelle.

La structure de ce contrat est vraiment intéressante - les utilisateurs peuvent effectuer les actions qu'ils souhaitent (déposer, emprunter, échanger, transférer, liquider, etc.) dans une chaîne d'actions. Vous souhaitez déposer 3 jetons différents, en retirer un 4ème et liquider un compte ? C'est un seul appel.

C'est la puissance de FREI-PI : les utilisateurs peuvent faire ce qu'ils veulent dans le cadre du protocole, tant que les invariants du marché principal des prêts tiennent à la fin de l'appel : un seul utilisateur ne peut entreprendre aucune action qui mettrait un compte en insécurité ou plusieurs Positions de garantie non sécurisées. Pour ce contrat, cela est effectué dans _verifyFinalState , en vérifiant la garantie de chaque compte concerné pour s'assurer que l'accord est meilleur qu'au début de la transaction.

Il y a quelques invariants supplémentaires inclus dans cette fonction qui complètent les invariants de base et aident avec des fonctions secondaires comme la fermeture des marchés, mais ce sont les vérifications de base qui maintiennent vraiment la sécurité du protocole.

FREI-PI centré sur l'entité

Un autre problème avec FREI-PI est le concept centré sur l'entité. Prenons l'exemple d'un marché de prêt et d'invariants de base supposés :

Techniquement, ce n'est pas le seul invariant, mais c'est pour l'entité utilisateur (c'est toujours un invariant de protocole de base, et généralement les invariants d'utilisateur sont des invariants de protocole de base). Les marchés des prêts ont également généralement 2 entités supplémentaires :

*Oracle

  • Gestion / Gouvernance

Chaque immuabilité supplémentaire rend le protocole plus difficile à garantir, donc moins il y en a, mieux c'est. C'est en fait ce que Dan Elitzer a dit dans son article intitulé : Why DeFi is Broken and How to Fix It #1 Oracle-less Protocol (indice : l'article ne dit pas vraiment que les oracles sont le problème).

##Oracle

Pour les oracles, prenez l'exploit Cream Finance de 130 millions de dollars. L'immuabilité de base des entités oracle :

Il s'avère que la vérification des oracles au moment de l'exécution avec FREI-PI est délicate, mais faisable, avec un peu de prévoyance. De manière générale, Chainlink est un bon choix sur lequel s'appuyer principalement, satisfaisant la majeure partie de l'immuabilité. Dans les rares cas de manipulation ou de surprise, il peut être avantageux de disposer de mesures de protection qui réduisent la flexibilité au profit de la précision (telles que la vérification que la dernière valeur connue était supérieure de quelques pour cent à la valeur actuelle). De plus, le système SoloMargin de dYdX fait un excellent travail avec leur oracle DAI, voici le code (si vous ne pouvez pas le dire, je pense que c'est le meilleur système de contrat intelligent complexe jamais écrit).

Pour en savoir plus sur l'évaluation oracle et mettre en évidence les capacités de l'équipe d'Euler, ils ont écrit un bon article sur la manipulation informatique des prix oracle Uniswap V3 TWAP.

Administration / Gouvernance

La création d'invariants pour les entités gérées est la plus délicate. Ceci est principalement dû au fait que la plupart de leur rôle est de modifier d'autres invariants existants. Cela dit, si vous pouvez éviter d'utiliser des rôles administratifs, vous devriez le faire.

Fondamentalement, les invariants de base d'une entité gérée pourraient être :

Interprétation : les administrateurs peuvent faire des choses qui ne devraient pas briser les invariants, à moins qu'ils ne changent radicalement les choses pour protéger les fonds des utilisateurs (par exemple : déplacer des actifs dans un contrat de sauvetage, c'est supprimer des invariants). Les administrateurs doivent également être considérés comme des utilisateurs, de sorte que l'invariance des utilisateurs du marché principal des prêts doit également être valable pour eux (ce qui signifie qu'ils ne peuvent pas attaquer d'autres utilisateurs ou protocoles). Actuellement, certaines actions de l'administrateur sont impossibles à vérifier lors de l'exécution via FREI-PI, mais avec des invariants suffisamment forts ailleurs, nous espérons que la plupart des problèmes pourront être atténués. Je dis actuellement, car on peut imaginer utiliser un système de preuve zk qui pourrait vérifier l'état entier du contrat (par utilisateur, par oracle, etc.).

À titre d'exemple d'un administrateur brisant l'immuabilité, prenez l'action de gouvernance composée qui a bloqué le marché cETH en août 2022. Fondamentalement, cette mise à niveau brise l'immuabilité d'Oracle : Oracle fournit des informations précises et (relativement) en temps réel. En raison de fonctionnalités manquantes, Oracle peut fournir des informations incorrectes. Une vérification FREI-PI en cours d'exécution, vérifiant que l'Oracle concerné peut fournir des informations en temps réel, peut empêcher que cela ne se produise avec la mise à niveau. Cela peut être incorporé dans _setPriceOracle pour vérifier si tous les actifs ont reçu les informations en temps réel. La bonne chose à propos de FREI-PI pour les rôles d'administrateur est que les rôles d'administrateur sont relativement insensibles au prix (ou du moins ils devraient l'être), donc plus d'utilisation de gaz ne devrait pas être un gros problème.

La complexité est dangereuse

Ainsi, alors que les invariants les plus importants sont les invariants de base du protocole, il peut également y avoir des invariants centrés sur l'entité qui doivent être détenus par les invariants de base. Cependant, l'ensemble d'invariants le plus simple (et le plus petit) est probablement le plus sûr. Simple est bon Un exemple brillant est Uniswap…

Pourquoi Uniswap n'a jamais été piraté (probablement)

Les AMM peuvent avoir l'invariance de base la plus simple de toute primitive DeFi : tokenBalanceX * tokenBalanceY == k (par exemple, modèle de produit constant). Chaque fonction d'Uniswap V2 tourne autour de cet invariant simple :

  1. Menthe : ajouté à k
  2. Brûler : soustraire de k
  3. Swap : transférez x et y, mais conservez k.
  4. Skim : réajustez tokenBalanceX * tokenBalanceY pour le rendre égal à k, et supprimez la partie redondante.

Le secret de sécurité d'Uniswap V2 : le noyau est une simple immuabilité, et toutes les fonctions sont au service de celui-ci. La seule autre entité qui peut être discutée est la gouvernance, qui peut activer un commutateur de frais, qui ne touche pas l'immuabilité de base, juste la distribution de la propriété du solde du jeton. Cette simplicité dans leur déclaration de sécurité est la raison pour laquelle Uniswap n'a jamais été piraté. La simplicité n'est pas vraiment un mépris pour les excellents développeurs de contrats intelligents d'Uniswap, bien au contraire, il faut d'excellents ingénieurs pour trouver la simplicité.

Problème de gaz

Mon Twitter est déjà plein de cris d'horreur et de douleur des optimiseurs que ces vérifications sont inutiles et inefficaces. Deux choses à propos de cette question :

  1. Savez-vous quoi d'autre est inefficace ? J'ai dû envoyer des messages à ~~Laurence~~ des pirates nord-coréens via Etherscan, transférer de l'argent en utilisant ETH et menacer que le FBI intervienne.
  2. Vous avez probablement déjà chargé toutes les données dont vous avez besoin à partir du stockage, donc à la fin de l'appel, ajoutez simplement une petite vérification requise aux données chaudes. Voulez-vous que votre accord coûte des frais négligeables, ou le laisser mourir ?

Si le coût est prohibitif, repensez les variables de base et essayez de simplifier.

Qu'est-ce que cela signifie pour moi?

En tant que développeur, il est important de définir et d'exprimer les invariants de base au début du processus de développement. Comme suggestion concrète : la première fonction à vous faire écrire est _verifyAfter, pour vérifier vos invariants après chaque appel à votre contrat. Mettez-le dans votre contrat et déployez-le là-bas. Complétez cet invariant (et d'autres invariants centrés sur l'entité) avec des tests d'invariants plus larges qui sont vérifiés avant le déploiement (guide Foundry).

Les magasins transitoires ouvrent des optimisations et des améliorations intéressantes que Nascent expérimentera - je vous suggère de considérer comment les magasins transitoires peuvent être utilisés comme un outil pour une meilleure sécurité dans les contextes d'appel.

Dans cet article, peu de temps est consacré à l'introduction du modèle FREI-PI pour la validation des entrées, mais c'est aussi très important. Définir les limites de l'entrée est une tâche difficile pour éviter les débordements et les situations similaires. Pensez à consulter et à suivre l'évolution de notre outil : le pyromètre (actuellement en bêta, merci de nous attribuer une étoile). Il peut explorer et aider à trouver des endroits où vous ne ferez peut-être pas de validation des entrées.

en conclusion

En plus de tout acronyme accrocheur (FREI-PI) ou nom de schéma, le bit vraiment important est :

Trouvez la simplicité dans l'immuabilité de base de votre protocole. Et travaillez comme un diable pour vous assurer qu'il n'est jamais détruit (ou attrapé avant qu'il ne le soit).

Voir l'original
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
  • Récompense
  • Commentaire
  • Partager
Commentaire
0/400
Aucun commentaire
  • Épingler
Trader les cryptos partout et à tout moment
qrCode
Scan pour télécharger Gate app
Communauté
Français (Afrique)
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)