Approfondissement de la lecture Cross-L2 pour les portefeuilles et autres cas d'utilisation

Avancé2/29/2024, 5:22:58 AM
Dans cet article, Vitalik aborde directement l'aspect technique spécifique d'un sous-problème : comment lire plus facilement de la L2 à la L1, de la L1 à la L2 ou d'une L2 à l'autre. La résolution de ce problème est cruciale pour mettre en place une architecture de séparation des actifs et des clés, mais elle présente également de précieux cas d'utilisation dans d'autres domaines, notamment l'optimisation de la fiabilité des appels inter-L2, y compris des cas d'utilisation tels que le transfert d'actifs entre la L1 et la L2.

Merci tout particulièrement à Yoav Weiss, Dan Finlay, Martin Koppelmann et aux équipes d'Arbitrum, Optimism, Polygon, Scroll et SoulWallet pour leurs commentaires.

Dans ce billet sur les trois transitions, j'ai expliqué certaines des principales raisons pour lesquelles il est utile de commencer à penser explicitement à la prise en charge L1 + Cross-L2, à la sécurité des portefeuilles et à la confidentialité en tant que fonctionnalités de base nécessaires de l'écosystème, plutôt que de créer chacun de ces éléments sous forme d'addons pouvant être conçus séparément pour chaque portefeuille.

Ce billet se concentrera plus directement sur les aspects techniques d'un sous-problème spécifique : comment faciliter la lecture de la L1 de la L2, de la L2 de la L1 ou d'une L2 d'une autre L2. La résolution de ce problème est cruciale pour mettre en œuvre une architecture de séparation des actifs et des clés, mais elle présente également de précieux cas d'utilisation dans d'autres domaines, notamment l'optimisation de la fiabilité des appels inter-L2, y compris des cas d'utilisation tels que le transfert d'actifs entre les niveaux 1 et 2.

Prélectures recommandées

Table des matières

Quel est l'objectif ?

Une fois que les L2 seront plus courantes, les utilisateurs disposeront d'actifs répartis sur plusieurs L2, et peut-être aussi sur la L1. Une fois que les portefeuilles à contrats intelligents (multisig, social recovery ou autre) seront courants, les clés nécessaires pour accéder à certains comptes changeront au fil du temps, et les anciennes clés devront ne plus être valides. Une fois que ces deux choses se seront produites, l'utilisateur devra trouver un moyen de modifier les clés qui lui permettent d'accéder à de nombreux comptes situés dans de nombreux endroits différents, sans effectuer un très grand nombre de transactions.

En particulier, nous avons besoin d'un moyen de gérer les adresses contrefactuelles : des adresses qui n'ont pas encore été « enregistrées » sur la chaîne, mais qui doivent néanmoins recevoir et détenir des fonds en toute sécurité. Nous dépendons tous d'adresses contrefactuelles : lorsque vous utilisez Ethereum pour la première fois, vous pouvez générer une adresse ETH que quelqu'un peut utiliser pour vous payer, sans « enregistrer » l'adresse en chaîne (ce qui impliquerait de payer des taxes, et donc de détenir déjà des ETH).

Avec les EOA, toutes les adresses commencent par être des adresses contrefactuelles. Avec les portefeuilles à contrats intelligents, les adresses contrefactuelles sont toujours possibles, en grande partie grâce à CREATE2, qui vous permet d'avoir une adresse ETH qui ne peut être renseignée que par un contrat intelligent dont le code correspond à un hachage particulier.

Algorithme de calcul d'adresse EIP-1014 (CREATE2).

Cependant, les portefeuilles à contrats intelligents présentent un nouveau défi : la possibilité de changer les clés d'accès. L'adresse, qui est un hash du code d'initialisation, ne peut contenir que la clé de vérification initiale du portefeuille. La clé de vérification actuelle serait stockée dans la mémoire du portefeuille, mais cet enregistrement ne se propage pas comme par magie aux autres L2.

Si un utilisateur possède de nombreuses adresses sur de nombreuses L2, y compris des adresses que le L2 auquel il se trouve (parce que c'est contrefactuel) ne connaît pas, alors il semble qu'il n'y a qu'un seul moyen de permettre aux utilisateurs de modifier leurs clés : l'architecture de séparation des actifs et des magasins de clés. Chaque utilisateur possède (i) un « contrat de stockage de clés » (sur la L1 ou sur une L2 en particulier), qui stocke la clé de vérification de tous les portefeuilles ainsi que les règles relatives au changement de clé, et (ii) des « contrats de portefeuille » sur la L1 et sur de nombreux L2, qui lisent les chaînes pour obtenir la clé de vérification.

Il existe deux manières de le mettre en œuvre :

  • Version allégée (à cocher uniquement pour mettre à jour les clés) : chaque portefeuille stocke la clé de vérification localement et contient une fonction qui peut être appelée pour vérifier l'état actuel du magasin de clés, et mettre à jour sa clé de vérification stockée localement pour qu'elle corresponde. Lorsqu'un portefeuille est utilisé pour la première fois sur une L2 en particulier, il est obligatoire d'appeler cette fonction pour obtenir la clé de vérification actuelle auprès du keystore.
    • Avantage : utilise les preuves inter-chaînes avec parcimonie, donc ce n'est pas grave si les preuves inter-chaînes coûtent cher. Tous les fonds ne peuvent être dépensés qu'avec les clés actuelles, donc c'est sûr.
    • Inconvénient : pour modifier la clé de vérification, vous devez effectuer une modification de clé intégrée à la fois dans le keystore et dans chaque portefeuille déjà initialisé (mais pas dans les portefeuilles contrefactuels). Cela peut coûter cher de l'essence.
  • Version lourde (vérifiez chaque taxe) : une preuve inter-chaînes indiquant la clé qui se trouve actuellement dans le keystore est nécessaire pour chaque transaction.
    • Point positif : moins de complexité systémique et la mise à jour du keystore ne coûte pas cher.
    • Inconvénient : cher par taxe, il faut donc beaucoup plus d'ingénierie pour rendre les preuves inter-chaînes suffisamment bon marché. Pas facilement compatible avec l'ERC-4337, qui ne prend actuellement pas en charge la lecture intercontractuelle d'objets mutables lors de la validation.

À quoi ressemble une épreuve inter-chaînes ?

Pour en montrer toute la complexité, nous allons explorer le cas le plus difficile : celui où le keystore se trouve sur une L2 et le portefeuille sur une autre L2. Si le keystore ou le portefeuille se trouvent sur L1, seule la moitié de ce design est nécessaire.

Supposons que le keystore se trouve sur Linea et que le portefeuille se trouve sur Kakarot. Une preuve complète des clés du portefeuille comprend :

  • Une preuve de la racine d'état actuelle de Linea, étant donné la racine d'état actuelle d'Ethereum que Kakarot connaît
  • Une preuve prouvant les clés actuelles du keystore, compte tenu de la racine d'état Linea actuelle

Il y a deux principales questions de mise en œuvre ici :

  1. Quel type de preuve utilisons-nous ? (Est-ce que ce sont des preuves de Merkle ? autre chose ?)
  2. Comment la L2 apprend-elle la racine récente de l'état L1 (Ethereum) (ou, comme nous le verrons, potentiellement l'état L1 complet) en premier lieu ? Et sinon, comment la L1 apprend-elle la racine de l'état L2 ?
    • Dans les deux cas, quel est le délai entre le moment où quelque chose se passe d'un côté et celui où cela est prouvable de l'autre ?

Quels types de systèmes de preuve pouvons-nous utiliser ?

Il existe cinq options principales :

  • Les preuves de Merkle
  • ZK-SNARKS à usage général
  • Preuves spéciales (par ex. avec KZG)
  • Des preuves Verkle, qui se situent quelque part entre KZG et ZK-SNARK en termes de charge de travail et de coût de l'infrastructure.
  • Aucune preuve et comptez sur la lecture directe de l'État

En termes de travaux d'infrastructure requis et de coûts pour les utilisateurs, je les classe à peu près comme suit :

Le terme « agrégation » fait référence à l'idée de regrouper toutes les preuves fournies par les utilisateurs au sein de chaque bloc dans une grande méta-preuve qui les combine toutes. C'est possible pour SNArks et pour KZG, mais pas pour les agences Merkle (vous pouvez légèrement combiner les branches Merkle, mais cela vous permet d'économiser du log (txs par bloc) /log (nombre total de keystores), peut-être 15 à 30 % en pratique, donc ça ne vaut probablement pas le coût).

L'agrégation ne vaut la peine que lorsque le système compte un nombre important d'utilisateurs. En réalité, il est normal pour une implémentation en version 1 de laisser de côté l'agrégation et de l'implémenter pour la version 2.

Comment fonctionneraient les preuves Merkle ?

Celui-ci est simple : suivez directement le schéma de la section précédente. Plus précisément, chaque « preuve » (en supposant que le cas de difficulté maximale consiste à prouver une L2 contre une autre) contiendrait :

  • Une branche de Merkle prouvant la racine d'État de la banque de clés L2, étant donné la dernière racine d'État d'Ethereum connue par la L2. La racine d'état de L2, qui détient la banque de clés, est stockée sur un emplacement de stockage connu avec une adresse connue (le contrat sur L1 représente la L2). Le chemin dans l'arborescence peut donc être codé en dur.
  • Une agence Merkle prouvant les clés de vérification actuelles, étant donné la racine de la bibliothèque de clés L2. Là encore, la clé de vérification est stockée dans un emplacement de stockage connu à une adresse connue, de sorte que le chemin peut être codé en dur.

Malheureusement, les preuves d'état d'Ethereum sont compliquées, mais il existe des bibliothèques pour les vérifier, et si vous utilisez ces bibliothèques, ce mécanisme n'est pas trop compliqué à mettre en œuvre.

Le plus gros problème, c'est le coût. Les preuves de Merkle sont longues, et les arbres Patricia le sont malheureusement environ 3,9 fois plus que nécessaire (précisément : une preuve de Merkle idéale pour un arbre contenant N objets fait 32 log2 (N) octets, et comme les arbres Patricia d'Ethereum ont 16 feuilles par enfant, les preuves de ces arbres font 32 15 log16 (N) ~= 125 log2 (N) octets). Dans un État comptant environ 250 millions (~2 m²) de comptes, chaque preuve fait 125 * 28 = 3 500 octets, soit environ 56 000 de gaz, plus les frais supplémentaires liés au décodage et à la vérification des hachages.

Deux preuves réunies coûteraient entre 100 000 et 150 000 gaz (sans compter la vérification de la signature si elle est utilisée par transaction), soit bien plus que les 21 000 gaz de base actuels par transaction. Mais la disparité ne fait qu'empirer si les preuves sont vérifiées en L2. Les calculs au sein d'une L2 ne coûtent pas cher, car ils se font hors chaîne et dans un écosystème comportant beaucoup moins de nœuds que la L1. Les données, quant à elles, doivent être publiées en L1. La comparaison n'est donc pas de 21 000 gaz contre 150 000 gaz ; il s'agit de 21 000 L2 contre 100 000 gaz L1.

Nous pouvons calculer ce que cela signifie en comparant les coûts du gaz L1 et les coûts du gaz L2 :

La L1 coûte actuellement 15 à 25 fois plus chère que la L2 pour les envois simples, et 20 à 50 fois plus chère pour les échanges de jetons. Les envois simples sont relativement gourmands en données, mais les swaps sont beaucoup plus gourmands en termes de calcul. Les swaps constituent donc une meilleure référence pour déterminer le coût approximatif du calcul L1 par rapport au calcul L2. Compte tenu de tout cela, si nous supposons un ratio de coûts multiplié par 30 entre les coûts de calcul L1 et les coûts de calcul L2, cela signifie que le fait d'ajouter une preuve Merkle à L2 coûtera l'équivalent de cinquante transactions régulières.

Bien sûr, l'utilisation d'un arbre de Merkle binaire peut réduire les coûts d'environ 4 fois, mais malgré tout, le coût sera trop élevé dans la plupart des cas. Et si nous sommes prêtes à faire le sacrifice de ne plus être compatibles avec l'arbre d'états hexaires actuel d'Ethereum, autant rechercher des options encore meilleures.

Comment fonctionneraient les preuves ZK-SNARK ?

D'un point de vue conceptuel, l'utilisation des zk-SNARK est également facile à comprendre : il vous suffit de remplacer les preuves de Merkle dans le schéma ci-dessus par un ZK-SNARK prouvant que ces preuves de Merkle existent. Un ZK-SNARK coûte environ 400 000 gaz de calcul, et environ 400 octets (à comparer : 21 000 gaz et 100 octets pour une transaction de base, à l'avenir, réductible à environ 25 octets avec compression). Du point de vue informatique, un ZK-SNARK coûte 19 fois le coût d'une transaction de base aujourd'hui, et du point de vue des données, un ZK-SNARK coûte 4 fois plus cher qu'une transaction de base aujourd'hui, et 16 fois ce qu'une transaction de base pourrait coûter à l'avenir.

Ces chiffres constituent une nette amélioration par rapport aux preuves de Merkle, mais ils restent assez chers. Il existe deux manières d'améliorer cela : (i) des preuves KZG spécialisées, ou (ii) l'agrégation, similaire à l'agrégation ERC-4337 mais en utilisant des mathématiques plus sophistiquées. Nous pouvons examiner les deux.

Comment fonctionneraient les épreuves KZG spéciales ?

Attention, cette section est beaucoup plus mathématique que les autres. C'est parce que nous allons au-delà des outils à usage général et que nous créons quelque chose de spécial pour être moins cher. Nous devons donc passer beaucoup plus de temps à « passer sous le capot ». Si vous n'aimez pas les mathématiques approfondies, passez directement à la section suivante.

Tout d'abord, un résumé du fonctionnement des engagements du KZG :

  • Nous pouvons représenter un ensemble de données [D_1... D_n] avec la preuve KZG d'un polynôme dérivé des données : plus précisément, le polynôme P où P (w) = D_1, P (w²) = D_2... P (w) = D_n. w ici est une « racine de l'unité », une valeur où w = 1 pour une taille de domaine d'évaluation N (tout cela se fait dans un corps fini).
  • Pour « passer » à P, nous créons un point de courbe elliptique com (P) = PG + P¹ S¹ + ... + P* S. Ici :
    • G est le point générateur de la courbe
    • P est le coefficient du IIe degré du polynôme P
    • S est le point I de la configuration fiable
  • Pour prouver P (z) = a, nous créons un polynôme quotient Q = (P - a)/(X - z), et nous créons un engagement com (Q) à cet égard. Il n'est possible de créer un tel polynôme que si P (z) est réellement égal à a.
  • Pour vérifier une preuve, nous vérifions l'équation Q * (X - z) = P - a en vérifiant la preuve com (Q) et l'engagement polynomial com (P) : nous vérifions e (com (Q), com (X - z)) ? = e (com (P) - com (a), com (1))

Certaines propriétés clés qu'il est important de comprendre sont les suivantes :

  • La preuve, c'est juste la valeur com (Q), qui fait 48 octets
  • com (P1) + com (P₂) = com (P¹ + P₂)
  • Cela signifie également que vous pouvez « modifier » une valeur dans un engagement existant. Supposons que nous sachions que D_i est actuellement a, que nous voulions le définir sur b, et que l'engagement existant envers D est com (P). Un engagement en faveur de « P, mais avec P (w) = b, et aucune autre évaluation n'a changé », puis nous avons défini com (New_P) = com (P) + (b-a) * com (L), où L est un « polynôme de Lagrange » qui vaut 1 pour wet 0 pour les autres points w.
  • Pour effectuer ces mises à jour efficacement, tous les N engagements vis-à-vis des polynômes de Lagrange (com (L)) peuvent être précalculés et stockés par chaque client. Dans un contrat en chaîne, il peut être trop important de stocker tous les N engagements. Vous pourriez donc prendre un engagement KZG sur l'ensemble des valeurs com (L_i) (ou hash (com (L_i)). Ainsi, chaque fois que quelqu'un a besoin de mettre à jour l'arborescence de la chaîne, il peut simplement fournir le com (L_i) approprié avec une preuve de son exactitude.

Nous avons donc une structure qui nous permet de continuer à ajouter des valeurs à la fin d'une liste qui ne cesse de s'allonger, mais avec une certaine limite de taille (en réalité, des centaines de millions pourraient être viables). Nous l'utilisons ensuite comme structure de données pour gérer (i) un engagement envers la liste des clés de chaque L2, stockée sur cette L2 et mise en miroir sur la L1, et (ii) un engagement envers la liste des engagements clés L2, stockée sur l'Ethereum L1 et reflétée sur chaque L2.

La mise à jour des engagements pourrait soit faire partie de la logique principale de la L2, soit être mise en œuvre sans modification du protocole principal de la L2 par le biais de passerelles de dépôt et de retrait.

Pour obtenir une preuve complète, il faudrait donc :

  • Le dernier com (liste de touches) sur le keystore L2 (48 octets)
  • Preuve KZG que com (liste de clés) est une valeur contenue dans com (mirror_list), l'engagement à respecter la liste de tous les engagements de la liste clé (48 octets)
  • Preuve KZG de votre clé en com (liste de clés) (48 octets, plus 4 octets pour l'index)

Il est en fait possible de fusionner les deux preuves KZG en une seule, donc nous obtenons une taille totale de 100 octets seulement.

Notez une subtilité : comme la liste des touches est une liste, et non une carte clé/valeur comme c'est le cas pour l'État, la liste des touches devra attribuer les positions de manière séquentielle. Le contrat d'engagement des clés contiendrait son propre registre interne associant chaque keystore à un identifiant, et pour chaque clé, il stockerait le hash (clé, adresse du keystore) au lieu d'une simple clé, afin de communiquer sans ambiguïté aux autres L2 de quel keystore parle une entrée en particulier.

L'avantage de cette technique, c'est qu'elle fonctionne très bien en L2. Les données font 100 octets, soit environ 4 fois plus courtes qu'un ZK-SNARK et bien plus courtes qu'une preuve Merkle. Le coût de calcul correspond en grande partie à un chèque d'appariement de taille 2, soit environ 119 000 essence. En L1, les données sont moins importantes que les calculs. Malheureusement, le KZG coûte un peu plus cher que les preuves de Merkle.

Comment fonctionneraient les arbres Verkle ?

Les arbres Verkle consistent essentiellement à empiler les engagements KZG (ou les engagements IPA, qui peuvent être plus efficaces et utiliser une cryptographie plus simple) les uns sur les autres : pour stocker 2 valeurs, vous pouvez prendre un engagement KZG sur une liste de 2 valeurs, chacune d'entre elles constituant en elle-même un engagement KZG à 2 valeurs. Les arbres Verkle sont fortement < a href= " https://notes.ethereum.org/@vbuterin/verkle_tree_eip " > considéré pour l'arbre d'état d'Ethereum, parce que les arbres de Verkle peuvent être utilisés pour contenir des cartes de valeurs clés et pas seulement des listes (en gros, vous pouvez créer un arbre de 2 m² mais le démarrer vide, en ne remplissant que des parties spécifiques de l'arbre une fois que vous en avez réellement besoin).

À quoi ressemble un arbre Verkle. En pratique, vous pouvez donner à chaque nœud une largeur de 256 == 2 pour les arbres basés sur l'IPA, ou de 2 ² pour les arbres basés sur le KzG.

Les preuves dans les arbres Verkle sont légèrement plus longues qu'en KZG ; elles peuvent faire quelques centaines d'octets. Ils sont également difficiles à vérifier, surtout si vous essayez de regrouper de nombreuses preuves en une seule.

En réalité, les arbres Verkle devraient être considérés comme des arbres Merkle, mais ils sont plus viables sans SNARking (en raison de la baisse des coûts de données) et moins chers avec SnarKing (grâce à la baisse des coûts des prouvateurs).

Le principal avantage des arbres de Verkle est la possibilité d'harmoniser les structures de données : les preuves de Verkle peuvent être utilisées directement sur l'état L1 ou L2, sans structures de superposition, et en utilisant exactement le même mécanisme pour L1 et L2. Une fois que les ordinateurs quantiques poseront un problème, ou une fois que les branches de Merkle seront suffisamment efficaces, les arbres de Verkle pourraient être remplacés sur place par un arbre de hachage binaire doté d'une fonction de hachage adaptée à Snark.

Agrégation

Si N utilisateurs effectuent N transactions (ou, de façon plus réaliste, N ERC-4337 UserOperations) qui doivent prouver N réclamations inter-chaînes, nous pouvons économiser beaucoup de gaz en agrégeant ces preuves : le constructeur qui combinerait ces transactions dans un bloc ou un ensemble pouvant être intégré à un bloc peut créer une preuve unique prouvant toutes ces affirmations simultanément.

Cela peut vouloir dire :

Dans les trois cas, les preuves ne coûteraient que quelques centaines de milliers d'essence chacune. Le créateur devrait en créer un sur chaque L2 pour les utilisateurs de cette L2 ; par conséquent, pour que cela soit utile, le schéma dans son ensemble doit être suffisamment utilisé pour qu'il y ait très souvent au moins quelques transactions dans le même bloc sur plusieurs L2 majeurs.

Si des zk-SNARK sont utilisés, le principal coût marginal est simplement la « logique commerciale » qui consiste à transmettre des chiffres entre les contrats, soit peut-être quelques milliers de L2 par utilisateur. Si des appareils KZG sont utilisés, le testeur devra ajouter 48 litres de gaz pour chaque unité de stockage de gaz utilisée dans ce bloc. Ainsi, le coût marginal du système par utilisateur ajouterait environ 800 litres de gaz supplémentaires par litre (et non par utilisateur) en plus. Mais ces coûts sont bien inférieurs à ceux liés à l'absence d'agrégation, qui implique inévitablement plus de 10 000 litres de gaz L1 et des centaines de milliers de gaz L2 par utilisateur. Pour les arbres Verkle, vous pouvez soit utiliser directement les multi-preuves Verkle, en ajoutant environ 100 à 200 octets par utilisateur, soit créer un ZK-SNARK à partir d'un Verkle multiproof, ce qui coûte des coûts similaires à ceux des branches ZK-SNARKS de Merkle mais c'est nettement moins cher à prouver.

Du point de vue de la mise en œuvre, il est probablement préférable de demander à des groupeurs d'agréger les preuves inter-chaînes via la norme d'abstraction de comptes ERC-4337. L'ERC-4337 dispose déjà d'un mécanisme permettant aux créateurs d'agréger certaines parties de UserOperations de manière personnalisée. Il existe même une implémentation < a href= " https://hackmd.io/@voltrevo/BJ0QBY3ZI " > pour l'agrégation de signatures BLS, ce qui pourrait réduire les coûts de gaz sur la couche L2 de 1,5 à 3 fois selon les autres formes de compression incluses.

Schéma tiré d'un article < a href= " https://hackmd.io/@voltrevo/bj0qby3zi " > sur la mise en œuvre du portefeuille BLS montrant le flux de travail des signatures agrégées BLS dans une version antérieure de l'ERC-4337. Le flux de travail d'agrégation des preuves inter-chaînes sera probablement très similaire.

Lecture directe par l'État

Une dernière possibilité, utilisable uniquement pour la L2 lisant la L1 (et non la L1 lisant L2), est de modifier les L2 pour leur permettre de passer des appels statiques aux contrats de L1 directement.

Cela peut être fait à l'aide d'un opcode ou d'une précompilation, qui autorise les appels en L1 où vous fournissez l'adresse de destination, le gaz et les données d'appel, et qui renvoie le résultat, mais comme ces appels sont statiques, ils ne peuvent pas réellement modifier l'état L1. Les L2 doivent déjà connaître la L1 pour traiter les dépôts. Rien de fondamental n'empêche donc la mise en œuvre d'une telle solution ; il s'agit principalement d'un défi de mise en œuvre technique (voir : cet appel d'offres d'Optimism visant à prendre en charge les appels statiques vers la L1).

Notez que si le keystore est en L1 et que les L2 intègrent la fonctionnalité d'appel statique L1, aucune preuve n'est requise ! Cependant, si les L2 n'intègrent pas les appels statiques L1, ou si le keystore est en L2 (ce qui devra peut-être être le cas, une fois que la L1 deviendra trop chère pour que les utilisateurs puissent l'utiliser ne serait-ce qu'un tout petit peu), alors des preuves seront requises.

Comment la L2 connaît-elle la récente racine d'État d'Ethereum ?

Tous les schémas ci-dessus nécessitent que la L2 accède soit à la racine de l'état L1 récent, soit à l'intégralité de l'état L1 récent. Heureusement, toutes les L2 disposent déjà de certaines fonctionnalités qui leur permettent d'accéder à l'état récent de la L1. C'est parce qu'ils ont besoin d'une telle fonctionnalité pour traiter les messages provenant de la L1 vers la L2, notamment les dépôts.

Et en effet, si une L2 possède une fonction de dépôt, vous pouvez utiliser cette L2 telle quelle pour transférer les racines de l'État L1 dans un contrat sur la L2 : il suffit de créer un contrat en L1, d'appeler l'opcode BLOCKHASH, et de le transmettre à L2 sous forme de message de dépôt. L'en-tête du bloc complet peut être reçu, et sa racine d'état extraite, côté L2. Cependant, il serait préférable que chaque L2 dispose d'un moyen explicite d'accéder directement à l'état L1 complet récent ou aux racines récentes de l'État L1.

Le principal défi lié à l'optimisation de la façon dont les L2 reçoivent les racines d'état L1 récentes est de garantir à la fois la sécurité et une faible latence :

  • Si les L2 implémentent la fonctionnalité de « lecture directe de la L1 » de manière paresseuse, en lisant uniquement les racines d'état L1 finalisées, le délai sera normalement de 15 minutes, mais dans les cas extrêmes de fuites d'inactivité (que vous devez tolérer), le délai peut être de plusieurs semaines.
  • Les L2 peuvent absolument être conçues pour lire des racines d'État L1 bien plus récentes, mais comme la L1 peut revenir en arrière (même avec une finalité à un seul emplacement, des retours peuvent se produire lors de fuites d'inactivité), la L2 devrait également être capable de revenir en arrière. C'est un défi technique du point de vue du génie logiciel, mais au moins Optimism possède déjà cette capacité.
  • Si vous utilisez le pont de dépôt pour intégrer les racines de l'État L1 à la L2, la simple viabilité économique peut prendre beaucoup de temps entre les mises à jour des dépôts : si le coût total d'un dépôt est de 100 000 gaz, et que nous supposons que l'ETH est de 1800 dollars, et que les frais sont de 200 gwei, et que les racines L1 sont introduites en L2 une fois par jour, cela représenterait un coût de 36 dollars par L2 par jour, soit 13148 dollars par L2 et par an pour maintenir le système. Avec un retard d'une heure, cela passe à 315 569 dollars par L2 et par an. Dans le meilleur des cas, un filet constant d'utilisateurs fortunés impatients couvre les frais de mise à jour et met le système à jour pour tous les autres. Dans le pire des cas, un acteur altruiste devrait payer lui-même.
  • Les « oracles » (du moins, le type de technologie que certains appellent « oracles ») ne sont pas une solution acceptable ici : la gestion des clés de portefeuille est une fonctionnalité de bas niveau très critique sur le plan de la sécurité. Elle devrait donc dépendre au maximum de quelques éléments d'infrastructure de bas niveau très simples et fiables sur le plan cryptographique.

De plus, dans la direction opposée (L1 lit L2) :

  • Si l'on se fie à des résultats optimistes, State Roots met une semaine à atteindre la L1 à cause du délai de prévention des fraudes. En ce qui concerne les cumuls ZK, cela prend quelques heures pour l'instant à cause des temps de démonstration et des limites économiques, mais les technologies futures permettront de réduire ce chiffre.
  • Les préconfirmations (provenant de séquenceurs, d'attestateurs, etc.) ne sont pas une solution acceptable pour que la L1 lise la L2. La gestion des portefeuilles est une fonctionnalité de bas niveau très critique pour la sécurité. Le niveau de sécurité de la communication L2 - > L1 doit donc être absolu : il ne devrait même pas être possible de transmettre une fausse racine d'état L1 en reprenant le jeu de validateurs L2. Les seules racines de l'État auxquelles la L1 devrait avoir confiance sont celles qui ont été considérées comme définitives par le contrat de détention des racines de l'État de la L2 en L1.

Certaines de ces vitesses pour les opérations inter-chaînes en toute confiance sont trop lentes pour de nombreux cas d'utilisation de DeFi ; dans ces cas, vous avez besoin de ponts plus rapides avec des modèles de sécurité plus imparfaits. Pour ce qui est de la mise à jour des clés de portefeuille, des délais plus longs sont toutefois plus acceptables : vous ne retardez pas les transactions de plusieurs heures, vous retardez les changements de clés. Vous n'aurez qu'à conserver les anciennes clés plus longtemps. Si vous changez de clé parce que des clés sont volées, c'est que vous êtes exposée à une période de vulnérabilité importante, mais cela peut être atténué, par exemple en installant une fonction de blocage des portefeuilles.

En fin de compte, la meilleure solution pour minimiser la latence est que les L2 implémentent la lecture directe des racines d'état L1 de manière optimale, où chaque bloc L2 (ou le journal de calcul de la racine d'état) contient un pointeur vers le bloc L1 le plus récent. Ainsi, si la L1 revient, la L2 peut également revenir en arrière. Les contrats Keystore doivent être passés soit sur le réseau principal, soit sur des L2 qui sont des ZK-Rollups, ce qui permet de passer rapidement en L1.

Les blocs de la chaîne L2 peuvent dépendre non seulement des blocs L2 précédents, mais aussi d'un bloc L1. Si la L1 revient après un tel lien, la L2 revient également. Il convient de noter que c'est également ainsi qu'une version antérieure (antérieure à Dank) du sharding était envisagée ; voir le code ici.

De quel niveau de connexion à Ethereum une autre chaîne a-t-elle besoin pour détenir des portefeuilles dont les keystores sont enracinés sur Ethereum ou un L2 ?

Étonnamment, pas tant que ça. En fait, il n'est même pas nécessaire que ce soit un cumul : s'il s'agit d'un L3 ou d'un Validium, vous pouvez y conserver des portefeuilles, à condition que vous déteniez des keystores en L1 ou en ZK. Ce dont vous avez besoin, c'est que la chaîne ait un accès direct aux racines de l'État d'Ethereum et qu'elle s'engage techniquement et socialement à être prête à se réorganiser si Ethereum se réorganise, et à hard fork si Ethereum fait des hard forks.

Un problème de recherche intéressant est de déterminer dans quelle mesure il est possible pour une chaîne d'avoir cette forme de connexion avec plusieurs autres chaînes (par ex. Ethereum et Zcash). Le faire naïvement est possible : votre chaîne pourrait accepter de se réorganiser en cas de réorganisation d'Ethereum ou de Zcash (et de hard fork s'il s'agit d'Ethereum ou de Zcash), mais les opérateurs de vos nœuds et votre communauté en général ont deux fois plus de dépendances techniques et politiques. Une telle technique pourrait donc être utilisée pour se connecter à quelques autres chaînes, mais à un coût plus élevé. Les schémas basés sur les ponts ZK ont des propriétés techniques intéressantes, mais leur principal point faible est qu'ils ne résistent pas aux attaques à 51 % ou aux hard forks. Il existe peut-être des solutions plus intelligentes.

Préserver la confidentialité

Idéalement, nous voulons également préserver la confidentialité. Si vous avez de nombreux portefeuilles gérés par le même keystore, nous voulons nous assurer que :

  • On ignore si ces portefeuilles sont tous connectés les uns aux autres.
  • Les tuteurs sociaux ne savent pas quelles sont les adresses qu'ils surveillent.

Cela pose quelques problèmes :

  • Nous ne pouvons pas utiliser directement les preuves Merkle, car elles ne préservent pas la confidentialité.
  • Si nous utilisons KZG ou SNARKs, la preuve doit fournir une version masquée de la clé de vérification, sans révéler son emplacement.
  • Si nous utilisons l'agrégation, l'agrégateur ne devrait pas connaître l'emplacement en texte clair ; il devrait plutôt recevoir des preuves en aveugle et avoir un moyen de les agréger.
  • Nous ne pouvons pas utiliser la « version allégée » (utilisez des preuves inter-chaînes uniquement pour mettre à jour les clés), car cela crée une fuite de confidentialité : si de nombreux portefeuilles sont mis à jour en même temps à cause d'une procédure de mise à jour, le timing indique que ces portefeuilles sont probablement liés. Nous devons donc utiliser la « version lourde » (preuves inter-chaînes pour chaque transaction).

Avec les SNARKs, les solutions sont simples sur le plan conceptuel : les preuves masquent des informations par défaut, et l'agrégateur doit produire un SNARK récursif pour prouver les SNARK.

Le principal défi de cette approche aujourd'hui est que l'agrégateur doit créer un SNARK récursif, ce qui est assez lent actuellement.

Avec KZG, nous pouvons utiliser < a href= " https://notes.ethereum.org/@vbuterin/non_index_revealing_proof " > pour ce travailler sur des preuves KZG non révélatrices d'index (voir aussi : une version plus formalisée de ce travail dans l'article de Caulk) comme point de départ . L'agrégation de preuves en aveugle est toutefois un problème ouvert qui nécessite plus d'attention.

La lecture directe de la L1 depuis la L2 ne préserve malheureusement pas la confidentialité, même si la mise en œuvre d'une fonctionnalité de lecture directe reste très utile, à la fois pour minimiser la latence et pour d'autres applications.

Résumé

  • Pour avoir des portefeuilles de relance sociale inter-chaînes, le flux de travail le plus réaliste est un portefeuille qui gère un magasin de clés en un seul endroit, et des portefeuilles sur de nombreux sites, où le portefeuille lit le keystore soit (i) pour mettre à jour sa vue locale de la clé de vérification, soit (ii) pendant le processus de vérification de chaque transaction.
  • Les preuves inter-chaînes sont un élément clé pour rendre cela possible. Nous devons optimiser ces preuves de manière rigoureuse. Soit les zk-SNARKS, en attente des preuves de Verkle, soit une solution KZG personnalisée, semblent être la meilleure option.
  • À plus long terme, des protocoles d'agrégation dans le cadre desquels les bundlers génèrent des preuves agrégées dans le cadre de la création d'un ensemble de toutes les UserOperations soumises par les utilisateurs seront nécessaires pour minimiser les coûts. Cela devrait probablement être intégré à l'écosystème ERC-4337, même si des modifications seront probablement nécessaires.
  • Les L2 doivent être optimisés afin de minimiser la latence lors de la lecture de l'état L1 (ou du moins de la racine de l'état) depuis l'intérieur de la L2. La lecture directe de l'état L1 par les L2 est idéale et permet d'économiser de l'espace de preuve.
  • Les portefeuilles ne peuvent pas être placés uniquement sur des réseaux L2 ; vous pouvez également les placer sur des systèmes où le niveau de connexion à Ethereum est faible (L3, ou même des chaînes distinctes qui acceptent uniquement d'inclure les racines d'État d'Ethereum et de procéder à une réorganisation ou à un hard fork lorsque Ethereum se reforme ou fait des forks).
  • Cependant, les keystores devraient se trouver soit en L1, soit en ZK-Rollup L2, un système de haute sécurité. Le fait d'être en L1 permet d'économiser beaucoup de complexité, même si à long terme, cela peut coûter trop cher. D'où la nécessité de créer des keystores en L2.
  • La préservation de la vie privée demandera un travail supplémentaire et compliquera certaines options. Cependant, nous devrions probablement nous orienter vers des solutions préservant la confidentialité de toute façon, et au moins nous assurer que tout ce que nous proposons est compatible avec la préservation de la vie privée.

Avertissement:

  1. Cet article est repris de [Vitalik]. Tous les droits d'auteur appartiennent à l'auteur original [Vitalik Buterin]. En cas d'objection à cette réimpression, contactez l'équipe de Gate Learn, elle s'en occupera rapidement.
  2. Avertissement en matière de responsabilité : Les points de vue et opinions exprimés dans cet article sont uniquement ceux de l'auteur et ne constituent en aucun cas un conseil d'investissement.
  3. Les traductions de l'article dans d'autres langues sont effectuées par l'équipe de Gate Learn. Sauf mention contraire, il est interdit de copier, de distribuer ou de plagier les articles traduits.

Approfondissement de la lecture Cross-L2 pour les portefeuilles et autres cas d'utilisation

Avancé2/29/2024, 5:22:58 AM
Dans cet article, Vitalik aborde directement l'aspect technique spécifique d'un sous-problème : comment lire plus facilement de la L2 à la L1, de la L1 à la L2 ou d'une L2 à l'autre. La résolution de ce problème est cruciale pour mettre en place une architecture de séparation des actifs et des clés, mais elle présente également de précieux cas d'utilisation dans d'autres domaines, notamment l'optimisation de la fiabilité des appels inter-L2, y compris des cas d'utilisation tels que le transfert d'actifs entre la L1 et la L2.

Merci tout particulièrement à Yoav Weiss, Dan Finlay, Martin Koppelmann et aux équipes d'Arbitrum, Optimism, Polygon, Scroll et SoulWallet pour leurs commentaires.

Dans ce billet sur les trois transitions, j'ai expliqué certaines des principales raisons pour lesquelles il est utile de commencer à penser explicitement à la prise en charge L1 + Cross-L2, à la sécurité des portefeuilles et à la confidentialité en tant que fonctionnalités de base nécessaires de l'écosystème, plutôt que de créer chacun de ces éléments sous forme d'addons pouvant être conçus séparément pour chaque portefeuille.

Ce billet se concentrera plus directement sur les aspects techniques d'un sous-problème spécifique : comment faciliter la lecture de la L1 de la L2, de la L2 de la L1 ou d'une L2 d'une autre L2. La résolution de ce problème est cruciale pour mettre en œuvre une architecture de séparation des actifs et des clés, mais elle présente également de précieux cas d'utilisation dans d'autres domaines, notamment l'optimisation de la fiabilité des appels inter-L2, y compris des cas d'utilisation tels que le transfert d'actifs entre les niveaux 1 et 2.

Prélectures recommandées

Table des matières

Quel est l'objectif ?

Une fois que les L2 seront plus courantes, les utilisateurs disposeront d'actifs répartis sur plusieurs L2, et peut-être aussi sur la L1. Une fois que les portefeuilles à contrats intelligents (multisig, social recovery ou autre) seront courants, les clés nécessaires pour accéder à certains comptes changeront au fil du temps, et les anciennes clés devront ne plus être valides. Une fois que ces deux choses se seront produites, l'utilisateur devra trouver un moyen de modifier les clés qui lui permettent d'accéder à de nombreux comptes situés dans de nombreux endroits différents, sans effectuer un très grand nombre de transactions.

En particulier, nous avons besoin d'un moyen de gérer les adresses contrefactuelles : des adresses qui n'ont pas encore été « enregistrées » sur la chaîne, mais qui doivent néanmoins recevoir et détenir des fonds en toute sécurité. Nous dépendons tous d'adresses contrefactuelles : lorsque vous utilisez Ethereum pour la première fois, vous pouvez générer une adresse ETH que quelqu'un peut utiliser pour vous payer, sans « enregistrer » l'adresse en chaîne (ce qui impliquerait de payer des taxes, et donc de détenir déjà des ETH).

Avec les EOA, toutes les adresses commencent par être des adresses contrefactuelles. Avec les portefeuilles à contrats intelligents, les adresses contrefactuelles sont toujours possibles, en grande partie grâce à CREATE2, qui vous permet d'avoir une adresse ETH qui ne peut être renseignée que par un contrat intelligent dont le code correspond à un hachage particulier.

Algorithme de calcul d'adresse EIP-1014 (CREATE2).

Cependant, les portefeuilles à contrats intelligents présentent un nouveau défi : la possibilité de changer les clés d'accès. L'adresse, qui est un hash du code d'initialisation, ne peut contenir que la clé de vérification initiale du portefeuille. La clé de vérification actuelle serait stockée dans la mémoire du portefeuille, mais cet enregistrement ne se propage pas comme par magie aux autres L2.

Si un utilisateur possède de nombreuses adresses sur de nombreuses L2, y compris des adresses que le L2 auquel il se trouve (parce que c'est contrefactuel) ne connaît pas, alors il semble qu'il n'y a qu'un seul moyen de permettre aux utilisateurs de modifier leurs clés : l'architecture de séparation des actifs et des magasins de clés. Chaque utilisateur possède (i) un « contrat de stockage de clés » (sur la L1 ou sur une L2 en particulier), qui stocke la clé de vérification de tous les portefeuilles ainsi que les règles relatives au changement de clé, et (ii) des « contrats de portefeuille » sur la L1 et sur de nombreux L2, qui lisent les chaînes pour obtenir la clé de vérification.

Il existe deux manières de le mettre en œuvre :

  • Version allégée (à cocher uniquement pour mettre à jour les clés) : chaque portefeuille stocke la clé de vérification localement et contient une fonction qui peut être appelée pour vérifier l'état actuel du magasin de clés, et mettre à jour sa clé de vérification stockée localement pour qu'elle corresponde. Lorsqu'un portefeuille est utilisé pour la première fois sur une L2 en particulier, il est obligatoire d'appeler cette fonction pour obtenir la clé de vérification actuelle auprès du keystore.
    • Avantage : utilise les preuves inter-chaînes avec parcimonie, donc ce n'est pas grave si les preuves inter-chaînes coûtent cher. Tous les fonds ne peuvent être dépensés qu'avec les clés actuelles, donc c'est sûr.
    • Inconvénient : pour modifier la clé de vérification, vous devez effectuer une modification de clé intégrée à la fois dans le keystore et dans chaque portefeuille déjà initialisé (mais pas dans les portefeuilles contrefactuels). Cela peut coûter cher de l'essence.
  • Version lourde (vérifiez chaque taxe) : une preuve inter-chaînes indiquant la clé qui se trouve actuellement dans le keystore est nécessaire pour chaque transaction.
    • Point positif : moins de complexité systémique et la mise à jour du keystore ne coûte pas cher.
    • Inconvénient : cher par taxe, il faut donc beaucoup plus d'ingénierie pour rendre les preuves inter-chaînes suffisamment bon marché. Pas facilement compatible avec l'ERC-4337, qui ne prend actuellement pas en charge la lecture intercontractuelle d'objets mutables lors de la validation.

À quoi ressemble une épreuve inter-chaînes ?

Pour en montrer toute la complexité, nous allons explorer le cas le plus difficile : celui où le keystore se trouve sur une L2 et le portefeuille sur une autre L2. Si le keystore ou le portefeuille se trouvent sur L1, seule la moitié de ce design est nécessaire.

Supposons que le keystore se trouve sur Linea et que le portefeuille se trouve sur Kakarot. Une preuve complète des clés du portefeuille comprend :

  • Une preuve de la racine d'état actuelle de Linea, étant donné la racine d'état actuelle d'Ethereum que Kakarot connaît
  • Une preuve prouvant les clés actuelles du keystore, compte tenu de la racine d'état Linea actuelle

Il y a deux principales questions de mise en œuvre ici :

  1. Quel type de preuve utilisons-nous ? (Est-ce que ce sont des preuves de Merkle ? autre chose ?)
  2. Comment la L2 apprend-elle la racine récente de l'état L1 (Ethereum) (ou, comme nous le verrons, potentiellement l'état L1 complet) en premier lieu ? Et sinon, comment la L1 apprend-elle la racine de l'état L2 ?
    • Dans les deux cas, quel est le délai entre le moment où quelque chose se passe d'un côté et celui où cela est prouvable de l'autre ?

Quels types de systèmes de preuve pouvons-nous utiliser ?

Il existe cinq options principales :

  • Les preuves de Merkle
  • ZK-SNARKS à usage général
  • Preuves spéciales (par ex. avec KZG)
  • Des preuves Verkle, qui se situent quelque part entre KZG et ZK-SNARK en termes de charge de travail et de coût de l'infrastructure.
  • Aucune preuve et comptez sur la lecture directe de l'État

En termes de travaux d'infrastructure requis et de coûts pour les utilisateurs, je les classe à peu près comme suit :

Le terme « agrégation » fait référence à l'idée de regrouper toutes les preuves fournies par les utilisateurs au sein de chaque bloc dans une grande méta-preuve qui les combine toutes. C'est possible pour SNArks et pour KZG, mais pas pour les agences Merkle (vous pouvez légèrement combiner les branches Merkle, mais cela vous permet d'économiser du log (txs par bloc) /log (nombre total de keystores), peut-être 15 à 30 % en pratique, donc ça ne vaut probablement pas le coût).

L'agrégation ne vaut la peine que lorsque le système compte un nombre important d'utilisateurs. En réalité, il est normal pour une implémentation en version 1 de laisser de côté l'agrégation et de l'implémenter pour la version 2.

Comment fonctionneraient les preuves Merkle ?

Celui-ci est simple : suivez directement le schéma de la section précédente. Plus précisément, chaque « preuve » (en supposant que le cas de difficulté maximale consiste à prouver une L2 contre une autre) contiendrait :

  • Une branche de Merkle prouvant la racine d'État de la banque de clés L2, étant donné la dernière racine d'État d'Ethereum connue par la L2. La racine d'état de L2, qui détient la banque de clés, est stockée sur un emplacement de stockage connu avec une adresse connue (le contrat sur L1 représente la L2). Le chemin dans l'arborescence peut donc être codé en dur.
  • Une agence Merkle prouvant les clés de vérification actuelles, étant donné la racine de la bibliothèque de clés L2. Là encore, la clé de vérification est stockée dans un emplacement de stockage connu à une adresse connue, de sorte que le chemin peut être codé en dur.

Malheureusement, les preuves d'état d'Ethereum sont compliquées, mais il existe des bibliothèques pour les vérifier, et si vous utilisez ces bibliothèques, ce mécanisme n'est pas trop compliqué à mettre en œuvre.

Le plus gros problème, c'est le coût. Les preuves de Merkle sont longues, et les arbres Patricia le sont malheureusement environ 3,9 fois plus que nécessaire (précisément : une preuve de Merkle idéale pour un arbre contenant N objets fait 32 log2 (N) octets, et comme les arbres Patricia d'Ethereum ont 16 feuilles par enfant, les preuves de ces arbres font 32 15 log16 (N) ~= 125 log2 (N) octets). Dans un État comptant environ 250 millions (~2 m²) de comptes, chaque preuve fait 125 * 28 = 3 500 octets, soit environ 56 000 de gaz, plus les frais supplémentaires liés au décodage et à la vérification des hachages.

Deux preuves réunies coûteraient entre 100 000 et 150 000 gaz (sans compter la vérification de la signature si elle est utilisée par transaction), soit bien plus que les 21 000 gaz de base actuels par transaction. Mais la disparité ne fait qu'empirer si les preuves sont vérifiées en L2. Les calculs au sein d'une L2 ne coûtent pas cher, car ils se font hors chaîne et dans un écosystème comportant beaucoup moins de nœuds que la L1. Les données, quant à elles, doivent être publiées en L1. La comparaison n'est donc pas de 21 000 gaz contre 150 000 gaz ; il s'agit de 21 000 L2 contre 100 000 gaz L1.

Nous pouvons calculer ce que cela signifie en comparant les coûts du gaz L1 et les coûts du gaz L2 :

La L1 coûte actuellement 15 à 25 fois plus chère que la L2 pour les envois simples, et 20 à 50 fois plus chère pour les échanges de jetons. Les envois simples sont relativement gourmands en données, mais les swaps sont beaucoup plus gourmands en termes de calcul. Les swaps constituent donc une meilleure référence pour déterminer le coût approximatif du calcul L1 par rapport au calcul L2. Compte tenu de tout cela, si nous supposons un ratio de coûts multiplié par 30 entre les coûts de calcul L1 et les coûts de calcul L2, cela signifie que le fait d'ajouter une preuve Merkle à L2 coûtera l'équivalent de cinquante transactions régulières.

Bien sûr, l'utilisation d'un arbre de Merkle binaire peut réduire les coûts d'environ 4 fois, mais malgré tout, le coût sera trop élevé dans la plupart des cas. Et si nous sommes prêtes à faire le sacrifice de ne plus être compatibles avec l'arbre d'états hexaires actuel d'Ethereum, autant rechercher des options encore meilleures.

Comment fonctionneraient les preuves ZK-SNARK ?

D'un point de vue conceptuel, l'utilisation des zk-SNARK est également facile à comprendre : il vous suffit de remplacer les preuves de Merkle dans le schéma ci-dessus par un ZK-SNARK prouvant que ces preuves de Merkle existent. Un ZK-SNARK coûte environ 400 000 gaz de calcul, et environ 400 octets (à comparer : 21 000 gaz et 100 octets pour une transaction de base, à l'avenir, réductible à environ 25 octets avec compression). Du point de vue informatique, un ZK-SNARK coûte 19 fois le coût d'une transaction de base aujourd'hui, et du point de vue des données, un ZK-SNARK coûte 4 fois plus cher qu'une transaction de base aujourd'hui, et 16 fois ce qu'une transaction de base pourrait coûter à l'avenir.

Ces chiffres constituent une nette amélioration par rapport aux preuves de Merkle, mais ils restent assez chers. Il existe deux manières d'améliorer cela : (i) des preuves KZG spécialisées, ou (ii) l'agrégation, similaire à l'agrégation ERC-4337 mais en utilisant des mathématiques plus sophistiquées. Nous pouvons examiner les deux.

Comment fonctionneraient les épreuves KZG spéciales ?

Attention, cette section est beaucoup plus mathématique que les autres. C'est parce que nous allons au-delà des outils à usage général et que nous créons quelque chose de spécial pour être moins cher. Nous devons donc passer beaucoup plus de temps à « passer sous le capot ». Si vous n'aimez pas les mathématiques approfondies, passez directement à la section suivante.

Tout d'abord, un résumé du fonctionnement des engagements du KZG :

  • Nous pouvons représenter un ensemble de données [D_1... D_n] avec la preuve KZG d'un polynôme dérivé des données : plus précisément, le polynôme P où P (w) = D_1, P (w²) = D_2... P (w) = D_n. w ici est une « racine de l'unité », une valeur où w = 1 pour une taille de domaine d'évaluation N (tout cela se fait dans un corps fini).
  • Pour « passer » à P, nous créons un point de courbe elliptique com (P) = PG + P¹ S¹ + ... + P* S. Ici :
    • G est le point générateur de la courbe
    • P est le coefficient du IIe degré du polynôme P
    • S est le point I de la configuration fiable
  • Pour prouver P (z) = a, nous créons un polynôme quotient Q = (P - a)/(X - z), et nous créons un engagement com (Q) à cet égard. Il n'est possible de créer un tel polynôme que si P (z) est réellement égal à a.
  • Pour vérifier une preuve, nous vérifions l'équation Q * (X - z) = P - a en vérifiant la preuve com (Q) et l'engagement polynomial com (P) : nous vérifions e (com (Q), com (X - z)) ? = e (com (P) - com (a), com (1))

Certaines propriétés clés qu'il est important de comprendre sont les suivantes :

  • La preuve, c'est juste la valeur com (Q), qui fait 48 octets
  • com (P1) + com (P₂) = com (P¹ + P₂)
  • Cela signifie également que vous pouvez « modifier » une valeur dans un engagement existant. Supposons que nous sachions que D_i est actuellement a, que nous voulions le définir sur b, et que l'engagement existant envers D est com (P). Un engagement en faveur de « P, mais avec P (w) = b, et aucune autre évaluation n'a changé », puis nous avons défini com (New_P) = com (P) + (b-a) * com (L), où L est un « polynôme de Lagrange » qui vaut 1 pour wet 0 pour les autres points w.
  • Pour effectuer ces mises à jour efficacement, tous les N engagements vis-à-vis des polynômes de Lagrange (com (L)) peuvent être précalculés et stockés par chaque client. Dans un contrat en chaîne, il peut être trop important de stocker tous les N engagements. Vous pourriez donc prendre un engagement KZG sur l'ensemble des valeurs com (L_i) (ou hash (com (L_i)). Ainsi, chaque fois que quelqu'un a besoin de mettre à jour l'arborescence de la chaîne, il peut simplement fournir le com (L_i) approprié avec une preuve de son exactitude.

Nous avons donc une structure qui nous permet de continuer à ajouter des valeurs à la fin d'une liste qui ne cesse de s'allonger, mais avec une certaine limite de taille (en réalité, des centaines de millions pourraient être viables). Nous l'utilisons ensuite comme structure de données pour gérer (i) un engagement envers la liste des clés de chaque L2, stockée sur cette L2 et mise en miroir sur la L1, et (ii) un engagement envers la liste des engagements clés L2, stockée sur l'Ethereum L1 et reflétée sur chaque L2.

La mise à jour des engagements pourrait soit faire partie de la logique principale de la L2, soit être mise en œuvre sans modification du protocole principal de la L2 par le biais de passerelles de dépôt et de retrait.

Pour obtenir une preuve complète, il faudrait donc :

  • Le dernier com (liste de touches) sur le keystore L2 (48 octets)
  • Preuve KZG que com (liste de clés) est une valeur contenue dans com (mirror_list), l'engagement à respecter la liste de tous les engagements de la liste clé (48 octets)
  • Preuve KZG de votre clé en com (liste de clés) (48 octets, plus 4 octets pour l'index)

Il est en fait possible de fusionner les deux preuves KZG en une seule, donc nous obtenons une taille totale de 100 octets seulement.

Notez une subtilité : comme la liste des touches est une liste, et non une carte clé/valeur comme c'est le cas pour l'État, la liste des touches devra attribuer les positions de manière séquentielle. Le contrat d'engagement des clés contiendrait son propre registre interne associant chaque keystore à un identifiant, et pour chaque clé, il stockerait le hash (clé, adresse du keystore) au lieu d'une simple clé, afin de communiquer sans ambiguïté aux autres L2 de quel keystore parle une entrée en particulier.

L'avantage de cette technique, c'est qu'elle fonctionne très bien en L2. Les données font 100 octets, soit environ 4 fois plus courtes qu'un ZK-SNARK et bien plus courtes qu'une preuve Merkle. Le coût de calcul correspond en grande partie à un chèque d'appariement de taille 2, soit environ 119 000 essence. En L1, les données sont moins importantes que les calculs. Malheureusement, le KZG coûte un peu plus cher que les preuves de Merkle.

Comment fonctionneraient les arbres Verkle ?

Les arbres Verkle consistent essentiellement à empiler les engagements KZG (ou les engagements IPA, qui peuvent être plus efficaces et utiliser une cryptographie plus simple) les uns sur les autres : pour stocker 2 valeurs, vous pouvez prendre un engagement KZG sur une liste de 2 valeurs, chacune d'entre elles constituant en elle-même un engagement KZG à 2 valeurs. Les arbres Verkle sont fortement < a href= " https://notes.ethereum.org/@vbuterin/verkle_tree_eip " > considéré pour l'arbre d'état d'Ethereum, parce que les arbres de Verkle peuvent être utilisés pour contenir des cartes de valeurs clés et pas seulement des listes (en gros, vous pouvez créer un arbre de 2 m² mais le démarrer vide, en ne remplissant que des parties spécifiques de l'arbre une fois que vous en avez réellement besoin).

À quoi ressemble un arbre Verkle. En pratique, vous pouvez donner à chaque nœud une largeur de 256 == 2 pour les arbres basés sur l'IPA, ou de 2 ² pour les arbres basés sur le KzG.

Les preuves dans les arbres Verkle sont légèrement plus longues qu'en KZG ; elles peuvent faire quelques centaines d'octets. Ils sont également difficiles à vérifier, surtout si vous essayez de regrouper de nombreuses preuves en une seule.

En réalité, les arbres Verkle devraient être considérés comme des arbres Merkle, mais ils sont plus viables sans SNARking (en raison de la baisse des coûts de données) et moins chers avec SnarKing (grâce à la baisse des coûts des prouvateurs).

Le principal avantage des arbres de Verkle est la possibilité d'harmoniser les structures de données : les preuves de Verkle peuvent être utilisées directement sur l'état L1 ou L2, sans structures de superposition, et en utilisant exactement le même mécanisme pour L1 et L2. Une fois que les ordinateurs quantiques poseront un problème, ou une fois que les branches de Merkle seront suffisamment efficaces, les arbres de Verkle pourraient être remplacés sur place par un arbre de hachage binaire doté d'une fonction de hachage adaptée à Snark.

Agrégation

Si N utilisateurs effectuent N transactions (ou, de façon plus réaliste, N ERC-4337 UserOperations) qui doivent prouver N réclamations inter-chaînes, nous pouvons économiser beaucoup de gaz en agrégeant ces preuves : le constructeur qui combinerait ces transactions dans un bloc ou un ensemble pouvant être intégré à un bloc peut créer une preuve unique prouvant toutes ces affirmations simultanément.

Cela peut vouloir dire :

Dans les trois cas, les preuves ne coûteraient que quelques centaines de milliers d'essence chacune. Le créateur devrait en créer un sur chaque L2 pour les utilisateurs de cette L2 ; par conséquent, pour que cela soit utile, le schéma dans son ensemble doit être suffisamment utilisé pour qu'il y ait très souvent au moins quelques transactions dans le même bloc sur plusieurs L2 majeurs.

Si des zk-SNARK sont utilisés, le principal coût marginal est simplement la « logique commerciale » qui consiste à transmettre des chiffres entre les contrats, soit peut-être quelques milliers de L2 par utilisateur. Si des appareils KZG sont utilisés, le testeur devra ajouter 48 litres de gaz pour chaque unité de stockage de gaz utilisée dans ce bloc. Ainsi, le coût marginal du système par utilisateur ajouterait environ 800 litres de gaz supplémentaires par litre (et non par utilisateur) en plus. Mais ces coûts sont bien inférieurs à ceux liés à l'absence d'agrégation, qui implique inévitablement plus de 10 000 litres de gaz L1 et des centaines de milliers de gaz L2 par utilisateur. Pour les arbres Verkle, vous pouvez soit utiliser directement les multi-preuves Verkle, en ajoutant environ 100 à 200 octets par utilisateur, soit créer un ZK-SNARK à partir d'un Verkle multiproof, ce qui coûte des coûts similaires à ceux des branches ZK-SNARKS de Merkle mais c'est nettement moins cher à prouver.

Du point de vue de la mise en œuvre, il est probablement préférable de demander à des groupeurs d'agréger les preuves inter-chaînes via la norme d'abstraction de comptes ERC-4337. L'ERC-4337 dispose déjà d'un mécanisme permettant aux créateurs d'agréger certaines parties de UserOperations de manière personnalisée. Il existe même une implémentation < a href= " https://hackmd.io/@voltrevo/BJ0QBY3ZI " > pour l'agrégation de signatures BLS, ce qui pourrait réduire les coûts de gaz sur la couche L2 de 1,5 à 3 fois selon les autres formes de compression incluses.

Schéma tiré d'un article < a href= " https://hackmd.io/@voltrevo/bj0qby3zi " > sur la mise en œuvre du portefeuille BLS montrant le flux de travail des signatures agrégées BLS dans une version antérieure de l'ERC-4337. Le flux de travail d'agrégation des preuves inter-chaînes sera probablement très similaire.

Lecture directe par l'État

Une dernière possibilité, utilisable uniquement pour la L2 lisant la L1 (et non la L1 lisant L2), est de modifier les L2 pour leur permettre de passer des appels statiques aux contrats de L1 directement.

Cela peut être fait à l'aide d'un opcode ou d'une précompilation, qui autorise les appels en L1 où vous fournissez l'adresse de destination, le gaz et les données d'appel, et qui renvoie le résultat, mais comme ces appels sont statiques, ils ne peuvent pas réellement modifier l'état L1. Les L2 doivent déjà connaître la L1 pour traiter les dépôts. Rien de fondamental n'empêche donc la mise en œuvre d'une telle solution ; il s'agit principalement d'un défi de mise en œuvre technique (voir : cet appel d'offres d'Optimism visant à prendre en charge les appels statiques vers la L1).

Notez que si le keystore est en L1 et que les L2 intègrent la fonctionnalité d'appel statique L1, aucune preuve n'est requise ! Cependant, si les L2 n'intègrent pas les appels statiques L1, ou si le keystore est en L2 (ce qui devra peut-être être le cas, une fois que la L1 deviendra trop chère pour que les utilisateurs puissent l'utiliser ne serait-ce qu'un tout petit peu), alors des preuves seront requises.

Comment la L2 connaît-elle la récente racine d'État d'Ethereum ?

Tous les schémas ci-dessus nécessitent que la L2 accède soit à la racine de l'état L1 récent, soit à l'intégralité de l'état L1 récent. Heureusement, toutes les L2 disposent déjà de certaines fonctionnalités qui leur permettent d'accéder à l'état récent de la L1. C'est parce qu'ils ont besoin d'une telle fonctionnalité pour traiter les messages provenant de la L1 vers la L2, notamment les dépôts.

Et en effet, si une L2 possède une fonction de dépôt, vous pouvez utiliser cette L2 telle quelle pour transférer les racines de l'État L1 dans un contrat sur la L2 : il suffit de créer un contrat en L1, d'appeler l'opcode BLOCKHASH, et de le transmettre à L2 sous forme de message de dépôt. L'en-tête du bloc complet peut être reçu, et sa racine d'état extraite, côté L2. Cependant, il serait préférable que chaque L2 dispose d'un moyen explicite d'accéder directement à l'état L1 complet récent ou aux racines récentes de l'État L1.

Le principal défi lié à l'optimisation de la façon dont les L2 reçoivent les racines d'état L1 récentes est de garantir à la fois la sécurité et une faible latence :

  • Si les L2 implémentent la fonctionnalité de « lecture directe de la L1 » de manière paresseuse, en lisant uniquement les racines d'état L1 finalisées, le délai sera normalement de 15 minutes, mais dans les cas extrêmes de fuites d'inactivité (que vous devez tolérer), le délai peut être de plusieurs semaines.
  • Les L2 peuvent absolument être conçues pour lire des racines d'État L1 bien plus récentes, mais comme la L1 peut revenir en arrière (même avec une finalité à un seul emplacement, des retours peuvent se produire lors de fuites d'inactivité), la L2 devrait également être capable de revenir en arrière. C'est un défi technique du point de vue du génie logiciel, mais au moins Optimism possède déjà cette capacité.
  • Si vous utilisez le pont de dépôt pour intégrer les racines de l'État L1 à la L2, la simple viabilité économique peut prendre beaucoup de temps entre les mises à jour des dépôts : si le coût total d'un dépôt est de 100 000 gaz, et que nous supposons que l'ETH est de 1800 dollars, et que les frais sont de 200 gwei, et que les racines L1 sont introduites en L2 une fois par jour, cela représenterait un coût de 36 dollars par L2 par jour, soit 13148 dollars par L2 et par an pour maintenir le système. Avec un retard d'une heure, cela passe à 315 569 dollars par L2 et par an. Dans le meilleur des cas, un filet constant d'utilisateurs fortunés impatients couvre les frais de mise à jour et met le système à jour pour tous les autres. Dans le pire des cas, un acteur altruiste devrait payer lui-même.
  • Les « oracles » (du moins, le type de technologie que certains appellent « oracles ») ne sont pas une solution acceptable ici : la gestion des clés de portefeuille est une fonctionnalité de bas niveau très critique sur le plan de la sécurité. Elle devrait donc dépendre au maximum de quelques éléments d'infrastructure de bas niveau très simples et fiables sur le plan cryptographique.

De plus, dans la direction opposée (L1 lit L2) :

  • Si l'on se fie à des résultats optimistes, State Roots met une semaine à atteindre la L1 à cause du délai de prévention des fraudes. En ce qui concerne les cumuls ZK, cela prend quelques heures pour l'instant à cause des temps de démonstration et des limites économiques, mais les technologies futures permettront de réduire ce chiffre.
  • Les préconfirmations (provenant de séquenceurs, d'attestateurs, etc.) ne sont pas une solution acceptable pour que la L1 lise la L2. La gestion des portefeuilles est une fonctionnalité de bas niveau très critique pour la sécurité. Le niveau de sécurité de la communication L2 - > L1 doit donc être absolu : il ne devrait même pas être possible de transmettre une fausse racine d'état L1 en reprenant le jeu de validateurs L2. Les seules racines de l'État auxquelles la L1 devrait avoir confiance sont celles qui ont été considérées comme définitives par le contrat de détention des racines de l'État de la L2 en L1.

Certaines de ces vitesses pour les opérations inter-chaînes en toute confiance sont trop lentes pour de nombreux cas d'utilisation de DeFi ; dans ces cas, vous avez besoin de ponts plus rapides avec des modèles de sécurité plus imparfaits. Pour ce qui est de la mise à jour des clés de portefeuille, des délais plus longs sont toutefois plus acceptables : vous ne retardez pas les transactions de plusieurs heures, vous retardez les changements de clés. Vous n'aurez qu'à conserver les anciennes clés plus longtemps. Si vous changez de clé parce que des clés sont volées, c'est que vous êtes exposée à une période de vulnérabilité importante, mais cela peut être atténué, par exemple en installant une fonction de blocage des portefeuilles.

En fin de compte, la meilleure solution pour minimiser la latence est que les L2 implémentent la lecture directe des racines d'état L1 de manière optimale, où chaque bloc L2 (ou le journal de calcul de la racine d'état) contient un pointeur vers le bloc L1 le plus récent. Ainsi, si la L1 revient, la L2 peut également revenir en arrière. Les contrats Keystore doivent être passés soit sur le réseau principal, soit sur des L2 qui sont des ZK-Rollups, ce qui permet de passer rapidement en L1.

Les blocs de la chaîne L2 peuvent dépendre non seulement des blocs L2 précédents, mais aussi d'un bloc L1. Si la L1 revient après un tel lien, la L2 revient également. Il convient de noter que c'est également ainsi qu'une version antérieure (antérieure à Dank) du sharding était envisagée ; voir le code ici.

De quel niveau de connexion à Ethereum une autre chaîne a-t-elle besoin pour détenir des portefeuilles dont les keystores sont enracinés sur Ethereum ou un L2 ?

Étonnamment, pas tant que ça. En fait, il n'est même pas nécessaire que ce soit un cumul : s'il s'agit d'un L3 ou d'un Validium, vous pouvez y conserver des portefeuilles, à condition que vous déteniez des keystores en L1 ou en ZK. Ce dont vous avez besoin, c'est que la chaîne ait un accès direct aux racines de l'État d'Ethereum et qu'elle s'engage techniquement et socialement à être prête à se réorganiser si Ethereum se réorganise, et à hard fork si Ethereum fait des hard forks.

Un problème de recherche intéressant est de déterminer dans quelle mesure il est possible pour une chaîne d'avoir cette forme de connexion avec plusieurs autres chaînes (par ex. Ethereum et Zcash). Le faire naïvement est possible : votre chaîne pourrait accepter de se réorganiser en cas de réorganisation d'Ethereum ou de Zcash (et de hard fork s'il s'agit d'Ethereum ou de Zcash), mais les opérateurs de vos nœuds et votre communauté en général ont deux fois plus de dépendances techniques et politiques. Une telle technique pourrait donc être utilisée pour se connecter à quelques autres chaînes, mais à un coût plus élevé. Les schémas basés sur les ponts ZK ont des propriétés techniques intéressantes, mais leur principal point faible est qu'ils ne résistent pas aux attaques à 51 % ou aux hard forks. Il existe peut-être des solutions plus intelligentes.

Préserver la confidentialité

Idéalement, nous voulons également préserver la confidentialité. Si vous avez de nombreux portefeuilles gérés par le même keystore, nous voulons nous assurer que :

  • On ignore si ces portefeuilles sont tous connectés les uns aux autres.
  • Les tuteurs sociaux ne savent pas quelles sont les adresses qu'ils surveillent.

Cela pose quelques problèmes :

  • Nous ne pouvons pas utiliser directement les preuves Merkle, car elles ne préservent pas la confidentialité.
  • Si nous utilisons KZG ou SNARKs, la preuve doit fournir une version masquée de la clé de vérification, sans révéler son emplacement.
  • Si nous utilisons l'agrégation, l'agrégateur ne devrait pas connaître l'emplacement en texte clair ; il devrait plutôt recevoir des preuves en aveugle et avoir un moyen de les agréger.
  • Nous ne pouvons pas utiliser la « version allégée » (utilisez des preuves inter-chaînes uniquement pour mettre à jour les clés), car cela crée une fuite de confidentialité : si de nombreux portefeuilles sont mis à jour en même temps à cause d'une procédure de mise à jour, le timing indique que ces portefeuilles sont probablement liés. Nous devons donc utiliser la « version lourde » (preuves inter-chaînes pour chaque transaction).

Avec les SNARKs, les solutions sont simples sur le plan conceptuel : les preuves masquent des informations par défaut, et l'agrégateur doit produire un SNARK récursif pour prouver les SNARK.

Le principal défi de cette approche aujourd'hui est que l'agrégateur doit créer un SNARK récursif, ce qui est assez lent actuellement.

Avec KZG, nous pouvons utiliser < a href= " https://notes.ethereum.org/@vbuterin/non_index_revealing_proof " > pour ce travailler sur des preuves KZG non révélatrices d'index (voir aussi : une version plus formalisée de ce travail dans l'article de Caulk) comme point de départ . L'agrégation de preuves en aveugle est toutefois un problème ouvert qui nécessite plus d'attention.

La lecture directe de la L1 depuis la L2 ne préserve malheureusement pas la confidentialité, même si la mise en œuvre d'une fonctionnalité de lecture directe reste très utile, à la fois pour minimiser la latence et pour d'autres applications.

Résumé

  • Pour avoir des portefeuilles de relance sociale inter-chaînes, le flux de travail le plus réaliste est un portefeuille qui gère un magasin de clés en un seul endroit, et des portefeuilles sur de nombreux sites, où le portefeuille lit le keystore soit (i) pour mettre à jour sa vue locale de la clé de vérification, soit (ii) pendant le processus de vérification de chaque transaction.
  • Les preuves inter-chaînes sont un élément clé pour rendre cela possible. Nous devons optimiser ces preuves de manière rigoureuse. Soit les zk-SNARKS, en attente des preuves de Verkle, soit une solution KZG personnalisée, semblent être la meilleure option.
  • À plus long terme, des protocoles d'agrégation dans le cadre desquels les bundlers génèrent des preuves agrégées dans le cadre de la création d'un ensemble de toutes les UserOperations soumises par les utilisateurs seront nécessaires pour minimiser les coûts. Cela devrait probablement être intégré à l'écosystème ERC-4337, même si des modifications seront probablement nécessaires.
  • Les L2 doivent être optimisés afin de minimiser la latence lors de la lecture de l'état L1 (ou du moins de la racine de l'état) depuis l'intérieur de la L2. La lecture directe de l'état L1 par les L2 est idéale et permet d'économiser de l'espace de preuve.
  • Les portefeuilles ne peuvent pas être placés uniquement sur des réseaux L2 ; vous pouvez également les placer sur des systèmes où le niveau de connexion à Ethereum est faible (L3, ou même des chaînes distinctes qui acceptent uniquement d'inclure les racines d'État d'Ethereum et de procéder à une réorganisation ou à un hard fork lorsque Ethereum se reforme ou fait des forks).
  • Cependant, les keystores devraient se trouver soit en L1, soit en ZK-Rollup L2, un système de haute sécurité. Le fait d'être en L1 permet d'économiser beaucoup de complexité, même si à long terme, cela peut coûter trop cher. D'où la nécessité de créer des keystores en L2.
  • La préservation de la vie privée demandera un travail supplémentaire et compliquera certaines options. Cependant, nous devrions probablement nous orienter vers des solutions préservant la confidentialité de toute façon, et au moins nous assurer que tout ce que nous proposons est compatible avec la préservation de la vie privée.

Avertissement:

  1. Cet article est repris de [Vitalik]. Tous les droits d'auteur appartiennent à l'auteur original [Vitalik Buterin]. En cas d'objection à cette réimpression, contactez l'équipe de Gate Learn, elle s'en occupera rapidement.
  2. Avertissement en matière de responsabilité : Les points de vue et opinions exprimés dans cet article sont uniquement ceux de l'auteur et ne constituent en aucun cas un conseil d'investissement.
  3. Les traductions de l'article dans d'autres langues sont effectuées par l'équipe de Gate Learn. Sauf mention contraire, il est interdit de copier, de distribuer ou de plagier les articles traduits.
Lancez-vous
Inscrivez-vous et obtenez un bon de
100$
!