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:
| Slot | Campo | Dimensione | Descrizione |
|---|---|---|---|
| 0 | owner | 32 byte | Indirizzo del proprietario del contract |
| 1 | version | u64 | Versione del contract |
| 2 | governance_hook | bool | Integrazione governance abilitata |
| 3 | fee_hook | bool | Logica commissioni personalizzata abilitata |
| 4 | paused | bool | Stato di pausa del contract |
| 5-99 | Riservato | - | Uso futuro |
Funzioni di BaseContract
| Funzione | Accesso | Descrizione |
|---|---|---|
owner() | Pubblico | Ottieni il proprietario del contract |
transfer_ownership(new_owner) | Solo proprietario | Trasferisci la proprietà |
version() | Pubblico | Ottieni la versione del contract |
upgrade(new_version) | Solo proprietario | Aggiorna il contract |
pause() | Solo proprietario | Metti in pausa tutte le operazioni |
unpause() | Solo proprietario | Riprendi le operazioni |
Misurazione del Gas
Costi del Gas
| Operazione | Gas | Descrizione |
|---|---|---|
SLOAD | 100 | Lettura storage |
SSTORE (nuovo) | 20.000 | Scrittura storage (slot vuoto) |
SSTORE (aggiornamento) | 5.000 | Scrittura storage (slot esistente) |
CALL | 2.300 | Chiamata cross-contract |
CREATE | 32.000 | Deploy del contract |
TRANSFER | 300 | Trasferimento token |
LOG | 375 | Emissione evento (base) |
LOG_TOPIC | 375 | Per ogni topic aggiuntivo |
LOG_DATA | 8 | Per byte di dati evento |
CALLDATA | 16 | Per 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