Articol preluat din: Techub News
Autor: Tia, Techub News
Blockchain-ul sacrifică eficiența din cauza designului său descentralizat, astfel că îmbunătățirea vitezei de execuție a fost întotdeauna o problemă urgentă de rezolvat. „Nivelul de execuție” al blockchain-ului este partea esențială care procesează fiecare tranzacție și o adaugă în lanț. Pentru a accelera capacitatea de procesare, îmbunătățirile la nivelul execuției devin una dintre strategiile centrale, iar execuția paralelă este o realizare importantă în această privință.
Blockchain-urile tradiționale adoptă de obicei metoda secvențială de procesare a tranzacțiilor, ceea ce limitează semnificativ viteza tranzacțiilor, mai ales în rețelele aglomerate, ceea ce poate duce la congestionare. Cu toate acestea, prin execuția paralelă, mai multe tranzacții pot fi procesate simultan, crescând astfel semnificativ eficiența execuției și reducând presiunea asupra lanțului.
Pentru a înțelege mai bine ce este paralelismul, vom începe prin a introduce execuția și vom lua drept exemplu Ethereum în modul PBS după Merge pentru a explica ce este execuția, arătând în același timp locul execuției în întreaga viață a tranzacției.
Etapele specifice ale execuției tranzacției
Tranzacția intră în pool-ul de memorie și este filtrată și sortată: acesta este stadiul de preprocesare după ce tranzacția a fost trimisă, care include interacțiunea dintre Mempool, Căutător și Constructor, completând filtrarea și sortarea tranzacției.
Constructorul construiește blocul (dar nu îl execută): Constructorul aranjează tranzacțiile profitabile într-un bloc pentru a finaliza pachetarea și sortarea tranzacțiilor.
Propozitorul verifică și trimite blocul: după ce blocul este construit, Constructorul trimite propunerea blocului Propozitorului. Propozitorul verifică structura blocului și conținutul tranzacțiilor, apoi trimite oficial blocul în rețea pentru a începe execuția.
Executarea tranzacțiilor: după ce blocul este trimis, nodurile execută tranzacțiile din bloc una câte una. Acesta este stadiul cheie pentru actualizarea stării, fiecare tranzacție va declanșa apeluri de contract inteligent, modificări ale soldului contului sau schimbări de stare.
Martorul atestă: validatorul atestă rezultatul execuției blocului și rădăcina stării și o folosește ca confirmare finală. Acest lucru asigură veridicitatea și validitatea blocului la nivelul execuției și previne inconsistențele.
Sincronizarea stării: fiecare nod își va sincroniza rezultatul execuției blocului (cum ar fi soldurile conturilor, actualizările stării contractelor etc.) în propria stare locală, după ce execută fiecare tranzacție, nodul calculează și stochează o nouă rădăcină de stare, folosită ca stare inițială în următorul bloc.
Desigur, aceasta este doar sincronizarea stării tranzacției pe baza blocului, pentru a menține starea actualizată pe lanț, în general, nodurile vor sincroniza datele bloc cu bloc și vor continua să verifice blocurile și starea. Dar pentru a atinge finalitatea în cadrul mecanismului POS, va trebui ca agregatorul să agregheze semnăturile martorilor din fiecare Slot într-o semnătură completă și să o transmită la Propozitorul din următorul Slot, iar validatorul trebuie să confirme starea tuturor blocurilor din acel Epoch pe baza numărului de voturi după un Epoch, formând un punct de verificare de consens temporar. După ce două Epoch-uri consecutive obțin sprijinul majorității validatorilor, blocurile și tranzacțiile vor atinge finalitatea.
Privind întreaga viață a tranzacției, execuția are loc după ce Propozitorul verifică structura blocului și conținutul tranzacțiilor trimise de Constructor. Procesul efectiv de execuție necesită procesarea fiecărei tranzacții pas cu pas și actualizarea stării contului sau a contractului corespunzător. Odată ce toate tranzacțiile au fost executate, Propozitorul va calcula o nouă rădăcină de stare (rădăcina Merkle), care este un rezumat al rezultatelor execuției tuturor tranzacțiilor din blocul curent și al stării globale finale. Pe scurt, întregul proces de execuție a blocului include o serie de calcule necesare pentru a transforma Ethereum dintr-o stare anterioară într-o stare următoare, de la execuția fiecărei tranzacții până la calculul rădăcinii Merkle.
Executare secvențială
În contrast cu paralelismul, execuția secvențială este modul de execuție mai comun în blockchain-uri în prezent. De obicei, tranzacțiile sunt executate pas cu pas. Când o tranzacție finalizată, Ethereum va actualiza starea contului și informațiile relevante (de exemplu, soldul, datele de stocare ale contractului) în arborele de stare al contului, generând un nou hash de stare al contului. Odată ce toate arborii de stare sunt actualizați, se va forma un nod rădăcină numit rădăcina Merkle a stării. După finalizarea rădăcinii Merkle a stării, rădăcinii Merkle a tranzacției și rădăcinii Merkle a chitanțelor, capul blocului va fi supus calculului hash, generând hash-ul blocului respectiv.
În această privință, ordinea de execuție a tranzacțiilor este crucială. Deoarece arborele Merkle este un arbore binar de hash-uri, rădăcinile Merkle formate în ordini diferite vor fi diferite.
Execuție paralelă
Într-un mediu de execuție paralelă, nodurile vor încerca să proceseze tranzacțiile din blocuri în mod paralel. Nu le execută în ordinea unu câte unu, ci le alocă pe diferite „căi de execuție”, permițându-le să fie executate simultan. Prin execuția paralelă, sistemul poate gestiona mai eficient tranzacțiile din blocuri, crescând capacitatea de procesare.
După finalizarea tuturor tranzacțiilor, nodurile vor agrega rezultatele execuției (adică actualizările de stare influențate de tranzacții), formând o nouă stare a blocului. Această stare va fi adăugată la blockchain, reprezentând cea mai recentă stare globală de pe lanț.
Conflict de stare
Deoarece paralelismul va gestiona tranzacțiile pe căi diferite simultan, o dificultate majoră a paralelismului este conflictul de stare. Adică pot exista mai multe tranzacții care citesc sau scriu asupra aceleași părți de date (stare) pe blockchain în aceeași perioadă de timp. Dacă această situație nu este gestionată corect, rezultatul execuției va fi incert. Deoarece ordinea actualizării stării este diferită, rezultatul final al calculului va fi diferit. De exemplu,
Să presupunem că avem două tranzacții, tranzacția A și tranzacția B, ambele încercând să actualizeze soldul aceluiași cont:
Tranzacția A: crește soldul contului cu 10.
Tranzacția B: crește soldul contului cu 20.
Soldul inițial al contului este 100.
Dacă executăm în mod secvențial, rezultatul ordinii execuției este determinat:
1. Execută întâi tranzacția A, apoi tranzacția B:
Soldul contului crește mai întâi cu 10, devenind 110.
Apoi crește cu 20, devenind în cele din urmă 130.
2. Execută întâi tranzacția B, apoi tranzacția A:
Soldul contului crește mai întâi cu 20, devenind 120.
Apoi crește cu 10, devenind în cele din urmă 130.
În aceste două ordine, soldul final este 130, deoarece sistemul asigură consistența ordinii execuției tranzacțiilor.
Dar în mediu de execuție paralelă, tranzacția A și tranzacția B pot citi în același timp soldul inițial de 100 și își pot desfășura calculele:
Tranzacția A citește un sold de 100, iar după calcul îl actualizează la 110.
Tranzacția B citește, de asemenea, un sold de 100 și după calcul îl actualizează la 120.
În această situație, datorită execuției simultane a tranzacțiilor, soldul final este actualizat doar la 120, nu 130, deoarece operațiile tranzacției A și tranzacției B s-au „suprapus” și au generat un conflict de stare.
Această problemă de conflict de stare este adesea denumită „suprapunere de date”, adică atunci când tranzacțiile încearcă să modifice simultan aceleași date, acestea pot suprascrie rezultatele de calcul ale celorlalte, ducând la o stare finală incorectă. O altă problemă pe care o poate provoca un conflict de stare este imposibilitatea de a garanta ordinea execuției. Deoarece mai multe tranzacții finalizează operațiunile în momente diferite, ordinea diferită poate duce la rezultate diferite de calcul, făcând astfel rezultatul incert.
Pentru a evita această incertitudine, sistemele de execuție paralelă ale blockchain-ului vor introduce de obicei unele mecanisme de detectare a conflictelor și de revenire, sau vor analiza anticipat dependențele tranzacțiilor, asigurându-se că acestea sunt executate în paralel fără a afecta consistența finală a stării.
Paralelism optimist și paralelism determinist
Există două metode de a aborda posibilele probleme de conflict de stare: paralelism determinist și paralelism optimist. Aceste două modele au compromisuri în ceea ce privește eficiența și complexitatea designului.
Paralelismul determinist necesită declararea anticipată a accesului la stare, validatorul sau secvențatorul vor verifica accesul declarat la stare în timpul ordonării tranzacțiilor. Dacă mai multe tranzacții încearcă să scrie în aceeași stare, aceste tranzacții vor fi marcate ca fiind în conflict, evitându-se astfel execuția simultană. Diferitele lanțuri implementează forme diferite de a declara anticipat accesul la stare, dar în general includ următoarele metode:
Prin constrângerea specificațiilor contractelor: dezvoltatorii stabilesc direct în contractele inteligente domeniile de acces la stare. De exemplu, transferul de tokenuri ERC-20 necesită acces la câmpurile de sold ale expeditorului și destinatarului.
Prin declararea datelor structurate ale tranzacției: se adaugă câmpuri specializate în tranzacție pentru a marca accesul la stare.
Prin analiza compilatorului: compilatoarele limbajelor de nivel înalt pot analiza static codul contractului, generând automat un set de acces la stare.
Prin declararea forțată a cadrelor: anumite cadre cer dezvoltatorilor să specifice explicit starea pe care trebuie să o acceseze la apelarea funcțiilor.
Paralelismul optimist va gestiona optimist tranzacțiile, iar când apare un conflict, va re-executa tranzacțiile afectate în ordine. Pentru a evita cât mai mult conflictele, cheia designului paralelismului optimist este de a realiza rapid estimări și ipoteze despre stare prin date istorice, analize statice etc. Adică sistemul, fără o verificare completă, presupune că anumite operațiuni sau actualizări de stare sunt valide, încercând să evite așteptarea tuturor proceselor de verificare, astfel îmbunătățind performanța și capacitatea de procesare.
Deși paralelismul optimist poate evita în mod eficient conflictele prin estimări rapide și ipoteze ale stării, există totuși unele provocări inevitabile, în special în cazul execuției contractelor sau tranzacțiilor între lanțuri, dacă conflictele apar frecvent, re-execuția poate încetini semnificativ performanța sistemului și poate crește consumul de resurse de calcul.
Paralelismul determinist evită conflictele posibile prin verificarea dependenței stării înainte de tranzacție, dar deoarece trebuie să declare cu precizie dependențele stării înainte de trimiterea tranzacției, acest lucru impune cerințe mai mari asupra dezvoltatorilor, crescând astfel complexitatea implementării.
Dilema paralelismului EVM
Nu doar că există distincții între paralelismul determinist și cel optimist, dar în procesul de implementare a paralelismului trebuie să ne gândim și la arhitectura bazei de date a lanțului. Problema conflictului de stare în paralelism este deosebit de dificilă în EVM-ul bazat pe structura Merkle. Arborele Merkle este o structură de hash în straturi, iar după fiecare modificare a unor date de stare de către o tranzacție, hash-ul rădăcinii arborelui Merkle trebuie să fie și el actualizat. Acest proces de actualizare este recursiv, calculându-se de la frunzele arborelui către rădăcină. Deoarece hash-ul este ireversibil, adică poate fi calculat pe nivelurile superioare doar după ce modificările de date pe nivelurile inferioare au fost finalizate, această caracteristică face ca actualizarea paralelă să fie foarte dificilă.
Dacă cele două tranzacții sunt executate în paralel și accesează aceeași stare (cum ar fi soldul contului), se va produce un conflict al nodurilor arborelui Merkle. Rezolvarea acestui conflict necesită, de obicei, mecanisme suplimentare de gestionare a tranzacțiilor, asigurându-se că se obține un hash rădăcină consistent în toate ramurile. Acest lucru nu este ușor de realizat pentru EVM, deoarece trebuie să facă compromisuri între paralelism și consistența stării.
Soluții de paralelism non-EVM
Solana
Spre deosebire de arborele de stare global al Ethereum, Solana utilizează un model de conturi. Fiecare cont este un spațiu de stocare independent, stocat în registru, evitând astfel problemele de conflict de cale.
Solana este paralelism determinist. În Solana, fiecare tranzacție trebuie să declare clar conturile pe care le va accesa și permisiunile de acces necesare (doar citire sau citire/scriere) în momentul trimiterii. Acest design permite nodurilor blockchain să analizeze în avans resursele necesare fiecărei tranzacții înainte de execuție. Deoarece tranzacția a declarat clar toate relațiile de dependență ale conturilor înainte de a începe execuția, nodul poate determina ce tranzacții vor accesa același cont, care tranzacții pot fi executate în siguranță în paralel, realizând astfel programarea inteligentă, evitând conflictele, și punând astfel bazele programării paralele.
Deoarece fiecare tranzacție declară înainte de execuție conturile și permisiunile necesare, Solana poate verifica dacă există relații de dependență între conturi (modelul Sealevel). Dacă tranzacțiile nu au conturi de citire/scriere comune, sistemul le poate aloca pe procese diferite pentru a fi executate în paralel.
Aptos
Designul execuției paralele al Aptos diferă semnificativ de Ethereum, având unele inovații cheie în arhitectura și mecanismele sale, în special în ceea ce privește modelul de conturi și stocarea stării.
Ethereum necesită actualizări frecvente ale arborelui de stare global (MPT) în timpul execuției tranzacțiilor. Toate stările conturilor și contractelor sunt stocate într-un arbore de stare comun, orice tranzacție trebuie să acceseze și să actualizeze o parte din acest arbore de stare. Aptos, pe de altă parte, împarte conturile în unități de stare independente, fiecare obiect fiind o pereche cheie-valoare independentă, obiectele pot exista independent fără a se influența reciproc, ele se leagă doar atunci când există relații de referință clare. Obiectele nu au căi de arbore comune, nu apare concurența la blocare, putând fi complet paralele.
Structura de date de bază a Aptos este Jellyfish Merkle Tree. Starea fiecărui obiect este stocată în cele din urmă în JMT, ca pereche cheie-valoare independentă. Spre deosebire de MPT-ul Ethereum, Jellyfish Merkle Tree are o structură complet binară, ceea ce simplifică atât calea de stocare, cât și calea de interogare a nodului, reducând semnificativ timpul de verificare. În plus, poziția fiecărui cont în arbore este fixă, iar nodurile din arbore sunt stocate independent, permițând actualizări și căutări paralele pentru mai multe conturi.
Aptos este paralelism optimist, nu necesită furnizarea prealabilă a tuturor relațiilor de dependență ale conturilor declarate. Pentru aceasta, Aptos folosește Block-STM, care utilizează ordinea predefinită a tranzacțiilor pentru a estima dependențele, reducând astfel numărul de anulări.
EVM paralel
Comparativ cu paralelismul non-EVM, EVM paralel se confruntă cu dificultăți tehnice mai mari în gestionarea dependențelor de stare, detectarea conflictelor, gestionarea gazului și mecanismele de revenire. Pentru a înțelege mai bine acest lucru, putem consulta modul în care unele proiecte EVM paralele (cum ar fi Sui, Monad, Canto) abordează aceste probleme.
Sui
Sui, la fel ca Aptos, folosește un model de obiecte pentru a gestiona starea, fiecare obiect (de exemplu, cont, stare de contract inteligent) fiind un resursă independentă, aceste obiecte fiind distinse prin identificatori unici. Când tranzacțiile implică obiecte diferite, acestea pot fi procesate în paralel, deoarece operează asupra unor stări diferite, fără a genera conflicte directe.
Deși Sui utilizează un model de obiecte pentru a gestiona starea, pentru a fi compatibil cu EVM, arhitectura Sui folosește straturi de adaptare suplimentare sau mecanisme de abstractizare pentru a conecta modelul de obiecte cu modelul de conturi EVM.
În Sui, programarea tranzacțiilor utilizează strategia paralelismului optimist, presupunând că nu există conflicte între tranzacții. Dacă apare un conflict, sistemul va utiliza mecanismele de revenire pentru a restabili starea.
Sui utilizează un model de obiecte și tehnologia de izolare a stării, evitând eficient problemele de dependență a stării. Fiecare obiect este o resursă independentă, tranzacțiile diferite pot fi executate în paralel, crescând astfel capacitatea de procesare și eficiența. Dar trade-off-ul acestei metode este complexitatea modelului de obiecte și costul mecanismului de revenire. Dacă apar conflicte între tranzacții, va trebui să se revină asupra unor stări, ceea ce va crește povara sistemului și poate afecta eficiența procesării paralele. Comparativ cu sistemele non-EVM de paralelism (cum ar fi Solana), Sui necesită mai multe resurse de calcul și stocare pentru a menține o paralelitate eficientă.
Monad
La fel ca Sui, Monad utilizează și paralelismul optimist. Însă paralelismul optimist al Monad va prezice totuși unele tranzacții cu relații de dependență înainte de execuția reală, prezicerea fiind realizată prin analiza statică a codului Monad. Prezicerea necesită acces la stare, iar modul în care Ethereum stochează starea face ca accesul la stare să fie foarte dificil, pentru a face procesul de citire a stării mai eficient în paralel, Monad a refăcut baza de date.
Arborele de stare Monad este împărțit în partiții, fiecare partiție menține propriul sub-arbore de stare. La actualizare, este suficient să se modifice fragmentele relevante, fără a reconstruie întregul arbore de stare. Se folosește o tabelă de indexare a stării pentru a localiza rapid starea dintr-o partiție, reducând interacțiunea între partiții.
Sinteză
Paralelismul are ca obiectiv creșterea eficienței execuției la nivelul execuției printr-o abordare de execuție pe multiple căi, iar pentru a realiza execuția pe multiple căi, lanțul trebuie să efectueze o serie de operațiuni, cum ar fi detectarea conflictelor și mecanismele de revenire, pentru a asigura execuția în paralel fără a afecta consistența finală a stării, necesitând totodată anumite îmbunătățiri ale bazei de date.
Desigur, îmbunătățirea eficienței nivelului de execuție nu este limitată doar la paralelism, optimizarea etapei de execuție poate fi realizată și prin reducerea operațiunilor de citire/scriere necesare pentru o tranzacție în baza de date. Îmbunătățirea vitezei întregului lanț implică o gamă mai largă, incluzând și îmbunătățirea eficienței nivelului de consens.
Fiecare tehnologie are condiții specifice de limitare. Paralelismul este doar una dintre modalitățile de a îmbunătăți eficiența, decizia finală de a folosi această tehnologie trebuie să ia în considerare prietenia pentru dezvoltatori, dacă poate fi realizată fără a sacrifica descentralizarea etc. Stivuirea tehnologiilor nu este întotdeauna mai bună, cel puțin în cazul Ethereum, paralelismul nu este atât de atrăgător. Dacă ne uităm doar din perspectiva îmbunătățirii eficienței, adăugarea paralelismului nu este soluția optimă pentru Ethereum, fie din perspectiva simplității, fie din perspectiva foaiei de parcurs centrată pe Rollup a Ethereum.