Учебники

Python Blockchain — Краткое руководство

Python Blockchain — Введение

В уроке по Blockchain мы подробно узнали о теории блокчейна. Блокчейн является фундаментальным строительным блоком самой популярной в мире цифровой валюты Биткойн. В этом учебнике подробно рассматриваются тонкости Биткойна, полностью объясняющие архитектуру блокчейна. Следующим шагом является создание нашей собственной цепочки блоков.

Сатоши Накамото создал первую в мире виртуальную валюту под названием Биткойн. Глядя на успех Биткойн, многие другие создали свои собственные виртуальные валюты. Чтобы назвать несколько — Litecoin, Zcash и так далее.

Теперь вы также можете запустить свою собственную валюту. Давайте назовем это как TPCoin (TutorialsPoint Coin). Вы напишете блокчейн для записи всех транзакций, связанных с TPCoin. TPCoin можно использовать для покупки пицц, гамбургеров, салатов и т. Д. Могут быть и другие поставщики услуг, которые присоединятся к вашей сети и начнут принимать TPCoin в качестве валюты для предоставления своих услуг. Возможности безграничны.

В этом уроке мы расскажем, как создать такую ​​систему и запустить свою собственную цифровую валюту на рынке.

Компоненты, участвующие в разработке проекта Blockchain

Вся разработка проекта блокчейна состоит из трех основных компонентов —

  • клиент
  • Шахтеры
  • Blockchain

клиент

Клиент — это тот, кто будет покупать товары у других поставщиков. Клиент сам может стать продавцом и принимать деньги от других за товары, которые он поставляет. Здесь мы предполагаем, что клиент может быть как поставщиком, так и получателем TPCoins. Таким образом, мы создадим клиентский класс в нашем коде, который будет иметь возможность отправлять и получать деньги.

шахтер

Майнер — это тот, кто выбирает транзакции из пула транзакций и собирает их в блок. Майнер должен предоставить действительное доказательство работы, чтобы получить награду за майнинг. Все деньги, которые майнер собирает в качестве вознаграждения, будут для него оставлены. Он может потратить эти деньги на покупку товаров или услуг у других зарегистрированных поставщиков в сети, так же, как это делает клиент, описанный выше.

Blockchain

Наконец, блокчейн — это структура данных, которая объединяет все добытые блоки в хронологическом порядке. Эта цепь неизменна и, следовательно, устойчива к темпераменту.

Вы можете следовать этому руководству, напечатав код, представленный на каждом шаге, в новой записной книжке Jupyter. Кроме того, вы можете скачать весь блокнот Jupyter с www.anaconda.com .

В следующей главе мы разработаем клиент, который использует нашу систему блокчейнов.

Python Blockchain — развивающийся клиент

Клиент — это кто-то, кто держит TPCoins и передает их за товары / услуги от других поставщиков в сети, включая его собственную. Мы должны определить класс Client для этой цели. Чтобы создать глобально уникальную идентификацию для клиента, мы используем PKI (Инфраструктура открытых ключей). В этой главе давайте поговорим об этом подробнее.

Клиент должен иметь возможность отправлять деньги из своего кошелька другому известному человеку. Точно так же клиент должен иметь возможность принимать деньги от третьей стороны. Чтобы потратить деньги, клиент должен создать транзакцию, указав имя отправителя и сумму, подлежащую выплате. Для получения денег клиент предоставит свою личность третьему лицу — по существу, отправителю денег. Мы не храним остаток суммы, которую клиент держит в своем кошельке. Во время транзакции мы рассчитаем фактический баланс, чтобы у клиента был достаточный баланс для осуществления платежа.

Для разработки класса Client и для остальной части кода в проекте нам потребуется импортировать много библиотек Python. Они перечислены ниже —

# 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

В дополнение к указанным выше стандартным библиотекам мы собираемся подписать наши транзакции, создать хэш объектов и т. Д. Для этого вам потребуется импортировать следующие библиотеки:

# 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

В следующей главе давайте поговорим о классе клиента.

Python Blockchain — клиентский класс

Класс Client генерирует закрытый и открытый ключи с помощью встроенного алгоритма Python RSA . Заинтересованный читатель может обратиться к этому учебнику для реализации RSA. Во время инициализации объекта мы создаем закрытые и открытые ключи и сохраняем их значения в переменной экземпляра.

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

Обратите внимание, что вы никогда не должны терять свой закрытый ключ. Для ведения учета сгенерированный закрытый ключ может быть скопирован на защищенное внешнее хранилище, или вы можете просто записать его ASCII-представление на листе бумаги.

Сгенерированный открытый ключ будет использоваться как личность клиента. Для этого мы определяем свойство с именем identity, которое возвращает HEX-представление открытого ключа.

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

Идентификационные данные уникальны для каждого клиента и могут быть общедоступными. Любой сможет отправить вам виртуальную валюту, используя эту личность, и она будет добавлена ​​в ваш кошелек.

Полный код для класса Client показан здесь —

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

Dinesh = Client()
print (Dinesh.identity)

Приведенный выше код создает экземпляр Client и присваивает его переменной Dinesh . Мы печатаем открытый ключ Dinesh , вызывая его метод идентификации . Вывод показан здесь —

30819f300d06092a864886f70d010101050003818d0030818902818100b547fafceeb131e07
0166a6b23fec473cce22c3f55c35ce535b31d4c74754fecd820aa94c1166643a49ea5f49f72
3181ff943eb3fdc5b2cb2db12d21c06c880ccf493e14dd3e93f3a9e175325790004954c34d3
c7bc2ccc9f0eb5332014937f9e49bca9b7856d351a553d9812367dc8f2ac734992a4e6a6ff6
6f347bd411d07f0203010001

Теперь давайте перейдем к созданию транзакции в следующей главе.

Python Blockchain — класс транзакций

В этой главе давайте создадим класс Transaction, чтобы клиент мог кому-то отправлять деньги. Обратите внимание, что клиент может быть как отправителем, так и получателем денег. Когда вы хотите получить деньги, другой отправитель создаст транзакцию и укажет в ней ваш публичный адрес. Мы определяем инициализацию класса транзакции следующим образом:

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

Метод init принимает три параметра — открытый ключ отправителя, открытый ключ получателя и отправляемую сумму. Они хранятся в переменных экземпляра для использования другими методами. Кроме того, мы создаем еще одну переменную для хранения времени транзакции.

Затем мы напишем служебный метод to_dict, который объединяет все четыре вышеупомянутые переменные экземпляра в объекте словаря. Это просто для того, чтобы вся информация о транзакции была доступна через одну переменную.

Как вы знаете из предыдущего урока, первый блок в блокчейне — это блок Genesis . Блок Genesis содержит первую транзакцию, инициированную создателем блокчейна. Личность этого человека может храниться в секрете, как в случае с биткойнами. Поэтому, когда создается эта первая транзакция, создатель может просто отправить свою личность как Genesis . Таким образом, при создании словаря мы проверяем, является ли отправитель Genesis, и если это так, мы просто присваиваем некоторое строковое значение переменной-идентификатору; иначе мы присваиваем идентификатор отправителя переменной идентификатора .

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

Весь код для метода to_dict показан ниже —

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

Наконец, мы подпишем этот объект словаря, используя закрытый ключ отправителя. Как и прежде, мы используем встроенную PKI с алгоритмом SHA. Сгенерированная подпись декодируется, чтобы получить представление ASCII для печати и сохранения ее в нашей цепочке блоков. Код метода sign_transaction показан здесь —

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

Теперь мы проверим этот класс транзакций .

Класс тестовой транзакции

Для этого мы создадим двух пользователей, называемых Динеш и Рамеш . Динеш отправит 5 TPCoins Рамешу. Для этого сначала мы создаем клиентов с именами Dinesh и Ramesh.

Dinesh = Client()
Ramesh = Client()

Помните, что при создании экземпляра класса Client будут созданы открытый и закрытый ключи, уникальные для клиента. Поскольку Динеш отправляет платеж Рамешу, ему понадобится открытый ключ Рамеша, полученный с помощью свойства идентификатора клиента.

Таким образом, мы создадим экземпляр транзакции, используя следующий код —

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

Обратите внимание, что первым параметром является отправитель, вторым параметром является открытый ключ получателя, а третьим параметром является сумма, подлежащая передаче. Метод sign_transaction извлекает закрытый ключ отправителя из первого параметра для подписания транзакции.

После создания объекта транзакции вы подпишите его, вызвав его метод sign_transaction . Этот метод возвращает сгенерированную подпись в формате для печати. Мы генерируем и печатаем подпись, используя следующие две строки кода:

signature = t.sign_transaction()
print (signature) 

Когда вы запустите приведенный выше код, вы увидите вывод, похожий на этот —

7c7e3c97629b218e9ec6e86b01f9abd8e361fd69e7d373c38420790b655b9abe3b575e343c7
13703ca1aee781acd7157a0624db3d57d7c2f1172730ee3f45af943338157f899965856f6b0
0e34db240b62673ad5a08c8e490f880b568efbc36035cae2e748f1d802d5e8e66298be826f5
c6363dc511222fb2416036ac04eb972

Теперь, когда наша базовая инфраструктура создания клиента и транзакции готова, у нас теперь будет несколько клиентов, выполняющих несколько транзакций, как в реальной жизни.

Создание нескольких транзакций

Сделки, совершенные различными клиентами, ставятся в очередь в системе; майнеры забирают транзакции из этой очереди и добавляют их в блок. Затем они будут добывать блок, и победивший майнер получит привилегию добавить блок в цепочку блоков и тем самым заработать немного денег для себя.

Мы опишем этот процесс майнинга позже, когда будем обсуждать создание блокчейна. Прежде чем писать код для нескольких транзакций, добавим небольшую служебную функцию для печати содержимого данной транзакции.

Просмотр транзакции

Функция display_transaction принимает один параметр типа транзакции. Объект словаря в полученной транзакции копируется во временную переменную с именем dict, и с помощью ключей словаря различные значения выводятся на консоль.

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

Далее мы определяем очередь транзакций для хранения наших объектов транзакций.

Очередь транзакций

Чтобы создать очередь, мы объявляем переменную глобального списка под названием транзакции следующим образом:

transactions = []

Мы просто добавим каждую вновь созданную транзакцию в эту очередь. Обратите внимание, что для краткости мы не будем реализовывать логику управления очередями в этом руководстве.

Создание нескольких клиентов

Теперь мы начнем создавать транзакции. Сначала мы создадим четырех клиентов, которые будут отправлять друг другу деньги для получения различных услуг или товаров от других.

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

На данный момент у нас есть четыре клиента: Динеш, Рамеш, Сима и Виджай. В настоящее время мы предполагаем, что у каждого из этих клиентов есть несколько TPCoin в их кошельках для проведения транзакций. Идентификационные данные каждого из этих клиентов будут определены с помощью свойства identity этих объектов.

Создание первой транзакции

Теперь мы начинаем нашу первую транзакцию следующим образом:

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

В этой транзакции Динеш отправляет 5 TPCoins Рамешу. Чтобы транзакция была успешной, мы должны убедиться, что у Динеша в кошельке достаточно денег для этого платежа. Обратите внимание, что нам потребуется транзакция генезиса, чтобы запустить циркуляцию TPCoin в системе. Вы напишете код транзакции для этой генезисной транзакции очень скоро, когда будете читать дальше.

Мы подпишем эту транзакцию, используя закрытый ключ Динеша, и добавим ее в очередь транзакций следующим образом:

t1.sign_transaction()
transactions.append(t1)

После первой транзакции, сделанной Dinesh, мы создадим еще несколько транзакций между различными клиентами, которые мы создали выше.

Добавление дополнительных транзакций

Теперь мы создадим еще несколько транзакций, каждая транзакция выдаст несколько TPCoins другой стороне. Когда кто-то тратит деньги, нет необходимости, чтобы он проверял наличие достаточных остатков в этом кошельке. В любом случае майнер будет проверять каждую транзакцию на баланс, который имеет отправитель при инициации транзакции.

В случае недостаточного баланса майнер пометит эту транзакцию как недействительную и не добавит ее в этот блок.

Следующий код создает и добавляет еще девять транзакций в нашу очередь.

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)

Когда вы запустите приведенный выше код, у вас будет десять транзакций в очереди, чтобы майнеры могли создавать свои блоки.

Демпинг транзакций

Как менеджер блокчейна, вы можете периодически просматривать содержимое очереди транзакций. Для этой цели вы можете использовать функцию display_transaction, которую мы разработали ранее. Чтобы сбросить все транзакции в очереди, просто выполните итерацию списка транзакций и для каждой указанной транзакции вызовите функцию display_transaction, как показано здесь:

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

Транзакции разделены пунктирной линией для различия. Если вы запустите приведенный выше код, вы увидите список транзакций, как показано ниже —

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

Для краткости я напечатал только первые несколько транзакций в списке. В приведенном выше коде мы печатаем все транзакции, начиная с самой первой транзакции, за исключением транзакции генезиса, которая никогда не была добавлена ​​в этот список. Поскольку транзакции периодически добавляются в блоки, вам, как правило, будет интересно просматривать только список транзакций, которые еще предстоит отработать. В этом случае вам нужно будет создать соответствующий цикл for для итераций по транзакциям, которые еще не добыты.

До сих пор вы узнали, как создавать клиентов, разрешать их между собой и поддерживать очередь ожидающих транзакций, которые должны быть добыты. Теперь самая важная часть этого урока — это создание самой блокчейна. Вы узнаете это на следующем уроке.

Python Blockchain — класс блоков

Блок состоит из различного количества транзакций. Для простоты в нашем случае мы будем предполагать, что блок состоит из фиксированного числа транзакций, которое в данном случае равно трем. Поскольку блок должен хранить список этих трех транзакций, мы объявим переменную экземпляра под названием verify_transactions следующим образом:

self.verified_transactions = []

Мы назвали эту переменную как Verified_transactions, чтобы указать, что только проверенные действительные транзакции будут добавлены в блок. Каждый блок также содержит хэш-значение предыдущего блока, поэтому цепочка блоков становится неизменной.

Чтобы сохранить предыдущий хеш, мы объявляем переменную экземпляра следующим образом:

self.previous_block_hash = ""

Наконец, мы объявляем еще одну переменную Nonce для хранения одноразовых номеров, созданных майнером в процессе майнинга.

self.Nonce = ""

Полное определение класса Block приведено ниже —

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

Поскольку каждому блоку требуется значение хеша предыдущего блока, мы объявляем глобальную переменную с именем last_block_hash следующим образом:

last_block_hash = ""

Теперь давайте создадим наш первый блок в блокчейне.

Python Blockchain — Создание блока Genesis

Мы предполагаем, что создатель TPCoins изначально выдает 500 TPCoins известному клиенту Dinesh . Для этого он сначала создает экземпляр Dinesh —

Dinesh = Client()

Затем мы создаем транзакцию генезиса и отправляем 500 TPCoins на публичный адрес Динеша.

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

Теперь мы создаем экземпляр класса Block и называем его block0 .

block0 = Block()

Мы инициализируем переменные экземпляра previous_block_hash и Nonce как None , поскольку это самая первая транзакция, которая будет сохранена в нашей цепочке блоков.

block0.previous_block_hash = None
Nonce = None

Далее мы добавим вышеупомянутую транзакцию t0 в список Verified_transactions, поддерживаемый в блоке —

block0.verified_transactions.append (t0)

На этом этапе блок полностью инициализирован и готов к добавлению в нашу цепочку блоков. Мы будем создавать блокчейн для этой цели. Перед тем, как добавить блок в цепочку блоков, мы хешируем блок и сохраняем его значение в глобальной переменной last_block_hash, которую мы объявили ранее. Это значение будет использовано следующим майнером в его блоке.

Мы используем следующие две строки кода для хеширования блока и сохранения значения дайджеста.

digest = hash (block0)
last_block_hash = digest

Наконец, мы создаем блокчейн, как мы увидим в следующей главе.

Python Создание блокчейна

Блокчейн содержит список блоков, связанных друг с другом. Чтобы сохранить весь список, мы создадим переменную списка с именем TPCoins —

TPCoins = []

Мы также напишем служебный метод dump_blockchain для выгрузки содержимого всей цепочки блоков. Сначала мы печатаем длину блокчейна, чтобы узнать, сколько блоков в настоящий момент присутствует в блокчейне.

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

Обратите внимание, что с течением времени количество блоков в цепочке блоков будет чрезвычайно большим для печати. Таким образом, когда вы печатаете содержимое блокчейна, вам может потребоваться выбрать диапазон, который вы хотели бы изучить. В приведенном ниже коде мы напечатали весь блокчейн, так как мы не добавляем слишком много блоков в текущей демонстрации.

Чтобы перебрать цепочку, мы настроили цикл for следующим образом:

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

Каждый указанный блок копируется во временную переменную с именем block_temp .

Мы печатаем номер блока как заголовок для каждого блока. Обратите внимание, что числа будут начинаться с нуля, первый блок — это блок генеза, который нумеруется с нуля.

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

В каждом блоке мы сохранили список из трех транзакций (за исключением блока генезиса) в переменной с именем Verified_transactions . Мы повторяем этот список в цикле for, и для каждого полученного элемента мы вызываем функцию display_transaction, чтобы отобразить детали транзакции.

for transaction in block_temp.verified_transactions:
   display_transaction (transaction)

Полное определение функции показано ниже —

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

Обратите внимание, что здесь мы вставили разделители в соответствующих точках кода, чтобы разграничить блоки и транзакции внутри него.

Так как мы создали блокчейн для хранения блоков, наша следующая задача — создать блоки и начать добавлять их в блокчейн. Для этого мы добавим блок генезиса, который вы уже создали на предыдущем шаге.

Python Blockchain — Добавление блока Genesis

Добавление блока в цепочку блоков включает добавление созданного блока в наш список TPCoins .

TPCoins.append (block0)

Обратите внимание, что в отличие от остальных блоков в системе, блок генезиса содержит только одну транзакцию, инициированную инициатором системы TPCoins. Теперь вы сбросите содержимое блокчейна, вызвав нашу глобальную функцию dump_blockchain

dump_blockchain(TPCoins)

Когда вы выполните эту функцию, вы увидите следующий вывод:

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

На данный момент система блокчейна готова к использованию. Теперь мы дадим возможность заинтересованным клиентам стать майнерами, предоставив им функциональность майнинга.

Python Blockchain — Создание майнеров

Для включения майнинга нам нужно разработать функцию майнинга. Функциональность майнинга должна генерировать дайджест по заданной строке сообщения и предоставлять подтверждение работы. Давайте обсудим это в этой главе.

Функция дайджеста сообщения

Мы напишем служебную функцию sha256 для создания дайджеста по данному сообщению —

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

Функция sha256 принимает сообщение в качестве параметра, кодирует его в ASCII, генерирует шестнадцатеричный дайджест и возвращает значение вызывающей стороне.

Функция майнинга

Сейчас мы разрабатываем функцию шахты, которая реализует нашу собственную стратегию майнинга. В этом случае наша стратегия заключается в создании хэша для данного сообщения с префиксом с указанным числом 1. Заданное количество единиц указано в качестве параметра для моей функции, указанной в качестве уровня сложности.

Например, если вы укажете уровень сложности 2, сгенерированный хэш для данного сообщения должен начинаться с двух 1 — например, 11xxxxxxxx. Если уровень сложности равен 3, сгенерированный хэш должен начинаться с трех 1 — как 111xxxxxxxx. Учитывая эти требования, мы теперь разработаем функцию майнинга, как показано в шагах, приведенных ниже.

Шаг 1

Функция майнинга принимает два параметра — сообщение и уровень сложности.

def mine(message, difficulty=1):

Шаг 2

Уровень сложности должен быть больше или равен 1, мы обеспечиваем это следующим утверждением assert:

assert difficulty >= 1

Шаг 3

Мы создаем префиксную переменную, используя заданный уровень сложности.

prefix = '1' * difficulty

Обратите внимание, что если уровень сложности равен 2, префикс будет «11», а если уровень сложности равен 3, префикс будет «111» и т. Д. Мы проверим, существует ли этот префикс в сгенерированном дайджесте сообщения. Чтобы переварить само сообщение, мы используем следующие две строки кода:

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

Мы продолжаем добавлять новый номер i в хэш сообщения в каждой итерации и генерируем новый дайджест по объединенному сообщению. Поскольку входные данные для функции sha256 меняются на каждой итерации, значение дайджеста также изменяется. Мы проверяем, имеет ли это значение дайджеста установленный префикс .

if digest.startswith(prefix):

Если условие выполнено, мы завершаем цикл for и возвращаем вызывающему значение дайджеста .

Весь мой код показан здесь —

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

Для вашего понимания мы добавили оператор print, который печатает дайджест-значение и количество итераций, необходимых для выполнения условия перед возвратом из функции.

Тестирование функции майнинга

Чтобы проверить нашу функцию майнинга, просто выполните следующее утверждение —

mine ("test message", 2)

Когда вы запустите приведенный выше код, вы увидите вывод, похожий на приведенный ниже —

after 138 iterations found nonce:
11008a740eb2fa6bf8d55baecda42a41993ca65ce66b2d3889477e6bfad1484c

Обратите внимание, что сгенерированный дайджест начинается с «11». Если вы измените уровень сложности на 3, сгенерированный дайджест начнется с «111», и, конечно, для этого потребуется больше итераций. Как видите, майнер с большей вычислительной мощностью сможет добывать данное сообщение раньше. Вот как шахтеры конкурируют друг с другом за получение своих доходов.

Теперь мы готовы добавить больше блоков в нашу цепочку блоков. Давайте узнаем это в нашей следующей главе.

Python Blockchain — Добавление блоков

Каждый майнер забирает транзакции из ранее созданного пула транзакций. Чтобы отслеживать количество уже обработанных сообщений, мы должны создать глобальную переменную —

last_transaction_index = 0

Теперь у нас будет наш первый майнер, добавляющий блок в блокчейн.

Добавление первого блока

Чтобы добавить новый блок, мы сначала создаем экземпляр класса Block .

block = Block()

Мы забираем первые 3 транзакции из очереди —

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

Перед добавлением транзакции в блок майнер проверит правильность транзакции. Достоверность транзакции проверяется путем проверки на равенство хеша, предоставленного отправителем, хешу, сгенерированному майнером с использованием открытого ключа отправителя. Также майнер проверит, достаточно ли у отправителя баланса для оплаты текущей транзакции.

Для краткости мы не включили эту функцию в учебник. После проверки транзакции мы добавляем ее в список Verified_transactions в экземпляре блока .

block.verified_transactions.append (temp_transaction)

Мы увеличиваем индекс последней транзакции, чтобы следующий майнер подобрал последующие транзакции в очереди.

last_transaction_index += 1

Мы добавляем ровно три транзакции в блок. Как только это будет сделано, мы инициализируем остальные переменные экземпляра класса Block . Сначала добавим хеш последнего блока.

block.previous_block_hash = last_block_hash

Далее мы добываем блок с уровнем сложности 2.

block.Nonce = mine (block, 2)

Обратите внимание, что первым параметром функции mine является двоичный объект. Теперь мы хэшируем весь блок и создаем для него дайджест.

digest = hash (block)

Наконец, мы добавляем созданный блок в цепочку блоков и повторно инициализируем глобальную переменную last_block_hash для использования в следующем блоке.

Весь код для добавления блока показан ниже —

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

Когда вы добавите эти два блока, вы также увидите количество итераций, которое потребовалось, чтобы найти Nonce. На данный момент наш блокчейн состоит из 4 блоков, включая блок генезиса.

Сбрасывает весь блокчейн

Вы можете проверить содержимое всей цепочки блоков, используя следующую инструкцию:

dump_blockchain(TPCoins)

Вы увидите результат, аналогичный показанному ниже —

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

Python Blockchain — область применения и заключение

В этом уроке мы узнали, как построить проект блокчейна в Python. Есть много областей, где вам нужно добавить дополнительную функциональность в этот проект.

Например, вам нужно написать функции для управления очередью транзакций. После того, как транзакции будут добыты и принятый блок принят системой, их больше не нужно хранить.

Также майнеры наверняка предпочтут забрать транзакции с самой высокой комиссией. В то же время вы должны быть уверены, что транзакции с низкой комиссией или без комиссии не будут голодать вечно.

Вам нужно будет разработать алгоритмы управления очередью. Кроме того, текущий учебник не включает код интерфейса клиента. Вам нужно будет разработать это как для обычных клиентов, так и для майнеров. Полноценный проект блокчейна будет содержать еще несколько строк кода и выходит за рамки этого руководства. Заинтересованный читатель может скачать источник биткойнов для дальнейшего изучения.

Выводы

Этот четкий урок должен помочь вам создать собственный проект блокчейна.

Для разработки полноценного блокчейна вы можете узнать больше из источника биткойнов .

Для более крупных коммерческих или некоммерческих проектов вы можете рассмотреть возможность использования Ethereum — готовой платформы приложений для блокчейна.