Vai al contenuto principale

Introduzione a Ethereum per sviluppatori Python, parte 1

Python
web3.py
Principiante
Marc Garreau
8 settembre 2020
12 minuti di lettura

Quindi, hai sentito parlare di questo Ethereum e sei pronto ad avventurarti nella tana del bianconiglio? Questo articolo coprirà rapidamente alcune basi della blockchain, per poi farti interagire con un nodo Ethereum simulato: leggendo i dati dei blocchi, controllando i saldi degli account e inviando transazioni. Lungo il percorso, evidenzieremo le differenze tra i modi tradizionali di creare app e questo nuovo paradigma decentralizzato.

Prerequisiti (leggeri)

Questo articolo aspira a essere accessibile a un'ampia gamma di sviluppatori. Saranno coinvolti strumenti Python, ma sono solo un veicolo per le idee: nessun problema se non sei uno sviluppatore Python. Tuttavia, farò solo alcune supposizioni su ciò che già sai, in modo da poter passare rapidamente alle parti specifiche di Ethereum.

Presupposti:

  • Sai muoverti in un terminale,
  • Hai scritto qualche riga di codice Python,
  • La versione 3.6 o superiore di Python è installata sulla tua macchina (l'uso di un ambiente virtuale (opens in a new tab) è fortemente incoraggiato), e
  • hai usato pip, l'installatore di pacchetti di Python. Ancora una volta, se una di queste condizioni non è vera, o non hai intenzione di riprodurre il codice in questo articolo, probabilmente potrai comunque seguire senza problemi.

Blockchain, in breve

Ci sono molti modi per descrivere Ethereum, ma al suo centro c'è una blockchain. Le blockchain sono composte da una serie di blocchi, quindi iniziamo da lì. In termini più semplici, ogni blocco sulla blockchain di Ethereum è solo un insieme di metadati e un elenco di transazioni. In formato JSON, si presenta più o meno così:

1{
2 "number": 1234567,
3 "hash": "0xabc123...",
4 "parentHash": "0xdef456...",
5 ...,
6 "transactions": [...]
7}

Ogni blocco ha un riferimento al blocco che lo ha preceduto; il parentHash è semplicemente l'hash del blocco precedente.

Nota: Ethereum fa un uso regolare di funzioni di hash per produrre valori di dimensione fissa ("hash"). Gli hash svolgono un ruolo importante in Ethereum, ma per ora puoi tranquillamente pensarli come ID univoci.

Un diagramma che raffigura una blockchain includendo i dati all'interno di ogni blocco

Una blockchain è essenzialmente una lista concatenata; ogni blocco ha un riferimento al blocco precedente.

Questa struttura dati non è una novità, ma le regole (ovvero i protocolli peer-to-peer) che governano la rete lo sono. Non c'è un'autorità centrale; la rete di peer deve collaborare per sostenere la rete e competere per decidere quali transazioni includere nel blocco successivo. Quindi, quando vuoi inviare del denaro a un amico, dovrai trasmettere quella transazione alla rete, per poi aspettare che venga inclusa in un blocco imminente.

L'unico modo per la blockchain di verificare che il denaro sia stato veramente inviato da un utente all'altro è utilizzare una valuta nativa (cioè creata e governata da) quella blockchain. In Ethereum, questa valuta si chiama ether e la blockchain di Ethereum contiene l'unico registro ufficiale dei saldi degli account.

Un nuovo paradigma

Questo nuovo stack tecnologico decentralizzato ha generato nuovi strumenti per sviluppatori. Tali strumenti esistono in molti linguaggi di programmazione, ma noi li guarderemo attraverso la lente di Python. Per ribadire: anche se Python non è il tuo linguaggio preferito, non dovrebbe essere un grosso problema seguire.

Gli sviluppatori Python che vogliono interagire con Ethereum probabilmente ricorreranno a Web3.py (opens in a new tab). Web3.py è una libreria che semplifica notevolmente il modo in cui ti connetti a un nodo Ethereum, per poi inviare e ricevere dati da esso.

Nota: "nodo Ethereum" e "client Ethereum" sono usati in modo intercambiabile. In entrambi i casi, si riferiscono al software eseguito da un partecipante alla rete Ethereum. Questo software può leggere i dati dei blocchi, ricevere aggiornamenti quando nuovi blocchi vengono aggiunti alla catena, trasmettere nuove transazioni e altro ancora. Tecnicamente, il client è il software, il nodo è il computer che esegue il software.

I client Ethereum possono essere configurati per essere raggiungibili tramite IPC (opens in a new tab), HTTP o Websocket, quindi Web3.py dovrà rispecchiare questa configurazione. Web3.py si riferisce a queste opzioni di connessione come provider. Dovrai scegliere uno dei tre provider per collegare l'istanza di Web3.py al tuo nodo.

Un diagramma che mostra come web3.py utilizza IPC per connettere la tua applicazione a un nodo Ethereum

Configura il nodo Ethereum e Web3.py per comunicare tramite lo stesso protocollo, ad es. IPC in questo diagramma.

Una volta che Web3.py è configurato correttamente, puoi iniziare a interagire con la blockchain. Ecco un paio di esempi di utilizzo di Web3.py come anteprima di ciò che verrà:

1# leggi i dati del blocco:
2w3.eth.get_block('latest')
3
4# invia una transazione:
5w3.eth.send_transaction({'from': ..., 'to': ..., 'value': ...})

Installazione

In questa guida, lavoreremo solo all'interno di un interprete Python. Non creeremo directory, file, classi o funzioni.

Nota: Negli esempi seguenti, i comandi che iniziano con $ sono destinati a essere eseguiti nel terminale. (Non digitare il $, indica solo l'inizio della riga.)

Per prima cosa, installa IPython (opens in a new tab) per avere un ambiente user-friendly in cui esplorare. IPython offre il completamento con il tasto Tab, tra le altre funzionalità, rendendo molto più facile vedere cosa è possibile fare all'interno di Web3.py.

pip install ipython

Web3.py è pubblicato con il nome web3. Installalo in questo modo:

pip install web3

Un'ultima cosa: più tardi simuleremo una blockchain, il che richiede un paio di dipendenze in più. Puoi installarle tramite:

pip install 'web3[tester]'

Sei pronto per iniziare!

Nota: Il pacchetto web3[tester] funziona fino a Python 3.10.xx

Avviare una sandbox

Apri un nuovo ambiente Python eseguendo ipython nel tuo terminale. Questo è paragonabile all'esecuzione di python, ma è dotato di più funzionalità.

ipython

Questo stamperà alcune informazioni sulle versioni di Python e IPython che stai eseguendo, dopodiché dovresti vedere un prompt in attesa di input:

1In [1]:

Ora stai guardando una shell Python interattiva. Essenzialmente, è una sandbox in cui giocare. Se sei arrivato fin qui, è il momento di importare Web3.py:

1In [1]: from web3 import Web3

Introduzione al modulo Web3

Oltre a essere un gateway per Ethereum, il modulo Web3 (opens in a new tab) offre alcune funzioni di utilità. Esploriamone un paio.

In un'applicazione Ethereum, avrai comunemente bisogno di convertire le denominazioni di valuta. Il modulo Web3 fornisce un paio di metodi di supporto proprio per questo: from_wei (opens in a new tab) e to_wei (opens in a new tab).

Nota: I computer sono notoriamente pessimi nel gestire la matematica decimale. Per aggirare questo problema, gli sviluppatori spesso memorizzano gli importi in dollari in centesimi. Ad esempio, un articolo con un prezzo di $5.99 potrebbe essere memorizzato nel database come 599.

Un modello simile viene utilizzato quando si gestiscono transazioni in ether. Tuttavia, invece di due cifre decimali, l'ether ne ha 18! La denominazione più piccola di ether si chiama wei, quindi questo è il valore specificato quando si inviano transazioni.

1 ether = 1000000000000000000 wei

1 wei = 0.000000000000000001 ether

Prova a convertire alcuni valori da e verso wei. Nota che ci sono nomi per molte delle denominazioni (opens in a new tab) tra ether e wei. Una delle più note tra queste è il gwei, poiché è spesso il modo in cui vengono rappresentate le commissioni della transazione.

1In [2]: Web3.to_wei(1, 'ether')
2Out[2]: 1000000000000000000
3
4In [3]: Web3.from_wei(500000000, 'gwei')
5Out[3]: Decimal('0.5')

Altri metodi di utilità sul modulo Web3 includono convertitori di formato dati (ad es., toHex (opens in a new tab)), helper per gli indirizzi (ad es., isAddress (opens in a new tab)) e funzioni di hash (ad es., keccak (opens in a new tab)). Molti di questi verranno trattati più avanti nella serie. Per visualizzare tutti i metodi e le proprietà disponibili, utilizza l'autocompletamento di IPython digitando Web3. e premendo due volte il tasto Tab dopo il punto.

Parlare con la catena

I metodi di utilità sono adorabili, ma passiamo alla blockchain. Il passo successivo è configurare Web3.py per comunicare con un nodo Ethereum. Qui abbiamo l'opzione di utilizzare i provider IPC, HTTP o Websocket.

Non seguiremo questa strada, ma un esempio di flusso di lavoro completo utilizzando l'HTTP Provider potrebbe assomigliare a questo:

  • Scarica un nodo Ethereum, ad es. Geth (opens in a new tab).
  • Avvia Geth in una finestra del terminale e attendi che sincronizzi la rete. La porta HTTP predefinita è 8545, ma è configurabile.
  • Dì a Web3.py di connettersi al nodo tramite HTTP, su localhost:8545. w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))
  • Usa l'istanza w3 per interagire con il nodo.

Sebbene questo sia un modo "reale" per farlo, il processo di sincronizzazione richiede ore ed è inutile se desideri solo un ambiente di sviluppo. Web3.py espone un quarto provider per questo scopo, l'EthereumTesterProvider. Questo provider di test si collega a un nodo Ethereum simulato con permessi rilassati e valuta finta con cui giocare.

Un diagramma che mostra l'EthereumTesterProvider che collega la tua applicazione web3.py a un nodo Ethereum simulato

L'EthereumTesterProvider si connette a un nodo simulato ed è comodo per ambienti di sviluppo rapidi.

Quel nodo simulato si chiama eth-tester (opens in a new tab) e lo abbiamo installato come parte del comando pip install web3[tester]. Configurare Web3.py per utilizzare questo provider di test è semplice come:

1In [4]: w3 = Web3(Web3.EthereumTesterProvider())

Ora sei pronto per navigare sulla catena! Non è una cosa che si dice. Me la sono appena inventata. Facciamo un rapido tour.

Il tour rapido

Prima di tutto, un controllo di integrità:

1In [5]: w3.is_connected()
2Out[5]: True

Poiché stiamo utilizzando il provider di test, questo non è un test molto prezioso, ma se fallisce, è probabile che tu abbia digitato qualcosa di sbagliato durante l'istanziazione della variabile w3. Ricontrolla di aver incluso le parentesi interne, ovvero Web3.EthereumTesterProvider().

Tappa del tour n. 1: account

Per comodità, il provider di test ha creato alcuni account e li ha precaricati con ether di test.

Per prima cosa, vediamo un elenco di quegli account:

1In [6]: w3.eth.accounts
2Out[6]: ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
3 '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF',
4 '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69', ...]

Se esegui questo comando, dovresti vedere un elenco di dieci stringhe che iniziano con 0x. Ognuna è un indirizzo pubblico ed è, in qualche modo, analoga al numero di conto di un conto corrente. Forniresti questo indirizzo a qualcuno che volesse inviarti ether.

Come accennato, il provider di test ha precaricato ciascuno di questi account con alcuni ether di test. Scopriamo quanto c'è nel primo account:

1In [7]: w3.eth.get_balance(w3.eth.accounts[0])
2Out[7]: 1000000000000000000000000

Sono un sacco di zeri! Prima di andare a ridere fino alla finta banca, ricorda quella lezione sulle denominazioni di valuta di prima. I valori in ether sono rappresentati nella denominazione più piccola, i wei. Convertilo in ether:

1In [8]: w3.from_wei(1000000000000000000000000, 'ether')
2Out[8]: Decimal('1000000')

Un milione di ether di test: non è affatto male.

Tappa del tour n. 2: dati del blocco

Diamo un'occhiata allo stato di questa blockchain simulata:

1In [9]: w3.eth.get_block('latest')
2Out[9]: AttributeDict({
3 'number': 0,
4 'hash': HexBytes('0x9469878...'),
5 'parentHash': HexBytes('0x0000000...'),
6 ...
7 'transactions': []
8})

Vengono restituite molte informazioni su un blocco, ma ci sono solo un paio di cose da sottolineare qui:

  • Il numero del blocco è zero, indipendentemente da quanto tempo fa hai configurato il provider di test. A differenza della vera rete Ethereum, che aggiunge un nuovo blocco ogni 12 secondi, questa simulazione aspetterà finché non le darai del lavoro da fare.
  • transactions è un elenco vuoto, per lo stesso motivo: non abbiamo ancora fatto nulla. Questo primo blocco è un blocco vuoto, solo per dare il via alla catena.
  • Nota che il parentHash è solo un mucchio di byte vuoti. Questo significa che è il primo blocco della catena, noto anche come blocco genesi.

Tappa del tour n. 3: transazioni

Siamo bloccati al blocco zero finché non c'è una transazione in sospeso, quindi diamogliene una. Invia alcuni ether di test da un account all'altro:

1In [10]: tx_hash = w3.eth.send_transaction({
2 'from': w3.eth.accounts[0],
3 'to': w3.eth.accounts[1],
4 'value': w3.to_wei(3, 'ether'),
5 'gas': 21000
6})

Questo è in genere il punto in cui aspetteresti diversi secondi affinché la tua transazione venga inclusa in un nuovo blocco. L'intero processo si svolge più o meno così:

  1. Invia una transazione e conserva l'hash della transazione. Finché il blocco contenente la transazione non viene creato e trasmesso, la transazione è "in sospeso". tx_hash = w3.eth.send_transaction({ … })
  2. Attendi che la transazione venga inclusa in un blocco: w3.eth.wait_for_transaction_receipt(tx_hash)
  3. Continua la logica dell'applicazione. Per visualizzare la transazione andata a buon fine: w3.eth.get_transaction(tx_hash)

Il nostro ambiente simulato aggiungerà la transazione in un nuovo blocco all'istante, in modo da poter visualizzare immediatamente la transazione:

1In [11]: w3.eth.get_transaction(tx_hash)
2Out[11]: AttributeDict({
3 'hash': HexBytes('0x15e9fb95dc39...'),
4 'blockNumber': 1,
5 'transactionIndex': 0,
6 'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
7 'to': '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF',
8 'value': 3000000000000000000,
9 ...
10})
Mostra tutto

Vedrai alcuni dettagli familiari qui: i campi from, to e value dovrebbero corrispondere agli input della nostra chiamata send_transaction. L'altro dettaglio rassicurante è che questa transazione è stata inclusa come prima transazione ('transactionIndex': 0) all'interno del blocco numero 1.

Possiamo anche verificare facilmente il successo di questa transazione controllando i saldi dei due account coinvolti. Tre ether dovrebbero essersi spostati da uno all'altro.

1In [12]: w3.eth.get_balance(w3.eth.accounts[0])
2Out[12]: 999996999979000000000000
3
4In [13]: w3.eth.get_balance(w3.eth.accounts[1])
5Out[13]: 1000003000000000000000000

Il secondo sembra a posto! Il saldo è passato da 1.000.000 a 1.000.003 ether. Ma cosa è successo al primo account? Sembra aver perso poco più di tre ether. Ahimè, niente nella vita è gratis e l'utilizzo della rete pubblica di Ethereum richiede di compensare i propri peer per il loro ruolo di supporto. Una piccola commissione della transazione è stata detratta dall'account che ha inviato la transazione: questa commissione è la quantità di gas bruciato (21000 unità di gas per un trasferimento di ETH) moltiplicata per una commissione di base che varia in base all'attività della rete più una mancia che va al validatore che include la transazione in un blocco.

Maggiori informazioni sul gas

Nota: Sulla rete pubblica, le commissioni della transazione sono variabili in base alla domanda della rete e alla rapidità con cui desideri che una transazione venga elaborata. Se sei interessato a un'analisi dettagliata di come vengono calcolate le commissioni, consulta il mio post precedente su come le transazioni vengono incluse in un blocco.

E respira

Siamo stati su questo per un po', quindi questo sembra un buon posto come un altro per fare una pausa. La tana del bianconiglio continua e continueremo a esplorare nella seconda parte di questa serie. Alcuni concetti in arrivo: connessione a un nodo reale, contratti intelligenti e token. Hai domande di follow-up? Fammelo sapere! Il tuo feedback influenzerà la direzione che prenderemo da qui in poi. Le richieste sono benvenute tramite Twitter (opens in a new tab).

Ultimo aggiornamento della pagina: 6 febbraio 2025

Questo tutorial è stato utile?