Python Blockchain - Szybki przewodnik

W samouczku dotyczącym Blockchain szczegółowo poznaliśmy teorię stojącą za blockchain. Blockchain to podstawowy element składowy najpopularniejszej na świecie cyfrowej waluty Bitcoin. W samouczku szczegółowo omówiono zawiłości Bitcoina, wyjaśniając w pełni architekturę blockchain. Następnym krokiem jest zbudowanie własnego łańcucha bloków.

Satoshi Nakamoto stworzył pierwszą wirtualną walutę na świecie o nazwie Bitcoin. Patrząc na sukces Bitcoina, wielu innych stworzyło własne wirtualne waluty. Aby wymienić tylko kilka - Litecoin, Zcash i tak dalej.

Teraz możesz również chcieć uruchomić własną walutę. Nazwijmy to TPCoin (TutorialsPoint Coin). Napiszesz łańcuch bloków, aby rejestrować wszystkie transakcje, które dotyczą TPCoin. TPCoin może być używany do kupowania pizzy, burgerów, sałatek itp. Mogą istnieć inni dostawcy usług, którzy dołączyliby do Twojej sieci i zaczęli akceptować TPCoin jako walutę do świadczenia swoich usług. Możliwości są nieskończone.

W tym samouczku wyjaśnijmy, jak zbudować taki system i wprowadzić na rynek własną cyfrową walutę.

Komponenty zaangażowane w rozwój projektów Blockchain

Cały rozwój projektu blockchain składa się z trzech głównych komponentów -

  • Client
  • Miners
  • Blockchain

Klient

Klient jest tym, który kupuje towary od innych sprzedawców. Klient może sam zostać sprzedawcą i przyjąć pieniądze od innych w zamian za dostarczone przez siebie towary. Zakładamy tutaj, że klient może być zarówno dostawcą, jak i odbiorcą TPCoinów. W ten sposób utworzymy w naszym kodzie klasę klienta, która ma możliwość wysyłania i odbierania pieniędzy.

Górnik

Górnik to ten, który pobiera transakcje z puli transakcji i łączy je w blok. Górnik musi przedstawić ważny dowód pracy, aby otrzymać nagrodę za wydobycie. Wszystkie pieniądze, które górnik zbierze jako opłatę, będą dla niego zatrzymane. Może wydać te pieniądze na zakup towarów lub usług od innych zarejestrowanych sprzedawców w sieci, tak jak robi to klient opisany powyżej.

Blockchain

Wreszcie, Blockchain to struktura danych, która łączy wszystkie wydobyte bloki w porządku chronologicznym. Ten łańcuch jest niezmienny, a zatem odporny na hartowanie.

Możesz skorzystać z tego samouczka, wpisując kod przedstawiony w każdym kroku w nowym notatniku Jupyter. Alternatywnie możesz pobrać cały notatnik Jupyter ze strony www.anaconda.com .

W następnym rozdziale opracujemy klienta korzystającego z naszego systemu blockchain.

Klient to osoba, która posiada TPCoiny i dokonuje transakcji za towary / usługi od innych dostawców w sieci, w tym swoich własnych. Powinniśmy zdefiniowaćClientklasę w tym celu. Aby stworzyć globalnie unikalną identyfikację dla klienta, używamy PKI (Public Key Infrastructure). W tym rozdziale omówimy to szczegółowo.

Klient powinien mieć możliwość wysłania pieniędzy ze swojego portfela innej znanej osobie. Podobnie klient powinien mieć możliwość przyjmowania pieniędzy od osoby trzeciej. Aby wydać pieniądze, klient utworzyłby transakcję określającą nazwę nadawcy i kwotę do zapłaty. Aby otrzymać pieniądze, klient przekaże swoją tożsamość osobie trzeciej - zasadniczo nadawcy pieniędzy. Nie przechowujemy salda pieniędzy, które klient trzyma w swoim portfelu. Podczas transakcji obliczamy rzeczywiste saldo, aby upewnić się, że saldo klienta jest wystarczające do dokonania płatności.

Aby opracować Clientclass i dla reszty kodu w projekcie będziemy musieli zaimportować wiele bibliotek Pythona. Są one wymienione poniżej -

# 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

Oprócz powyższych standardowych bibliotek, będziemy podpisywać nasze transakcje, tworzyć hash obiektów itp. W tym celu należy zaimportować następujące biblioteki -

# 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

W następnym rozdziale porozmawiajmy o klasie klienta.

Plik Client klasa generuje private i public klucze przy użyciu wbudowanego języka Python RSAalgorytm. Zainteresowany czytelnik może odnieść się dothis tutorialdo realizacji RSA. Podczas inicjalizacji obiektu tworzymy klucze prywatne i publiczne oraz przechowujemy ich wartości w zmiennej instancji.

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

Pamiętaj, że nigdy nie powinieneś zgubić swojego klucza prywatnego. W celu prowadzenia dokumentacji wygenerowany klucz prywatny można skopiować na zabezpieczoną pamięć zewnętrzną lub po prostu zapisać jego reprezentację w formacie ASCII na kartce papieru.

Wygenerowany plik publicklucz będzie używany jako tożsamość klienta. W tym celu definiujemy właściwość o nazwieidentity która zwraca reprezentację klucza publicznego w formacie HEX.

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

Plik identityjest unikalny dla każdego klienta i może być publicznie dostępny. Korzystając z tego, każdy mógłby wysłać do Ciebie wirtualną walutęidentity i zostanie dodany do Twojego portfela.

Pełny kod pliku Client klasa jest pokazana tutaj -

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')

Klient testowy

Teraz napiszemy kod, który zilustruje, jak używać Client klasa -

Dinesh = Client()
print (Dinesh.identity)

Powyższy kod tworzy instancję Client i przypisuje go do zmiennej Dinesh. Drukujemy klucz publicznyDinesh dzwoniąc do jego identitymetoda. Wyjście jest pokazane tutaj -

30819f300d06092a864886f70d010101050003818d0030818902818100b547fafceeb131e07
0166a6b23fec473cce22c3f55c35ce535b31d4c74754fecd820aa94c1166643a49ea5f49f72
3181ff943eb3fdc5b2cb2db12d21c06c880ccf493e14dd3e93f3a9e175325790004954c34d3
c7bc2ccc9f0eb5332014937f9e49bca9b7856d351a553d9812367dc8f2ac734992a4e6a6ff6
6f347bd411d07f0203010001

Teraz przejdźmy do tworzenia transakcji w następnym rozdziale.

W tym rozdziale stwórzmy plik Transactionclass, aby klient mógł wysłać komuś pieniądze. Pamiętaj, że klient może być zarówno nadawcą, jak i odbiorcą pieniędzy. Gdy chcesz otrzymać pieniądze, inny nadawca utworzy transakcję i określi Twójpublicadres w nim. Definiujemy inicjalizację klasy transakcji w następujący sposób -

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

Plik init metoda przyjmuje trzy parametry - nadawcy public klucz odbiorcy publicklucz i kwotę do wysłania. Są one przechowywane w zmiennych instancji do wykorzystania przez inne metody. Dodatkowo tworzymy jeszcze jedną zmienną do przechowywania czasu transakcji.

Następnie piszemy metodę narzędzia o nazwie to_dictktóry łączy wszystkie cztery wyżej wymienione zmienne instancji w obiekcie słownika. Ma to na celu umieszczenie całej informacji o transakcji dostępnej za pośrednictwem jednej zmiennej.

Jak wiesz z wcześniejszego samouczka, pierwszy blok w łańcuchu bloków to plik Genesisblok. Blok Genesis zawiera pierwszą transakcję zainicjowaną przez twórcę łańcucha bloków. Tożsamość tej osoby może być utrzymywana w tajemnicy, jak w przypadku Bitcoinów. Więc kiedy ta pierwsza transakcja zostanie utworzona, twórca może po prostu wysłać swoją tożsamość jakoGenesis. Dlatego podczas tworzenia słownika sprawdzamy, czy jest to nadawcaGenesisa jeśli tak, po prostu przypisujemy wartość ciągu do zmiennej tożsamości; w przeciwnym razie przypisujemy tożsamość nadawcy do plikuidentity zmienna.

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

Słownik konstruujemy używając następującego wiersza kodu

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

Cały kod dla to_dict metoda jest pokazana poniżej -

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})

Na koniec podpiszemy ten obiekt słownika kluczem prywatnym nadawcy. Tak jak poprzednio używamy wbudowanego PKI z algorytmem SHA. Wygenerowany podpis jest dekodowany, aby uzyskać reprezentację ASCII do wydrukowania i przechowywania w naszym łańcuchu bloków. Pliksign_transaction kod metody jest pokazany tutaj -

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')

Teraz to przetestujemy Transaction klasa.

Testowanie klasy transakcji

W tym celu utworzymy dwóch użytkowników tzw Dinesh i Ramesh. Dinesh wyśle ​​5 TPCoinów do Ramesha. W tym celu najpierw tworzymy klientów o nazwie Dinesh and Ramesh.

Dinesh = Client()
Ramesh = Client()

Pamiętaj, że podczas tworzenia wystąpienia pliku Client klasa, plik public andzostaną utworzone klucze prywatne unikalne dla klienta. Ponieważ Dinesh wysyła płatność do Ramesha, będzie potrzebował klucza publicznego Ramesha, który jest uzyskiwany przy użyciu właściwości tożsamości klienta.

W ten sposób utworzymy instancję transakcji za pomocą następującego kodu -

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

Zwróć uwagę, że pierwszym parametrem jest nadawca, drugim jest klucz publiczny odbiorcy, a trzecim parametrem jest kwota do przesłania. Pliksign_transaction metoda pobiera klucz prywatny nadawcy z pierwszego parametru do podpisywania transakcji.

Po utworzeniu obiektu transakcji podpiszesz go, wywołując jego sign_transactionmetoda. Ta metoda zwraca wygenerowany podpis w formacie do druku. Generujemy i drukujemy podpis za pomocą następujących dwóch wierszy kodu -

signature = t.sign_transaction()
print (signature)

Po uruchomieniu powyższego kodu zobaczysz dane wyjściowe podobne do tego -

7c7e3c97629b218e9ec6e86b01f9abd8e361fd69e7d373c38420790b655b9abe3b575e343c7
13703ca1aee781acd7157a0624db3d57d7c2f1172730ee3f45af943338157f899965856f6b0
0e34db240b62673ad5a08c8e490f880b568efbc36035cae2e748f1d802d5e8e66298be826f5
c6363dc511222fb2416036ac04eb972

Teraz, gdy nasza podstawowa infrastruktura tworzenia klienta i transakcji jest gotowa, będziemy mieć wielu klientów wykonujących wiele transakcji, tak jak w prawdziwej sytuacji.

Transakcje dokonywane przez różnych klientów są kolejkowane w systemie; górnicy odbierają transakcje z tej kolejki i dodają je do bloku. Następnie wydobędą blok, a wygrywający górnik będzie miał przywilej dodania bloku do łańcucha blokowego, a tym samym zarobi dla siebie trochę pieniędzy.

Opiszemy ten proces wydobywania później, gdy omówimy tworzenie łańcucha bloków. Zanim napiszemy kod dla wielu transakcji, dodajmy małą funkcję narzędzia do drukowania treści danej transakcji.

Wyświetlanie transakcji

Plik display_transactionfunkcja przyjmuje pojedynczy parametr typu transakcji. Obiekt słownika w odebranej transakcji jest kopiowany do tymczasowej zmiennej o nazwiedict a używając klawiszy słownika, różne wartości są drukowane na konsoli.

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 ('-----')

Następnie definiujemy kolejkę transakcji do przechowywania naszych obiektów transakcyjnych.

Kolejka transakcji

Aby utworzyć kolejkę, deklarujemy plik globalny list zmienna o nazwie transactions w następujący sposób -

transactions = []

Po prostu dodamy każdą nowo utworzoną transakcję do tej kolejki. Należy pamiętać, że dla zwięzłości nie zaimplementujemy logiki zarządzania kolejkami w tym samouczku.

Tworzenie wielu klientów

Teraz zaczniemy tworzyć transakcje. Najpierw stworzymy czterech klientów, którzy będą przesyłać sobie pieniądze za uzyskanie różnych usług lub towarów od innych.

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

W tym momencie mamy czterech klientów o nazwach Dinesh, Ramesh, Seema i Vijay. Obecnie zakładamy, że każdy z tych klientów posiada w swoich portfelach kilka TPCoinów do przeprowadzania transakcji. Tożsamość każdego z tych klientów zostanie określona przy użyciu właściwości tożsamości tych obiektów.

Tworzenie pierwszej transakcji

Teraz rozpoczynamy naszą pierwszą transakcję w następujący sposób -

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

W tej transakcji Dinesh wysyła 5 TPCoinów do Ramesha. Aby transakcja zakończyła się sukcesem, będziemy musieli upewnić się, że Dinesh ma w portfelu wystarczającą ilość pieniędzy na tę płatność. Zauważ, że do uruchomienia obiegu TPCoin w systemie będziemy potrzebować transakcji Genesis. W trakcie czytania napiszesz kod transakcji dla tej transakcji Genesis.

Podpiszemy tę transakcję kluczem prywatnym Dinesha i dodamy ją do kolejki transakcji w następujący sposób -

t1.sign_transaction()
transactions.append(t1)

Po pierwszej transakcji dokonanej przez Dinesh utworzymy kilka kolejnych transakcji między różnymi klientami, które utworzyliśmy powyżej.

Dodawanie większej liczby transakcji

Utworzymy teraz kilka dodatkowych transakcji, z których każda przekazuje kilka TPCoinów innej stronie. Kiedy ktoś wydaje pieniądze, nie musi sprawdzać, czy w tym portfelu znajdują się wystarczające salda. W każdym razie górnik sprawdzałby każdą transakcję pod kątem salda, które ma nadawca podczas inicjowania transakcji.

W przypadku niewystarczającego salda górnik oznaczy tę transakcję jako nieważną i nie doda jej do tego bloku.

Poniższy kod tworzy i dodaje dziewięć kolejnych transakcji do naszej kolejki.

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)

Po uruchomieniu powyższego kodu w kolejce będzie dziesięć transakcji, w których górnicy utworzą swoje bloki.

Transakcje dumpingowe

Jako menedżer blockchain możesz okresowo przeglądać zawartość kolejki transakcji. W tym celu możesz użyćdisplay_transactionfunkcja, którą opracowaliśmy wcześniej. Aby zrzucić wszystkie transakcje w kolejce, po prostu wykonaj iterację listy transakcji i dla każdej transakcji, do której się odwołuje, wywołaj plikdisplay_transaction funkcja, jak pokazano tutaj -

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

Dla rozróżnienia transakcje są oddzielone linią przerywaną. Jeśli uruchomisz powyższy kod, zobaczysz listę transakcji, jak pokazano poniżej -

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
-----
--------------

Dla zwięzłości wydrukowałem tylko kilka pierwszych transakcji na liście. W powyższym kodzie wypisujemy wszystkie transakcje zaczynające się od pierwszej transakcji z wyjątkiem transakcji genesis, która nigdy nie została dodana do tej listy. Ponieważ transakcje są okresowo dodawane do bloków, na ogół będziesz zainteresowany przeglądaniem tylko listy transakcji, które nie zostały jeszcze wydobyte. W takim przypadku musisz utworzyć odpowiedni plikfor pętla do iteracji transakcji, które nie zostały jeszcze wydobyte.

Do tej pory nauczyłeś się, jak tworzyć klientów, pozwalać im między sobą i utrzymywać kolejkę oczekujących transakcji, które mają być wydobywane. Teraz najważniejsza część tego samouczka, czyli tworzenie samego łańcucha bloków. Dowiesz się tego z następnej lekcji.

Blok składa się z różnej liczby transakcji. Dla uproszczenia w naszym przypadku założymy, że blok składa się ze stałej liczby transakcji, która w tym przypadku wynosi trzy. Ponieważ blok musi przechowywać listę tych trzech transakcji, zadeklarujemy zmienną instancji o nazwieverified_transactions w następujący sposób -

self.verified_transactions = []

Nazwaliśmy tę zmienną jako verified_transactionsaby wskazać, że tylko zweryfikowane, ważne transakcje zostaną dodane do bloku. Każdy blok zawiera również wartość skrótu poprzedniego bloku, dzięki czemu łańcuch bloków staje się niezmienny.

Aby zapisać poprzedni hash, deklarujemy zmienną instancji w następujący sposób -

self.previous_block_hash = ""

Na koniec deklarujemy jeszcze jedną zmienną o nazwie Nonce do przechowywania nonce wytworzonej przez górnika podczas procesu wydobycia.

self.Nonce = ""

Pełna definicja Block klasa jest podana poniżej -

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

Ponieważ każdy blok potrzebuje wartości hasha poprzedniego bloku, deklarujemy zmienną globalną o nazwie last_block_hash w następujący sposób -

last_block_hash = ""

Teraz stwórzmy nasz pierwszy blok w łańcuchu bloków.

Zakładamy, że twórca TPCoins początkowo rozdaje znanemu klientowi 500 TPCoins Dinesh. W tym celu najpierw tworzy instancję Dinesha -

Dinesh = Client()

Następnie tworzymy transakcję Genesis i wysyłamy 500 TPCoins na adres publiczny Dinesha.

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

Teraz tworzymy wystąpienie Block klasa i nazwij to block0.

block0 = Block()

Inicjujemy previous_block_hash i Nonce zmienne instancji do None, ponieważ jest to pierwsza transakcja przechowywana w naszym łańcuchu blokowym.

block0.previous_block_hash = None
Nonce = None

Następnie dodamy powyższą transakcję t0 do pliku verified_transactions lista utrzymywana w bloku -

block0.verified_transactions.append (t0)

W tym momencie blok jest całkowicie zainicjowany i jest gotowy do dodania do naszego łańcucha bloków. W tym celu będziemy tworzyć blockchain. Zanim dodamy blok do łańcucha bloków, haszujemy blok i przechowujemy jego wartość w zmiennej globalnej o nazwielast_block_hashktóre deklarowaliśmy wcześniej. Ta wartość zostanie wykorzystana przez następnego górnika w jego bloku.

Używamy następujących dwóch wierszy kodu do haszowania bloku i przechowywania wartości skrótu.

digest = hash (block0)
last_block_hash = digest

Na koniec tworzymy łańcuch bloków, jak zobaczymy w następnym rozdziale.

Blockchain zawiera listę bloków połączonych ze sobą. Aby przechowywać całą listę, utworzymy zmienną listy o nazwie TPCoins -

TPCoins = []

Napiszemy również metodę narzędziową o nazwie dump_blockchainza zrzucanie zawartości całego łańcucha bloków. Najpierw drukujemy długość łańcucha blokowego, aby wiedzieć, ile bloków jest obecnie obecnych w łańcuchu bloków.

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

Zauważ, że w miarę upływu czasu liczba bloków w łańcuchu bloków byłaby niezwykle wysoka do wydrukowania. Dlatego podczas drukowania zawartości łańcucha bloków być może będziesz musiał zdecydować, jaki zakres chcesz zbadać. W poniższym kodzie wydrukowaliśmy cały łańcuch bloków, ponieważ nie dodawalibyśmy zbyt wielu bloków w bieżącym demo.

Aby przejść przez łańcuch, utworzyliśmy plik for pętla w następujący sposób -

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

Każdy blok, do którego się odwołujemy, jest kopiowany do tymczasowej zmiennej o nazwie block_temp.

Drukujemy numer bloku jako nagłówek każdego bloku. Zauważ, że liczby zaczynałyby się od zera, pierwszy blok to blok genezy, który jest ponumerowany zero.

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

W każdym bloku zapisaliśmy listę trzech transakcji (z wyjątkiem bloku genezy) w zmiennej o nazwie verified_transactions. Powtarzamy tę listę w plikufor pętla i dla każdego pobranego elementu wywołujemy display_transaction funkcja wyświetlania szczegółów transakcji.

for transaction in block_temp.verified_transactions:
   display_transaction (transaction)

Cała definicja funkcji jest pokazana poniżej -

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 ('=====================================')

Zauważ, że tutaj wstawiliśmy separatory w odpowiednich miejscach w kodzie, aby oddzielić bloki i transakcje w nim zawarte.

Ponieważ stworzyliśmy teraz łańcuch bloków do przechowywania bloków, naszym następnym zadaniem jest utworzenie bloków i rozpoczęcie dodawania go do łańcucha bloków. W tym celu dodamy blok Genesis, który utworzyłeś już w poprzednim kroku.

Dodanie bloku do łańcucha bloków polega na dołączeniu utworzonego bloku do naszego TPCoins lista.

TPCoins.append (block0)

Zauważ, że w przeciwieństwie do pozostałych bloków w systemie, blok Genesis zawiera tylko jedną transakcję, która jest inicjowana przez twórcę systemu TPCoins. Teraz zrzucisz zawartość łańcucha blokowego, wywołując naszą funkcję globalnądump_blockchain -

dump_blockchain(TPCoins)

Po wykonaniu tej funkcji zobaczysz następujące dane wyjściowe -

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
-----
--------------
=====================================

W tym momencie system blockchain jest gotowy do użycia. Umożliwimy teraz zainteresowanym klientom zostanie górnikami, zapewniając im funkcję wydobywania.

Aby umożliwić wydobycie, musimy opracować funkcję wydobywczą. Funkcja wydobywania musi generować skrót dla danego ciągu wiadomości i dostarczać dowód pracy. Omówmy to w tym rozdziale.

Funkcja Message Digest

Napiszemy funkcję narzędzia o nazwie sha256 za stworzenie streszczenia danej wiadomości -

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

Plik sha256 funkcja przyjmuje message jako parametr koduje go do ASCII, generuje skrót szesnastkowy i zwraca wartość do dzwoniącego.

Funkcja górnicza

Teraz opracowujemy minefunkcja, która realizuje naszą własną strategię wyszukiwania. Naszą strategią w tym przypadku byłoby wygenerowanie skrótu na danej wiadomości, który jest poprzedzony określoną liczbą jedynek. Podana liczba jedynek jest określana jako parametrmine funkcja określona jako poziom trudności.

Na przykład, jeśli określisz poziom trudności 2, wygenerowany hash dla danej wiadomości powinien zaczynać się od dwóch jedynek - jak 11xxxxxxxx. Jeśli poziom trudności wynosi 3, wygenerowany hash powinien zaczynać się od trzech jedynek - jak 111xxxxxxxx. Biorąc pod uwagę te wymagania, teraz opracujemy funkcję wyszukiwania, jak pokazano w krokach podanych poniżej.

Krok 1

Funkcja wyszukiwania ma dwa parametry - komunikat i poziom trudności.

def mine(message, difficulty=1):

Krok 2

Poziom trudności musi być większy lub równy 1, zapewniamy to następującym stwierdzeniem assert -

assert difficulty >= 1

Krok 3

Tworzymy prefix zmienna za pomocą ustawionego poziomu trudności.

prefix = '1' * difficulty

Zwróć uwagę, że jeśli poziom trudności wynosi 2, prefiks to „11”, a jeśli poziom trudności to 3, prefiks będzie brzmiał „111” i tak dalej. Sprawdzimy, czy ten prefiks istnieje w wygenerowanym skrócie wiadomości. Aby przetrawić samą wiadomość, używamy następujących dwóch wierszy kodu -

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

Cały czas dodajemy nowy numer ido skrótu wiadomości w każdej iteracji i wygeneruj nowy skrót połączonej wiadomości. Jako dane wejściowe dosha256 funkcja zmienia się w każdej iteracji, rozszerzenie digestwartość również uległaby zmianie. Sprawdzamy, czy todigest wartość jest ustawiona powyżej prefix.

if digest.startswith(prefix):

Jeśli warunek zostanie spełniony, zakończymy for pętla i zwróć plik digest wartość dla dzwoniącego.

Cały mine kod jest pokazany tutaj -

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

Dla Twojego zrozumienia dodaliśmy rozszerzenie print instrukcja, która drukuje wartość skrótu i ​​liczbę iteracji potrzebnych do spełnienia warunku przed powrotem z funkcji.

Testowanie funkcji wyszukiwania

Aby przetestować naszą funkcję wyszukiwania, po prostu wykonaj następującą instrukcję -

mine ("test message", 2)

Po uruchomieniu powyższego kodu zobaczysz wyjście podobne do tego poniżej -

after 138 iterations found nonce:
11008a740eb2fa6bf8d55baecda42a41993ca65ce66b2d3889477e6bfad1484c

Zwróć uwagę, że wygenerowane podsumowanie zaczyna się od „11”. Jeśli zmienisz poziom trudności na 3, wygenerowane podsumowanie zacznie się od „111” i oczywiście prawdopodobnie będzie wymagało większej liczby iteracji. Jak widać, górnik o większej mocy obliczeniowej będzie mógł wcześniej wydobyć daną wiadomość. W ten sposób górnicy konkurują ze sobą o przychody.

Teraz jesteśmy gotowi, aby dodać więcej bloków do naszego łańcucha bloków. Nauczmy się tego w następnym rozdziale.

Każdy górnik pobierze transakcje z wcześniej utworzonej puli transakcji. Aby śledzić liczbę już wykopanych wiadomości, musimy utworzyć zmienną globalną -

last_transaction_index = 0

Teraz nasz pierwszy górnik doda blok do łańcucha bloków.

Dodawanie pierwszego bloku

Aby dodać nowy blok, najpierw tworzymy wystąpienie Block klasa.

block = Block()

Odbieramy 3 najlepsze transakcje z kolejki -

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

Przed dodaniem transakcji do bloku górnik zweryfikuje poprawność transakcji. Ważność transakcji jest weryfikowana poprzez testowanie pod kątem równości skrótu dostarczonego przez nadawcę z hashem wygenerowanym przez górnika przy użyciu klucza publicznego nadawcy. Ponadto górnik zweryfikuje, czy nadawca ma wystarczające saldo, aby zapłacić za bieżącą transakcję.

Dla zwięzłości nie zawarliśmy tej funkcji w samouczku. Po zweryfikowaniu transakcji dodajemy ją do plikuverified_transactions lista w block instancja.

block.verified_transactions.append (temp_transaction)

Zwiększamy indeks ostatniej transakcji, aby kolejny górnik odbierał kolejne transakcje w kolejce.

last_transaction_index += 1

Dodajemy do bloku dokładnie trzy transakcje. Gdy to zrobisz, zainicjujemy pozostałe zmienne instancji plikuBlockklasa. Najpierw dodajemy skrót ostatniego bloku.

block.previous_block_hash = last_block_hash

Następnie wydobywamy blok na poziomie trudności 2.

block.Nonce = mine (block, 2)

Zwróć uwagę, że pierwszy parametr pliku minefunkcja jest obiektem binarnym. Teraz haszujemy cały blok i tworzymy na nim skrót.

digest = hash (block)

Na koniec dodajemy utworzony blok do łańcucha bloków i ponownie inicjalizujemy zmienną globalną last_block_hash do wykorzystania w następnym bloku.

Cały kod do dodania bloku pokazano poniżej -

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

Dodawanie większej liczby bloków

Dodamy teraz dwa kolejne bloki do naszego łańcucha bloków. Kod do dodania kolejnych dwóch bloków podano poniżej -

# 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

Kiedy dodasz te dwa bloki, zobaczysz także liczbę iteracji potrzebnych do znalezienia Nonce. W tym momencie nasz łańcuch bloków składa się w całości z 4 bloków, w tym bloku Genesis.

Zrzucanie całego łańcucha blokowego

Możesz zweryfikować zawartość całego łańcucha bloków za pomocą następującej instrukcji -

dump_blockchain(TPCoins)

Zobaczysz dane wyjściowe podobne do pokazanych poniżej -

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
-----
--------------
=====================================

W tym samouczku nauczyliśmy się, jak zbudować projekt blockchain w Pythonie. Istnieje wiele obszarów, w których należy dodać dalsze funkcje do tego projektu.

Na przykład będziesz musiał napisać funkcje do zarządzania kolejką transakcji. Po wydobyciu transakcji i zaakceptowaniu wydobytego bloku przez system, nie trzeba ich już przechowywać.

Ponadto górnicy z pewnością woleliby odbierać transakcje z najwyższą opłatą. Jednocześnie będziesz musiał upewnić się, że transakcje z niską opłatą lub bez opłaty nie będą głodować na zawsze.

Będziesz musiał opracować algorytmy zarządzania kolejką. Ponadto bieżący samouczek nie zawiera kodu interfejsu klienta. Będziesz musiał go opracować zarówno dla zwykłych klientów, jak i górników. Pełnoprawny projekt blockchain miałby kilka dodatkowych linii kodu i wykracza poza zakres tego samouczka. Zainteresowany czytelnik może pobrać źródło bitcoin do dalszych badań.

Wnioski

Ten przejrzysty samouczek powinien pomóc Ci rozpocząć tworzenie własnego projektu blockchain.

Aby uzyskać pełnoprawny rozwój projektów blockchain, możesz dowiedzieć się więcej ze źródła bitcoin .

W przypadku większych projektów komercyjnych lub niekomercyjnych możesz rozważyć użycie Ethereum - gotowej do użycia platformy aplikacji blockchain .