MegaETH: Costruire una Blockchain in Tempo Reale

Per MegaETH, le prestazioni non sono in secondo piano, ma la base su cui costruiamo. La nostra blockchain è progettata da zero con l'obiettivo di garantire un'elevata velocità di elaborazione e, soprattutto, una bassa latenza, per consentire applicazioni che in precedenza erano impossibili.

In questo articolo esploreremo la nostra filosofia di progettazione, gli approcci adottati, i compromessi accettati e i benchmark che supportano le nostre affermazioni. Tutto ciò di cui andiamo fieri, senza scuse.

Comprendere la Latenza

Iniziamo con una panoramica sul ciclo di vita della latenza end-to-end. Una transazione inizia nel tuo browser e deve viaggiare fino al validatore di blocchi. Questo primo passaggio, spesso definito ritardo di propagazione alla velocità della luce, richiede circa 100 ms per la maggior parte delle città. A meno che non si voglia collocarsi fisicamente vicino al validatore di blocchi—necessario solo per applicazioni con requisiti di latenza estremi, come gli on-chain CLOBS—non c’è molto che si possa fare per ottimizzare questa fase.

Una volta che la transazione raggiunge il validatore di blocchi, entriamo nella fase del block time, in cui la transazione attende in una memoria temporanea prima di essere raggruppata, sequenziata ed eseguita. Sulla mainnet di Ethereum, questo processo dura circa 12 secondi, mentre blockchain ad alte prestazioni lo riducono a 1-2 secondi.

Infine, una ricevuta della transazione deve tornare all’utente, aggiungendo un ulteriore 100 ms. Poiché i ritardi di propagazione sono un limite fisico relativamente insignificante, ci concentreremo sull’aspetto ottimizzabile: i block times.

Sequenziamento & DA

Nel precedente paragrafo abbiamo accennato al fatto che una transazione deve attendere in una memoria temporanea prima di essere sequenziata, ma ciò non è del tutto vero: è una conseguenza del batching. Nella sequenziazione tradizionale, viene utilizzato un algoritmo di consenso che, per quanto ottimizzato, richiede almeno due scambi di messaggi tra nodi globali, aggiungendo inevitabili ritardi.

Sostituendo l’algoritmo di consenso con una produzione centralizzata dei blocchi, eliminiamo la necessità di coordinazione tra più partecipanti per produrre blocchi. In questo modo, il server attivo può elaborare le transazioni come un flusso continuo, invece di accodarle per essere elaborate in batch, riducendo al minimo i tempi di inattività tra i blocchi.

Questo approccio introduce un compromesso: la produzione centralizzata dei blocchi. Eliminare il consenso senza garantire la sicurezza della rete sarebbe assurdo, a meno che non si costruisca sopra un Layer 1 decentralizzato. Questo modello è stato proposto da Vitalik Buterin nel 2021 nel suo blog Endgame (link all’articolo), in cui concludeva che tutte le soluzioni per scalare una blockchain portano inevitabilmente alla produzione centralizzata dei blocchi.

Ma qui emerge un principio chiave che ci distingue: non vogliamo reinventare la ruota, né pensiamo di poter fare tutto meglio degli altri. Per questo, affidiamo la sicurezza a sistemi decentralizzati collaudati come Ethereum mainnet ed EigenDA, concentrandoci esclusivamente sulle prestazioni.

Misurare, Poi Costruire

Un altro aspetto che ci distingue è il nostro approccio iniziale alla progettazione del layer di esecuzione. Invece di fare ipotesi su dove ottimizzare, abbiamo deciso di misurare lo stato dell’arte prima di costruire. Purtroppo, reperire dati aggiornati sulle prestazioni è stato difficile, il che spiega perché i limiti dei sistemi esistenti siano spesso poco compresi al di fuori dei team tecnici.

Per risolvere questo problema, abbiamo adottato un approccio sperimentale rigoroso e metodico, partendo dalle basi per analizzare in dettaglio i colli di bottiglia.

  • --salta se non ti interessa--

Configurazione del test:

CPU: Intel Xeon w5-2465X (16 core @ 3.1GHz)

RAM: Samsung 512GB DDR5-4800

Disco: Intel SSD D7-P5510 7.68TB (NVMe PCIe 4.0)

Abbiamo simulato diversi scenari di carico di memoria utilizzando il comando cgroup di Linux. Questo ci ha permesso di testare casi di memoria limitata e carico pesante. Successivamente, abbiamo eseguito sia una sincronizzazione storica che una sincronizzazione in tempo reale per analizzare i fattori limitanti nell’esecuzione e come si evolvono con differenti modalità.

  • --riprendi da qui--

Definizioni chiave:

  • Historical Sync: sincronizzazione dei blocchi passati senza aggiornamenti dello stato in tempo reale.
  • Live Sync: sincronizzazione della blockchain con aggiornamenti continui dello stato dopo ogni blocco.

Overhead dell’EVM

Per isolare le prestazioni del layer di esecuzione dallo I/O del database, abbiamo eseguito una sincronizzazione storica con 512GB di memoria, persistendo i dati ogni 500.000 blocchi.

Analizzando le Transazioni Per Secondo (TPS) dei primi 18 milioni di blocchi della mainnet di Ethereum, abbiamo riscontrato una TPS media di circa 14.000 negli ultimi milioni di blocchi. La volatilità dei TPS nei precedenti può essere attribuita a transazioni meno complesse avviciniandosi alla genesi della blockchaing.

Ora, 14k TPS sono poche? Ovviamente no. Ma sono abbastanza per il World Computer? Sfortunatamente no. (Considera inoltre che questi non sono TPS "reali", poiché non include il sovraccarico dell’I/O del disco).

Un'analisi dettagliata ha evidenziato un problema nella traduzione in tempo reale del bytecode dell’EVM in istruzioni CPU, che avviene perché gli smart contract vengono interpretati durante l'esecuzione. Per risolvere questo, abbiamo implementato la compilazione Just-In-Time (JIT), che converte il bytecode dell’EVM in codice macchina nativo prima dell’esecuzione, portando a miglioramenti significativi nelle prestazioni.

Overhead dell’Aggiornamento dello Stato

Passando dalla sincronizzazione storica alla sincronizzazione in tempo reale, la TPS media crolla da 14.000 a circa 1.000 TPS.

La riduzione di 14x delle TPS è dovuta principalmente a:

Costo della merkleizzazione (9,3x di rallentamento)

Scrittura dello stato su database (1,5x di rallentamento)

Per minimizzare l’I/O del database, abbiamo sostituito il tradizionale Merkle Patricia Trie (MPT) con una struttura di stato ottimizzata per ridurre l’accesso al disco. Inoltre, abbiamo scelto un database progettato per operazioni ad alta intensità di scrittura.

L’analisi ha rivelato un problema fondamentale: oltre l'80% del tempo viene speso per I/O del database, mentre meno del 20% viene impiegato per eseguire le transazioni. Spostando l’intero stato in memoria, siamo riusciti a ridurre l’accesso al disco al 10% del tempo di costruzione del blocco.

Una Blockchain in Tempo Reale

Grazie a queste ottimizzazioni, oggi siamo in grado di raggiungere tempi di blocco di 10ms, anche sotto carichi estremi (100k TPS). Questo livello di prestazioni consente agli sviluppatori di creare applicazioni che prima venivano considerate impossibili sulla blockchain.

Una blockchain in tempo reale sarà realtà solo se continueremo a mettere le prestazioni al primo posto, senza compromessi.