La machine virtuelle Solana (SVM) est de plus en plus adoptée en tant que couche d'exécution pour diverses solutions de couche 2 (L2). Cependant, une limitation clé de la conception initiale de la SVM est l'obscurité de sa racine d'état global. Cela pose des défis importants pour les systèmes rollup, où les racines d'état globales sont cruciales pour garantir l'intégrité, permettre des preuves de fraude et prendre en charge des opérations entre les couches.
Dans un rollup, le proposeur soumet périodiquement la racine d'état L2 (racine de Merkle) à la couche 1 (L1), établissant des points de contrôle pour la chaîne L2. Ces points de contrôle permettent des preuves d'inclusion pour tout état de compte, permettant une exécution sans état d'un point de contrôle à un autre. Les preuves de fraude reposent sur ce mécanisme, car les participants peuvent fournir des preuves d'inclusion pour vérifier les entrées valides lors des litiges. De plus, les arbres de Merkle renforcent la sécurité des passerelles canoniques en permettant aux utilisateurs de générer des preuves d'inclusion pour les transactions de retrait, garantissant des interactions sans confiance entre L2 et L1.
Pour relever ces défis, Réseau SOONintroduit les arbres de Merkle dans la couche d'exécution du SVM, permettant aux clients de fournir des preuves d'inclusion. SOON s'intègre à la Preuve-de-l'Histoire en utilisant des entrées uniques pour incorporer directement les racines d'état dans les blockchains basées sur le SVM. Grâce à cette innovation, la pile SOON peut prendre en charge de nouveaux rollups basés sur le SVM avec une sécurité, une évolutivité et une utilité améliorées.
Solana a toujours été conçue avec un débit élevé comme objectif principal, nécessitant des compromis de conception délibérés — en particulier pendant ses premiers développements — pour atteindre ses performances novatrices. Parmi ces compromis, l'une des décisions les plus impactantes concernait la manière et le moment où Solana merkliserait son état.
Au final, cette décision a créé d'énormes défis pour les clients pour prouver l'état global ainsi que l'inclusion des transactions et la vérification simple des paiements (SPV). Le manque d'une racine d'état à hachage cohérent représentant l'état SVM merklisé pose des difficultés considérables pour des projets tels que les clients légers et les rollups.
Jetons un coup d'œil à la manière dont la merklization est effectuée sur d'autres chaînes, puis identifions les défis présentés par l'architecture du protocole de Solana.
Sur Bitcoin, les transactions sont stockées dans un bloc à l’aide d’un arbre de Merkle, dont les racines sont stockées dans le en-tête de blocLe protocole Bitcoin hachera les entrées et sorties d'une transaction (ainsi que quelques autres métadonnées) dans un Identifiant de transaction (TxID). Afin de prouver l'état sur Bitcoin, un utilisateur peut simplement calculer une preuve de Merkle pour vérifier le TxID par rapport à la racine de Merkle du bloc.
Ce processus de vérification valide également l'état, car l'ID de la Tx est unique à un ensemble d'entrées et de sorties, tous deux reflétant les changements d'état de l'adresse. Notez que les transactions Bitcoin peuvent également contenir Scripts Taproot, qui produisent des sorties de transaction qui peuvent être vérifiées lors de la vérification, souvent en réexécutant le script en utilisant les entrées de la transaction et les données témoins du script, et en validant par rapport à ses sorties.
Tout comme Bitcoin, Ethereum stocke les transactions à l'aide d'une structure de données personnalisée (dérivée d'un arbre de Merkle) appelée un Arbre de hachage de Patricia de Merkle (MPT). Cette structure de données est conçue pour des mises à jour rapides et une optimisation sur de grands ensembles de données. Naturellement, cela est dû au fait qu'Ethereum a significativement plus d'entrées et de sorties à gérer que Bitcoin.
Le Machine virtuelle Ethereum (EVM) agit comme une machine à états globale. L'EVM est essentiellement un environnement de calcul distribué gigantesque qui prend en charge l'exécution contrats intelligents, chacun d'eux réservant son propre espace d'adresse dans la mémoire globale. En conséquence, les clients qui souhaitent vérifier l'état sur Ethereum doivent non seulement tenir compte du résultat d'une transaction (logs, code de retour, etc.), mais aussi des modifications de l'état global résultant de la transaction.
Heureusement, l'EVM utilise astucieusement trois structures de trie importantes, qui stockent leurs racines dans chaque en-tête de bloc.
Étant donné une transaction, un client peut prouver son inclusion dans un bloc en évaluant la racine de l'arbre des transactions (comme Bitcoin), son résultat en évaluant l'arbre des reçus, et les changements à l'état global en évaluant l'arbre d'état.
Une des raisons de la haute capacité de Solana est le fait qu'elle n'a pas la structure multi-arbre qu'Ethereum a. Les dirigeants de Solana calculent des arbres de Merkle lors de la création de blocs, mais ils sont structurés différemment de ceux de l'EVM. Malheureusement, c'est là que réside le problème pour les rollups basés sur SVM.
Solana merklizes transactions into what are called entries, and there can be multiple entries per slot; hence, multiple transaction roots per block. Furthermore, Solana computes a Merkle root of account state only once per epoch (about 2.5 days), and this root is not available on the ledger.
En fait, les en-têtes de bloc Solana ne contiennent aucun arbre de Merkle. Au lieu de cela, ils contiennent les précédents et actuelsblockhash, qui est calculée à travers Solana'sPreuve de l'histoirel'algorithme de preuve de l'histoire (PoH). PoH exige que les validateurs enregistrent continuellement des "ticks" en hachant de manière récursive les entrées de bloc, qui peuvent être vides ou contenir des lots de transactions. Le tick final (hash) de l'algorithme PoH est le blockhash de ce bloc.
Le problème avec la Preuve d'Histoire est qu'elle rend très difficile la preuve de l'état à partir d'un blockhash. Solana est conçue pour diffuser les hachages PoH afin de maintenir son concept de temps écoulé. La racine de transaction n'est disponible que pour un tick PoH contenant une entrée avec des transactions, pas pour le bloc dans son ensemble, et il n'y a pas de racine d'état stockée dans une quelconque entrée.
Cependant, il existe un autre hachage que les clients peuvent utiliser : le hachage de la banque. Parfois appelés hachages de slot, les hachages de banque sont disponibles dans le SlotHashes sysvarcompte, qui peut être consulté par un client. Un hachage bancaire est créé pour chaque bloc (créneau) à partir d'une poignée d'entrées:
Comme on peut le voir, le hachage de la banque est surchargé avec plusieurs entrées de hachage, ce qui ajoute de la complexité pour les clients qui tentent de prouver des informations sur les transactions ou l'état. De plus, seul un hachage de banque pour une banque ayant effectué un "hachage des comptes d'époque" - le hachage de tous les comptes une fois par époque - aura cette racine particulière incluse. En outre, le compte SlotHashes sysvar est tronqué aux derniers 512 hachages de banque.
réseau SOONest un SVM L2 sur Ethereum. Tout en intégrant la merklisation dans SOON, nous avons donné la priorité à l'utilisation de solutions éprouvées et bien établies pour des raisons de stabilité, plutôt que de réinventer la roue. En décidant quelle structure de données ou quels algorithmes de hachage utiliser, nous avons pris en compte leur compatibilité avec les contrats L1 de la Empilement d'optimisme, leur capacité à s'intégrer parfaitement dans l'architecture de Solana, et s'ils peuvent atteindre de hautes performances sous le modèle de compte de Solana.
Nous avons constaté que lorsque le nombre de comptes augmente, le modèle de stockage de l'arbre de Patricia Merkle (MPT) basé sur LSM-Treeproduirait plus d'amplification de lecture/écriture de disque, entraînant des pertes de performances. En fin de compte, nous avons décidé d'intégrer le Erigon MPTen s'appuyant sur l'excellent travail en Rust réalisé par le rETHéquipe et ajout de support pour le modèle de compte Solana.
Comme mentionné précédemment, l'arbre d'état de SOON est un MPT conçu pour prendre en charge les comptes Solana. En tant que tel, nous avons défini un type de compte compatible avec SVM pour servir de données à chaque nœud feuille.
struct TrieSolanaAccount {
Lamports : U64,
données: Vec
exécutable: bool,
rent_epoch: u64,
propriétaire: Pubkey,
}
Pour permettre au module MPT de s'abonner en temps réel au dernier état des comptes SVM, nous avons introduit un notificateur de compte. Pendant la phase bancaire, le notificateur de compte informe le module MPT des changements d'état du compte, et le MPT met à jour de manière incrémentielle ces changements dans la structure de l'arbre.
Il est important de noter que le module MPT ne met à jour ses sous-arbres qu'à chaque 50 créneaux et ne calcule pas la racine de l'état à la fin de chaque créneau. Cette approche est adoptée pour deux raisons. Premièrement, calculer la racine de l'état à chaque créneau aurait un impact significatif sur les performances. Deuxièmement, la racine de l'état n'est nécessaire que lorsque le proposant soumet un outputRootà la L1. Par conséquent, il suffit de le calculer périodiquement, en fonction de la fréquence de soumission du proposant.
outputRoot = keccak256(version, state_root, withdraw_root, l2_block_hash)
Bientôt, le module MPT maintient simultanément deux types de structures de trie : le trie d'état pour l'état global et le trie de retrait pour les transactions de retrait. Le proposant génère périodiquement une sortie racine par la racine d'état et la racine de retrait, et la soumet à la L1.
Actuellement, SOON calcule la racine de l'état et la racine de retrait une fois tous les 450 créneaux et l'ajoute à la blockchain L2. En conséquence, la cohérence des données MPT entre les autres nœuds du réseau peut être assurée. Cependant, la structure de bloc de Solana n'inclut pas d'en-tête de bloc, ce qui signifie qu'il n'y a pas d'endroit pour stocker la racine de l'état. Examinons de plus près la structure de base de la blockchain Solana, puis explorons comment SOON introduit l'entrée unique pour stocker la racine de l'état.
La blockchain Solana est composée de slots, qui sont générés par le module PoH. Un slot contient plusieurs entrées, chaque entrée comprenant des ticks et des transactions. Au niveau des couches réseau et stockage, un slot est stocké et transmis en utilisant lambeauxcomme la plus petite unité. Les lambeaux peuvent être convertis en et à partir d'entrées.
pub struct Entry {
Le nombre de hachages depuis l'ID d'entrée précédent.
pub num_hashes: u64,
/// Le hash SHA-256 num_hashes
après l'ID d'entrée précédent.
pub hash: Hash,
/// Une liste non ordonnée de transactions qui ont été observées avant que l'ID d'entrée ne soit
/// généré. Ils ont peut-être été observés avant un identifiant d'entrée précédent mais étaient
/// repoussé dans cette liste pour assurer une interprétation déterministe du grand livre.
transactions publiques: Vec
}
Nous avons suivi la structure de la blockchain générée par PoH et conservé la structure des fragments, ce qui nous permet de réutiliser la couche de stockage existante, la couche réseau et le cadre RPC de Solana. Pour stocker des données supplémentaires sur la blockchain L2, nous avons introduit l'UniqueEntry. Cette caractéristique nous permet de personnaliser la charge utile de l'entrée et de définir les règles de validation indépendantes pour les données.
pub const UNIQUE_ENTRY_NUM_HASHES_FLAG: u64 = 0x8000_0000_0000_0000;
/// L'entrée unique est un type d'entrée spécial. Ce qui est utile lorsque nous avons besoin de stocker des données dans
/// blockstore but do not want to verify it.
///
La mise en page de num_hashes
est:
/// |…1 bit…|…63 bit…|
/// \ __/
/// \ \
/// drapeau champ personnalisé
pub trait UniqueEntry: Sized {
fn encode_to_entries(&self) -> Vec
fn decode_from_entries(
entrées: impl IntoIterator<Item = Entry>,
) -> Résultat
}
pub fn unique_entry(custom_field: u64, hash: Hash) -> Entry {
Entrée {
num_hashes: num_hashes(custom_field), hash, transactions: vec![],
}
pub fn num_hashes(custom_field: u64) -> u64 {
assert!(custom_field < (1 << 63));
UNIQUE_ENTRY_NUM_HASHES_FLAG | champ_personnalisé
}
pub fn is_unique(entry: &Entry) -> bool {
entry.num_hashes & UNIQUE_ENTRY_NUM_HASHES_FLAG != 0
}
Dans un UniqueEntry, num_hashes est utilisé comme mise en page de bits, où le premier drapeau de bit est utilisé pour distinguer entre une Entrée et une Entrée Unique, et les 63 bits suivants sont utilisés pour définir le type de charge utile. Le champ de hash sert de charge utile, contenant les données personnalisées requises.
Jetons un coup d'œil à un exemple d'utilisation de trois entrées uniques pour stocker le hachage de l'emplacement, la racine d'état et la racine de retrait.
Entrée unique de la racine MPT.
pub struct MptRoot {
emplacement de pub : Emplacement,
state_root du pub : B256,
pub withdrawal_root: B256,
}
impl UniqueEntry for MptRoot {
fn encode_to_entries(&self) -> Vec
laisser slot_entry = unique_entry(0, slot_to_hash(self.slot)); laisser state_root_entry = unique_entry(1, self.state_root.0.into()); laisser withdrawal_root_entry = unique_entry(2, self.withdrawal_root.0.into()); vec![slot_entry, state_root_entry, withdrawal_root_entry]
}
fn decode_from_entries(
entrées: impl IntoIterator
) -> Result
laissez mut entrées = entrées.into_iter(); laissez entrée = entrées.next().ok_or(UniqueEntryError::NoMoreEntries)?; laissez fente = hash_to_slot(entrée.hash); laissez entrée = entrées.next().ok_or(UniqueEntryError::NoMoreEntries)?; laissez state_root = B256::from(entrée.hash.to_bytes()); laissez entrée = entrées.next().ok_or(UniqueEntryError::NoMoreEntries)?; laissez withdrawal_root = B256::from(entrée.hash.to_bytes()); Ok(MptRoot { fente, state_root, withdrawal_root, })
}
}
Étant donné que UniqueEntry redéfinit la sémantique des num_hashes, il ne peut pas être traité lors de la phase de vérification de la PoH. Par conséquent, au début du processus de vérification, nous filtrons d'abord les entrées uniques et les dirigeons vers un flux de vérification personnalisé basé sur leur type de charge utile. Les entrées régulières restantes continuent à travers le processus de vérification PoH original.
Le pont natif est un élément crucial de l'infrastructure des solutions de couche 2, responsable de la transmission des messages entre le L1 et le L2. Les messages du L1 au L2 sont appelés transactions de dépôt, tandis que les messages du L2 au L1 sont appelés transactions de retrait.
Les transactions de dépôt sont généralement traitées par le pipeline de dérivationet ne nécessitent que l'utilisateur envoie une seule transaction sur la L1. Les transactions de retrait, en revanche, sont plus complexes et nécessitent que l'utilisateur envoie trois transactions pour compléter le processus :
La preuve d'inclusion d'une transaction de retrait garantit que le retrait a eu lieu sur le L2, améliorant considérablement la sécurité du pont canonique.
Dans la pile OP, le hachage de la transaction de retrait de l'utilisateur est stocké dans la variable d'état correspondante à la OptimismPortalLe contrat. L'interface RPC eth_getProof est ensuite utilisée pour fournir aux utilisateurs une preuve d'inclusion pour une transaction de retrait spécifique.
Contrairement à l'EVM, les données de contrat SVM sont stockées dans le champ de données des comptes, et tous les types de comptes existent au même niveau hiérarchique.
SOON a introduit un programme de pont natif : Bridge1111111111111111111111111111111111111. Chaque fois qu'un utilisateur initie une transaction de retrait, le programme de pont génère un index globalement unique pour chaque transaction de retrait et utilise cet index comme graine pour créer un nouveau Compte Dérivé de Programme(PDA) pour stocker la transaction de retrait correspondante.
pub struct RetraitTransaction {
/// compteur de retrait
pub nonce: U256,
/// utilisateur qui souhaite retirer
expéditeur pub: Pubkey,
/// adresse utilisateur en L1
cible publique: Adresse,
/// montant de retrait en lamports
valeur publique: U256,
/// limite de gaz en L1
pub gas_limit: U256,
/// données d'appel de retrait en L1
pub data: L1WithdrawalCalldata,
}
Nous avons défini la structure WithdrawalTransaction pour stocker les transactions de retrait dans le champ de données de la PDA.
Ce design fonctionne de manière similaire à la pile OP. Une fois que l'outputRoot contenant la transaction de retrait est soumise à la L1, l'utilisateur peut soumettre une preuve d'inclusion pour la transaction de retrait à la L1 afin de démarrer la période de défi.
Après que le proposant soumette outputRoot à L1, cela signifie que l'état de L2 a été réglé. Si un challenger détecte que le proposant a soumis un état incorrect, il peut lancer un défi pour protéger les fonds dans le pont.
L'un des aspects les plus critiques de la construction d'un système à l'épreuve des pannes est de permettre à la blockchain de passer de l'état S1 à l'état S2 de manière sans état. Cela garantit que le contrat d'arbitre sur L1 peut rejouer les instructions du programme de manière sans état pour effectuer l'arbitrage.
Cependant, au début de ce processus, le challenger doit prouver que toutes les entrées initiales de l'état S1 sont valides. Ces entrées initiales incluent les états des comptes participants (par exemple, lamports, données, propriétaire, etc.). Contrairement à l'EVM, le SVM sépare naturellement l'état du compte de la computation. Cependant, API SVM d'Anzapermet aux comptes d'être transmis à SVM grâce à un trait TransactionProcessingCallback, comme indiqué ci-dessous.
pub trait TransactionProcessingCallback {
fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option
fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option
fn add_builtin_account(&self, _name: &str, _program_id: &Pubkey) {}
}
Ainsi, nous n'avons besoin d'utiliser que l'état S1 ainsi que les preuves d'inclusion des comptes d'entrée pour vérifier la validité des entrées du programme de défi.
SOON marque une étape clé dans le développement de l'écosystème SVM. En intégrant la merklisation, SOON répond au manque de Solana d'une racine d'état globale, permettant des fonctionnalités essentielles telles que des preuves d'inclusion pour les preuves de faute, des retraits sécurisés et une exécution sans état.
De plus, la conception de merklisation de SOON au sein de SVM peut permettre aux clients légers d’utiliser des chaînes basées sur des SVM, même si Solana lui-même ne prend actuellement pas en charge les clients légers. Il est même possible qu’une partie de la conception puisse également aider à attirer des clients légers dans la chaîne principale.
En utilisant les essais de Patricia Merkle (MPT) pour la gestion de l'état, SOON s'aligne bientôt sur l'infrastructure d'Ethereum, améliorant l'interopérabilité et faisant progresser les solutions L2 basées sur SVM. Cette innovation renforce l'écosystème SVM en améliorant la sécurité, la scalabilité et la compatibilité, tout en favorisant la croissance des applications décentralisées.
La machine virtuelle Solana (SVM) est de plus en plus adoptée en tant que couche d'exécution pour diverses solutions de couche 2 (L2). Cependant, une limitation clé de la conception initiale de la SVM est l'obscurité de sa racine d'état global. Cela pose des défis importants pour les systèmes rollup, où les racines d'état globales sont cruciales pour garantir l'intégrité, permettre des preuves de fraude et prendre en charge des opérations entre les couches.
Dans un rollup, le proposeur soumet périodiquement la racine d'état L2 (racine de Merkle) à la couche 1 (L1), établissant des points de contrôle pour la chaîne L2. Ces points de contrôle permettent des preuves d'inclusion pour tout état de compte, permettant une exécution sans état d'un point de contrôle à un autre. Les preuves de fraude reposent sur ce mécanisme, car les participants peuvent fournir des preuves d'inclusion pour vérifier les entrées valides lors des litiges. De plus, les arbres de Merkle renforcent la sécurité des passerelles canoniques en permettant aux utilisateurs de générer des preuves d'inclusion pour les transactions de retrait, garantissant des interactions sans confiance entre L2 et L1.
Pour relever ces défis, Réseau SOONintroduit les arbres de Merkle dans la couche d'exécution du SVM, permettant aux clients de fournir des preuves d'inclusion. SOON s'intègre à la Preuve-de-l'Histoire en utilisant des entrées uniques pour incorporer directement les racines d'état dans les blockchains basées sur le SVM. Grâce à cette innovation, la pile SOON peut prendre en charge de nouveaux rollups basés sur le SVM avec une sécurité, une évolutivité et une utilité améliorées.
Solana a toujours été conçue avec un débit élevé comme objectif principal, nécessitant des compromis de conception délibérés — en particulier pendant ses premiers développements — pour atteindre ses performances novatrices. Parmi ces compromis, l'une des décisions les plus impactantes concernait la manière et le moment où Solana merkliserait son état.
Au final, cette décision a créé d'énormes défis pour les clients pour prouver l'état global ainsi que l'inclusion des transactions et la vérification simple des paiements (SPV). Le manque d'une racine d'état à hachage cohérent représentant l'état SVM merklisé pose des difficultés considérables pour des projets tels que les clients légers et les rollups.
Jetons un coup d'œil à la manière dont la merklization est effectuée sur d'autres chaînes, puis identifions les défis présentés par l'architecture du protocole de Solana.
Sur Bitcoin, les transactions sont stockées dans un bloc à l’aide d’un arbre de Merkle, dont les racines sont stockées dans le en-tête de blocLe protocole Bitcoin hachera les entrées et sorties d'une transaction (ainsi que quelques autres métadonnées) dans un Identifiant de transaction (TxID). Afin de prouver l'état sur Bitcoin, un utilisateur peut simplement calculer une preuve de Merkle pour vérifier le TxID par rapport à la racine de Merkle du bloc.
Ce processus de vérification valide également l'état, car l'ID de la Tx est unique à un ensemble d'entrées et de sorties, tous deux reflétant les changements d'état de l'adresse. Notez que les transactions Bitcoin peuvent également contenir Scripts Taproot, qui produisent des sorties de transaction qui peuvent être vérifiées lors de la vérification, souvent en réexécutant le script en utilisant les entrées de la transaction et les données témoins du script, et en validant par rapport à ses sorties.
Tout comme Bitcoin, Ethereum stocke les transactions à l'aide d'une structure de données personnalisée (dérivée d'un arbre de Merkle) appelée un Arbre de hachage de Patricia de Merkle (MPT). Cette structure de données est conçue pour des mises à jour rapides et une optimisation sur de grands ensembles de données. Naturellement, cela est dû au fait qu'Ethereum a significativement plus d'entrées et de sorties à gérer que Bitcoin.
Le Machine virtuelle Ethereum (EVM) agit comme une machine à états globale. L'EVM est essentiellement un environnement de calcul distribué gigantesque qui prend en charge l'exécution contrats intelligents, chacun d'eux réservant son propre espace d'adresse dans la mémoire globale. En conséquence, les clients qui souhaitent vérifier l'état sur Ethereum doivent non seulement tenir compte du résultat d'une transaction (logs, code de retour, etc.), mais aussi des modifications de l'état global résultant de la transaction.
Heureusement, l'EVM utilise astucieusement trois structures de trie importantes, qui stockent leurs racines dans chaque en-tête de bloc.
Étant donné une transaction, un client peut prouver son inclusion dans un bloc en évaluant la racine de l'arbre des transactions (comme Bitcoin), son résultat en évaluant l'arbre des reçus, et les changements à l'état global en évaluant l'arbre d'état.
Une des raisons de la haute capacité de Solana est le fait qu'elle n'a pas la structure multi-arbre qu'Ethereum a. Les dirigeants de Solana calculent des arbres de Merkle lors de la création de blocs, mais ils sont structurés différemment de ceux de l'EVM. Malheureusement, c'est là que réside le problème pour les rollups basés sur SVM.
Solana merklizes transactions into what are called entries, and there can be multiple entries per slot; hence, multiple transaction roots per block. Furthermore, Solana computes a Merkle root of account state only once per epoch (about 2.5 days), and this root is not available on the ledger.
En fait, les en-têtes de bloc Solana ne contiennent aucun arbre de Merkle. Au lieu de cela, ils contiennent les précédents et actuelsblockhash, qui est calculée à travers Solana'sPreuve de l'histoirel'algorithme de preuve de l'histoire (PoH). PoH exige que les validateurs enregistrent continuellement des "ticks" en hachant de manière récursive les entrées de bloc, qui peuvent être vides ou contenir des lots de transactions. Le tick final (hash) de l'algorithme PoH est le blockhash de ce bloc.
Le problème avec la Preuve d'Histoire est qu'elle rend très difficile la preuve de l'état à partir d'un blockhash. Solana est conçue pour diffuser les hachages PoH afin de maintenir son concept de temps écoulé. La racine de transaction n'est disponible que pour un tick PoH contenant une entrée avec des transactions, pas pour le bloc dans son ensemble, et il n'y a pas de racine d'état stockée dans une quelconque entrée.
Cependant, il existe un autre hachage que les clients peuvent utiliser : le hachage de la banque. Parfois appelés hachages de slot, les hachages de banque sont disponibles dans le SlotHashes sysvarcompte, qui peut être consulté par un client. Un hachage bancaire est créé pour chaque bloc (créneau) à partir d'une poignée d'entrées:
Comme on peut le voir, le hachage de la banque est surchargé avec plusieurs entrées de hachage, ce qui ajoute de la complexité pour les clients qui tentent de prouver des informations sur les transactions ou l'état. De plus, seul un hachage de banque pour une banque ayant effectué un "hachage des comptes d'époque" - le hachage de tous les comptes une fois par époque - aura cette racine particulière incluse. En outre, le compte SlotHashes sysvar est tronqué aux derniers 512 hachages de banque.
réseau SOONest un SVM L2 sur Ethereum. Tout en intégrant la merklisation dans SOON, nous avons donné la priorité à l'utilisation de solutions éprouvées et bien établies pour des raisons de stabilité, plutôt que de réinventer la roue. En décidant quelle structure de données ou quels algorithmes de hachage utiliser, nous avons pris en compte leur compatibilité avec les contrats L1 de la Empilement d'optimisme, leur capacité à s'intégrer parfaitement dans l'architecture de Solana, et s'ils peuvent atteindre de hautes performances sous le modèle de compte de Solana.
Nous avons constaté que lorsque le nombre de comptes augmente, le modèle de stockage de l'arbre de Patricia Merkle (MPT) basé sur LSM-Treeproduirait plus d'amplification de lecture/écriture de disque, entraînant des pertes de performances. En fin de compte, nous avons décidé d'intégrer le Erigon MPTen s'appuyant sur l'excellent travail en Rust réalisé par le rETHéquipe et ajout de support pour le modèle de compte Solana.
Comme mentionné précédemment, l'arbre d'état de SOON est un MPT conçu pour prendre en charge les comptes Solana. En tant que tel, nous avons défini un type de compte compatible avec SVM pour servir de données à chaque nœud feuille.
struct TrieSolanaAccount {
Lamports : U64,
données: Vec
exécutable: bool,
rent_epoch: u64,
propriétaire: Pubkey,
}
Pour permettre au module MPT de s'abonner en temps réel au dernier état des comptes SVM, nous avons introduit un notificateur de compte. Pendant la phase bancaire, le notificateur de compte informe le module MPT des changements d'état du compte, et le MPT met à jour de manière incrémentielle ces changements dans la structure de l'arbre.
Il est important de noter que le module MPT ne met à jour ses sous-arbres qu'à chaque 50 créneaux et ne calcule pas la racine de l'état à la fin de chaque créneau. Cette approche est adoptée pour deux raisons. Premièrement, calculer la racine de l'état à chaque créneau aurait un impact significatif sur les performances. Deuxièmement, la racine de l'état n'est nécessaire que lorsque le proposant soumet un outputRootà la L1. Par conséquent, il suffit de le calculer périodiquement, en fonction de la fréquence de soumission du proposant.
outputRoot = keccak256(version, state_root, withdraw_root, l2_block_hash)
Bientôt, le module MPT maintient simultanément deux types de structures de trie : le trie d'état pour l'état global et le trie de retrait pour les transactions de retrait. Le proposant génère périodiquement une sortie racine par la racine d'état et la racine de retrait, et la soumet à la L1.
Actuellement, SOON calcule la racine de l'état et la racine de retrait une fois tous les 450 créneaux et l'ajoute à la blockchain L2. En conséquence, la cohérence des données MPT entre les autres nœuds du réseau peut être assurée. Cependant, la structure de bloc de Solana n'inclut pas d'en-tête de bloc, ce qui signifie qu'il n'y a pas d'endroit pour stocker la racine de l'état. Examinons de plus près la structure de base de la blockchain Solana, puis explorons comment SOON introduit l'entrée unique pour stocker la racine de l'état.
La blockchain Solana est composée de slots, qui sont générés par le module PoH. Un slot contient plusieurs entrées, chaque entrée comprenant des ticks et des transactions. Au niveau des couches réseau et stockage, un slot est stocké et transmis en utilisant lambeauxcomme la plus petite unité. Les lambeaux peuvent être convertis en et à partir d'entrées.
pub struct Entry {
Le nombre de hachages depuis l'ID d'entrée précédent.
pub num_hashes: u64,
/// Le hash SHA-256 num_hashes
après l'ID d'entrée précédent.
pub hash: Hash,
/// Une liste non ordonnée de transactions qui ont été observées avant que l'ID d'entrée ne soit
/// généré. Ils ont peut-être été observés avant un identifiant d'entrée précédent mais étaient
/// repoussé dans cette liste pour assurer une interprétation déterministe du grand livre.
transactions publiques: Vec
}
Nous avons suivi la structure de la blockchain générée par PoH et conservé la structure des fragments, ce qui nous permet de réutiliser la couche de stockage existante, la couche réseau et le cadre RPC de Solana. Pour stocker des données supplémentaires sur la blockchain L2, nous avons introduit l'UniqueEntry. Cette caractéristique nous permet de personnaliser la charge utile de l'entrée et de définir les règles de validation indépendantes pour les données.
pub const UNIQUE_ENTRY_NUM_HASHES_FLAG: u64 = 0x8000_0000_0000_0000;
/// L'entrée unique est un type d'entrée spécial. Ce qui est utile lorsque nous avons besoin de stocker des données dans
/// blockstore but do not want to verify it.
///
La mise en page de num_hashes
est:
/// |…1 bit…|…63 bit…|
/// \ __/
/// \ \
/// drapeau champ personnalisé
pub trait UniqueEntry: Sized {
fn encode_to_entries(&self) -> Vec
fn decode_from_entries(
entrées: impl IntoIterator<Item = Entry>,
) -> Résultat
}
pub fn unique_entry(custom_field: u64, hash: Hash) -> Entry {
Entrée {
num_hashes: num_hashes(custom_field), hash, transactions: vec![],
}
pub fn num_hashes(custom_field: u64) -> u64 {
assert!(custom_field < (1 << 63));
UNIQUE_ENTRY_NUM_HASHES_FLAG | champ_personnalisé
}
pub fn is_unique(entry: &Entry) -> bool {
entry.num_hashes & UNIQUE_ENTRY_NUM_HASHES_FLAG != 0
}
Dans un UniqueEntry, num_hashes est utilisé comme mise en page de bits, où le premier drapeau de bit est utilisé pour distinguer entre une Entrée et une Entrée Unique, et les 63 bits suivants sont utilisés pour définir le type de charge utile. Le champ de hash sert de charge utile, contenant les données personnalisées requises.
Jetons un coup d'œil à un exemple d'utilisation de trois entrées uniques pour stocker le hachage de l'emplacement, la racine d'état et la racine de retrait.
Entrée unique de la racine MPT.
pub struct MptRoot {
emplacement de pub : Emplacement,
state_root du pub : B256,
pub withdrawal_root: B256,
}
impl UniqueEntry for MptRoot {
fn encode_to_entries(&self) -> Vec
laisser slot_entry = unique_entry(0, slot_to_hash(self.slot)); laisser state_root_entry = unique_entry(1, self.state_root.0.into()); laisser withdrawal_root_entry = unique_entry(2, self.withdrawal_root.0.into()); vec![slot_entry, state_root_entry, withdrawal_root_entry]
}
fn decode_from_entries(
entrées: impl IntoIterator
) -> Result
laissez mut entrées = entrées.into_iter(); laissez entrée = entrées.next().ok_or(UniqueEntryError::NoMoreEntries)?; laissez fente = hash_to_slot(entrée.hash); laissez entrée = entrées.next().ok_or(UniqueEntryError::NoMoreEntries)?; laissez state_root = B256::from(entrée.hash.to_bytes()); laissez entrée = entrées.next().ok_or(UniqueEntryError::NoMoreEntries)?; laissez withdrawal_root = B256::from(entrée.hash.to_bytes()); Ok(MptRoot { fente, state_root, withdrawal_root, })
}
}
Étant donné que UniqueEntry redéfinit la sémantique des num_hashes, il ne peut pas être traité lors de la phase de vérification de la PoH. Par conséquent, au début du processus de vérification, nous filtrons d'abord les entrées uniques et les dirigeons vers un flux de vérification personnalisé basé sur leur type de charge utile. Les entrées régulières restantes continuent à travers le processus de vérification PoH original.
Le pont natif est un élément crucial de l'infrastructure des solutions de couche 2, responsable de la transmission des messages entre le L1 et le L2. Les messages du L1 au L2 sont appelés transactions de dépôt, tandis que les messages du L2 au L1 sont appelés transactions de retrait.
Les transactions de dépôt sont généralement traitées par le pipeline de dérivationet ne nécessitent que l'utilisateur envoie une seule transaction sur la L1. Les transactions de retrait, en revanche, sont plus complexes et nécessitent que l'utilisateur envoie trois transactions pour compléter le processus :
La preuve d'inclusion d'une transaction de retrait garantit que le retrait a eu lieu sur le L2, améliorant considérablement la sécurité du pont canonique.
Dans la pile OP, le hachage de la transaction de retrait de l'utilisateur est stocké dans la variable d'état correspondante à la OptimismPortalLe contrat. L'interface RPC eth_getProof est ensuite utilisée pour fournir aux utilisateurs une preuve d'inclusion pour une transaction de retrait spécifique.
Contrairement à l'EVM, les données de contrat SVM sont stockées dans le champ de données des comptes, et tous les types de comptes existent au même niveau hiérarchique.
SOON a introduit un programme de pont natif : Bridge1111111111111111111111111111111111111. Chaque fois qu'un utilisateur initie une transaction de retrait, le programme de pont génère un index globalement unique pour chaque transaction de retrait et utilise cet index comme graine pour créer un nouveau Compte Dérivé de Programme(PDA) pour stocker la transaction de retrait correspondante.
pub struct RetraitTransaction {
/// compteur de retrait
pub nonce: U256,
/// utilisateur qui souhaite retirer
expéditeur pub: Pubkey,
/// adresse utilisateur en L1
cible publique: Adresse,
/// montant de retrait en lamports
valeur publique: U256,
/// limite de gaz en L1
pub gas_limit: U256,
/// données d'appel de retrait en L1
pub data: L1WithdrawalCalldata,
}
Nous avons défini la structure WithdrawalTransaction pour stocker les transactions de retrait dans le champ de données de la PDA.
Ce design fonctionne de manière similaire à la pile OP. Une fois que l'outputRoot contenant la transaction de retrait est soumise à la L1, l'utilisateur peut soumettre une preuve d'inclusion pour la transaction de retrait à la L1 afin de démarrer la période de défi.
Après que le proposant soumette outputRoot à L1, cela signifie que l'état de L2 a été réglé. Si un challenger détecte que le proposant a soumis un état incorrect, il peut lancer un défi pour protéger les fonds dans le pont.
L'un des aspects les plus critiques de la construction d'un système à l'épreuve des pannes est de permettre à la blockchain de passer de l'état S1 à l'état S2 de manière sans état. Cela garantit que le contrat d'arbitre sur L1 peut rejouer les instructions du programme de manière sans état pour effectuer l'arbitrage.
Cependant, au début de ce processus, le challenger doit prouver que toutes les entrées initiales de l'état S1 sont valides. Ces entrées initiales incluent les états des comptes participants (par exemple, lamports, données, propriétaire, etc.). Contrairement à l'EVM, le SVM sépare naturellement l'état du compte de la computation. Cependant, API SVM d'Anzapermet aux comptes d'être transmis à SVM grâce à un trait TransactionProcessingCallback, comme indiqué ci-dessous.
pub trait TransactionProcessingCallback {
fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option
fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option
fn add_builtin_account(&self, _name: &str, _program_id: &Pubkey) {}
}
Ainsi, nous n'avons besoin d'utiliser que l'état S1 ainsi que les preuves d'inclusion des comptes d'entrée pour vérifier la validité des entrées du programme de défi.
SOON marque une étape clé dans le développement de l'écosystème SVM. En intégrant la merklisation, SOON répond au manque de Solana d'une racine d'état globale, permettant des fonctionnalités essentielles telles que des preuves d'inclusion pour les preuves de faute, des retraits sécurisés et une exécution sans état.
De plus, la conception de merklisation de SOON au sein de SVM peut permettre aux clients légers d’utiliser des chaînes basées sur des SVM, même si Solana lui-même ne prend actuellement pas en charge les clients légers. Il est même possible qu’une partie de la conception puisse également aider à attirer des clients légers dans la chaîne principale.
En utilisant les essais de Patricia Merkle (MPT) pour la gestion de l'état, SOON s'aligne bientôt sur l'infrastructure d'Ethereum, améliorant l'interopérabilité et faisant progresser les solutions L2 basées sur SVM. Cette innovation renforce l'écosystème SVM en améliorant la sécurité, la scalabilité et la compatibilité, tout en favorisant la croissance des applications décentralisées.