Python Blockchain - Guida rapida

Nel tutorial su Blockchain, abbiamo appreso in dettaglio la teoria alla base della blockchain. La blockchain è l'elemento costitutivo fondamentale dietro la valuta digitale più popolare al mondo Bitcoin. Il tutorial ha affrontato a fondo le complessità di Bitcoin spiegando completamente l'architettura blockchain. Il prossimo passo è costruire la nostra blockchain.

Satoshi Nakamoto ha creato la prima valuta virtuale al mondo chiamata Bitcoin. Guardando al successo di Bitcoin, molti altri hanno creato le proprie valute virtuali. Per citarne alcuni: Litecoin, Zcash e così via.

Ora, potresti anche lanciare la tua valuta. Chiamiamolo TPCoin (TutorialsPoint Coin). Scriverai una blockchain per registrare tutte le transazioni che hanno a che fare con TPCoin. Il TPCoin può essere utilizzato per acquistare pizze, hamburger, insalate, ecc. Potrebbero esserci altri fornitori di servizi che si unirebbero alla tua rete e inizierebbero ad accettare TPCoin come valuta per offrire i loro servizi. Le possibilità sono infinite.

In questo tutorial, cerchiamo di capire come costruire un tale sistema e lanciare la tua valuta digitale sul mercato.

Componenti coinvolti nello sviluppo di progetti Blockchain

L'intero sviluppo del progetto blockchain consiste di tre componenti principali:

  • Client
  • Miners
  • Blockchain

Cliente

Il Cliente è colui che acquisterà merce da altri venditori. Il cliente stesso può diventare un venditore e accetterà denaro da altri contro i beni che fornisce. Partiamo dal presupposto che il cliente possa essere sia un fornitore che un destinatario di TPCoin. Pertanto, creeremo una classe client nel nostro codice che ha la capacità di inviare e ricevere denaro.

Minatore

Il minatore è colui che preleva le transazioni da un pool di transazioni e le assembla in un blocco. Il miner deve fornire una valida prova di lavoro per ottenere la ricompensa mineraria. Tutto il denaro che il minatore raccoglie come compenso sarà per lui da tenere. Può spendere quei soldi per acquistare beni o servizi da altri fornitori registrati sulla rete, proprio come fa un Cliente sopra descritto.

Blockchain

Infine, una Blockchain è una struttura di dati che concatena tutti i blocchi estratti in ordine cronologico. Questa catena è immutabile e quindi a prova di tempra.

Puoi seguire questo tutorial digitando il codice presentato in ogni passaggio in un nuovo notebook Jupyter. In alternativa, è possibile scaricare l'intero notebook Jupyter da www.anaconda.com .

Nel prossimo capitolo svilupperemo un client che utilizza il nostro sistema blockchain.

Un cliente è qualcuno che detiene TPCoin e li tratta per beni / servizi da altri fornitori sulla rete, compreso il proprio. Dovremmo definire un fileClientclasse per questo scopo. Per creare un'identificazione univoca a livello globale per il cliente, utilizziamo PKI (Public Key Infrastructure). In questo capitolo, parliamo di questo in dettaglio.

Il cliente dovrebbe essere in grado di inviare denaro dal suo portafoglio a un'altra persona conosciuta. Allo stesso modo, il cliente dovrebbe essere in grado di accettare denaro da una terza parte. Per spendere soldi, il cliente creerebbe una transazione specificando il nome del mittente e l'importo da pagare. Per ricevere denaro, il cliente fornirà la sua identità alla terza parte, essenzialmente un mittente del denaro. Non memorizziamo l'importo del saldo che il cliente tiene nel suo portafoglio. Durante una transazione, calcoleremo il saldo effettivo per garantire che il cliente abbia un saldo sufficiente per effettuare il pagamento.

Per sviluppare il Clientclass e per il resto del codice nel progetto, avremo bisogno di importare molte librerie Python. Questi sono elencati di seguito:

# import libraries
import hashlib
import random
import string
import json
import binascii
import numpy as np
import pandas as pd
import pylab as pl
import logging
import datetime
import collections

Oltre alle librerie standard di cui sopra, firmeremo le nostre transazioni, creeremo hash degli oggetti, ecc. Per questo, dovrai importare le seguenti librerie:

# following imports are required by PKI
import Crypto
import Crypto.Random
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

Nel prossimo capitolo, parliamo della classe client.

Il Client class genera il file private e public chiavi utilizzando il Python integrato RSAalgoritmo. Il lettore interessato può fare riferimento athis tutorialper l'implementazione di RSA. Durante l'inizializzazione dell'oggetto, creiamo chiavi private e pubbliche e memorizziamo i loro valori nella variabile di istanza.

self._private_key = RSA.generate(1024, random)
self._public_key = self._private_key.publickey()

Nota che non dovresti mai perdere la tua chiave privata. Per la conservazione dei dati, la chiave privata generata può essere copiata su una memoria esterna protetta o si può semplicemente annotare la sua rappresentazione ASCII su un pezzo di carta.

Il generato publicla chiave verrà utilizzata come identità del cliente. Per questo, definiamo una proprietà chiamataidentity che restituisce la rappresentazione HEX della chiave pubblica.

@property
   def identity(self):
      return
binascii.hexlify(self._public_key.exportKey(format='DER'))
.decode('ascii')

Il identityè unico per ogni cliente e può essere reso disponibile pubblicamente. Chiunque sarebbe in grado di inviarti valuta virtuale usando questoidentity e verrà aggiunto al tuo portafoglio.

Il codice completo per Client la classe è mostrata qui -

class Client:
   def __init__(self):
      random = Crypto.Random.new().read
      self._private_key = RSA.generate(1024, random)
      self._public_key = self._private_key.publickey()
      self._signer = PKCS1_v1_5.new(self._private_key)

   @property
   def identity(self):
      return
binascii.hexlify(self._public_key.exportKey(format='DER')).decode('ascii')

Client di prova

Ora scriveremo il codice che illustrerà come usare il Client classe -

Dinesh = Client()
print (Dinesh.identity)

Il codice precedente crea un'istanza di Client e lo assegna alla variabile Dinesh. Stampiamo la chiave pubblica diDinesh chiamando il suo identitymetodo. L'output è mostrato qui -

30819f300d06092a864886f70d010101050003818d0030818902818100b547fafceeb131e07
0166a6b23fec473cce22c3f55c35ce535b31d4c74754fecd820aa94c1166643a49ea5f49f72
3181ff943eb3fdc5b2cb2db12d21c06c880ccf493e14dd3e93f3a9e175325790004954c34d3
c7bc2ccc9f0eb5332014937f9e49bca9b7856d351a553d9812367dc8f2ac734992a4e6a6ff6
6f347bd411d07f0203010001

Passiamo ora alla creazione di una transazione nel prossimo capitolo.

In questo capitolo, creiamo un file Transactionclasse in modo che un cliente possa inviare denaro a qualcuno. Tieni presente che un cliente può essere sia un mittente che un destinatario del denaro. Quando desideri ricevere denaro, un altro mittente creerà una transazione e specificherà il tuopublicindirizzo in esso. Definiamo l'inizializzazione di una classe di transazione come segue:

def __init__(self, sender, recipient, value):
   self.sender = sender
   self.recipient = recipient
   self.value = value
   self.time = datetime.datetime.now()

Il init il metodo accetta tre parametri: quello del mittente public chiave, quella del destinatario publicchiave e l'importo da inviare. Questi vengono memorizzati nelle variabili di istanza per essere utilizzati da altri metodi. Inoltre, creiamo un'altra variabile per memorizzare l'ora della transazione.

Successivamente, scriviamo un metodo di utilità chiamato to_dictche combina tutte le quattro variabili di istanza sopra menzionate in un oggetto dizionario. Questo è solo per mettere l'intera informazione della transazione accessibile attraverso una singola variabile.

Come sai dal tutorial precedente che il primo blocco nella blockchain è un file Genesisbloccare. Il blocco Genesis contiene la prima transazione avviata dal creatore della blockchain. L'identità di questa persona può essere tenuta segreta come nel caso dei Bitcoin. Quindi, quando viene creata questa prima transazione, il creatore può semplicemente inviare la sua identità comeGenesis. Pertanto, durante la creazione del dizionario, controlliamo se il mittente èGenesise in tal caso assegniamo semplicemente un valore stringa alla variabile identità; altrimenti, assegniamo l'identità del mittente al fileidentity variabile.

if self.sender == "Genesis":
   identity = "Genesis"
else:
   identity = self.sender.identity

Costruiamo il dizionario usando la seguente riga di codice

return collections.OrderedDict({
   'sender': identity,
   'recipient': self.recipient,
   'value': self.value,
   'time' : self.time})

L'intero codice per to_dict metodo è mostrato di seguito -

def to_dict(self):
   if self.sender == "Genesis":
      identity = "Genesis"
   else:
      identity = self.sender.identity

   return collections.OrderedDict({
      'sender': identity,
      'recipient': self.recipient,
      'value': self.value,
      'time' : self.time})

Infine, firmeremo questo oggetto dizionario utilizzando la chiave privata del mittente. Come prima, usiamo l'infrastruttura PKI con algoritmo SHA. La firma generata viene decodificata per ottenere la rappresentazione ASCII per la stampa e la memorizzazione nella nostra blockchain. Ilsign_transaction il codice del metodo è mostrato qui -

def sign_transaction(self):
   private_key = self.sender._private_key
   signer = PKCS1_v1_5.new(private_key)
   h = SHA.new(str(self.to_dict()).encode('utf8'))
   return binascii.hexlify(signer.sign(h)).decode('ascii')

Ora lo testeremo Transaction classe.

Classe di transazione di prova

A tal fine creeremo due utenti, chiamati Dinesh e Ramesh. Dinesh invierà 5 TPCoin a Ramesh. Per questo prima creiamo i client chiamati Dinesh e Ramesh.

Dinesh = Client()
Ramesh = Client()

Ricorda che quando installi un file Client classe, il public andVerrebbero create chiavi private univoche per il client. Poiché Dinesh sta inviando il pagamento a Ramesh, avrà bisogno della chiave pubblica di Ramesh che si ottiene utilizzando la proprietà di identità del cliente.

Pertanto, creeremo l'istanza della transazione utilizzando il seguente codice:

t = Transaction(
   Dinesh,
   Ramesh.identity,
   5.0
)

Si noti che il primo parametro è il mittente, il secondo parametro è la chiave pubblica del destinatario e il terzo parametro è l'importo da trasferire. Ilsign_transaction recupera la chiave privata del mittente dal primo parametro per cantare la transazione.

Dopo aver creato l'oggetto transazione, lo firmerai chiamando il suo sign_transactionmetodo. Questo metodo restituisce la firma generata nel formato stampabile. Generiamo e stampiamo la firma utilizzando le seguenti due righe di codice:

signature = t.sign_transaction()
print (signature)

Quando esegui il codice sopra, vedrai l'output simile a questo -

7c7e3c97629b218e9ec6e86b01f9abd8e361fd69e7d373c38420790b655b9abe3b575e343c7
13703ca1aee781acd7157a0624db3d57d7c2f1172730ee3f45af943338157f899965856f6b0
0e34db240b62673ad5a08c8e490f880b568efbc36035cae2e748f1d802d5e8e66298be826f5
c6363dc511222fb2416036ac04eb972

Ora che la nostra infrastruttura di base per creare un cliente e una transazione è pronta, ora avremo più clienti che eseguono più transazioni come in una situazione di vita reale.

Le transazioni effettuate dai vari client vengono messe in coda nel sistema; i minatori raccolgono le transazioni da questa coda e le aggiungono al blocco. Quindi estrarranno il blocco e il minatore vincente avrà il privilegio di aggiungere il blocco alla blockchain e quindi guadagnare dei soldi per se stesso.

Descriveremo questo processo di mining più avanti quando discuteremo della creazione della blockchain. Prima di scrivere il codice per più transazioni, aggiungiamo una piccola funzione di utilità per stampare il contenuto di una data transazione.

Visualizzazione della transazione

Il display_transactionfunzione accetta un singolo parametro di tipo di transazione. L'oggetto dizionario all'interno della transazione ricevuta viene copiato in una variabile temporanea chiamatadict e utilizzando le chiavi del dizionario, i vari valori vengono stampati sulla console.

def display_transaction(transaction):
   #for transaction in transactions:
   dict = transaction.to_dict()
   print ("sender: " + dict['sender'])
   print ('-----')
   print ("recipient: " + dict['recipient'])
   print ('-----')
   print ("value: " + str(dict['value']))
   print ('-----')
   print ("time: " + str(dict['time']))
   print ('-----')

Successivamente, definiamo una coda di transazione per memorizzare i nostri oggetti di transazione.

Coda di transazione

Per creare una coda, dichiariamo un global list variabile chiamata transactions come segue -

transactions = []

Aggiungeremo semplicemente ogni transazione appena creata a questa coda. Si noti che per brevità, non implementeremo la logica di gestione delle code in questo tutorial.

Creazione di più client

Ora inizieremo a creare transazioni. In primo luogo, creeremo quattro clienti che si invieranno denaro l'un l'altro per ottenere vari servizi o beni da altri.

Dinesh = Client()
Ramesh = Client()
Seema = Client()
Vijay = Client()

A questo punto, abbiamo quattro clienti chiamati Dinesh, Ramesh, Seema e Vijay. Attualmente presumiamo che ciascuno di questi clienti detenga alcuni TPCoin nei propri portafogli per le transazioni. L'identità di ciascuno di questi client verrebbe specificata utilizzando la proprietà Identity di questi oggetti.

Creazione della prima transazione

Ora, iniziamo la nostra prima transazione come segue:

t1 = Transaction(
   Dinesh,
   Ramesh.identity,
   15.0
)

In questa transazione Dinesh invia 5 TPCoin a Ramesh. Affinché la transazione abbia successo, dovremo assicurarci che Dinesh abbia abbastanza soldi nel suo portafoglio per questo pagamento. Nota che avremo bisogno di una transazione genesis per avviare la circolazione di TPCoin nel sistema. Scriverai il codice di transazione per questa transazione genesis molto brevemente mentre leggi.

Firmeremo questa transazione utilizzando la chiave privata di Dinesh e la aggiungeremo alla coda delle transazioni come segue:

t1.sign_transaction()
transactions.append(t1)

Dopo la prima transazione effettuata da Dinesh, creeremo molte altre transazioni tra diversi clienti che abbiamo creato sopra.

Aggiunta di altre transazioni

Ora creeremo molte altre transazioni, ogni transazione assegnerà alcuni TPCoin a un'altra parte. Quando qualcuno spende soldi, non è necessario che controlli per avere saldi sufficienti in questo portafoglio. Il minatore in ogni caso convaliderà ogni transazione per il saldo che il mittente ha durante l'avvio della transazione.

In caso di saldo insufficiente, il miner contrassegnerà questa transazione come non valida e non la aggiungerà a questo blocco.

Il codice seguente crea e aggiunge altre nove transazioni alla nostra coda.

t2 = Transaction(
   Dinesh,
   Seema.identity,
   6.0
)
t2.sign_transaction()
transactions.append(t2)
t3 = Transaction(
   Ramesh,
   Vijay.identity,
   2.0
)
t3.sign_transaction()
transactions.append(t3)
t4 = Transaction(
   Seema,
   Ramesh.identity,
   4.0
)
t4.sign_transaction()
transactions.append(t4)
t5 = Transaction(
   Vijay,
   Seema.identity,
   7.0
)
t5.sign_transaction()
transactions.append(t5)
t6 = Transaction(
   Ramesh,
   Seema.identity,
   3.0
)
t6.sign_transaction()
transactions.append(t6)
t7 = Transaction(
   Seema,
   Dinesh.identity,
   8.0
)
t7.sign_transaction()
transactions.append(t7)
t8 = Transaction(
   Seema,
   Ramesh.identity,
   1.0
)
t8.sign_transaction()
transactions.append(t8)
t9 = Transaction(
   Vijay,
   Dinesh.identity,
   5.0
)
t9.sign_transaction()
transactions.append(t9)
t10 = Transaction(
   Vijay,
   Ramesh.identity,
   3.0
)
t10.sign_transaction()
transactions.append(t10)

Quando esegui il codice sopra, avrai dieci transazioni in coda per i miner per creare i loro blocchi.

Transazioni di dumping

In qualità di gestore blockchain, potresti periodicamente rivedere i contenuti della coda delle transazioni. A tale scopo, puoi utilizzare il filedisplay_transactionfunzione che abbiamo sviluppato in precedenza. Per eseguire il dump di tutte le transazioni nella coda, è sufficiente iterare l'elenco delle transazioni e per ogni transazione a cui si fa riferimento, chiamare il filedisplay_transaction funzione come mostrato qui -

for transaction in transactions:
   display_transaction (transaction)
   print ('--------------')

Le transazioni sono separate da una linea tratteggiata per distinguerle. Se esegui il codice sopra, vedrai l'elenco delle transazioni come mostrato di seguito -

sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c49214
4a9f463480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329e
c86794b04d773eb4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b
47e5157f8fe56c2ce3279c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311
c4d866c12d79d3fc3034563dfb0203010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e
674abe7abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8ad
d126b6e1a1308fb98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa93977
04de625d1836d3f42c7ee5683f6703259592cc24b09699376807f28fe0e00ff882974484
d805f874260dfc2d1627473b910203010001
-----
value: 15.0
-----
time: 2019-01-14 16:18:01.859915
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c49214
4a9f463480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329e
c86794b04d773eb4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b
47e5157f8fe56c2ce3279c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311
c4d866c12d79d3fc3034563dfb0203010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae14
3cbe59b3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fb
d9ee74b9e7ea12334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0
961b4f212d1fd5b5e49ae09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d0623
75799742a359b8f22c5362e5650203010001
-----
value: 6.0
-----
time: 2019-01-14 16:18:01.860966
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e
674abe7abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8ad
d126b6e1a1308fb98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa93977
04de625d1836d3f42c7ee5683f6703259592cc24b09699376807f28fe0e00ff882974484
d805f874260dfc2d1627473b910203010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876
f41338c62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cc
e25be99452a81df4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47
452590137869c25d9ff83d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f0
0e321b65e4c33acaf6469e18e30203010001
-----
value: 2.0
-----
time: 2019-01-14 16:18:01.861958
-----
--------------

Per brevità, ho stampato solo le prime poche transazioni nell'elenco. Nel codice sopra, stampiamo tutte le transazioni che iniziano con la primissima transazione tranne la transazione genesis che non è mai stata aggiunta a questo elenco. Poiché le transazioni vengono aggiunte periodicamente ai blocchi, in genere sarai interessato a visualizzare solo l'elenco delle transazioni che devono ancora essere estratte. In tal caso, dovrai creare un filefor loop per scorrere le transazioni che non sono ancora state minate.

Finora, hai imparato come creare client, consentire loro di tra di loro e mantenere una coda delle transazioni in sospeso che devono essere minate. Ora, arriva la parte più importante di questo tutorial e questa è la creazione di una blockchain stessa. Lo imparerai nella prossima lezione.

Un blocco è costituito da un numero variabile di transazioni. Per semplicità, nel nostro caso supporremo che il blocco sia costituito da un numero fisso di transazioni, che in questo caso è tre. Poiché il blocco deve memorizzare l'elenco di queste tre transazioni, dichiareremo una variabile di istanza chiamataverified_transactions come segue -

self.verified_transactions = []

Abbiamo chiamato questa variabile come verified_transactionsper indicare che solo le transazioni valide verificate verranno aggiunte al blocco. Ogni blocco contiene anche il valore hash del blocco precedente, in modo che la catena di blocchi diventi immutabile.

Per memorizzare l'hash precedente, dichiariamo una variabile di istanza come segue:

self.previous_block_hash = ""

Infine, dichiariamo un'altra variabile chiamata Nonce per conservare il nonce creato dal minatore durante il processo di estrazione.

self.Nonce = ""

La definizione completa di Block la classe è riportata di seguito -

class Block:
   def __init__(self):
      self.verified_transactions = []
      self.previous_block_hash = ""
      self.Nonce = ""

Poiché ogni blocco necessita del valore dell'hash del blocco precedente, dichiariamo una variabile globale chiamata last_block_hash come segue -

last_block_hash = ""

Ora creiamo il nostro primo blocco nella blockchain.

Partiamo dal presupposto che l'originatore di TPCoin inizialmente distribuisca 500 TPCoin a un cliente noto Dinesh. Per questo, crea prima un'istanza di Dinesh:

Dinesh = Client()

Quindi creiamo una transazione genesis e inviamo 500 TPCoin all'indirizzo pubblico di Dinesh.

t0 = Transaction (
   "Genesis",
   Dinesh.identity,
   500.0
)

Ora creiamo un'istanza di Block class e chiamalo block0.

block0 = Block()

Inizializziamo il file previous_block_hash e Nonce variabili di istanza a None, poiché questa è la prima transazione ad essere archiviata nella nostra blockchain.

block0.previous_block_hash = None
Nonce = None

Successivamente, aggiungeremo la transazione t0 sopra al file verified_transactions elenco mantenuto all'interno del blocco -

block0.verified_transactions.append (t0)

A questo punto il blocco è completamente inizializzato ed è pronto per essere aggiunto alla nostra blockchain. Creeremo la blockchain per questo scopo. Prima di aggiungere il blocco alla blockchain, eseguiremo l'hashing del blocco e memorizzeremo il suo valore nella variabile globale chiamatalast_block_hashche abbiamo dichiarato in precedenza. Questo valore verrà utilizzato dal prossimo minatore nel suo blocco.

Usiamo le seguenti due righe di codice per eseguire l'hashing del blocco e memorizzare il valore digest.

digest = hash (block0)
last_block_hash = digest

Infine, creiamo una blockchain come vedremo nel prossimo capitolo.

Una blockchain contiene un elenco di blocchi concatenati tra loro. Per memorizzare l'intero elenco, creeremo una variabile di elenco chiamata TPCoins -

TPCoins = []

Scriveremo anche un metodo di utilità chiamato dump_blockchainper scaricare il contenuto dell'intera blockchain. Per prima cosa stampiamo la lunghezza della blockchain in modo da sapere quanti blocchi sono attualmente presenti nella blockchain.

def dump_blockchain (self):
   print ("Number of blocks in the chain: " + str(len (self)))

Si noti che con il passare del tempo, il numero di blocchi nella blockchain sarebbe straordinariamente alto per la stampa. Pertanto, quando stampi il contenuto della blockchain potresti dover decidere l'intervallo che desideri esaminare. Nel codice sottostante, abbiamo stampato l'intera blockchain poiché non aggiungeremmo troppi blocchi nella demo corrente.

Per scorrere la catena, abbiamo impostato un file for loop come segue -

for x in range (len(TPCoins)):
   block_temp = TPCoins[x]

Ogni blocco referenziato viene copiato in una variabile temporanea chiamata block_temp.

Stampiamo il numero di blocco come intestazione per ogni blocco. Nota che i numeri inizierebbero con zero, il primo blocco è un blocco di genesi numerato zero.

print ("block # " + str(x))

All'interno di ogni blocco, abbiamo memorizzato un elenco di tre transazioni (ad eccezione del blocco genesis) in una variabile chiamata verified_transactions. Ripetiamo questo elenco in un filefor loop e per ogni elemento recuperato, chiamiamo display_transaction funzione per visualizzare i dettagli della transazione.

for transaction in block_temp.verified_transactions:
   display_transaction (transaction)

L'intera definizione della funzione è mostrata di seguito:

def dump_blockchain (self):
   print ("Number of blocks in the chain: " + str(len (self)))
   for x in range (len(TPCoins)):
      block_temp = TPCoins[x]
      print ("block # " + str(x))
      for transaction in block_temp.verified_transactions:
         display_transaction (transaction)
         print ('--------------')
      print ('=====================================')

Si noti che qui abbiamo inserito i separatori nei punti appropriati del codice per delimitare i blocchi e le transazioni al suo interno.

Poiché ora abbiamo creato una blockchain per l'archiviazione dei blocchi, il nostro prossimo compito è creare blocchi e iniziare ad aggiungerlo alla blockchain. A tale scopo, aggiungeremo un blocco genesi che hai già creato nel passaggio precedente.

L'aggiunta di un blocco alla blockchain implica l'aggiunta del blocco creato al nostro file TPCoins elenco.

TPCoins.append (block0)

Si noti che a differenza del resto dei blocchi nel sistema, il blocco genesis contiene solo una transazione che viene avviata dal creatore del sistema TPCoins. Ora scaricherai i contenuti della blockchain chiamando la nostra funzione globaledump_blockchain -

dump_blockchain(TPCoins)

Quando esegui questa funzione, vedrai il seguente output:

Number of blocks in the chain: 1
block # 0
sender: Genesis
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100ed272b52ccb539
e2cd779c6cc10ed1dfadf5d97c6ab6de90ed0372b2655626fb79f62d0e01081c163b0864
cc68d426bbe9438e8566303bb77414d4bfcaa3468ab7febac099294de10273a816f7047d
4087b4bafa11f141544d48e2f10b842cab91faf33153900c7bf6c08c9e47a7df8aa7e60d
c9e0798fb2ba3484bbdad2e4430203010001
-----
value: 500.0
-----
time: 2019-01-14 16:18:02.042739
-----
--------------
=====================================

A questo punto il sistema blockchain è pronto per l'uso. Ora consentiremo ai clienti interessati di diventare minatori fornendo loro una funzionalità di mining.

Per abilitare il mining, dobbiamo sviluppare una funzione di mining. La funzionalità di mining deve generare un digest su una determinata stringa di messaggio e fornire una prova di lavoro. Discutiamolo in questo capitolo.

Funzione Message Digest

Scriveremo una funzione di utilità chiamata sha256 per creare un digest su un dato messaggio -

def sha256(message):
return hashlib.sha256(message.encode('ascii')).hexdigest()

Il sha256 funzione richiede un file message come parametro, lo codifica in ASCII, genera un digest esadecimale e restituisce il valore al chiamante.

Funzione mineraria

Ora sviluppiamo il file minefunzione che implementa la nostra strategia di mining. La nostra strategia in questo caso sarebbe quella di generare un hash sul messaggio dato che è preceduto da un dato numero di 1. Il numero di 1 specificato è specificato come parametro permine funzione specificata come livello di difficoltà.

Ad esempio, se specifichi un livello di difficoltà 2, l'hash generato su un dato messaggio dovrebbe iniziare con due 1, come 11xxxxxxxx. Se il livello di difficoltà è 3, l'hash generato dovrebbe iniziare con tre 1, come 111xxxxxxxx. Dati questi requisiti, svilupperemo ora la funzione di mining come mostrato nei passaggi indicati di seguito.

Passo 1

La funzione di mining accetta due parametri: il messaggio e il livello di difficoltà.

def mine(message, difficulty=1):

Passo 2

Il livello di difficoltà deve essere maggiore o uguale a 1, lo assicuriamo con la seguente dichiarazione di asserzione:

assert difficulty >= 1

Passaggio 3

Creiamo un file prefix variabile utilizzando il livello di difficoltà impostato.

prefix = '1' * difficulty

Nota se il livello di difficoltà è 2 il prefisso sarebbe "11" e se il livello di difficoltà è 3, il prefisso sarebbe "111" e così via. Verificheremo se questo prefisso esiste nel digest generato del messaggio. Per digerire il messaggio stesso, utilizziamo le seguenti due righe di codice:

for i in range(1000):
   digest = sha256(str(hash(message)) + str(i))

Continuiamo ad aggiungere un nuovo numero iall'hash del messaggio in ogni iterazione e genera un nuovo digest sul messaggio combinato. Come input per ilsha256 la funzione cambia in ogni iterazione, il digestanche il valore cambierebbe. Controlliamo se questodigest il valore è superiore al set prefix.

if digest.startswith(prefix):

Se la condizione è soddisfatta, termineremo il file for loop e restituisci il file digest valore per il chiamante.

L'intero mine il codice è mostrato qui -

def mine(message, difficulty=1):
   assert difficulty >= 1
   prefix = '1' * difficulty
   for i in range(1000):
      digest = sha256(str(hash(message)) + str(i))
      if digest.startswith(prefix):
         print ("after " + str(i) + " iterations found nonce: "+ digest)
      return digest

Per tua comprensione, abbiamo aggiunto il file print istruzione che stampa il valore digest e il numero di iterazioni necessarie per soddisfare la condizione prima di tornare dalla funzione.

Test della funzione di mining

Per testare la nostra funzione di mining, esegui semplicemente la seguente istruzione:

mine ("test message", 2)

Quando esegui il codice sopra, vedrai l'output simile a quello qui sotto -

after 138 iterations found nonce:
11008a740eb2fa6bf8d55baecda42a41993ca65ce66b2d3889477e6bfad1484c

Notare che il digest generato inizia con "11". Se modifichi il livello di difficoltà a 3, il digest generato inizierà con "111" e, naturalmente, richiederà probabilmente più numero di iterazioni. Come puoi vedere, un minatore con più potenza di elaborazione sarà in grado di estrarre un dato messaggio prima. È così che i minatori competono tra loro per guadagnare i loro ricavi.

Ora siamo pronti per aggiungere altri blocchi alla nostra blockchain. Impariamo questo nel nostro prossimo capitolo.

Ogni miner raccoglierà le transazioni da un pool di transazioni creato in precedenza. Per tenere traccia del numero di messaggi già estratti, dobbiamo creare una variabile globale -

last_transaction_index = 0

Ora avremo il nostro primo minatore che aggiunge un blocco alla blockchain.

Aggiunta del primo blocco

Per aggiungere un nuovo blocco, creiamo prima un'istanza di Block classe.

block = Block()

Prendiamo le prime 3 transazioni dalla coda -

for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   # validate transaction

Prima di aggiungere la transazione al blocco, il miner verificherà la validità della transazione. La validità della transazione viene verificata testando l'uguaglianza tra l'hash fornito dal mittente e l'hash generato dal miner utilizzando la chiave pubblica del mittente. Inoltre, il miner verificherà che il mittente abbia un saldo sufficiente per pagare la transazione corrente.

Per brevità, non abbiamo incluso questa funzionalità nel tutorial. Dopo che la transazione è stata convalidata, la aggiungiamo al fileverified_transactions elenco nel file block esempio.

block.verified_transactions.append (temp_transaction)

Incrementiamo l'ultimo indice di transazione in modo che il prossimo minatore raccolga le transazioni successive nella coda.

last_transaction_index += 1

Aggiungiamo esattamente tre transazioni al blocco. Fatto ciò, inizializzeremo il resto delle variabili di istanza diBlockclasse. Per prima cosa aggiungiamo l'hash dell'ultimo blocco.

block.previous_block_hash = last_block_hash

Successivamente, estraiamo il blocco con un livello di difficoltà 2.

block.Nonce = mine (block, 2)

Si noti che il primo parametro di minela funzione è un oggetto binario. Ora eseguiamo l'hash dell'intero blocco e vi creiamo un digest.

digest = hash (block)

Infine, aggiungiamo il blocco creato alla blockchain e reinizializziamo la variabile globale last_block_hash per l'utilizzo nel blocco successivo.

L'intero codice per l'aggiunta del blocco è mostrato di seguito:

block = Block()
for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   # validate transaction
   # if valid
   block.verified_transactions.append (temp_transaction)
   last_transaction_index += 1

block.previous_block_hash = last_block_hash
block.Nonce = mine (block, 2)
digest = hash (block)
TPCoins.append (block)
last_block_hash = digest

Aggiunta di più blocchi

Aggiungeremo ora altri due blocchi alla nostra blockchain. Di seguito è riportato il codice per l'aggiunta dei due blocchi successivi:

# Miner 2 adds a block
block = Block()

for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   # validate transaction
   # if valid
   block.verified_transactions.append (temp_transaction)
   last_transaction_index += 1
block.previous_block_hash = last_block_hash
block.Nonce = mine (block, 2)digest = hash (block)
TPCoins.append (block)last_block_hash = digest
# Miner 3 adds a block
block = Block()

for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   #display_transaction (temp_transaction)
   # validate transaction
   # if valid
   block.verified_transactions.append (temp_transaction)
   last_transaction_index += 1

block.previous_block_hash = last_block_hash
block.Nonce = mine (block, 2)
digest = hash (block)

TPCoins.append (block)
last_block_hash = digest

Quando aggiungi questi due blocchi, vedrai anche il numero di iterazioni necessarie per trovare il Nonce. A questo punto, la nostra blockchain è composta da un totale di 4 blocchi incluso il blocco genesis.

Scaricare l'intera blockchain

Puoi verificare il contenuto dell'intera blockchain utilizzando la seguente dichiarazione:

dump_blockchain(TPCoins)

Vedresti l'output simile a quello mostrato di seguito -

Number of blocks in the chain: 4
block # 0
sender: Genesis
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100ed272b52ccb539e2cd779
c6cc10ed1dfadf5d97c6ab6de90ed0372b2655626fb79f62d0e01081c163b0864cc68d426bbe943
8e8566303bb77414d4bfcaa3468ab7febac099294de10273a816f7047d4087b4bafa11f141544d4
8e2f10b842cab91faf33153900c7bf6c08c9e47a7df8aa7e60dc9e0798fb2ba3484bbdad2e44302
03010001
-----
value: 500.0
-----
time: 2019-01-14 16:18:02.042739
-----
--------------
=====================================
block # 1
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
value: 15.0
-----
time: 2019-01-14 16:18:01.859915
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
value: 6.0
-----
time: 2019-01-14 16:18:01.860966
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876f41338c
62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cce25be99452a81d
f4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47452590137869c25d9ff83
d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f00e321b65e4c33acaf6469e18e302
03010001
-----
value: 2.0
-----
time: 2019-01-14 16:18:01.861958
-----
--------------
=====================================
block # 2
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
value: 4.0
-----
time: 2019-01-14 16:18:01.862946
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876f41338c
62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cce25be99452a81d
f4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47452590137869c25d9ff83
d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f00e321b65e4c33acaf6469e18e302
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
value: 7.0
-----
time: 2019-01-14 16:18:01.863932
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
value: 3.0
-----
time: 2019-01-14 16:18:01.865099
-----
--------------
=====================================
block # 3
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
value: 8.0
-----
time: 2019-01-14 16:18:01.866219
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
value: 1.0
-----
time: 2019-01-14 16:18:01.867223
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876f41338c
62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cce25be99452a81d
f4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47452590137869c25d9ff83
d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f00e321b65e4c33acaf6469e18e302
03010001
-----
recipient: 
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
value: 5.0
-----
time: 2019-01-14 16:18:01.868241
-----
--------------
=====================================

In questo tutorial, abbiamo imparato come costruire un progetto blockchain in Python. Ci sono molte aree in cui è necessario aggiungere ulteriori funzionalità a questo progetto.

Ad esempio, sarà necessario scrivere funzioni per la gestione della coda delle transazioni. Dopo che le transazioni sono state estratte e il blocco estratto è stato accettato dal sistema, non è più necessario archiviarle.

Inoltre, i miner preferirebbero sicuramente ritirare le transazioni con la commissione più alta. Allo stesso tempo, dovrai assicurarti che le transazioni con commissioni basse o nulle non moriranno di fame per sempre.

Dovrai sviluppare algoritmi per la gestione della coda. Inoltre, il tutorial corrente non include il codice dell'interfaccia client. Dovrai svilupparlo sia per i clienti normali che per i miner. Il progetto blockchain a tutti gli effetti incorrerebbe in molte altre righe di codice e va oltre lo scopo di questo tutorial. Il lettore interessato può scaricare la fonte bitcoin per ulteriori studi.

Conclusioni

Questo tutorial nitido dovrebbe aiutarti a creare il tuo progetto blockchain.

Per lo sviluppo di progetti blockchain a tutti gli effetti, puoi imparare di più dalla fonte bitcoin .

Per progetti commerciali o non commerciali più grandi, potresti prendere in considerazione l'utilizzo di Ethereum , una piattaforma di app blockchain pronta per l'uso.