Salt la conținutul principal

O introducere pentru dezvoltatorii Python despre Ethereum, partea 1

noțiuni de bazăpythonweb3.py
Începător
Marc Garreau
Snake charmers(opens in a new tab)
8 septembrie 2020
12 minute de citit minute read

Așadar, ați auzit despre acest Ethereum și sunteți gata să vă aventuraţi? Această postare va aborda succint câteva noțiuni elementare despre blockchain, apoi vă va îndruma să interacționați cu un nod Ethereum simulat – prin citirea datelor blocului, verificarea soldurilor conturilor și trimiterea de tranzacții. Pe parcurs vom evidenția diferențele dintre metodele tradiționale de construire a aplicațiilor și această nouă paradigmă descentralizată.

Condiții prealabile (soft)

Această postare îşi propune să fie accesibilă unei game largi de dezvoltatori. Vor fi implicate instrumente Python, dar acestea constituie doar un vehicul pentru idei – nu este nicio problemă dacă nu sunteți dezvoltator Python. Voi face totuşi doar câteva presupuneri despre ceea ce cunoașteți deja, astfel încât să putem trece rapid la aspectele specifice pentru Ethereum.

Ipoteze:

  • ştiţi cum să folosiți un terminal,
  • ați scris deja câteva linii de cod Python,
  • aveți instalat Python în versiunea 3.6 sau ulterioară pe computer (utilizarea unui mediu virtual(opens in a new tab) este foarte recomandată) și
  • ați folosit pip, programul de instalare a pachetelor Python. Reamintim că, în cazul când oricare dintre acestea nu este adevărată sau dacă nu intenționați să reproduceți codul din acest articol, sunt şanse să puteţi ţine pasul fără probleme.

Blockchain-urile, pe scurt

Ethereum poate fi descris în mai multe feluri, dar în esență este un blockchain. Blockchain-urile sunt alcătuite dintr-o serie de blocuri, deci haideți să începem de aici. La modul cel mai simplu, fiecare bloc de pe blockchain-ul Ethereum reprezintă pur și simplu niște metadate și o listă de tranzacții. În format JSON, aceasta arată cam așa:

1{
2 "number": 1234567,
3 "hash": "0xabc123...",
4 "parentHash": "0xdef456...",
5 "miner": "0xa1b2c3...",
6 ...,
7 "transactions": [...]
8}
Copiați

Fiecare bloc conține o referință la blocul precedent; „parentHash” este pur și simplu hash-ul blocului anterior.

Observaţie: Ethereum utilizează în mod regulat funcții hash(opens in a new tab) pentru a produce valori de mărime fixă („hash-uri”). Hash-urile joacă un rol important în Ethereum, dar deocamdată puteți să le consideraţi niște ID-uri unice.

O diagramă care ilustrează un blockchain, inclusiv datele din interiorul fiecărui bloc

Un blockchain este în esență o listă de link-uri; fiecare bloc face referire la blocul anterior.

Această structură de date nu reprezintă o noutate în sine, însă regulile (adică protocoalele peer-to-peer) care guvernează rețeaua sunt noi. Nu există o autoritate centrală; rețeaua de omologi („peers”) trebuie să colaboreze pentru a susține rețeaua și intră în competiţie pentru a decide ce tranzacții vor fi incluse în blocul următor. De aceea, atunci când doriți să trimiteți niște bani unui prieten, trebuie să transmiteți tranzacția în rețea, apoi să așteptați ca aceasta să fie inclusă într-un bloc ce urmează.

Singura modalitate ca blockchain-ul să verifice dacă banii au fost cu adevărat trimiși de la un utilizator la altul este să folosească o monedă nativă a acelui blockchain (adică creată și guvernată de acesta). În Ethereum această monedă se numește ether, iar blockchain-ul Ethereum conține singura înregistrare oficială a soldurilor conturilor.

O nouă paradigmă

Această nouă stivă tehnologică descentralizată a generat noi instrumente pentru dezvoltatori. Astfel de instrumente există în multe limbaje de programare, însă noi le vom analiza prin prisma Python. Menţionăm din nou: chiar dacă Python nu este limbajul dvs. preferat, nu va fi dificil să ţineţi pasul.

Dezvoltatorii Python care vor să interacționeze cu Ethereum vor căuta probabil să utilizeze Web3.py(opens in a new tab). Web3.py este o bibliotecă ce simplifică mult felul în care vă conectați la un nod Ethereum, după care trimiteți și primiți date de la acesta.

Observaţie: noţiunile de „nod Ethereum” și „client Ethereum” sunt utilizate ca sinonime. În ambele cazuri, se referă la software-ul rulat de un participant la rețeaua Ethereum. Acest software poate să citească datele blocurilor, să primească actualizări când se adaugă noi blocuri în lanț („minate”), să transmită noi tranzacții și multe altele.

Clienții Ethereum pot fi configurați pentru a fi accesați prin IPC(opens in a new tab), HTTP sau Websockets, astfel încât Web3.py va trebui să reflecte aceste configurații. Web3.py se referă la aceste opțiuni de conectare ca furnizori (providers). Va trebui să alegeți unul dintre cei trei furnizori pentru a conecta instanța Web3.py la nodul dvs.

O diagramă care arată cum foloseşte web3.py IPC-ul pentru a vă conecta aplicația la un nod Ethereum

Configurați nodul Ethereum și Web3.py pentru a comunica prin același protocol, de exemplu, IPC în această diagramă.

Odată ce Web3.py este configurat corespunzător, puteți începe să interacționați cu blockchain-ul. Iată câteva exemple de utilizare a Web3.py ca o previzualizare a ceea ce va urma:

1# read block data:
2w3.eth.getBlock('latest')
3
4# send a transaction:
5w3.eth.sendTransaction({'from': ..., 'to': ..., 'value': ...})
Copiați

Instalare

În această prezentare vom lucra doar în cadrul unui interpretator Python. Nu vom crea niciun dosar, fișier, nici clase sau funcții.

Observaţie: În exemplele de mai jos, comenzile care încep cu „$” sunt destinate a fi rulate în terminal. (Nu tastați „$”, acesta semnifică doar începutul liniei.)

Mai întâi instalați IPython(opens in a new tab) pentru a explora într-un mediu ușor de utilizat. IPython oferă, printre alte funcţionalităţi, completarea filei, ceea ce face mult mai ușor de văzut ce este posibil în Web3.py.

$ pip install ipython

Web3.py este publicat sub numele de web3. Se instalează în felul următor:

$ pip install web3

Încă un lucru – mai târziu vom simula un blockchain, ceea ce necesită alte câteva dependențe. Le puteți instala prin comanda:

$ pip install 'web3[tester]'

Sunteți gata să începeţi!

Deschideți un sandbox

Deschideți un mediu Python nou rulând ipython în terminalul dvs. Este similar cu a rula python, dar oferă funcţionalităţi mai speciale.

$ ipython

Aceasta va afișa câteva informații despre versiunile de Python și IPython pe care le rulați, apoi ar trebui să vedeți un prompt care așteaptă introducere de date:

1In [1]:
Copiați

Acum vedeți un shell Python interactiv. Practic, este un sandbox în care puteți juca. Dacă ați ajuns până aici, este timpul să importați Web.py:

1In [1]: from web3 import Web3
Copiați

Prezentarea modulului Web3

Pe lângă faptul că este un portal de acces la Ethereum, modulul Web3(opens in a new tab) oferă câteva funcții practice. Să explorăm câteva dintre acestea.

Într-o aplicație Ethereum va trebui în mod normal să convertiți denominațiile monetare. Modulul Web3 oferă câteva metode ajutătoare exact pentru acest lucru: fromWei(opens in a new tab) și toWei(opens in a new tab).

Observaţie: Computerele sunt extraordinar de slabe la manipularea zecimalelor în matematică. Pentru a rezolva această problemă, dezvoltatorii stochează adesea sumele de dolari în cenți. De exemplu, un articol cu un preț de 5,99 dolari poate fi stocat în baza de date ca fiind 599.

Un model similar este utilizat la gestionarea tranzacțiilor în ether. Numai că, în loc de două zecimale, ether-ul are 18! Cea mai mică denominație de ether se numește wei, prin urmare aceasta este valoarea specificată la trimiterea tranzacțiilor.

1 ether = 100000000000000000000000000 wei

1 wei = 0.00000000000000000000000001 ether

Încercați să convertiți câteva valori în și din wei. Rețineți că există și alte denumiri pentru numeroasele denominații(opens in a new tab) între ether și wei. Una din cele mai cunoscute dintre ele este gwei, deoarece este adesea modalitatea de reprezentare a taxelor de tranzacție.

1In [2]: Web3.toWei(1, 'ether')
2Out[2]: 1000000000000000000
3
4In [3]: Web3.fromWei(500000000, 'gwei')
5Out[3]: Decimal('0.5')
Copiați

Alte metode utilitare din modulul Web3 cuprind convertoare de formate de date (de exemplu, toHex(opens in a new tab)), ajutoare de adrese (de exemplu, isAddress(opens in a new tab)) și funcții hash (de exemplu, keccak(opens in a new tab)). Multe dintre acestea vor fi abordate mai târziu în această serie. Pentru a vizualiza toate metodele și proprietățile disponibile, utilizați autocompletarea IPython, tastând Web3. și apăsând tasta „Tab” de două ori după punct.

Comunicaţi cu lanțul

Metodele practice sunt excelente, dar haideți să trecem la blockchain. Pasul următor este configurarea Web3.py pentru a comunica cu un nod Ethereum. Aici avem opțiunea de a folosi furnizorii IPC, HTTP sau Websocket.

Nu vom merge pe această cale, dar un exemplu de flux de lucru complet folosind furnizorul HTTP ar putea arăta cam așa:

  • Descărcați un nod Ethereum, de exemplu, Geth(opens in a new tab).
  • Porniți Geth într-o fereastră de terminal și așteptați ca acesta să sincronizeze rețeaua. Portul HTTP implicit este 8545, dar este configurabil.
  • Spuneți-i modulului Web3.py să se conecteze la nod prin HTTP, pe localhost:8545. w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))
  • Utilizați instanța w3 pentru a interacționa cu nodul.

Deși acesta este un mod „real” de a o face, procesul de sincronizare durează ore întregi și nu este necesar dacă doriți doar un mediu de dezvoltare. Web3.py afișează un al patrulea furnizor în acest scop, EthereumTesterProvider. Acest furnizor de testare face legătura cu un nod Ethereum simulat, cu permisiuni relaxate și monedă falsă cu care puteţi testa.

O diagramă care ilustrează EthereumTesterProvider, ce leagă aplicația web3.py de un nod Ethereum simulat

EthereumTesterProvider se conectează la un nod simulat și este la îndemână pentru mediile de dezvoltare rapidă.

Acel nod simulat se numește eth-tester(opens in a new tab) și l-am instalat în cadrul comenzii pip install web3[tester]. Configurarea Web3.py pentru a utiliza acest furnizor tester este foarte simplă:

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

Acum sunteți gata pentru a naviga în lanț! Unii oameni spun că acest lucru nu există. Este invenţia mea. Haideți să facem un tur rapid.

Turul rapid

Să începem cu începutul, o verificare a sănătății:

1In [5]: w3.isConnected()
2Out[5]: True
Copiați

Din moment ce folosim furnizorul de testare, acesta nu este un test foarte valoros, dar dacă eșuează, este posibil să fi tastat ceva greșit la instanțierea variabilei w3. Verificați încă o dată dacă ați inclus parantezele interioare, adică Web3.EthereumTesterProvider().

Oprirea nr. 1: conturi

Pentru comoditate, furnizorul de testare a creat câteva conturi și le-a preîncărcat cu ether de testare.

În primul rând, să vedem o listă a acestor conturi:

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

Dacă executați această comandă, ar trebui să vedeți o listă de zece șiruri care încep cu 0x. Fiecare este o adresă publică și este oarecum analogă cu numărul de cont al unui cont de verificare. Ați furniza această adresă cuiva care ar dori să vă trimită ether.

Așa cum am menționat, furnizorul de testare a preîncărcat fiecare dintre aceste conturi cu niște ether de test. Să aflăm cât avem în primul cont:

1In [7]: w3.eth.getBalance(w3.eth.accounts[0])
2Out[7]: 1000000000000000000000000
Copiați

Asta înseamnă o mulțime de zerouri! Înainte de a vă bucura că aveți o tonă de bani, amintiți-vă de lecția dinainte despre denominaţiile monetare. Valorile etherului sunt reprezentate în cea mai mică denominaţie, wei. Convertiți-o în ether:

1In [8]: w3.fromWei(1000000000000000000000000, 'ether')
2Out[8]: Decimal('1000000')
Copiați

Un milion de ether de test – totuși nu este chiar atât de rău.

Oprirea nr. 2: datele blocului

Haideți să aruncăm o privire la starea acestui blockchain simulat:

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

Răspunsul cuprinde o mulţime de informaţii despre bloc, dar sunt numai câteva lucruri de subliniat aici:

  • Numărul blocului este zero - indiferent cu cât timp în urmă ați configurat furnizorul de testare. Spre deosebire de rețeaua Ethereum reală, care minează un bloc nou aproximativ la fiecare 15 secunde, această simulare va aștepta până când îi veți da ceva de lucru.
  • tranzactii este o listă goală, din același motiv: nu am făcut încă nimic. Acest prim bloc este un bloc gol, doar pentru a demara lanțul.
  • Observați că parentHash este doar un grup de octeți goi. Acest lucru înseamnă că este primul bloc din lanț, cunoscut și sub numele de blocul de geneză.

Oprirea nr. 3: tranzacții

Suntem blocați la blocul zero până când va exista o tranzacție de minat, așa că haideţi să-i dăm una. Trimiteți câțiva ether de test de la un cont la altul:

1In [10]: tx_hash = w3.eth.sendTransaction({
2 'from': w3.eth.accounts[0],
3 'to': w3.eth.accounts[1],
4 'value': w3.toWei(3, 'ether')
5})
Copiați

Acesta este de obicei punctul în care așteptați câteva secunde pentru ca tranzacția dvs. să fie minată într-un bloc nou. Procesul complet se desfășoară cam așa:

  1. Trimiteți o tranzacție și păstrați hash-ul tranzacției. Până când este minată, tranzacția este „în așteptare.” tx_hash = w3.eth.sendTransaction({ … })
  2. Așteptați ca tranzacția să fie minată: w3.eth.waitForTransactionReceipt(tx_hash)
  3. Continuați logica aplicației. Pentru a vizualiza tranzacția reușită: w3.eth.getTransaction(tx_hash)

Mediul nostru simulat va adăuga instantaneu tranzacția într-un bloc nou, astfel încât să putem vizualiza imediat tranzacția:

1In [11]: w3.eth.getTransaction(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})
Afișează tot
Copiați

Veți vedea câteva detalii familiare aici: câmpurile de la, la și valoare ar trebui să corespundă cu datele introduse din apelul nostru sendTransaction. Celălalt aspect încurajator este că această tranzacție a fost inclusă ca primă tranzacție ('transactionIndex': 0) în cadrul blocului numărul 1.

De asemenea, putem să vedem cu ușurință dacă tranzacţia a reuşit verificând soldurile celor două conturi implicate. Trei ether trebuie că s-au mutat de la unul la altul.

1In [12]: w3.eth.getBalance(w3.eth.accounts[0])
2Out[12]: 999996999999999999979000
3
4In [13]: w3.eth.getBalance(w3.eth.accounts[1])
5Out[13]: 1000003000000000000000000
Copiați

Acesta din urmă arată bine! Soldul a trecut de la 1.000.000 la 1.000.003 ether. Dar ce s-a întâmplat cu primul cont? Se pare că a pierdut ceva mai mult de trei ether. Din nefericire, nimic nu este gratuit în viaţă, iar utilizarea rețelei publice Ethereum necesită să vă recompensați colegii pentru rolul lor de sprijin. A fost dedusă o mică taxă de tranzacție de 21000 de wei din contul care a efectuat tranzacția.

Observaţie: În rețeaua publică, taxele de tranzacție sunt variabile în funcție de cererea rețelei și de rapiditatea cu care doriți ca o tranzacție să fie procesată. Dacă vă interesează o defalcare a modului în care sunt calculate taxele, consultați postarea mea anterioară despre cum sunt incluse tranzacțiile într-un bloc(opens in a new tab).

Și respirați

Ne-am ocupat de aceste lucruri de ceva vreme, așa că acum este un moment la fel de bun ca oricare altul pentru a lua o pauză. Aventura continuă şi ne vom continua explorarea în partea a doua a acestei serii. Câteva concepte care vor urma: conectarea la un nod real, contracte inteligente și tokenuri. Aveți întrebări în continuare? Anunțați-mă! Feedback-ul dvs. va influența direcția în care vom merge de aici încolo. Primim cu plăcere solicitări prin Twitter(opens in a new tab).

Ultima modificare: @nicklcanada(opens in a new tab), 15 ianuarie 2024

A fost util acest tutorial?