Pas plus tard qu’hier, un événement choquant s’est produit : Linea, la solution Ethereum Layer 2 développée par Consensys, la société mère de Metamask, a été fermée de manière proactive. La raison officielle invoquée était d’atténuer l’impact de l’incident de piratage de Velocore. Cet incident rappelle inévitablement un cas précédent où la chaîne BSC (BNB Chain) a également été fermée sous coordination officielle pour minimiser les pertes de piratage. Ces événements amènent souvent les gens à remettre en question les valeurs décentralisées que prône le Web3.
La raison principale derrière les événements susmentionnés réside dans l’imperfection de l’infrastructure, en particulier son manque de décentralisation. Si une blockchain était suffisamment décentralisée, elle ne devrait pas pouvoir s’arrêter aussi facilement. En raison de la structure unique de Ethereum Layer 2, la plupart des solutions Layer 2 s’appuient sur des séquenceurs centralisés. Malgré le discours croissant sur les séquenceurs décentralisés ces dernières années, compte tenu de l’objectif et de la structure de Layer 2, nous pouvons supposer que les séquenceurs de Layer 2 ont peu de chances d’atteindre des niveaux élevés de décentralisation. En fait, ils peuvent finir par être moins décentralisés que la chaîne BSC. Si c’est le cas, que peut-on faire ? Pour Layer 2, les risques les plus immédiats des séquenceurs non décentralisés sont le manque de résistance à la censure et de vivacité. S’il n’y a que quelques entités qui traitent les transactions (séquenceurs), elles ont un pouvoir absolu sur le fait de vous servir ou non : elles peuvent refuser vos transactions à volonté, vous laissant sans recours. Aborder la question de la résistance à la censure dans Layer 2 est clairement un sujet important. Au cours des dernières années, diverses solutions Ethereum Layer 2 ont proposé différentes approches pour s’attaquer à ce problème. Par exemple, Loopring, Degate et StarkEx ont introduit des fonctions de retrait forcé et de trappe d’évacuation, tandis qu’Arbitrum et d’autres Optimistic Rollups ont implémenté des fonctionnalités d’inclusion de force. Ces mécanismes peuvent imposer des contrôles sur les séquenceurs afin d’empêcher le refus arbitraire des transactions de l’utilisateur. Dans l’article d’aujourd’hui, NIC Lin de la Taipei Ethereum Association partage son expérience de première main, expérimentant les fonctionnalités de transaction résistantes à la censure de quatre principaux Rollups et fournissant une analyse approfondie du mécanisme Force Inclusion, en se concentrant sur le flux de travail et les méthodes opérationnelles. Cette analyse est particulièrement précieuse pour la communauté Ethereum et les grands détenteurs d’actifs.
La censure résistance dans les transactions est cruciale pour toute blockchain. Si une blockchain peut censurer et rejeter arbitrairement les transactions des utilisateurs, elle n’est pas différente d’un serveur Web2. La résistance à la censure des transactions d’Ethereum est actuellement assurée par ses nombreux validateurs. Si quelqu’un veut censurer la transaction de Bob et l’empêcher d’être incluse dans la blockchain, il devrait soit soudoyer la majorité des validateurs du réseau, soit spammer le réseau avec des transactions inutiles qui ont des frais plus élevés que ceux de Bob, occupant ainsi de l’espace de bloc. Les deux méthodes sont extrêmement coûteuses.
Remarque : Dans l’architecture PBS (Proposant-Builder Separation actuelle) d’Ethereum, le coût de la censure des transactions est considérablement réduit. Par exemple, vous pouvez examiner la proportion de blocs qui se conforment à la censure de l’OFAC sur les transactions Tornado Cash. La résistance à la censure actuelle repose sur des validateurs et des relais indépendants qui ne relèvent pas de la juridiction de l’OFAC et d’autres entités gouvernementales.
Mais qu’en est-il des cumuls ? Les cumuls ne nécessitent pas un grand nombre de validateurs pour garantir la sécurité. Même si un Rollup n’a qu’une seule entité centralisée (Sequencer) produisant des blocs, il reste aussi sécurisé que la couche 1 (L1). Cependant, la sécurité et la résistance à la censure sont deux questions différentes. Un Rollup, tout en étant aussi sécurisé qu’Ethereum, peut toujours censurer la transaction de n’importe quel utilisateur s’il ne dispose que d’un seul séquenceur centralisé.
Le séquenceur peut refuser de traiter la transaction d’un utilisateur, ce qui entraîne le verrouillage des fonds de l’utilisateur et l’empêche de quitter le cumul.
d’inclusion Au lieu d’exiger que les Rollups disposent d’un grand nombre de séquenceurs décentralisés, il est plus efficace d’exploiter directement les résistance à la censure de la couche 1 (L1) :
Étant donné que le séquenceur doit empaqueter les données de transaction et les envoyer au contrat de cumul sur L1, nous pouvons ajouter une fonctionnalité dans le contrat qui permet aux utilisateurs d’insérer eux-mêmes leurs transactions dans le contrat de cumul. Ce mécanisme est connu sous le nom d'« inclusion forcée ». Aussi long que le séquenceur ne puisse pas censurer les utilisateurs au niveau L1, il ne peut pas empêcher les utilisateurs d’insérer de force des transactions au niveau L1. De cette façon, le Rollup peut hériter de la résistance à la censure de L1.
Le séquenceur ne peut pas examiner les transactions L1 de l’utilisateur sans payer un coût élevé
Tout d’abord, discutons du processus de dépôt d’Optimism. Ce processus de dépôt implique non seulement le transfert de fonds vers Optimism, mais aussi l’envoi de « messages d’utilisateur à L2 ». Lorsqu’un nœud L2 reçoit un message nouvellement déposé, il convertit le message en une transaction L2 et l’exécute, en le remettant au destinataire spécifié.
Messages utilisateur déposés de L1 à L2
Contrat L1CrossDomainMessenger
Lorsqu’un utilisateur souhaite dépôt ETH ou ERC-20 des tokens dans Optimism, il interagit avec le contrat L1StandardBridge sur L1 via une page Web frontale, en spécifiant le montant à dépôt et l’adresse L2 qui recevra ces actifs. Le contrat L1StandardBridge transfère ensuite le message au contrat L1CrossDomainMessenger, qui agit en tant que bridge de communication principal entre L1 et L2. Le L1StandardBridge utilise ce composant de communication pour interagir avec le L2StandardBridge sur L2, déterminant qui peut mint des jetons sur L2 ou déverrouiller des jetons de L1. Les développeurs qui ont besoin de créer des contrats qui interopèrent et synchronisent les états entre L1 et L2 peuvent les créer sur le contrat L1CrossDomainMessenger.
Messages utilisateur transmis de L1 à L2 via le contrat CrossDomainMessenger
Remarque : Dans certaines images de cet article, CrossDomainMessenger s’écrit CrossChainMessenger.
Contrat OptimismPortal
Le contrat L1CrossDomainMessenger transfère ensuite le message à la couche la plus basse, le contrat OptimismPortal. Après le traitement du message, le contrat OptimismPortal émet un événement appelé TransactionDeposited, qui inclut des paramètres tels que « l’expéditeur », le « destinataire » et d’autres détails d’exécution pertinents. Les nœuds Optimism sur L2 écoutent cet événement TransactionDeposited à partir du contrat OptimismPortal et convertissent les paramètres de l’événement en une transaction L2. L’initiateur de cette transaction sera « l’expéditeur » spécifié dans l’événement, le destinataire sera le « destinataire » mentionné dans l’événement, et d’autres détails de la transaction seront également dérivés des paramètres de l’événement.
Les
nœuds L2 convertissent les paramètres de l’événement Transaction Deposited émis par OptimismPortal en une transaction L2.
Par exemple, lorsqu’un utilisateur dépose 0,01 ETH par le biais du contrat L1StandardBridge, le message et le ETH sont transmis au contrat OptimismPortal (adresse 0xbEb5... Quelques minutes plus tard, cela est converti en une transaction L2 : l’expéditeur du message est le contrat L1CrossDomainMessenger, le destinataire est le contrat L2CrossDomainMessenger sur L2, et le contenu du message indique que le L1StandardBridge a reçu un ETH dépôt 0.01 de Bob. Cela déclenche ensuite des processus supplémentaires, tels que la monting de 0,01 ETH pour le L2StandardBridge, qui le transfère ensuite à Bob.
Comment le déclencher
Si vous souhaitez inclure de force une transaction dans le contrat de cumul d’Optimism, votre objectif est de vous assurer qu’une transaction « initiée et exécutée à partir de votre adresse L2 sur L2 » peut être exécutée avec succès. Pour ce faire, vous devez envoyer le message directement au contrat OptimismPortal à l’aide de votre adresse L2 (notez que le contrat OptimismPortal est en fait sur L1, mais que le format d’adresse OP correspond au format d’adresse L1, vous pouvez donc appeler ce contrat à l’aide d’un compte L1 avec la même adresse que votre compte L2). « L’expéditeur » de la transaction L2 dérivée de l’événement Transaction déposée émis par le présent contrat sera alors votre compte L2, et le format de transaction sera cohérent avec une transaction L2 standard.
Dans la transaction L2 dérivée de l’événement Transaction Deposited, Bob lui-même sera l’initiateur, le destinataire sera le contrat Uniswap et il inclura le ETH spécifié, tout comme si Bob initiait lui-même la transaction L2
Pour utiliser la fonction Force Inclusion d’Optimism, vous devez appeler directement la fonction depositTransaction du contrat OptimismPortal et saisir les paramètres de la transaction que vous souhaitez exécuter sur L2. J’ai mené une expérience simple d’inclusion de la force. Le but de cette transaction était d’effectuer un auto-transfert sur L2 en utilisant mon adresse (0xeDc1... 6909) et inclure un message disant « Forcer l’inclusion ». Il s’agit de la transaction L1 que j’ai exécutée en appelant la fonction depositTransaction via le contrat OptimismPortal. Comme vous pouvez le voir à partir de l’événement Transaction Deposited qu’il a émis, l’expéditeur et le destinataire sont tous les deux moi-même.
Les valeurs restantes de la colonne opaque Data codent des informations telles que « combien de ETH la personne appelant la fonction depositTransaction attachée », « combien de ETH l’initiateur de la transaction L2 souhaite envoyer au destinataire », « Limite de gaz de la transaction L2 » et « Données pour le récepteur L2 ». Après avoir décodé ces informations, vous obtiendrez les détails suivants : « combien d’ETH la personne appelant la depositTransaction a joint » : 0, car je ne dépose pas d’ETH de L1 à L2 ; « combien d’ETH l’initiateur de la transaction L2 veut envoyer au destinataire » : 5566 (wei) ; « Limite de gaz de transaction L2 » : 50000 ; « Données pour le récepteur L2 » : 0x666f72636520696e636c7573696f6e, qui est l’encodage hexadécimal de la chaîne « inclusion de force ». Peu de temps après, la transaction L2 convertie est apparue : une transaction L2 où je transfère 5566 wei à moi-même, avec le champ Data contenant la chaîne « forcer l’inclusion ». De plus, dans l’avant-dernière ligne de la section Autres attributs, le TxnType (type de transaction) est indiqué comme transaction système 126 (Système), ce qui indique que cette transaction n’a pas été initiée par moi sur L2 mais a été convertie à partir de l’événement Deposited de la transaction L1.
Transaction L2 convertie
Si vous souhaitez appeler un contrat L2 via Force Inclusion et envoyer différentes données, il vous suffit de renseigner les paramètres de la fonction depositTransaction. N’oubliez pas d’utiliser la même adresse L1 que votre compte L2 lors de l’appel de la fonction depositTransaction. De cette façon, lorsque l’événement déposé est converti en une transaction L2, l’initiateur sera votre compte L2. Fenêtre du séquenceur Le nœud Optimism L2 qui convertit l’événement Transaction Deposited en une transaction L2 est en fait le séquenceur. Étant donné qu’il s’agit d’un ordre de transaction, seul le séquenceur peut décider quand convertir l’événement en transaction L2. Lorsque le séquenceur écoute l’événement TransactionDeposited, il ne convertit pas nécessairement l’événement en une transaction L2 immédiatement ; Il peut y avoir un retard. La durée maximale de ce délai s’appelle la fenêtre du séquenceur. Actuellement, la fenêtre du séquenceur sur le réseau principal Optimism est de 24 heures. Cela signifie que lorsqu’un utilisateur dépose de l’argent à partir de L1 ou utilise Forcer l’inclusion pour une transaction, dans le pire des cas, il sera inclus dans l’historique des transactions L2 après 24 heures.
Dans Optimism, l’opération L1 dépôt déclenche un événement Transaction Deposited, puis il suffit d’attendre que le séquenceur inclue l’opération. Cependant, dans Arbitrum, les opérations sur L1 (telles que le dépôt de fonds ou l’envoi de messages à L2) sont stockées dans une file d’attente sur L1, plutôt que de simplement émettre un événement. Le séquenceur dispose d’une période spécifique pour inclure ces transactions en file d’attente dans l’historique des transactions L2. Si le séquenceur ne le fait pas dans ce délai, n’importe qui peut intervenir pour terminer l’inclusion au nom du séquenceur.
Arbitrum maintient une file d’attente dans un contrat L1. Si le séquenceur ne parvient pas à traiter les transactions dans la file d’attente dans un certain délai, n’importe qui peut inclure de force ces transactions dans l’historique des transactions L2. Dans la conception d’Arbitrum, les opérations sur L1, telles que les dépôts, doivent passer par le contrat de boîte de réception différée, où, comme son nom l’indique, ces opérations seront retardées avant de prendre effet. Un autre contrat, la boîte de réception du séquenceur, permet au séquenceur de télécharger directement les transactions L2 vers L1. Chaque fois que le séquenceur télécharge des transactions L2, il peut également prendre certaines transactions en attente de la boîte de réception différée et les inclure dans l’historique des transactions.
Lorsque le séquenceur écrit de nouvelles transactions, il peut également inclure des transactions provenant de la boîte de réception DelayedInbox.
Conception complexe et manque de matériaux de référence
Si vous vous référez à la documentation officielle d’Arbitrum sur le séquenceur et l’inclusion de la force, vous trouverez une explication générale du fonctionnement de l’inclusion de la force, ainsi que des noms de paramètres et de fonctions : Les utilisateurs appellent d’abord la fonction sendUnsignedTransaction sur le contrat DelayedInbox. Si le séquenceur ne l’inclut pas dans un délai d’environ 24 heures, les utilisateurs peuvent appeler la fonction forceInclusion sur le contrat SequencerInbox. Cependant, la documentation officielle ne fournit pas de liens vers ces fonctions, vous devez donc les rechercher vous-même dans le code du contrat. Lorsque vous trouvez la fonction sendUnsignedTransaction, vous vous rendez compte que vous devez remplir vous-même la valeur nonce et la valeur maxFeePerGas. De qui s’agit-il nonce ? Le maxFeePerGas de quel réseau ? Comment faut-il le remplir correctement ? Il n’y a pas de documents de référence, pas même NatSpec. Vous trouverez également de nombreuses fonctions similaires dans le contrat Arbitrum : sendL1FundedUnsignedTransaction, sendUnsignedTransactionToFork, sendContractTransaction, sendL1FundedContractTransaction. Il n’y a pas de documents expliquant les différences entre ces fonctions, comment les utiliser, ou comment remplir les paramètres, pas même NatSpec.
Vous essayez de remplir les paramètres et de soumettre la transaction avec une approche par essais et erreurs, dans l’espoir de trouver l’utilisation correcte. Cependant, vous découvrez que toutes ces fonctions appliquent le crénelage d’adresse à votre adresse L1, ce qui fait que l’expéditeur de la transaction sur L2 est une adresse complètement différente, laissant votre adresse L2 inactive. Plus tard, vous êtes tombé par hasard sur un résultat de recherche Google révélant qu’Arbitrum dispose d’une bibliothèque de didacticiels avec des scripts montrant comment envoyer des transactions L2 à partir de L1 (essentiellement Forcer l’inclusion). Le tutoriel répertorie une fonction qui n’a pas été mentionnée précédemment : sendL2Message. Étonnamment, le paramètre de message requis est en fait une transaction L2 signée à l’aide d’un compte L2. Qui aurait cru que le « message envoyé à L2 par l’intermédiaire de Force Inclusion » est en fait une « transaction L2 signée » ? De plus, il n’y a pas de documents ou de NatSpec expliquant quand et comment utiliser cette fonction.
Conclusion : Générer manuellement une transaction forcée sur Arbitrum est assez compliqué. Il est recommandé de suivre le tutoriel officiel et d’utiliser le SDK Arbitrum. Contrairement à d’autres Rollups, Arbitrum manque d’une documentation claire pour les développeurs et d’annotations de code. De nombreuses fonctions manquent d’explications sur leurs objectifs et leurs paramètres, ce qui oblige les développeurs à passer beaucoup plus de temps que prévu à les intégrer et à les utiliser. J’ai également demandé de l’aide sur le Discord d’Arbitrum, mais je n’ai pas reçu de réponses satisfaisantes. Lorsque j’ai posé la question sur Discord, ils m’ont seulement demandé de regarder sendL2Message et n’ont pas expliqué les fonctions des autres méthodes (y compris celles mentionnées dans la documentation de Force Inclusion comme sendUnsignedTransaction), leurs objectifs, comment les utiliser ou quand les utiliser.
Malheureusement, StarkNet ne dispose actuellement pas d’un mécanisme d’inclusion de la force. Il n’y a que deux articles sur le forum officiel qui traitent de la censure et de l’inclusion forcée. La raison de l’incapacité à prouver les transactions ayant échoué est que le système de preuve à divulgation nulle de connaissance de StarkNet ne peut pas prouver une transaction ayant échoué, de sorte que l’inclusion forcée ne peut pas être autorisée. Si quelqu’un force malicieusement (ou involontairement) l’inclusion d’une transaction échouée et non prouvable, StarkNet serait bloqué : parce qu’une fois que la transaction est incluse de force, le Prover doit prouver l’échec de la transaction, mais il ne le peut pas. StarkNet devrait introduire la possibilité de prouver les transactions ayant échoué dans la version v0.15.0, après quoi le mécanisme d’inclusion de la force devrait être mis en œuvre.
pour la transmission de messages L1->L2 et l’inclusion forcée de zkSync est géré via la fonction requestL2Transaction du contrat de boîte aux lettres. Les utilisateurs spécifient l’adresse L2, les données d’appel, la quantité d’ETH à attacher, la valeur L2GasLimit et d’autres détails. La fonction requestL2Transaction combine ces paramètres dans une transaction L2 et la place dans la PriorityQueue. Lorsque le séquenceur empaquette des transactions et les télécharge vers L1 (via la fonction commitBatches), il indique le nombre de transactions à prendre de la PriorityQueue à inclure dans les enregistrements de transaction L2. En termes d’inclusion de force, zkSync est similaire à Optimism, où l’adresse L2 de l’initiateur (la même que l’adresse L1) est utilisée pour appeler les fonctions pertinentes et remplir les détails nécessaires (appelé, calldata, etc.), plutôt que comme Arbitrum, qui nécessite une transaction L2 signée. Cependant, dans sa conception, il est similaire à Arbitrum, car les deux maintiennent une file d’attente sur L1, et le séquenceur prend les transactions en attente directement soumises par les utilisateurs de la file d’attente et les écrit dans l’historique des transactions.
Si vous dépôt ETH via le bridge officiel de zkSync, comme cette transaction, il appelle la fonction requestL2Transaction du contrat MailBox. Cette fonction place la transaction Deposit ETH L2 dans la PriorityQueue et émet un événement NewPriorityRequest. Étant donné que le contrat encode les données de transaction L2 dans une chaîne d’octets, il n’est pas facilement lisible. Cependant, si vous regardez les paramètres de cette transaction L1, vous verrez que le destinataire L2 est également l’initiateur de la transaction (puisqu’il s’agit d’un dépôt pour soi-même). Au bout d’un certain temps, lorsque le séquenceur retire cette transaction L2 de la PriorityQueue et l’inclut dans l’historique des transactions, elle est convertie en une transaction L2 que vous transférez à vous-même. Le montant du transfert sera le montant en ETH attaché par l’initiateur dans la transaction L1 Deposit ETH. Sur L2, il y aura une transaction où 0xeDc1... 6909 se transfère à lui-même. Le type de transaction (TxnType) est 255, ce qui indique une transaction système. Ensuite, tout comme j’ai expérimenté la fonction de transaction forcée sur Optimism auparavant, j’ai appelé la fonction requestL2Transaction de zkSync et lancé une transaction d’auto-transfert : aucun ETH n’était attaché et les calldata contenaient l’encodage HEX de la chaîne « forcer l’inclusion ». Cela a ensuite été converti en une transaction L2 où je transfère à moi-même, avec les calldata contenant la chaîne hexadécimale pour « forcer l’inclusion » : 0x666f72636520696e636c7573696f6e.
L1 s’appuie sur un grand nombre de validateurs pour assurer la « sécurité » et la « résistance à la censure » du réseau. Les rollups, cependant, ont une résistance à la censure plus faible car les transactions sont écrites par quelques-uns ou même un seul séquenceur. Par conséquent, les cumuls ont besoin d’un mécanisme d’inclusion forcée pour permettre aux utilisateurs de contourner le séquenceur et d’écrire des transactions dans l’historique, empêchant ainsi la censure par le séquenceur de rendre le cumul inutilisable et empêchant les utilisateurs de retirer des fonds. Forcer l’inclusion permet aux utilisateurs d’écrire de force des transactions dans l’historique, mais la conception doit choisir si « les transactions peuvent être immédiatement insérées dans l’historique et prendre effet immédiatement ». Si l’effet immédiat est autorisé, cela aura un impact négatif sur le séquenceur, car les transactions en attente sur L2 pourraient être affectées par des transactions incluses de force à partir de L1. Par conséquent, les mécanismes actuels de Forcer l’inclusion dans les cumuls placent d’abord les transactions insérées à partir de L1 dans un état d’attente et donnent au séquenceur une fenêtre de temps pour réagir et décider d’inclure ou non ces transactions en attente. zkSync et Arbitrum maintiennent tous deux une file d’attente sur L1 pour gérer les transactions L2 ou les messages envoyés de L1 à L2. Arbitrum l’appelle DelayedInbox ; zkSync l’appelle PriorityQueue. Cependant, la méthode d’envoi de transactions L2 de zkSync est plus similaire à celle d’Optimism, où les messages sont envoyés à partir de L1 en utilisant l’adresse L2, de sorte que lorsqu’ils sont convertis en transaction L2, l’initiateur est l’adresse L2. La fonction d’envoi de transactions L2 dans Optimism s’appelle depositTransaction ; dans zkSync, il s’appelle requestL2Transaction. En revanche, Arbitrum génère une transaction L2 complète et la signe, puis l’envoie via la fonction sendL2Message. Sur L2, Arbitrum utilise la signature pour restaurer le signataire en tant qu’initiateur de la transaction L2. À l’heure actuelle, StarkNet ne dispose pas d’un mécanisme d’inclusion de la Force ; zkSync dispose d’un mécanisme d’inclusion forcée à moitié implémenté : il dispose d’une file d’attente prioritaire, et chaque transaction L2 dans la file d’attente a une période de validité d’inclusion, mais cette période de validité n’est actuellement que pour l’affichage. En pratique, le séquenceur peut choisir de ne pas inclure de transactions L2 à partir de la file d’attente prioritaire.
Avis de non-responsabilité : Les points de vue et opinions exprimés dans cet article ne représentent que les opinions personnelles de l’auteur et ne constituent pas un conseil en investissement.
Les autres versions linguistiques de l’article sont traduites par l’équipe de Gate Learn. Sans référence Gate.io, il est interdit de copier, distribuer ou plagier les articles traduits.
Pas plus tard qu’hier, un événement choquant s’est produit : Linea, la solution Ethereum Layer 2 développée par Consensys, la société mère de Metamask, a été fermée de manière proactive. La raison officielle invoquée était d’atténuer l’impact de l’incident de piratage de Velocore. Cet incident rappelle inévitablement un cas précédent où la chaîne BSC (BNB Chain) a également été fermée sous coordination officielle pour minimiser les pertes de piratage. Ces événements amènent souvent les gens à remettre en question les valeurs décentralisées que prône le Web3.
La raison principale derrière les événements susmentionnés réside dans l’imperfection de l’infrastructure, en particulier son manque de décentralisation. Si une blockchain était suffisamment décentralisée, elle ne devrait pas pouvoir s’arrêter aussi facilement. En raison de la structure unique de Ethereum Layer 2, la plupart des solutions Layer 2 s’appuient sur des séquenceurs centralisés. Malgré le discours croissant sur les séquenceurs décentralisés ces dernières années, compte tenu de l’objectif et de la structure de Layer 2, nous pouvons supposer que les séquenceurs de Layer 2 ont peu de chances d’atteindre des niveaux élevés de décentralisation. En fait, ils peuvent finir par être moins décentralisés que la chaîne BSC. Si c’est le cas, que peut-on faire ? Pour Layer 2, les risques les plus immédiats des séquenceurs non décentralisés sont le manque de résistance à la censure et de vivacité. S’il n’y a que quelques entités qui traitent les transactions (séquenceurs), elles ont un pouvoir absolu sur le fait de vous servir ou non : elles peuvent refuser vos transactions à volonté, vous laissant sans recours. Aborder la question de la résistance à la censure dans Layer 2 est clairement un sujet important. Au cours des dernières années, diverses solutions Ethereum Layer 2 ont proposé différentes approches pour s’attaquer à ce problème. Par exemple, Loopring, Degate et StarkEx ont introduit des fonctions de retrait forcé et de trappe d’évacuation, tandis qu’Arbitrum et d’autres Optimistic Rollups ont implémenté des fonctionnalités d’inclusion de force. Ces mécanismes peuvent imposer des contrôles sur les séquenceurs afin d’empêcher le refus arbitraire des transactions de l’utilisateur. Dans l’article d’aujourd’hui, NIC Lin de la Taipei Ethereum Association partage son expérience de première main, expérimentant les fonctionnalités de transaction résistantes à la censure de quatre principaux Rollups et fournissant une analyse approfondie du mécanisme Force Inclusion, en se concentrant sur le flux de travail et les méthodes opérationnelles. Cette analyse est particulièrement précieuse pour la communauté Ethereum et les grands détenteurs d’actifs.
La censure résistance dans les transactions est cruciale pour toute blockchain. Si une blockchain peut censurer et rejeter arbitrairement les transactions des utilisateurs, elle n’est pas différente d’un serveur Web2. La résistance à la censure des transactions d’Ethereum est actuellement assurée par ses nombreux validateurs. Si quelqu’un veut censurer la transaction de Bob et l’empêcher d’être incluse dans la blockchain, il devrait soit soudoyer la majorité des validateurs du réseau, soit spammer le réseau avec des transactions inutiles qui ont des frais plus élevés que ceux de Bob, occupant ainsi de l’espace de bloc. Les deux méthodes sont extrêmement coûteuses.
Remarque : Dans l’architecture PBS (Proposant-Builder Separation actuelle) d’Ethereum, le coût de la censure des transactions est considérablement réduit. Par exemple, vous pouvez examiner la proportion de blocs qui se conforment à la censure de l’OFAC sur les transactions Tornado Cash. La résistance à la censure actuelle repose sur des validateurs et des relais indépendants qui ne relèvent pas de la juridiction de l’OFAC et d’autres entités gouvernementales.
Mais qu’en est-il des cumuls ? Les cumuls ne nécessitent pas un grand nombre de validateurs pour garantir la sécurité. Même si un Rollup n’a qu’une seule entité centralisée (Sequencer) produisant des blocs, il reste aussi sécurisé que la couche 1 (L1). Cependant, la sécurité et la résistance à la censure sont deux questions différentes. Un Rollup, tout en étant aussi sécurisé qu’Ethereum, peut toujours censurer la transaction de n’importe quel utilisateur s’il ne dispose que d’un seul séquenceur centralisé.
Le séquenceur peut refuser de traiter la transaction d’un utilisateur, ce qui entraîne le verrouillage des fonds de l’utilisateur et l’empêche de quitter le cumul.
d’inclusion Au lieu d’exiger que les Rollups disposent d’un grand nombre de séquenceurs décentralisés, il est plus efficace d’exploiter directement les résistance à la censure de la couche 1 (L1) :
Étant donné que le séquenceur doit empaqueter les données de transaction et les envoyer au contrat de cumul sur L1, nous pouvons ajouter une fonctionnalité dans le contrat qui permet aux utilisateurs d’insérer eux-mêmes leurs transactions dans le contrat de cumul. Ce mécanisme est connu sous le nom d'« inclusion forcée ». Aussi long que le séquenceur ne puisse pas censurer les utilisateurs au niveau L1, il ne peut pas empêcher les utilisateurs d’insérer de force des transactions au niveau L1. De cette façon, le Rollup peut hériter de la résistance à la censure de L1.
Le séquenceur ne peut pas examiner les transactions L1 de l’utilisateur sans payer un coût élevé
Tout d’abord, discutons du processus de dépôt d’Optimism. Ce processus de dépôt implique non seulement le transfert de fonds vers Optimism, mais aussi l’envoi de « messages d’utilisateur à L2 ». Lorsqu’un nœud L2 reçoit un message nouvellement déposé, il convertit le message en une transaction L2 et l’exécute, en le remettant au destinataire spécifié.
Messages utilisateur déposés de L1 à L2
Contrat L1CrossDomainMessenger
Lorsqu’un utilisateur souhaite dépôt ETH ou ERC-20 des tokens dans Optimism, il interagit avec le contrat L1StandardBridge sur L1 via une page Web frontale, en spécifiant le montant à dépôt et l’adresse L2 qui recevra ces actifs. Le contrat L1StandardBridge transfère ensuite le message au contrat L1CrossDomainMessenger, qui agit en tant que bridge de communication principal entre L1 et L2. Le L1StandardBridge utilise ce composant de communication pour interagir avec le L2StandardBridge sur L2, déterminant qui peut mint des jetons sur L2 ou déverrouiller des jetons de L1. Les développeurs qui ont besoin de créer des contrats qui interopèrent et synchronisent les états entre L1 et L2 peuvent les créer sur le contrat L1CrossDomainMessenger.
Messages utilisateur transmis de L1 à L2 via le contrat CrossDomainMessenger
Remarque : Dans certaines images de cet article, CrossDomainMessenger s’écrit CrossChainMessenger.
Contrat OptimismPortal
Le contrat L1CrossDomainMessenger transfère ensuite le message à la couche la plus basse, le contrat OptimismPortal. Après le traitement du message, le contrat OptimismPortal émet un événement appelé TransactionDeposited, qui inclut des paramètres tels que « l’expéditeur », le « destinataire » et d’autres détails d’exécution pertinents. Les nœuds Optimism sur L2 écoutent cet événement TransactionDeposited à partir du contrat OptimismPortal et convertissent les paramètres de l’événement en une transaction L2. L’initiateur de cette transaction sera « l’expéditeur » spécifié dans l’événement, le destinataire sera le « destinataire » mentionné dans l’événement, et d’autres détails de la transaction seront également dérivés des paramètres de l’événement.
Les
nœuds L2 convertissent les paramètres de l’événement Transaction Deposited émis par OptimismPortal en une transaction L2.
Par exemple, lorsqu’un utilisateur dépose 0,01 ETH par le biais du contrat L1StandardBridge, le message et le ETH sont transmis au contrat OptimismPortal (adresse 0xbEb5... Quelques minutes plus tard, cela est converti en une transaction L2 : l’expéditeur du message est le contrat L1CrossDomainMessenger, le destinataire est le contrat L2CrossDomainMessenger sur L2, et le contenu du message indique que le L1StandardBridge a reçu un ETH dépôt 0.01 de Bob. Cela déclenche ensuite des processus supplémentaires, tels que la monting de 0,01 ETH pour le L2StandardBridge, qui le transfère ensuite à Bob.
Comment le déclencher
Si vous souhaitez inclure de force une transaction dans le contrat de cumul d’Optimism, votre objectif est de vous assurer qu’une transaction « initiée et exécutée à partir de votre adresse L2 sur L2 » peut être exécutée avec succès. Pour ce faire, vous devez envoyer le message directement au contrat OptimismPortal à l’aide de votre adresse L2 (notez que le contrat OptimismPortal est en fait sur L1, mais que le format d’adresse OP correspond au format d’adresse L1, vous pouvez donc appeler ce contrat à l’aide d’un compte L1 avec la même adresse que votre compte L2). « L’expéditeur » de la transaction L2 dérivée de l’événement Transaction déposée émis par le présent contrat sera alors votre compte L2, et le format de transaction sera cohérent avec une transaction L2 standard.
Dans la transaction L2 dérivée de l’événement Transaction Deposited, Bob lui-même sera l’initiateur, le destinataire sera le contrat Uniswap et il inclura le ETH spécifié, tout comme si Bob initiait lui-même la transaction L2
Pour utiliser la fonction Force Inclusion d’Optimism, vous devez appeler directement la fonction depositTransaction du contrat OptimismPortal et saisir les paramètres de la transaction que vous souhaitez exécuter sur L2. J’ai mené une expérience simple d’inclusion de la force. Le but de cette transaction était d’effectuer un auto-transfert sur L2 en utilisant mon adresse (0xeDc1... 6909) et inclure un message disant « Forcer l’inclusion ». Il s’agit de la transaction L1 que j’ai exécutée en appelant la fonction depositTransaction via le contrat OptimismPortal. Comme vous pouvez le voir à partir de l’événement Transaction Deposited qu’il a émis, l’expéditeur et le destinataire sont tous les deux moi-même.
Les valeurs restantes de la colonne opaque Data codent des informations telles que « combien de ETH la personne appelant la fonction depositTransaction attachée », « combien de ETH l’initiateur de la transaction L2 souhaite envoyer au destinataire », « Limite de gaz de la transaction L2 » et « Données pour le récepteur L2 ». Après avoir décodé ces informations, vous obtiendrez les détails suivants : « combien d’ETH la personne appelant la depositTransaction a joint » : 0, car je ne dépose pas d’ETH de L1 à L2 ; « combien d’ETH l’initiateur de la transaction L2 veut envoyer au destinataire » : 5566 (wei) ; « Limite de gaz de transaction L2 » : 50000 ; « Données pour le récepteur L2 » : 0x666f72636520696e636c7573696f6e, qui est l’encodage hexadécimal de la chaîne « inclusion de force ». Peu de temps après, la transaction L2 convertie est apparue : une transaction L2 où je transfère 5566 wei à moi-même, avec le champ Data contenant la chaîne « forcer l’inclusion ». De plus, dans l’avant-dernière ligne de la section Autres attributs, le TxnType (type de transaction) est indiqué comme transaction système 126 (Système), ce qui indique que cette transaction n’a pas été initiée par moi sur L2 mais a été convertie à partir de l’événement Deposited de la transaction L1.
Transaction L2 convertie
Si vous souhaitez appeler un contrat L2 via Force Inclusion et envoyer différentes données, il vous suffit de renseigner les paramètres de la fonction depositTransaction. N’oubliez pas d’utiliser la même adresse L1 que votre compte L2 lors de l’appel de la fonction depositTransaction. De cette façon, lorsque l’événement déposé est converti en une transaction L2, l’initiateur sera votre compte L2. Fenêtre du séquenceur Le nœud Optimism L2 qui convertit l’événement Transaction Deposited en une transaction L2 est en fait le séquenceur. Étant donné qu’il s’agit d’un ordre de transaction, seul le séquenceur peut décider quand convertir l’événement en transaction L2. Lorsque le séquenceur écoute l’événement TransactionDeposited, il ne convertit pas nécessairement l’événement en une transaction L2 immédiatement ; Il peut y avoir un retard. La durée maximale de ce délai s’appelle la fenêtre du séquenceur. Actuellement, la fenêtre du séquenceur sur le réseau principal Optimism est de 24 heures. Cela signifie que lorsqu’un utilisateur dépose de l’argent à partir de L1 ou utilise Forcer l’inclusion pour une transaction, dans le pire des cas, il sera inclus dans l’historique des transactions L2 après 24 heures.
Dans Optimism, l’opération L1 dépôt déclenche un événement Transaction Deposited, puis il suffit d’attendre que le séquenceur inclue l’opération. Cependant, dans Arbitrum, les opérations sur L1 (telles que le dépôt de fonds ou l’envoi de messages à L2) sont stockées dans une file d’attente sur L1, plutôt que de simplement émettre un événement. Le séquenceur dispose d’une période spécifique pour inclure ces transactions en file d’attente dans l’historique des transactions L2. Si le séquenceur ne le fait pas dans ce délai, n’importe qui peut intervenir pour terminer l’inclusion au nom du séquenceur.
Arbitrum maintient une file d’attente dans un contrat L1. Si le séquenceur ne parvient pas à traiter les transactions dans la file d’attente dans un certain délai, n’importe qui peut inclure de force ces transactions dans l’historique des transactions L2. Dans la conception d’Arbitrum, les opérations sur L1, telles que les dépôts, doivent passer par le contrat de boîte de réception différée, où, comme son nom l’indique, ces opérations seront retardées avant de prendre effet. Un autre contrat, la boîte de réception du séquenceur, permet au séquenceur de télécharger directement les transactions L2 vers L1. Chaque fois que le séquenceur télécharge des transactions L2, il peut également prendre certaines transactions en attente de la boîte de réception différée et les inclure dans l’historique des transactions.
Lorsque le séquenceur écrit de nouvelles transactions, il peut également inclure des transactions provenant de la boîte de réception DelayedInbox.
Conception complexe et manque de matériaux de référence
Si vous vous référez à la documentation officielle d’Arbitrum sur le séquenceur et l’inclusion de la force, vous trouverez une explication générale du fonctionnement de l’inclusion de la force, ainsi que des noms de paramètres et de fonctions : Les utilisateurs appellent d’abord la fonction sendUnsignedTransaction sur le contrat DelayedInbox. Si le séquenceur ne l’inclut pas dans un délai d’environ 24 heures, les utilisateurs peuvent appeler la fonction forceInclusion sur le contrat SequencerInbox. Cependant, la documentation officielle ne fournit pas de liens vers ces fonctions, vous devez donc les rechercher vous-même dans le code du contrat. Lorsque vous trouvez la fonction sendUnsignedTransaction, vous vous rendez compte que vous devez remplir vous-même la valeur nonce et la valeur maxFeePerGas. De qui s’agit-il nonce ? Le maxFeePerGas de quel réseau ? Comment faut-il le remplir correctement ? Il n’y a pas de documents de référence, pas même NatSpec. Vous trouverez également de nombreuses fonctions similaires dans le contrat Arbitrum : sendL1FundedUnsignedTransaction, sendUnsignedTransactionToFork, sendContractTransaction, sendL1FundedContractTransaction. Il n’y a pas de documents expliquant les différences entre ces fonctions, comment les utiliser, ou comment remplir les paramètres, pas même NatSpec.
Vous essayez de remplir les paramètres et de soumettre la transaction avec une approche par essais et erreurs, dans l’espoir de trouver l’utilisation correcte. Cependant, vous découvrez que toutes ces fonctions appliquent le crénelage d’adresse à votre adresse L1, ce qui fait que l’expéditeur de la transaction sur L2 est une adresse complètement différente, laissant votre adresse L2 inactive. Plus tard, vous êtes tombé par hasard sur un résultat de recherche Google révélant qu’Arbitrum dispose d’une bibliothèque de didacticiels avec des scripts montrant comment envoyer des transactions L2 à partir de L1 (essentiellement Forcer l’inclusion). Le tutoriel répertorie une fonction qui n’a pas été mentionnée précédemment : sendL2Message. Étonnamment, le paramètre de message requis est en fait une transaction L2 signée à l’aide d’un compte L2. Qui aurait cru que le « message envoyé à L2 par l’intermédiaire de Force Inclusion » est en fait une « transaction L2 signée » ? De plus, il n’y a pas de documents ou de NatSpec expliquant quand et comment utiliser cette fonction.
Conclusion : Générer manuellement une transaction forcée sur Arbitrum est assez compliqué. Il est recommandé de suivre le tutoriel officiel et d’utiliser le SDK Arbitrum. Contrairement à d’autres Rollups, Arbitrum manque d’une documentation claire pour les développeurs et d’annotations de code. De nombreuses fonctions manquent d’explications sur leurs objectifs et leurs paramètres, ce qui oblige les développeurs à passer beaucoup plus de temps que prévu à les intégrer et à les utiliser. J’ai également demandé de l’aide sur le Discord d’Arbitrum, mais je n’ai pas reçu de réponses satisfaisantes. Lorsque j’ai posé la question sur Discord, ils m’ont seulement demandé de regarder sendL2Message et n’ont pas expliqué les fonctions des autres méthodes (y compris celles mentionnées dans la documentation de Force Inclusion comme sendUnsignedTransaction), leurs objectifs, comment les utiliser ou quand les utiliser.
Malheureusement, StarkNet ne dispose actuellement pas d’un mécanisme d’inclusion de la force. Il n’y a que deux articles sur le forum officiel qui traitent de la censure et de l’inclusion forcée. La raison de l’incapacité à prouver les transactions ayant échoué est que le système de preuve à divulgation nulle de connaissance de StarkNet ne peut pas prouver une transaction ayant échoué, de sorte que l’inclusion forcée ne peut pas être autorisée. Si quelqu’un force malicieusement (ou involontairement) l’inclusion d’une transaction échouée et non prouvable, StarkNet serait bloqué : parce qu’une fois que la transaction est incluse de force, le Prover doit prouver l’échec de la transaction, mais il ne le peut pas. StarkNet devrait introduire la possibilité de prouver les transactions ayant échoué dans la version v0.15.0, après quoi le mécanisme d’inclusion de la force devrait être mis en œuvre.
pour la transmission de messages L1->L2 et l’inclusion forcée de zkSync est géré via la fonction requestL2Transaction du contrat de boîte aux lettres. Les utilisateurs spécifient l’adresse L2, les données d’appel, la quantité d’ETH à attacher, la valeur L2GasLimit et d’autres détails. La fonction requestL2Transaction combine ces paramètres dans une transaction L2 et la place dans la PriorityQueue. Lorsque le séquenceur empaquette des transactions et les télécharge vers L1 (via la fonction commitBatches), il indique le nombre de transactions à prendre de la PriorityQueue à inclure dans les enregistrements de transaction L2. En termes d’inclusion de force, zkSync est similaire à Optimism, où l’adresse L2 de l’initiateur (la même que l’adresse L1) est utilisée pour appeler les fonctions pertinentes et remplir les détails nécessaires (appelé, calldata, etc.), plutôt que comme Arbitrum, qui nécessite une transaction L2 signée. Cependant, dans sa conception, il est similaire à Arbitrum, car les deux maintiennent une file d’attente sur L1, et le séquenceur prend les transactions en attente directement soumises par les utilisateurs de la file d’attente et les écrit dans l’historique des transactions.
Si vous dépôt ETH via le bridge officiel de zkSync, comme cette transaction, il appelle la fonction requestL2Transaction du contrat MailBox. Cette fonction place la transaction Deposit ETH L2 dans la PriorityQueue et émet un événement NewPriorityRequest. Étant donné que le contrat encode les données de transaction L2 dans une chaîne d’octets, il n’est pas facilement lisible. Cependant, si vous regardez les paramètres de cette transaction L1, vous verrez que le destinataire L2 est également l’initiateur de la transaction (puisqu’il s’agit d’un dépôt pour soi-même). Au bout d’un certain temps, lorsque le séquenceur retire cette transaction L2 de la PriorityQueue et l’inclut dans l’historique des transactions, elle est convertie en une transaction L2 que vous transférez à vous-même. Le montant du transfert sera le montant en ETH attaché par l’initiateur dans la transaction L1 Deposit ETH. Sur L2, il y aura une transaction où 0xeDc1... 6909 se transfère à lui-même. Le type de transaction (TxnType) est 255, ce qui indique une transaction système. Ensuite, tout comme j’ai expérimenté la fonction de transaction forcée sur Optimism auparavant, j’ai appelé la fonction requestL2Transaction de zkSync et lancé une transaction d’auto-transfert : aucun ETH n’était attaché et les calldata contenaient l’encodage HEX de la chaîne « forcer l’inclusion ». Cela a ensuite été converti en une transaction L2 où je transfère à moi-même, avec les calldata contenant la chaîne hexadécimale pour « forcer l’inclusion » : 0x666f72636520696e636c7573696f6e.
L1 s’appuie sur un grand nombre de validateurs pour assurer la « sécurité » et la « résistance à la censure » du réseau. Les rollups, cependant, ont une résistance à la censure plus faible car les transactions sont écrites par quelques-uns ou même un seul séquenceur. Par conséquent, les cumuls ont besoin d’un mécanisme d’inclusion forcée pour permettre aux utilisateurs de contourner le séquenceur et d’écrire des transactions dans l’historique, empêchant ainsi la censure par le séquenceur de rendre le cumul inutilisable et empêchant les utilisateurs de retirer des fonds. Forcer l’inclusion permet aux utilisateurs d’écrire de force des transactions dans l’historique, mais la conception doit choisir si « les transactions peuvent être immédiatement insérées dans l’historique et prendre effet immédiatement ». Si l’effet immédiat est autorisé, cela aura un impact négatif sur le séquenceur, car les transactions en attente sur L2 pourraient être affectées par des transactions incluses de force à partir de L1. Par conséquent, les mécanismes actuels de Forcer l’inclusion dans les cumuls placent d’abord les transactions insérées à partir de L1 dans un état d’attente et donnent au séquenceur une fenêtre de temps pour réagir et décider d’inclure ou non ces transactions en attente. zkSync et Arbitrum maintiennent tous deux une file d’attente sur L1 pour gérer les transactions L2 ou les messages envoyés de L1 à L2. Arbitrum l’appelle DelayedInbox ; zkSync l’appelle PriorityQueue. Cependant, la méthode d’envoi de transactions L2 de zkSync est plus similaire à celle d’Optimism, où les messages sont envoyés à partir de L1 en utilisant l’adresse L2, de sorte que lorsqu’ils sont convertis en transaction L2, l’initiateur est l’adresse L2. La fonction d’envoi de transactions L2 dans Optimism s’appelle depositTransaction ; dans zkSync, il s’appelle requestL2Transaction. En revanche, Arbitrum génère une transaction L2 complète et la signe, puis l’envoie via la fonction sendL2Message. Sur L2, Arbitrum utilise la signature pour restaurer le signataire en tant qu’initiateur de la transaction L2. À l’heure actuelle, StarkNet ne dispose pas d’un mécanisme d’inclusion de la Force ; zkSync dispose d’un mécanisme d’inclusion forcée à moitié implémenté : il dispose d’une file d’attente prioritaire, et chaque transaction L2 dans la file d’attente a une période de validité d’inclusion, mais cette période de validité n’est actuellement que pour l’affichage. En pratique, le séquenceur peut choisir de ne pas inclure de transactions L2 à partir de la file d’attente prioritaire.
Avis de non-responsabilité : Les points de vue et opinions exprimés dans cet article ne représentent que les opinions personnelles de l’auteur et ne constituent pas un conseil en investissement.
Les autres versions linguistiques de l’article sont traduites par l’équipe de Gate Learn. Sans référence Gate.io, il est interdit de copier, distribuer ou plagier les articles traduits.