Passa al contenuto principale

Runtime dei Contract

Il runtime dei contract Savitri esegue gli smart contract in un ambiente isolato (sandbox) con misurazione del gas, isolamento dello storage ed emissione di eventi.

Flusso di Esecuzione

Arriva TX di chiamata al Contract


Validazione BaseContract
│ verifica paused, owner, slot riservati

Inizializzazione Gas Meter
│ gas_limit dalla commissione TX

Configurazione ContractStorage
│ carica stato contract, inizializza cache

Dispatch della funzione
│ associa function_selector → handler

Esecuzione (con tracciamento del gas)
│ SLOAD, SSTORE, eventi, chiamate

Risultato
├── Successo → commit dello storage, emissione eventi
└── Fallimento → annulla tutte le modifiche

BaseContract

Tutti i contract estendono BaseContract. Gli slot 0-99 sono riservati:

SlotCampoDimensioneDescrizione
0owner32 byteIndirizzo del proprietario del contract
1versionu64Versione del contract
2governance_hookboolIntegrazione governance abilitata
3fee_hookboolLogica commissioni personalizzata abilitata
4pausedboolStato di pausa del contract
5-99Riservato-Uso futuro

Funzioni di BaseContract

FunzioneAccessoDescrizione
owner()PubblicoOttieni il proprietario del contract
transfer_ownership(new_owner)Solo proprietarioTrasferisci la proprietà
version()PubblicoOttieni la versione del contract
upgrade(new_version)Solo proprietarioAggiorna il contract
pause()Solo proprietarioMetti in pausa tutte le operazioni
unpause()Solo proprietarioRiprendi le operazioni

Misurazione del Gas

Costi del Gas

OperazioneGasDescrizione
SLOAD100Lettura storage
SSTORE (nuovo)20.000Scrittura storage (slot vuoto)
SSTORE (aggiornamento)5.000Scrittura storage (slot esistente)
CALL2.300Chiamata cross-contract
CREATE32.000Deploy del contract
TRANSFER300Trasferimento token
LOG375Emissione evento (base)
LOG_TOPIC375Per ogni topic aggiuntivo
LOG_DATA8Per byte di dati evento
CALLDATA16Per byte di dati chiamata

Contabilità a Blocchi

Il misuratore di gas utilizza la contabilità a blocchi (100 operazioni per blocco) per ridurre l'overhead. Il gas viene contabilizzato per blocchi anziché per singola operazione.

Limite del Gas

Il limite del gas viene derivato dalla commissione della transazione:

gas_limit = tx.fee / gas_price

Se il gas si esaurisce durante l'esecuzione, l'intera transazione viene annullata.

Storage dei Contract

Modello Basato su Slot

Ogni contract dispone di un archivio chiave-valore indipendente dove le chiavi sono numeri di slot u64 e i valori sono array di 32 byte.

Derivazione degli Slot

Per le mapping, gli slot vengono derivati tramite Keccak256 per prevenire collisioni:

// Valore semplice
slot = FIXED_SLOT_NUMBER

// Mapping (address → value)
slot = keccak256(address || base_slot)[0..8] as u64

// Mapping annidato (address → address → value)
inner = keccak256(outer_key || base_slot)
slot = keccak256(inner_key || inner)[0..8] as u64

Albero di Merkle

Lo storage del contract mantiene un albero di Merkle per la generazione delle prove di stato.

Sistema di Eventi

I contract emettono eventi per i consumatori esterni:

pub struct CustomEvent {
pub event_type: String, // es., "Transfer", "Approval"
pub topics: Vec<Vec<u8>>, // campi indicizzati
pub data: Vec<u8>, // dati non indicizzati
}

Eventi standard (da BaseContract):

  • OwnershipTransferred(old_owner, new_owner)
  • ContractUpgraded(old_version, new_version)
  • Paused(by)
  • Unpaused(by)

Deploy dei Contract

1. Crea DeployTransaction (to = None, data = bytecode)
2. Assegna l'indirizzo del contract (derivato da deployer + nonce)
3. Inizializza BaseContract (imposta owner, version)
4. Esegui il costruttore del contract (funzione initialize)
5. Memorizza ContractInfo in CF_CONTRACTS
6. Emetti evento ContractDeployed

Chiamate ai Contract

1. Analizza function_selector dai dati TX
2. Carica il contract da CF_CONTRACTS
3. Verifica lo stato di BaseContract (non in pausa, ecc.)
4. Inizializza GasMeter con gas_limit
5. Esegui la funzione con ContractStorage
6. In caso di successo: commit delle modifiche storage, detrai gas
7. In caso di fallimento: annulla tutte le modifiche

Interprete EVM

Il modulo evm_interpreter fornisce l'esecuzione di base del bytecode EVM per la compatibilità con i contract Ethereum. Ciò consente di distribuire contract compilati in Solidity su Savitri.

Monitoraggio della Memoria

Il memory_monitor traccia l'utilizzo della memoria a runtime per prevenire attacchi DoS:

  • Limiti di memoria per contract
  • Tracciamento delle allocazioni
  • Terminazione automatica al superamento dei limiti

Esecuzione Parallela

Il modulo parallel abilita l'esecuzione parallela dei contract per le transazioni indipendenti:

  • Analisi delle dipendenze tra le transazioni
  • Rilevamento dei conflitti sugli slot di storage
  • Esecuzione parallela delle TX non in conflitto
  • Fallback seriale per le TX in conflitto

Tracciamento dei Contract

Il modulo tracing fornisce tracce di esecuzione per il debugging:

  • Log di esecuzione passo per passo
  • Tracciamento di letture/scritture dello storage
  • Consumo di gas per operazione
  • Log di emissione degli eventi