Dati on-chain e SQL · Lezione 1 di 9
Blocchi, intestazioni e API dei nodi
13 min di lettura
I blocchi sono il mezzo con cui una blockchain registra il tempo e l'ordine, quindi ogni cronologia, totale giornaliero e affermazione di tipo "prima o dopo" in un caso dipende dalla loro corretta interpretazione. La parte più recente di qualsiasi catena è provvisoria: i blocchi vicini alla punta possono essere sostituiti. Dietro ogni explorer, dataset e piattaforma di analisi vi è un nodo che risponde a chiamate API. Un investigatore che conosce il contenuto di un'intestazione, il grado di affidabilità del suo timestamp, il momento in cui è sicuro fare affidamento su un blocco e la chiamata al nodo che ha prodotto un dato può escludere i dati provvisori dalle risultanze e verificare ciò che mostra uno strumento di terze parti.
Cosa contiene un'intestazione
Un'intestazione di blocco è un piccolo record che riassume il blocco e lo collega al suo predecessore. Un'intestazione Bitcoin è composta esattamente da 80 bytes con sei campi:
| Campo | Dimensione | Significato per l'analista |
|---|---|---|
| versione | 4 bytes | Insieme di regole e bit di segnalazione dei soft fork |
| hash del blocco precedente | 32 bytes | Collegamento al predecessore; la catena è costruita a partire da questi collegamenti |
| radice di Merkle | 32 bytes | Impegno crittografico relativo a ogni transazione nel blocco |
| tempo | 4 bytes | Timestamp Unix scelto dal miner |
| bits | 4 bytes | Codifica compatta del target di difficoltà |
| nonce | 4 bytes | Valore che i miner variano per trovare un hash valido |
L'hash del blocco non è un campo dell'intestazione. È il doppio SHA-256 degli 80 bytes dell'intestazione e gli explorer lo visualizzano con l'ordine dei byte invertito: ecco perché inizia con degli zeri. Il metodo getblock di Bitcoin Core aggiunge valori derivati quali height, mediantime, chainwork e confirmations.
Un'intestazione Ethereum include anche impegni crittografici relativi allo stato e alle ricevute. I campi più utilizzati dagli analisti sono:
| Campo | Significato |
|---|---|
number, parentHash | Altezza e collegamento al predecessore |
timestamp | Tempo Unix in secondi |
miner | Destinatario delle commissioni scelto dal proponente del blocco (il nome è precedente alla proof of stake) |
stateRoot, transactionsRoot, receiptsRoot | Impegni crittografici relativi a stato, transazioni e ricevute |
logsBloom | Filtro di Bloom che consente ai client di saltare i blocchi privi di log corrispondenti |
gasLimit, gasUsed, baseFeePerGas | Capacità, utilizzo e commissione base bruciata per unità di gas (London, agosto 2021) |
withdrawalsRoot | Prelievi dei validatori (Shanghai, aprile 2023) |
blobGasUsed, excessBlobGas, parentBeaconBlockRoot | Contabilizzazione dei blob e collegamento al consenso (Cancun, marzo 2024) |
requestsHash | Richieste dal livello di esecuzione al livello di consenso, EIP-7685 (Prague, maggio 2025) |
I campi compaiono solo a partire dall'aggiornamento che li ha introdotti, quindi una pipeline deve accettarne l'assenza nei blocchi meno recenti. Dal Merge, difficulty e nonce sono pari a zero e mixHash contiene il valore di casualità della beacon chain. I dati precedenti al Merge contengono anche blocchi ommer (uncle): blocchi validi che hanno perso una competizione, ma che sono stati referenziati da un blocco successivo tramite sha3Uncles e l'elenco uncles. I rispettivi miner ricevevano una ricompensa ridotta senza alcuna transazione, quindi l'analisi delle ricompense per l'epoca del mining deve includerli.
Altezza e hash a confronto
L'altezza (number in Ethereum) è una posizione: il conteggio dei blocchi a partire dalla genesi. L'hash del blocco è un'identità. Due blocchi possono condividere la stessa altezza quando la catena si biforca, e solo uno finisce nella catena principale; un hash identifica esattamente un blocco per sempre. Utilizzare l'altezza per gli intervalli ("blocchi da 21.000.000 a 21.000.999") e l'hash come chiave di archiviazione. In una relazione, citare entrambi: "altezza 880.214, hash 0000…3f1a". Il metodo getblockhash di Bitcoin restituisce l'hash attualmente presente a una determinata altezza, che può cambiare vicino alla punta.
Timestamp e loro affidabilità
Il timestamp di un blocco è il tempo che un analista associa a ogni transazione al suo interno.
I timestamp di Bitcoin sono impostati dal miner nel rispetto di due regole: il tempo deve essere superiore alla mediana degli 11 blocchi precedenti (il tempo mediano passato) e i nodi rifiutano i blocchi che anticipano di oltre due ore il proprio orologio (le versioni precedenti di Bitcoin Core utilizzavano un tempo "corretto in base alla rete" ricavato dai peer). Un blocco può quindi avere un timestamp precedente a quello del suo predecessore e qualsiasi singolo timestamp può presentare uno scarto di un'ora o più. Utilizzare l'altezza per l'ordinamento; mediantime non diminuisce mai e funge da orologio monotono. Riportare i tempi di Bitcoin solo con una precisione supportata dai dati.
Dal Merge (settembre 2022), Ethereum ha slot fissi di 12 secondi: il timestamp di un blocco è uguale al tempo della genesi più 12 secondi moltiplicati per il numero del suo slot. Un intervallo di 24 o 36 secondi tra blocchi consecutivi indica che uno o due slot non hanno avuto alcun blocco. Prima del Merge, i miner impostavano i timestamp secondo regole meno rigide; sui rollup, il sequencer li imposta secondo le regole specifiche di ciascun rollup.
Due cautele si applicano ovunque. Il tempo del blocco indica quando una transazione è stata inclusa, non quando è stata firmata o trasmessa. Inoltre, i tempi di "prima osservazione" mostrati dagli explorer provengono dai rispettivi osservatori della mempool: sono indizi utili, ma locali a un singolo osservatore.
Riorganizzazioni e finalità
Una riorganizzazione (reorg) si verifica quando i nodi passano a un ramo concorrente. I blocchi sostituiti sono obsoleti o, nell'uso comune, orfani. In Bitcoin, i nodi seguono il ramo con il maggior lavoro cumulativo (chainwork); le transazioni di un blocco obsoleto ricompaiono solitamente nel ramo vincente, eventualmente in un blocco e in una posizione diversi, oppure non ricompaiono affatto se al loro posto è stata confermata una transazione in conflitto. getchaintips elenca i rami noti e getblock riporta un valore di confirmations pari a −1 per un blocco esterno alla catena principale. In Ethereum, la proof of stake rende le riorganizzazioni più rare e solitamente limitate a un solo blocco; i log forniti tramite sottoscrizioni o filtri riportano removed: true quando una riorganizzazione li elimina.
La finalità è il punto in cui un blocco può essere considerato permanente. La finalità di Bitcoin è probabilistica: ogni conferma rende la sostituzione esponenzialmente più costosa, sei conferme sono una convenzione comune per i trasferimenti di valore e gli output coinbase non possono essere spesi per 100 blocchi. Ethereum ha una finalità economica esplicita: gli slot formano epoche di 32 slot (6,4 minuti) e i checkpoint diventano giustificati e poi finalizzati, normalmente dopo circa due epoche (circa 13 minuti). L'annullamento di un blocco finalizzato richiederebbe la distruzione di almeno un terzo di tutti gli ETH in staking.
JSON-RPC di Ethereum espone questo meccanismo tramite tag di blocco accettati al posto di un numero: earliest, latest, safe, finalized e pending. Ciò che restituisce safe dipende dal client di consenso (spesso l'ultimo checkpoint giustificato, anche se alcuni client lo fanno avanzare entro pochi secondi dalla testa), quindi occorre verificare il proprio nodo anziché fare supposizioni.
| Utilizzo | Bitcoin | Ethereum |
|---|---|---|
| Avvisi in tempo reale | 0–1 conferme, con gestione delle riorganizzazioni | latest, con gestione delle riorganizzazioni |
| Decisioni operative | Profondità prevista dalla politica adottata, spesso 3–6 | safe o finalized |
| Relazioni e statistiche | Ben oltre 6, oppure un limite fisso nel passato | finalized |
Gestione dei dati orfani
Una pipeline robusta memorizza l'hash e l'hash del predecessore di ogni blocco, verifica per ogni nuovo blocco che l'hash del suo predecessore corrisponda all'hash memorizzato all'altezza immediatamente inferiore e, in caso contrario, risale all'antenato comune, acquisisce il nuovo ramo e sposta le righe sostituite in una tabella di audit invece di eliminarle. Le query seguenti utilizzano lo schema generico della Lezione 2.7, in cui blocks contiene la catena canonica e transactions contiene il relativo block_hash:
-- Conteggi giornalieri delle transazioni, mai oltre il blocco finalizzato
SELECT DATE_TRUNC('day', b.block_timestamp) AS day_utc,
COUNT(*) AS tx_count
FROM transactions t
JOIN blocks b ON b.hash = t.block_hash -- hash, non altezza: esclude le righe dei blocchi obsoleti
WHERE b.number <= :finalized_block -- da eth_getBlockByNumber("finalized", false)
GROUP BY DATE_TRUNC('day', b.block_timestamp)
ORDER BY day_utc;
-- Blocchi il cui predecessore manca: lacune o riorganizzazioni non gestite
SELECT c.number, c.hash, c.parent_hash
FROM blocks c
LEFT JOIN blocks p ON p.hash = c.parent_hash
WHERE p.hash IS NULL
AND c.number > (SELECT MIN(number) FROM blocks);
La tabella di audit è importante nelle attività investigative. Si supponga che un exchange abbia accreditato un deposito dopo una conferma, che il blocco sia diventato obsoleto e che al suo posto sia stata confermata una transazione in conflitto che spendeva le stesse monete. La pagina memorizzata nella cache di un explorer potrebbe ancora mostrare la prima transazione; solo i dati memorizzati del blocco obsoleto spiegano perché un deposito "confermato" non sia mai esistito sulla catena finale.
Lettura della catena tramite le API dei nodi
Bitcoin Core espone JSON-RPC (una richiesta HTTP POST con un method e un array params, autenticata tramite file cookie o utente configurato), disponibile anche tramite bitcoin-cli. Gli importi vengono restituiti in BTC come numeri JSON. I client di esecuzione Ethereum (Geth, Nethermind, Besu, Erigon, Reth) implementano una specifica JSON-RPC comune e la maggior parte delle catene EVM, dei rollup e delle sidechain offre la stessa interfaccia. Le quantità sono stringhe esadecimali ("0x1b4"); spazi dei nomi quali debug_ e trace_ sono specifici del client.
Metodi di Bitcoin Core
| Metodo | Valore restituito | Note |
|---|---|---|
getblockchaininfo | Catena, altezza, hash migliore, stato di potatura | Prima chiamata di ogni sessione |
getblockhash height / getblockheader hash | Hash a una determinata altezza / intestazione e confirmations | L'hash può cambiare vicino alla punta |
getblock hash verbosity | 0: esadecimale; 1: intestazione e txid; 2: transazioni decodificate con commissioni; 3: aggiunge prevout per ogni input | Commissioni e prevout richiedono dati di annullamento |
getrawtransaction txid verbosity | 0: esadecimale; 1: decodificata; 2: aggiunge commissione e prevout | Le transazioni confermate richiedono txindex=1 o un hash di blocco |
gettxout txid n | Dettagli dell'output se attualmente non speso | Null se speso o sconosciuto |
gettxspendingprevout | Transazione che spende gli output specificati | Solo mempool, a meno che non sia abilitato txospenderindex (31.0 e successive) |
getchaintips | Rami noti e relativo stato | Indagine sulle riorganizzazioni |
Una sequenza tipica per esaminare un pagamento:
getblockhash 880214 -> "0000…3f1a"
getblock "0000…3f1a" 1 -> campi dell'intestazione ed elenco dei txid
getrawtransaction "4f3a…c21e" 2 -> transazione decodificata con valori prevout e commissione
gettxout "4f3a…c21e" 1 -> output 1 se ancora non speso, altrimenti null
gettxout indica se un output è attualmente non speso, non quando o da quale transazione sia stato speso. Bitcoin Core non dispone di un metodo che elenchi la cronologia di un indirizzo; a tale scopo occorre un indicizzatore di indirizzi o un data warehouse.
Metodi di Ethereum
| Metodo | Valore restituito |
|---|---|
eth_chainId | ID della catena (EIP-695): conferma su quale rete ci si trova |
eth_blockNumber, eth_getBlockByNumber, eth_getBlockByHash | Altezza più recente; intestazione con hash delle transazioni, oppure transazioni complete quando il secondo parametro è true |
eth_getTransactionByHash, eth_getTransactionReceipt | Transazione firmata con from ricavato; stato, gas e log |
eth_getBlockReceipts | Tutte le ricevute di un blocco in una sola chiamata |
eth_getLogs | Log corrispondenti a un filtro |
eth_getBalance, eth_getCode, eth_getStorageAt, eth_call | Stato a un determinato blocco |
Due comportamenti possono cogliere impreparati i principianti. Una ricevuta null significa che la transazione è in attesa o sconosciuta al nodo, non che sia fallita. Inoltre, i metodi di stato rispondono con riferimento al blocco passato: eth_getBalance con latest cambia ogni pochi secondi, quindi occorre passare un numero di blocco esplicito per qualsiasi dato riportato in una relazione.
eth_getLogs accetta fromBlock e toBlock (oppure un solo blockHash), un address facoltativo e un array posizionale topics. Questa richiesta interroga un contratto di token per ottenere gli eventi Transfer destinati a un indirizzo nell'intervallo di blocchi da 21.000.000 (0x1406f40) a 21.000.999 (0x1407327):
{"jsonrpc": "2.0", "id": 1, "method": "eth_getLogs",
"params": [{
"fromBlock": "0x1406f40", "toBlock": "0x1407327",
"address": "0x5d3a…7c10",
"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
null,
"0x000000000000000000000000c3d4…22e1"]
}]}
I topic vengono confrontati in base alla posizione: il primo è la firma di Transfer, null accetta qualsiasi mittente e topics[2] fissa il destinatario. Una posizione può anche contenere un array con il significato di "uno qualsiasi di questi"; spostando l'indirizzo con padding nella seconda posizione si ottengono i trasferimenti inviati da tale indirizzo.
Metodi di debug e tracciamento
Le chiamate interne richiedono una nuova esecuzione. I client di tipo Geth offrono debug_traceTransaction, debug_traceBlockByNumber e debug_traceCall con un tracer quale callTracer (albero delle chiamate) o prestateTracer (stato interessato). I client di tipo Parity (Erigon, Nethermind, Reth) offrono trace_transaction, trace_block, trace_filter e trace_replayTransaction, che restituiscono elenchi piatti di chiamate. Entrambe le tipologie sono onerose, spesso disabilitate o soggette a costi aggiuntivi da parte dei fornitori, e differiscono nella struttura (ad esempio, nell'inclusione o meno delle chiamate ai precompilati), quindi occorre normalizzarle prima di combinare i risultati.
Quali domande richiedono quale nodo
| Domanda | Bitcoin | Ethereum |
|---|---|---|
| Blocchi, transazioni, ricevute, log | Nodo completo (txindex=1 per la ricerca tramite txid) | Nodo completo; la cronologia precedente al Merge potrebbe essere scaduta |
| Saldo attuale o stato degli UTXO | gettxout, oppure un indicizzatore per gli indirizzi | Nodo completo, eth_getBalance |
| Saldo a un blocco passato | Indicizzatore o data warehouse | Nodo archivio |
| Cronologia degli indirizzi | Indicizzatore di indirizzi o data warehouse | Indicizzatore o data warehouse (nessun metodo nativo) |
| Chiamate interne di transazioni meno recenti | Non applicabile | Nodo archivio con tracciamento |
I nodi potati non possono fornire in alcun modo i blocchi meno recenti. Un nodo completo Ethereum interrogato su uno stato precedente alla propria finestra di conservazione restituisce un errore anziché un numero errato: un segnale della necessità di un nodo archivio.
Fornitori, limiti di frequenza e fiducia
I fornitori di servizi in hosting impongono limiti di frequenza (richieste al secondo o "unità di calcolo" ponderate) e limitano eth_getLogs per intervallo di blocchi, numero di risultati o entrambi. Un'estrazione robusta suddivide un intervallo in segmenti, dimezza un segmento rifiutato dal fornitore, riprova con attese crescenti esponenzialmente e registra gli intervalli completati. Le richieste batch JSON-RPC riducono il sovraccarico, ma sono conteggiate ai fini degli stessi limiti. I fornitori sollevano anche questioni di fiducia:
- Teste incoerenti. Dietro un bilanciatore di carico, chiamate consecutive possono raggiungere nodi ad altezze diverse. Vincolare il lavoro a un hash di blocco, non a
latest. - Errori e ritardi. Per i fatti da cui dipende una risultanza, confrontare l'hash del blocco all'altezza di riferimento tra due fonti indipendenti o con il proprio nodo e registrare la versione del client (
web3_clientVersion, oppure il camposubversiondigetnetworkinfo). - Riservatezza delle query. Un fornitore vede ogni indirizzo su cui vengono richieste informazioni; i suoi log possono rivelare il soggetto di un'indagine. Il proprio nodo non presenta questo rischio.
Per la cronologia in grandi volumi, l'estrazione RPC è lenta. I dataset pubblici (i dataset sulle criptovalute di Google BigQuery, realizzati con gli strumenti open source blockchain-etl; il dataset AWS Public Blockchain Data in Parquet) e i framework ETL (Ethereum ETL, Bitcoin ETL, cryo, TrueBlocks, The Graph) sono le alternative; la Lezione 2.7 li mette a confronto.
Focus sull'esame: sono previste domande che richiedono di scegliere la fonte di dati o il limite corretto per una risultanza: i sei campi dell'intestazione Bitcoin e il fatto che l'hash del blocco sia calcolato; l'altezza come posizione rispetto all'hash come identità; il tempo mediano passato, la regola delle due ore e i timestamp Bitcoin fuori ordine rispetto agli slot Ethereum esatti di 12 secondi; le riorganizzazioni, i logremoved: truee i dati di audit dei blocchi obsoleti; le conferme, le epoche e i taglatest,safeefinalized; la verbosità digetblockegetrawtransactionetxindex; i limiti digettxout; la corrispondenza posizionale dei topic ineth_getLogs; i metodidebug_rispetto ai metoditrace_; quando è necessario un nodo archivio; la verifica dei fornitori.