Когда я читал статью про блокчейн на JavaScript, мне было интересно познакомиться с идеями о блокчейн-разработке, которые отличаются от тех, что мне уже известны. А как только я начал читать код, мне захотелось сопоставить его с аналогичным Python-кодом, чтобы ещё и разобраться с его отличиями от кода, написанного на JavaScript.
Цель этого материала заключается в том, чтобы выявить отличия языков. Его можно считать Python-дополнением к исходной статье.

Несмотря на то, что исходная статья появилась на свет после того, как её автор ознакомился с примером блокчейн-разработки на Python, мне хотелось написать Python-код, который как можно более точно воспроизводит JavaScript-код из статьи. Это позволит сопоставить реализацию блокчейна на разных языках.
Я, кроме того, собираюсь сделать так, чтобы моя реализация блокчейна, как и в статье про JavaScript, тоже поместилась бы в 60 строк.
Хотя я собирался повторить структуру того материала, чтобы тем же путём, что и его автор, прийти к готовому коду, я, всё же, включу сюда и кое-что своё. В частности, я предпочитаю другое определение блокчейна. Оно звучит так: «Блокчейн — это система регистрации информации, выполняемой таким способом, который усложняет или делает невозможным изменение информации, взлом системы или мошенничество с информацией».
В этом проекте мы будем использовать Python, поэтому, если он у вас не установлен — найдите дистрибутив, подходящий для вашей ОС, и установите его.
Блок — это объект, в котором имеется некая информация. Поэтому начнём работу с создания класса
Определения класса
Комментарии тоже выполняются похожим образом. В Python для оформления однострочных комментариев применяется символ
Реализацию алгоритма
В методе
Займёмся классом
Классы, представляющие собой блокчейн, похожи в обоих языках.
Для того чтобы создать первичный блок, мы просто вызываем
Преобразование числа в строку выполняется в Python с помощью функции
Похоже выглядит и метод для получения самого свежего блока. Только тут, в отличие от JavaScript-проекта, для выяснения длины цепочки блоков, вместо свойства
Для того чтобы добавить блок в блокчейн, мы просто вызываем метод
В методе, используемом для проверки блокчейна, мы пользуемся функцией
При проверке условий в Python, вместо
Реализовать алгоритм доказательства выполнения работы мы можем, начав с добавления в класс
Создадим свойство, в котором будет храниться сложность:
Отредактируем метод
Импортируем модуль и воспользуемся классом
Результаты работы этого кода должны выглядеть примерно так:
Но заработает это всё только после добавления в класс
Для настройки времени блока нам понадобится соответствующее свойство:
Посмотрим на тернарный оператор, используемый в системе настройки сложности. Если переписать JS-конструкцию с тернарным оператором на Python, то получится следующее:
Итоговый Python-код (без нормального форматирования) укладывается в обещанные 60 строк:
Надеюсь, вам понравились оба материала, и вы нашли в них что-то полезное.
Если бы вам понадобилось создать блокчейн-систему — какими инструментами вы воспользовались бы?

Цель этого материала заключается в том, чтобы выявить отличия языков. Его можно считать Python-дополнением к исходной статье.

Несмотря на то, что исходная статья появилась на свет после того, как её автор ознакомился с примером блокчейн-разработки на Python, мне хотелось написать Python-код, который как можно более точно воспроизводит JavaScript-код из статьи. Это позволит сопоставить реализацию блокчейна на разных языках.
Я, кроме того, собираюсь сделать так, чтобы моя реализация блокчейна, как и в статье про JavaScript, тоже поместилась бы в 60 строк.
О блокчейне
Хотя я собирался повторить структуру того материала, чтобы тем же путём, что и его автор, прийти к готовому коду, я, всё же, включу сюда и кое-что своё. В частности, я предпочитаю другое определение блокчейна. Оно звучит так: «Блокчейн — это система регистрации информации, выполняемой таким способом, который усложняет или делает невозможным изменение информации, взлом системы или мошенничество с информацией».
Подготовка среды разработки
В этом проекте мы будем использовать Python, поэтому, если он у вас не установлен — найдите дистрибутив, подходящий для вашей ОС, и установите его.
Создание блока
Блок — это объект, в котором имеется некая информация. Поэтому начнём работу с создания класса
Block:class Block: def __init__(self, timestamp=None, data=None): self.timestamp = timestamp or time() # В this.data должна храниться информация, вроде сведений о транзакциях. self.data = [] if data is None else data
Определения класса
Block в Python и JavaScript получились очень похожими. В Python вместо this используется self, а аналогом метода constructor является init.Комментарии тоже выполняются похожим образом. В Python для оформления однострочных комментариев применяется символ
#, а в JavaScript — конструкция //.Реализацию алгоритма
sha256 я взял из библиотеки hashlib. В JS-проекте она берётся из пакета crypto.from hashlib import sha256 class Block: def __init__(self, timestamp=None, data=None): self.timestamp = timestamp or time() self.data = [] if data is None else data self.hash = self.getHash() self.prevHash = None # Хеш предыдущего блока def getHash(self): hash = sha256() hash.update(str(self.prevHash).encode('utf-8')) hash.update(str(self.timestamp).encode('utf-8')) hash.update(str(self.data).encode('utf-8')) return hash.hexdigest()
В методе
getHash всё начинается с пустого хеша, который мы формируем с использованием данных, хранящихся в блоке. Хеш вычисляется на основе хеша предыдущего блока, отметки времени и данных, хранящихся в блоке. Всё это преобразуется в последовательности байтов с помощью метода .encode('utf-8').Блокчейн
Займёмся классом
Blockchain.class Blockchain: def __init__(self): # В этом свойстве будут содержаться все блоки. self.chain = []
Классы, представляющие собой блокчейн, похожи в обоих языках.
Для того чтобы создать первичный блок, мы просто вызываем
Block с текущей отметкой времени, для получения которой используем time(). Для этого нам нужно импортировать библиотеку time.Преобразование числа в строку выполняется в Python с помощью функции
str(), а не с помощью метода toString(), как делается в JavaScript.from time import time class Blockchain: def __init__(self): # Создаём первичный блок self.chain = [Block(str(int(time())))]
Похоже выглядит и метод для получения самого свежего блока. Только тут, в отличие от JavaScript-проекта, для выяснения длины цепочки блоков, вместо свойства
length используется функция len().def getLastBlock(self): return self.chain[len(self.chain) - 1]
Для того чтобы добавить блок в блокчейн, мы просто вызываем метод
addBlock. Код получился почти таким же, как в JS-проекте, только тут, вместо метода push(), используется метод append().def addBlock(self, block): # Так как мы добавляем новый блок, prevHash будет хешем предыдущего последнего блока. block.prevHash = self.getLastBlock().hash # Так как теперь в prevHash имеется значение, мы должны пересчитать хеш блока. block.hash = block.getHash() self.chain.append(block)
Проверка блокчейна
В методе, используемом для проверки блокчейна, мы пользуемся функцией
range(). В этом — серьёзное отличие нашего кода от кода JS-проекта. И, кроме того, так как мы в Python не пользуемся константами, тут мы просто применяем обычные переменные.При проверке условий в Python, вместо
||, используется or.def isValid(self): # Перед перебором цепочки блоков нужно установить i в 1, так как до первичного блока никаких блоков нет. В результате мы начинаем со второго блока. for i in range(1, len(self.chain)): currentBlock = self.chain[i] prevBlock = self.chain[i - 1] # Проверка if (currentBlock.hash != currentBlock.getHash() or prevBlock hash != currentBlock.prevHash): return False return True
Алгоритм доказательства выполнения работы
Реализовать алгоритм доказательства выполнения работы мы можем, начав с добавления в класс
Block метода mine() и свойства nonce. Тут стоит проявить внимательность, так как свойство nonce должно быть объявлено до вызова метода self.getHash(). В противном случае будет выдана ошибка AttributeError: 'Block' object has no attribute 'nonce'.class Block: def __init__(self, timestamp=None, data=None): self.timestamp = timestamp or time() self.data = [] if data is None else data self.prevHash = None # хеш предыдущего блока self.nonce = 0 self.hash = self.getHash() # Наша хеш-функция. def getHash(self): hash = sha256() hash.update(str(self.prevHash).encode('utf-8')) hash.update(str(self.timestamp).encode('utf-8')) hash.update(str(self.data).encode('utf-8')) hash.update(str(self.nonce).encode('utf-8')) return hash.hexdigest() def mine(self, difficulty): # Тут запускается цикл, работающий до тех пор, пока хеш не будет начинаться со строки # 0...000 длины <difficulty>. while self.hash[:difficulty] != '0' * difficulty: # Инкрементируем nonce, что позволяет получить совершенно новый хеш. self.nonce += 1 # Пересчитываем хеш блока с учётом нового значения nonce. self.hash = self.getHash()
Создадим свойство, в котором будет храниться сложность:
self.difficulty = 1
Отредактируем метод
addBlock:def addBlock(self, block): block.prevHash = self.getLastBlock().hash block.hash = block.getHash() block.mine(self.difficulty) self.chain.append(block)
Тестирование блокчейна
Импортируем модуль и воспользуемся классом
Blockchain так же, как таким же классом в JS-проекте:from blockchain import Block from blockchain import Blockchain from time import time JeChain = Blockchain() # Добавим новый блок JeChain.addBlock(Block(str(int(time())), ({"from": "John", "to": "Bob", "amount": 100}))) # (Это - всего лишь интересный эксперимент, для создания настоящей криптовалюты обычно нужно сделать намного больше, чем сделали мы). # Вывод обновлённого блокчейна print(JeChain)
Результаты работы этого кода должны выглядеть примерно так:
[ { "data": [], "timestamp": "1636153236", "nonce": 0, "hash": "4caa5f684eb3871cb0eea217a6d043896b3775f047e699d92bd29d0285541678", "prevHash": null }, { "data": { "from": "John", "to": "Bob", "amount": 100 }, "timestamp": "1636153236", "nonce": 14, "hash": "038f82c6e6605acfcad4ade04e454eaa1cfa3d17f8c2980f1ee474eefb9613e9", "prevHash": "4caa5f684eb3871cb0eea217a6d043896b3775f047e699d92bd29d0285541678" } ]
Но заработает это всё только после добавления в класс
Blockchain метода __repr__():import json def __repr__(self): return json.dumps([{'data': item.data, 'timestamp': item.timestamp, 'nonce': item.nonce, 'hash': item.hash, 'prevHash': item.prevHash} for item in self.chain], indent=4)
Дополнение: сложность и время блока
Для настройки времени блока нам понадобится соответствующее свойство:
self.blockTime = 30000
Посмотрим на тернарный оператор, используемый в системе настройки сложности. Если переписать JS-конструкцию с тернарным оператором на Python, то получится следующее:
(if_test_is_false, if_test_is_true)[test]:def addBlock(self, block): block.prevHash = self.getLastBlock().hash block.hash = block.getHash() block.mine(self.difficulty) self.chain.append(block) self.difficulty += (-1, 1)[int(time()) - int(self.getLastBlock().timestamp) < self.blockTime]
Итоговый Python-код (без нормального форматирования) укладывается в обещанные 60 строк:
# -*- coding: utf-8 -*- from hashlib import sha256 import json from time import time class Block: def __init__(self, timestamp=None, data=None): self.timestamp = timestamp or time() self.data = [] if data is None else data self.prevHash = None self.nonce = 0 self.hash = self.getHash() def getHash(self): hash = sha256() hash.update(str(self.prevHash).encode('utf-8')) hash.update(str(self.timestamp).encode('utf-8')) hash.update(str(self.data).encode('utf-8')) hash.update(str(self.nonce).encode('utf-8')) return hash.hexdigest() def mine(self, difficulty): while self.hash[:difficulty] != '0' * difficulty: self.nonce += 1 self.hash = self.getHash() class Blockchain: def __init__(self): self.chain = [Block(str(int(time())))] self.difficulty = 1 self.blockTime = 30000 def getLastBlock(self): return self.chain[len(self.chain) - 1] def addBlock(self, block): block.prevHash = self.getLastBlock().hash block.hash = block.getHash() block.mine(self.difficulty) self.chain.append(block) self.difficulty += (-1, 1)[int(time()) - int(self.getLastBlock().timestamp) < self.blockTime] def isValid(self): for i in range(1, len(self.chain)): currentBlock = self.chain[i] prevBlock = self.chain[i - 1] if (currentBlock.hash != currentBlock.getHash() or prevBlock.hash != currentBlock.prevHash): return False return True def __repr__(self): return json.dumps([{'data': item.data, 'timestamp': item.timestamp, 'nonce': item.nonce, 'hash': item.hash, 'prevHash': item.prevHash} for item in self.chain], indent=4)
Надеюсь, вам понравились оба материала, и вы нашли в них что-то полезное.
Если бы вам понадобилось создать блокчейн-систему — какими инструментами вы воспользовались бы?

