Блокчейн Python - Краткое руководство

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

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

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

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

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

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

  • Client
  • Miners
  • Blockchain

Клиент

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

Шахтер

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

Блокчейн

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

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

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

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

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

Для развития Clientclass, а для остальной части кода в проекте нам потребуется импортировать множество библиотек 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

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

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

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

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

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

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

В identityуникальна для каждого клиента и может быть сделана общедоступной. Кто угодно сможет отправить вам виртуальную валюту с помощью этогоidentity и он будет добавлен в ваш кошелек.

Полный код для 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 назвав его identityметод. Результат показан здесь -

30819f300d06092a864886f70d010101050003818d0030818902818100b547fafceeb131e07
0166a6b23fec473cce22c3f55c35ce535b31d4c74754fecd820aa94c1166643a49ea5f49f72
3181ff943eb3fdc5b2cb2db12d21c06c880ccf493e14dd3e93f3a9e175325790004954c34d3
c7bc2ccc9f0eb5332014937f9e49bca9b7856d351a553d9812367dc8f2ac734992a4e6a6ff6
6f347bd411d07f0203010001

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

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

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

В init метод принимает три параметра - отправителя public ключ, получатель publicключ и сумму, которую нужно отправить. Они хранятся в переменных экземпляра для использования другими методами. Дополнительно мы создаем еще одну переменную для хранения времени транзакции.

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

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

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

Теперь мы проверим это Transaction класс.

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

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

Dinesh = Client()
Ramesh = Client()

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

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

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

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

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

Чтобы создать очередь, мы объявляем глобальный list переменная называется transactions следующим образом -

transactions = []

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

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

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

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

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

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

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

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

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

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

t1.sign_transaction()
transactions.append(t1)

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

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

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

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

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

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 цикл для перебора транзакций, которые еще не были добыты.

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

Блок состоит из различного количества транзакций. Для простоты в нашем случае мы будем предполагать, что блок состоит из фиксированного количества транзакций, которое в данном случае равно трем. Поскольку блоку необходимо хранить список этих трех транзакций, мы объявим переменную экземпляра с именемverified_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 = ""

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

Мы предполагаем, что создатель 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

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

Блокчейн содержит список связанных друг с другом блоков. Чтобы сохранить весь список, мы создадим переменную списка под названием 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 ('=====================================')

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

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

Добавление блока в цепочку блоков включает добавление созданного блока к нашему 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
-----
--------------
=====================================

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

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

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

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

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

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

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

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

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

Шаг 1

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

def mine(message, difficulty=1):

Шаг 2

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

assert difficulty >= 1

Шаг 3

Мы создаем prefix переменная с использованием установленного уровня сложности.

prefix = '1' * difficulty

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

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

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

if digest.startswith(prefix):

Если условие выполнено, мы прекратим for цикл и верните digest ценность для вызывающего абонента.

Целиком mine код показан здесь -

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», и, конечно же, вероятно, потребуется большее количество итераций. Как видите, майнер с большей вычислительной мощностью сможет добыть данное сообщение раньше. Вот так майнеры соревнуются друг с другом за прибыль.

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

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

last_transaction_index = 0

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

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

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

block = Block()

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

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

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

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

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. Есть много областей, в которых вам нужно добавить дополнительные функции в этот проект.

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

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

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

Выводы

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

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

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