Avez-vous déjà voulu en savoir plus sur le fonctionnement du Rollup ? La théorie est bonne, mais l’expérience pratique est toujours préférable. Malheureusement, les projets existants ne permettent pas toujours de voir facilement ce qui se passe. C’est pourquoi nous avons créé BYOR (Build Your Own Rollup). Il s’agit d’un rollup souverain avec un minimum de fonctionnalités, qui met l’accent sur la facilité de lecture et de compréhension du code.
Ce qui nous motive dans ce projet, c’est que les gens, qu’ils soient de l’extérieur ou de l’intérieur, comprennent mieux ce que font réellement les rollups autour de nous. Vous pouvez jouer sur le BYOR déployé par Holesky ou lire le code source sur GitHub.
Qu’est-ce que BYOR ?
Le projet BYOR est une version simplifiée du rollup souverain. Contrairement aux preuves de cumul optimistes et à divulgation nulle de connaissance, les cumuls souverains ne valident pas les racines étatiques sur Ethereum et s’appuient uniquement sur la disponibilité des données et le consensus sur Ethereum. Cela empêche les ponts de minimisation de la confiance entre L1 et BYOR, mais simplifie considérablement le code et est idéal à des fins éducatives.
La base de code se compose de trois programmes : les contrats intelligents, les nœuds et les portefeuilles. Lorsqu’ils sont déployés ensemble, ils permettent aux utilisateurs finaux d’interagir avec le réseau. Il est intéressant de noter que l’état du réseau est entièrement déterminé par les données on-chain, ce qui signifie que plusieurs nœuds peuvent réellement fonctionner. Chaque nœud peut également publier des données indépendamment en tant que séquenceur.
Voici la liste complète des fonctionnalités implémentées dans BYOR :
Tri des frais
Publiez le statut à L1 et obtenez le statut à partir de L1
Ignorer les transactions non valides
Vérifier le solde du compte
Envoyer des transactions
Vérifier l’état de la transaction
Utiliser le portefeuille
Dans une application de portefeuille, il agit comme le front-end du réseau, où les utilisateurs peuvent soumettre des transactions et vérifier l’état de leurs comptes ou l’état des transactions. Sur la page de destination, vous verrez une vue d’ensemble qui fournit des statistiques sur l’état actuel du cumul, suivi de l’état de votre compte. Très probablement, il n’y a qu’un seul bouton pour se connecter au portefeuille de votre choix et il y a des nouvelles sur le robinet à jeton. En dessous, il y a une barre de recherche où vous pouvez coller l’adresse ou le hachage de transaction de quelqu’un pour explorer l’état actuel de L2. Enfin, il existe deux listes de transactions : la première est la liste des transactions dans le mempool L2, et la seconde est la liste des transactions publiées dans L1.
Pour commencer, utilisez le bouton WalletConnect pour connecter votre portefeuille. Une fois connecté, vous pouvez recevoir une notification indiquant que votre portefeuille est connecté au mauvais réseau. Si votre application prend en charge la commutation réseau, cliquez sur le bouton Changer de réseau pour basculer vers le réseau de test Holesky. Sinon, basculez manuellement.
Vous pouvez désormais envoyer des jetons à quelqu’un en fournissant l’adresse du destinataire, le nombre de jetons à envoyer et les frais requis. Une fois envoyé, l’application de portefeuille vous demandera de signer le message. S’il est signé avec succès, le message est envoyé au pool de mémoire du nœud L2, en attente d’être publié sur L1. Le temps nécessaire pour qu’une transaction soit regroupée dans une version par lots peut varier. Toutes les 10 secondes, le nœud L2 vérifie le contenu à publier. Les transactions avec des frais plus élevés sont envoyées en premier, donc si vous spécifiez des frais moins élevés et que vous avez beaucoup de trafic de transactions, vous risquez de rencontrer de longs temps d’attente.
Comment ça marche ?
Diagramme d’architecture de cumul ## pile technologique
Nous avons construit chaque composant en utilisant les techniques suivantes :
Le code BYOR est conçu pour être facilement compris en examinant la base de code. N’hésitez pas à explorer notre base de code ! Lisez d’abord README.md, pour comprendre la structure du projet, veuillez lire le fichier ARCHITECTURE.md.
Voici quelques points saillants intéressants du code :
Contrats intelligents
Identificateur de licence SPDX : MIT
Solidité du pragma ^0.8.0 ;
C’est le seul contrat intelligent nécessaire. Son nom dérive du fait que les entrées sont stockées dans des fonctions de transition d’état. Le seul but de ce contrat est de stocker facilement toutes les transactions. Le lot sérialisé est publié dans ce contrat actif en tant que calldata et il émet un événement BatchAppended avec l’adresse de l’éditeur du lot. Bien que nous puissions concevoir le système de manière à ce qu’il publie les transactions directement dans EOA au lieu de contrats, les données peuvent être facilement récupérées via JSON-RPC en émettant des événements. La seule exigence pour ce contrat intelligent est qu’il ne doit pas être appelé à partir d’un autre contrat intelligent, mais directement à partir d’EOA.
Schéma de la base de données
CRÉER DES COMPTES TABLE (
texte de l’adresse CLÉ PRIMAIRE NON NULLE,
balance entier DEFAULT 0 NOT NULL,
nonce integer DEFAULT 0 NOT NULL
);
Transactions CREATE TABLE (
id entier,
à partir du texte NOT NULL,
au texte NOT NULL,
valeur entier NOT NULL,
nonce entier NOT NULL,
fee entier NOT NULL,
feeReceipent text NOT NULL,
l1SubmittedDate entier NOT NULL,
texte de hachage NOT NULL
CLÉ PRIMAIRE(de, nonce)
);
-- Ce tableau comporte une seule ligne
CREATE TABLE fetcherStates (
chainId integer PRIMARY KEY NOT NULL,
lastFetchedBlock entier DEFAULT 0 NOT NULL
);
Il s’agit de l’intégralité du schéma de base de données utilisé pour stocker les informations sur le cumul. Vous vous demandez peut-être pourquoi nous avons besoin d’une base de données alors que toutes les données nécessaires sont stockées sur L1. Bien que cela soit vrai, le stockage local des données permet d’économiser du temps et des ressources en évitant les acquisitions en double. Traitez toutes les données stockées dans ce schéma comme des mémos d’état, des hachages de transaction et d’autres informations calculées.
La table fetcherStates est utilisée pour garder une trace du dernier bloc que nous avons récupéré lors de la recherche de l’événement BatchAnnexed. Ceci est utile lorsque le nœud s’arrête et redémarre ; Il sait où reprendre la recherche.
Fonctions de transition d’état
const DEFAULT_ACCOUNT = { solde : 0, nonce : 0 }
function uteTransaction(state, tx, feeRecipient) {
const fromAccount = getAccount(état, tx.from, DEFAULT_ACCOUNT)
const toAccount = getAccount(état, tx.to, DEFAULT_ACCOUNT)
Étape 1 : Mettre à jour le nonce
fromAccount.nonce = tx.nonce
Étape 2 : Transférer la valeur
fromAccount.balance -= tx.value
toAccount.balance += tx.value
Étape 3 : Payer les frais
fromAccount.balance -= tx.fee
feeRecipientAccount.balance += tx.fee
}
Les fonctions présentées ci-dessus sont au cœur du mécanisme de transition d’état dans BYOR. Il suppose que la transaction peut être exécutée en toute sécurité, avec le bon nonce et un solde suffisant pour effectuer le paiement défini. En raison de cette hypothèse, il n’y a pas d’étapes de gestion ou de validation des erreurs dans cette fonction. Au lieu de cela, ces étapes sont effectuées avant que la fonction ne soit appelée. Chaque état de compte est stocké dans une carte. Si un compte n’existe pas déjà dans ce mappage, il sera défini sur la valeur par défaut visible en haut de la liste des codes. Sur les trois comptes utilisés, le nonce est mis à jour et le solde est alloué.
Signature des transactions
Signature de transaction : Nous utilisons la norme EIP-712 pour signer les données dactylographiées. Cela nous permet de montrer clairement aux utilisateurs ce qu’ils signent. Comme indiqué ci-dessus, lors de l’envoi d’une transaction, nous pouvons afficher le destinataire, le montant et les frais de manière conviviale.
Pour obtenir le nouvel événement, nous récupérons tous les événements BatchAppended du dernier bloc récupéré à partir du contrat Inputs. Le nombre maximal d’événements que nous récupérons est le bloc le plus récent ou le dernier bloc récupéré plus la limite de taille du lot. Après avoir récupéré tous les événements, nous extrayons les données d’appel, l’horodatage et l’adresse de l’éditeur de chaque transaction. Mettez à jour le dernier bloc que nous récupérons vers le dernier bloc que nous récupérons. Les données d’appel, l’horodatage et l’éditeur extraits sont ensuite regroupés et renvoyés par la fonction pour un traitement ultérieur.
Les pools de mémoire et leur tri des coûts
function popNHighestFee(txPool, n) {
txPool.sort((a, b) => b.fee - a.fee))
return txPool.splice(0, n)
}
Un mempool est un objet qui gère un tableau de transactions signées. L’aspect le plus intéressant est la façon dont il détermine l’ordre dans lequel les transactions sont enregistrées dans L1. Comme indiqué dans le code ci-dessus, les transactions sont triées en fonction de leurs frais. Cela permet au prix médian des frais dans le système de fluctuer en fonction de l’activité on-chain.
Même si vous spécifiez des frais élevés, les transactions doivent toujours produire un état valide si elles doivent être ajoutées à l’état actuel. Par conséquent, vous ne pouvez pas soumettre des transactions invalides simplement en raison de frais élevés.
BYOR fait-il vraiment évoluer Ethereum ?
Optimism et ZK rollup ont construit des systèmes pour prouver que les racines d’état publiées sont cohérentes avec les fonctions de transition d’état et les données qu’elles valident, mais pas les rollups souverains. Par conséquent, l’incapacité de ce type de rollup à faire évoluer Ethereum peut sembler contre-intuitive au premier abord. Cependant, cela devient raisonnable lorsque nous réalisons que d’autres types de cumuls ne peuvent utiliser que L1 pour prouver que la racine d’état publiée est correcte. Pour distinguer si les données du cumul souverain sont correctes ou non, nous devons exécuter un nœud L1 ainsi qu’un logiciel supplémentaire pour formaliser le nœud L2 afin d’exécuter des fonctions de transition d’état, augmentant ainsi la charge de calcul.
Perspectives d’avenir
La construction de ce projet a été une excellente expérience d’apprentissage pour nous, et nous espérons que vous trouverez également nos efforts utiles. Nous espérons revenir à BYOR à l’avenir et y ajouter un système de protection contre la fraude. Cela en fera un bilan vraiment optimiste et une fois de plus une leçon sur le fonctionnement interne des systèmes que nous utilisons tous les jours.
Voir l'original
Cette page peut inclure du contenu de tiers fourni à des fins d'information uniquement. Gate ne garantit ni l'exactitude ni la validité de ces contenus, n’endosse pas les opinions exprimées, et ne fournit aucun conseil financier ou professionnel à travers ces informations. Voir la section Avertissement pour plus de détails.
Créez votre propre Rollup – une liste de projets BYOR
Compilation : Plan de traduction Denlink
Avez-vous déjà voulu en savoir plus sur le fonctionnement du Rollup ? La théorie est bonne, mais l’expérience pratique est toujours préférable. Malheureusement, les projets existants ne permettent pas toujours de voir facilement ce qui se passe. C’est pourquoi nous avons créé BYOR (Build Your Own Rollup). Il s’agit d’un rollup souverain avec un minimum de fonctionnalités, qui met l’accent sur la facilité de lecture et de compréhension du code.
Ce qui nous motive dans ce projet, c’est que les gens, qu’ils soient de l’extérieur ou de l’intérieur, comprennent mieux ce que font réellement les rollups autour de nous. Vous pouvez jouer sur le BYOR déployé par Holesky ou lire le code source sur GitHub.
Qu’est-ce que BYOR ?
Le projet BYOR est une version simplifiée du rollup souverain. Contrairement aux preuves de cumul optimistes et à divulgation nulle de connaissance, les cumuls souverains ne valident pas les racines étatiques sur Ethereum et s’appuient uniquement sur la disponibilité des données et le consensus sur Ethereum. Cela empêche les ponts de minimisation de la confiance entre L1 et BYOR, mais simplifie considérablement le code et est idéal à des fins éducatives.
La base de code se compose de trois programmes : les contrats intelligents, les nœuds et les portefeuilles. Lorsqu’ils sont déployés ensemble, ils permettent aux utilisateurs finaux d’interagir avec le réseau. Il est intéressant de noter que l’état du réseau est entièrement déterminé par les données on-chain, ce qui signifie que plusieurs nœuds peuvent réellement fonctionner. Chaque nœud peut également publier des données indépendamment en tant que séquenceur.
Voici la liste complète des fonctionnalités implémentées dans BYOR :
Utiliser le portefeuille
Dans une application de portefeuille, il agit comme le front-end du réseau, où les utilisateurs peuvent soumettre des transactions et vérifier l’état de leurs comptes ou l’état des transactions. Sur la page de destination, vous verrez une vue d’ensemble qui fournit des statistiques sur l’état actuel du cumul, suivi de l’état de votre compte. Très probablement, il n’y a qu’un seul bouton pour se connecter au portefeuille de votre choix et il y a des nouvelles sur le robinet à jeton. En dessous, il y a une barre de recherche où vous pouvez coller l’adresse ou le hachage de transaction de quelqu’un pour explorer l’état actuel de L2. Enfin, il existe deux listes de transactions : la première est la liste des transactions dans le mempool L2, et la seconde est la liste des transactions publiées dans L1.
Pour commencer, utilisez le bouton WalletConnect pour connecter votre portefeuille. Une fois connecté, vous pouvez recevoir une notification indiquant que votre portefeuille est connecté au mauvais réseau. Si votre application prend en charge la commutation réseau, cliquez sur le bouton Changer de réseau pour basculer vers le réseau de test Holesky. Sinon, basculez manuellement.
Vous pouvez désormais envoyer des jetons à quelqu’un en fournissant l’adresse du destinataire, le nombre de jetons à envoyer et les frais requis. Une fois envoyé, l’application de portefeuille vous demandera de signer le message. S’il est signé avec succès, le message est envoyé au pool de mémoire du nœud L2, en attente d’être publié sur L1. Le temps nécessaire pour qu’une transaction soit regroupée dans une version par lots peut varier. Toutes les 10 secondes, le nœud L2 vérifie le contenu à publier. Les transactions avec des frais plus élevés sont envoyées en premier, donc si vous spécifiez des frais moins élevés et que vous avez beaucoup de trafic de transactions, vous risquez de rencontrer de longs temps d’attente.
Comment ça marche ?
Nous avons construit chaque composant en utilisant les techniques suivantes :
Exploration du code
Le code BYOR est conçu pour être facilement compris en examinant la base de code. N’hésitez pas à explorer notre base de code ! Lisez d’abord README.md, pour comprendre la structure du projet, veuillez lire le fichier ARCHITECTURE.md.
Voici quelques points saillants intéressants du code :
Contrats intelligents
Identificateur de licence SPDX : MIT
Solidité du pragma ^0.8.0 ;
Entrées contract {
event BatchAppended(adresse expéditeur) ;
function appendBatch(octets calldata) external {
require(msg.sender == tx.origin) ;
émettre BatchAppended(msg.sender) ;
}
}
C’est le seul contrat intelligent nécessaire. Son nom dérive du fait que les entrées sont stockées dans des fonctions de transition d’état. Le seul but de ce contrat est de stocker facilement toutes les transactions. Le lot sérialisé est publié dans ce contrat actif en tant que calldata et il émet un événement BatchAppended avec l’adresse de l’éditeur du lot. Bien que nous puissions concevoir le système de manière à ce qu’il publie les transactions directement dans EOA au lieu de contrats, les données peuvent être facilement récupérées via JSON-RPC en émettant des événements. La seule exigence pour ce contrat intelligent est qu’il ne doit pas être appelé à partir d’un autre contrat intelligent, mais directement à partir d’EOA.
Schéma de la base de données
CRÉER DES COMPTES TABLE (
texte de l’adresse CLÉ PRIMAIRE NON NULLE,
balance entier DEFAULT 0 NOT NULL,
nonce integer DEFAULT 0 NOT NULL
);
Transactions CREATE TABLE (
id entier,
à partir du texte NOT NULL,
au texte NOT NULL,
valeur entier NOT NULL,
nonce entier NOT NULL,
fee entier NOT NULL,
feeReceipent text NOT NULL,
l1SubmittedDate entier NOT NULL,
texte de hachage NOT NULL
CLÉ PRIMAIRE(de, nonce)
);
-- Ce tableau comporte une seule ligne
CREATE TABLE fetcherStates (
chainId integer PRIMARY KEY NOT NULL,
lastFetchedBlock entier DEFAULT 0 NOT NULL
);
Il s’agit de l’intégralité du schéma de base de données utilisé pour stocker les informations sur le cumul. Vous vous demandez peut-être pourquoi nous avons besoin d’une base de données alors que toutes les données nécessaires sont stockées sur L1. Bien que cela soit vrai, le stockage local des données permet d’économiser du temps et des ressources en évitant les acquisitions en double. Traitez toutes les données stockées dans ce schéma comme des mémos d’état, des hachages de transaction et d’autres informations calculées.
La table fetcherStates est utilisée pour garder une trace du dernier bloc que nous avons récupéré lors de la recherche de l’événement BatchAnnexed. Ceci est utile lorsque le nœud s’arrête et redémarre ; Il sait où reprendre la recherche.
Fonctions de transition d’état
const DEFAULT_ACCOUNT = { solde : 0, nonce : 0 }
function uteTransaction(state, tx, feeRecipient) {
const fromAccount = getAccount(état, tx.from, DEFAULT_ACCOUNT)
const toAccount = getAccount(état, tx.to, DEFAULT_ACCOUNT)
Étape 1 : Mettre à jour le nonce
fromAccount.nonce = tx.nonce
Étape 2 : Transférer la valeur
fromAccount.balance -= tx.value
toAccount.balance += tx.value
Étape 3 : Payer les frais
fromAccount.balance -= tx.fee
feeRecipientAccount.balance += tx.fee
}
Les fonctions présentées ci-dessus sont au cœur du mécanisme de transition d’état dans BYOR. Il suppose que la transaction peut être exécutée en toute sécurité, avec le bon nonce et un solde suffisant pour effectuer le paiement défini. En raison de cette hypothèse, il n’y a pas d’étapes de gestion ou de validation des erreurs dans cette fonction. Au lieu de cela, ces étapes sont effectuées avant que la fonction ne soit appelée. Chaque état de compte est stocké dans une carte. Si un compte n’existe pas déjà dans ce mappage, il sera défini sur la valeur par défaut visible en haut de la liste des codes. Sur les trois comptes utilisés, le nonce est mis à jour et le solde est alloué.
Signature des transactions
Obtention de l’événement L1
function getNewStates() {
const lastBatchBlock = getLastBatchBlock()
événements const = getLogs(lastBatchBlock)
const calldata = getCalldata(events)
const timestamps = getTimestamps(events)
const posters = getTransactionPosters(events)
updateLastFetchedBlock(lastBatchBlock)
return zip(affiches, horodatages, calldata)
}
Pour obtenir le nouvel événement, nous récupérons tous les événements BatchAppended du dernier bloc récupéré à partir du contrat Inputs. Le nombre maximal d’événements que nous récupérons est le bloc le plus récent ou le dernier bloc récupéré plus la limite de taille du lot. Après avoir récupéré tous les événements, nous extrayons les données d’appel, l’horodatage et l’adresse de l’éditeur de chaque transaction. Mettez à jour le dernier bloc que nous récupérons vers le dernier bloc que nous récupérons. Les données d’appel, l’horodatage et l’éditeur extraits sont ensuite regroupés et renvoyés par la fonction pour un traitement ultérieur.
Les pools de mémoire et leur tri des coûts
function popNHighestFee(txPool, n) {
txPool.sort((a, b) => b.fee - a.fee))
return txPool.splice(0, n)
}
Un mempool est un objet qui gère un tableau de transactions signées. L’aspect le plus intéressant est la façon dont il détermine l’ordre dans lequel les transactions sont enregistrées dans L1. Comme indiqué dans le code ci-dessus, les transactions sont triées en fonction de leurs frais. Cela permet au prix médian des frais dans le système de fluctuer en fonction de l’activité on-chain.
Même si vous spécifiez des frais élevés, les transactions doivent toujours produire un état valide si elles doivent être ajoutées à l’état actuel. Par conséquent, vous ne pouvez pas soumettre des transactions invalides simplement en raison de frais élevés.
BYOR fait-il vraiment évoluer Ethereum ?
Optimism et ZK rollup ont construit des systèmes pour prouver que les racines d’état publiées sont cohérentes avec les fonctions de transition d’état et les données qu’elles valident, mais pas les rollups souverains. Par conséquent, l’incapacité de ce type de rollup à faire évoluer Ethereum peut sembler contre-intuitive au premier abord. Cependant, cela devient raisonnable lorsque nous réalisons que d’autres types de cumuls ne peuvent utiliser que L1 pour prouver que la racine d’état publiée est correcte. Pour distinguer si les données du cumul souverain sont correctes ou non, nous devons exécuter un nœud L1 ainsi qu’un logiciel supplémentaire pour formaliser le nœud L2 afin d’exécuter des fonctions de transition d’état, augmentant ainsi la charge de calcul.
Perspectives d’avenir
La construction de ce projet a été une excellente expérience d’apprentissage pour nous, et nous espérons que vous trouverez également nos efforts utiles. Nous espérons revenir à BYOR à l’avenir et y ajouter un système de protection contre la fraude. Cela en fera un bilan vraiment optimiste et une fois de plus une leçon sur le fonctionnement interne des systèmes que nous utilisons tous les jours.