Cos'è la Ethereum Virtual Machine? - Next Generation Currency

in #ita6 years ago

ethereum.jpg
[Photo from https://pixabay.com/it/pelle-portafoglio-business-carte-3080553/]

La Ethereum Virtual Machine, spesso abbreviata attraverso l’acronimo EVM, è il centro di calcolo che permette l’esecuzione di codici complessi (smart contracts) al di sopra della piattaforma Ethereum. È quindi una macchina virtuale, ossia un software in grado di emulare in tutto e per tutto una macchina fisica, attraverso un processo di virtualizzazione in cui vengono assegnate le risorse fisiche (CPU, RAM, disco fisso,… ) alle applicazioni che vengono eseguite al di sopra della macchina virtuale (tra cui il sistema operativo della stessa).

Si intuisce quindi come svolga un ruolo simile a quanto la Java Virtual Machine (JVM) svolge per Java, ossia un ambiente sicuro, isolato e protetto dal resto dei processi in esecuzione o dei file nel computer host. Un crash o un malfunzionamento della EVM non provocano effetti collaterali nel file system del computer host e nei processi del sistema operativo. Si intuisce allora come la EVM funga anche da garante per i nodi della rete, che “offrono” la propria infrastruttura fisica per la memorizzazione ed il processing di smart contracts potenzialmente dannosi. La natura open e permissionless della blockchain di Ethereum (come quella di Bitcoin), permette infatti a chiunque di farne parte e di deployare smart contracts, potenzialmente malevoli.

Formalizzando la EVM è il runtime environment per smart contracts che implementa e rende possibile il meccanismo di consenso della rete, fornendo al tempo stesso un ambiente isolato e protetto dal resto della rete host locale. Come spiegato in altri articoli, in Ethereum sono presenti due tipi di account, ciascuno avente un indirizzo di 20 byte:
Externally Owned Account (EOA), controllato da una coppia di chiavi pubblica-privata (siamo nel mondo della crittografia pubblica) e quindi riferito ad una persona/entità
Contract Account (CA), controllato dal codice presente proprio nel contratto
Ogni account è composto da 4 campi: il nonce, un numero che nel caso di un EOA rappresenta il numero di transazioni inviate dall’indirizzo dell’account mentre per un CA rappresenta il numero di contratti creati dall’account (perché se ancora non lo sai un contratto può essere creato da un externally owned account oppure da un altro contratto, d’altronde è intelligente…); il balance, ossia il numero di wei (l’unità base di ether, 1 ether= 1e+18wei) posseduti dall’account; il contract code dell’account, che nel caso di un EOA è solo l’hash della stringa vuota; lo storage dell’account, vuoto di default.

Ogni nodo della rete ha la propria EVM (che può essere implementata in differenti linguaggi tra cui Java, C++, Python, Go ) ed esegue in particolare lo stesso set di istruzioni degli altri nodi. Gli smart contracts, sviluppati con linguaggi di alto livello quali Solidity e Serpent, prima di essere eseguiti, vengono compilati producendo il bytecode interpretabile poi dalla EVM. Questo consiste di una serie di bytes, dove ogni byte rappresenta un’operazione. In generale l’esecuzione del codice è un loop infinito che consiste nel ripetere continuamente le operazioni indirizzate dal program counter (PC), inizializzato all’indirizzo 0 che avanza poi a passo 1, fintanto che non va incontro ad istruzioni di tipo RETURN, STOP o ad un errore. In particolare le operazioni possono agire su 3 tipi di spazi di memorizzazione dati:
lo stack, ossia il contenitore a pila dove avvengono le operazioni, in cui i dati (temporanei) possono essere immessi (PUSH) o prelevati (POP), secondo una politica LIFO (Last In First Out).

Tali dati sono oggetti di dimensione 256 bit ciascuno, mentre lo stack ha una dimensione massima di 1024 elementi. In particolare è possibile copiare uno dei 16 elementi in cima allo stack o fare uno swap del primo elemento con uno dei 16 sottostanti. Tutte le operazioni coinvolgono gli ultimi elementi inseriti (il numero di questi elementi dipende dall’operazione) nello stack, e salvare il risultato PUSHandolo nuovamente in cima (come rappresentato nella figura sotto). È inoltre possibile spostare elementi dallo stack verso la memoria o lo storage, sempre con politica LIFO
la memoria, un array di byte infinitamente espandibile e volatile, re-instanziata dopo ogni message call. Le scritture avvengono su granularità di parole di 8 o 256 bit, mentre le letture solo su parole di 256 bit.

L’espansione della memoria è pagata per mezzo di gas lo storage, per una memorizzazione a lungo termine dei contratti, sottoforma di key-value (256 bits to 256 bits). A differenza dello stack e della memory che si resettano alla fine di ogni computazione, lo storage è persistente ed è parte integrante dello stato globale del sistema (si ricorda che la piattaforma in sé può essere vista come una macchina a stati, dove lo stato di ogni singolo account determina lo stato globale del sistema)

È importante ricordare che il prezzo da pagare per il calcolo computazionale della EVM si traduce nel concetto di gas, il carburante del sistema Ethereum sottoforma di ether (wei). Ad ogni smart contract è infatti associato un gasPrice ed un limite di gas “consumabile” dal contratto, da cui i miners traggono ricompensa per il lavoro svolto. Questa informazione è contenuta nello stato computazionale del sistema definito in ogni stato dalla tupla (block_state, transaction, message, code, memory, stack, pc, gas), dove block_state rappresenta lo stato globale del sistema contenente tutti gli accounts, includendo quindi i balances e lo storage.

Prima di ogni computazione è compito del processore assicurarsi che siano disponibili e valide le seguenti informazioni : lo stato del sistema, il gas rimanente, l’indirizzo del dell’account che possiede il codice che sta per essere eseguito, l’indirizzo del mittente della transazione che ha originato l’esecuzione, l’indirizzo dell’account che ha causato l’esecuzione del codice (può essere differente dal mittente), il gas price della transazione che ha originato l’esecuzione, i dati in input per l’esecuzione, il valore (in wei) passati all’account come parte dell’esecuzione corrente, codice macchina da essere eseguito, il block header del blocco corrente, lunghezza della corrente message call o dello stack di creazione del contratto.
All’inizio dell’esecuzione la situazione è la seguente
PC: 0 STACK: [] MEM: [], STORAGE: {}

La EVM esegue poi le transazioni iterativamente e ricorsivamente, processando lo stato del sistema (il globale di Ethereum) lo stato della macchina a stati per ogni loop. Quest ultimo comprende il gas disponibile, il program counter, il contenuto della memoria, il numero di parole attive nella memoria ed il contenuto dello stack. Durante ogni ciclo viene decrementata la corretta quantità gas disponibile nell’account ed incrementato il program counter per la prossima istruzione. Alla fine di ogni loop ci sono 3 possibilità:

  1. viene lanciata un’eccezione (la macchina si troverebbe in uno stato incoerente, ad esempio gas non sufficiente, istruzione non valida) che porta al blocco dell’esecuzione, con conseguente scarto di tutti i cambiamenti, ad eccezione dei gas consumati
  2. i passa al prossimo loop
  3. la macchina raggiunge un blocco controllato dell’esecuzione, come previsto dal codice e l’esecuzione del processo termina
Sort:  

Articolo non proprio per tutti ma molto interessante. Devo riprendere a fare un po' di sperimentazione sulla blockchain

Grazie mille!