Перейти к основному содержанию

Введение в Ethereum для разработчиков на Python, часть 1

Python
web3.py
Beginner
Marc Garreau
8 сентября 2020 г.
11 минута прочтения

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

Рекомендуемые предварительные требования

Эта статья призвана быть доступной для широкого круга разработчиков. В статье будут использоваться инструменты Python, но они являются лишь средством для демонстрации идей. Ничего страшного, если вы не являетесь Python-разработчиком. Однако я сделаю несколько предположений о том, что вы уже знаете, чтобы мы могли быстрее перейти к особенностям Ethereum.

Предположения:

  • Вы умеете работать в терминале,
  • Вы написали несколько строк кода на Python,
  • На вашем компьютере установлен Python версии 3.6 или выше (настоятельно рекомендуется использовать виртуальную средуopens in a new tab), и
  • вы использовали pip, установщик пакетов Python. Опять же, если что-то из этого неверно или вы не планируете воспроизводить код из этой статьи, вы, скорее всего, все равно сможете без проблем следить за изложением.

Коротко о блокчейнах

Существует много способов описать Ethereum, но в его основе лежит блокчейн. Блокчейны состоят из серии блоков, так что давайте с этого и начнем. Проще говоря, каждый блок в блокчейне Ethereum — это просто набор метаданных и список транзакций. В формате JSON это выглядит примерно так:

1{
2 "number": 1234567,
3 "hash": "0xabc123...",
4 "parentHash": "0xdef456...",
5 ...,
6 "transactions": [...]
7}

Каждый блок содержит ссылку на предшествующий ему блок; parentHash — это просто хэш предыдущего блока.

Примечание. Ethereum регулярно использует хэш-функции для получения значений фиксированного размера («хэшей»). Хэши играют важную роль в Ethereum, но пока вы можете спокойно считать их уникальными идентификаторами.

Диаграмма, изображающая блокчейн, включая данные внутри каждого блока

По сути, блокчейн — это связный список; каждый блок содержит ссылку на предыдущий.

Эта структура данных не нова, но новаторскими являются правила (т. е. одноранговые протоколы), которые управляют сетью. Центрального органа управления нет; участники сети должны сотрудничать для ее поддержания и конкурировать за право решать, какие транзакции включать в следующий блок. Итак, когда вы хотите отправить деньги другу, вам нужно будет объявить об этой транзакции в сети, а затем дождаться ее включения в один из следующих блоков.

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

Новая парадигма

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

Python-разработчики, которые хотят взаимодействовать с Ethereum, скорее всего, воспользуются Web3.pyopens in a new tab. Web3.py — это библиотека, которая значительно упрощает подключение к узлу Ethereum, а также отправку и получение данных от него.

Примечание. «Узел Ethereum» и «клиент Ethereum» используются как взаимозаменяемые понятия. В любом случае речь идет о программном обеспечении, которое запускает участник сети Ethereum. Это программное обеспечение может читать данные блоков, получать обновления при добавлении в цепь новых блоков, объявлять о новых транзакциях и многое другое. Технически клиент — это программное обеспечение, а узел — это компьютер, на котором это программное обеспечение запущено.

Клиенты Ethereum могут быть настроены для доступа по IPCopens in a new tab, HTTP или через веб-сокеты, поэтому Web3.py должен будет отражать эту конфигурацию. Web3.py называет эти варианты подключения провайдерами. Вам нужно будет выбрать одного из трех провайдеров, чтобы связать экземпляр Web3.py с вашим узлом.

Диаграмма, показывающая, как web3.py использует IPC для подключения вашего приложения к узлу Ethereum

Настройте узел Ethereum и Web3.py для обмена данными по одному и тому же протоколу, например, IPC на этой диаграмме.

После правильной настройки Web3.py вы можете начать взаимодействовать с блокчейном. Вот пара примеров использования Web3.py в качестве предварительного ознакомления с тем, что нас ждет впереди:

1# чтение данных блока:
2w3.eth.get_block('latest')
3
4# отправка транзакции:
5w3.eth.send_transaction({'from': ..., 'to': ..., 'value': ...})

Установка

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

Примечание. В приведенных ниже примерах команды, начинающиеся с $, предназначены для запуска в терминале. (Не вводите символ $, он просто обозначает начало строки.)

Сначала установите IPythonopens in a new tab, чтобы получить удобную среду для изучения. Помимо прочих функций, IPython предлагает автодополнение по клавише Tab, что значительно упрощает изучение возможностей Web3.py.

pip install ipython

Web3.py опубликован под названием web3. Установить его можно так:

pip install web3

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

pip install 'web3[tester]'

Все готово к работе!

Примечание. Пакет web3[tester] работает вплоть до версии Python 3.10.xx

Запускаем песочницу

Откройте новую среду Python, запустив ipython в своем терминале. Это похоже на запуск python, но с большим количеством дополнительных возможностей.

ipython

Будет выведена информация о версиях Python и IPython, после чего вы увидите приглашение для ввода:

1In [1]:

Теперь вы смотрите в интерактивную оболочку Python. По сути, это песочница для экспериментов. Если вы дошли до этого места, то пора импортировать Web3.py:

1In [1]: from web3 import Web3

Знакомство с модулем Web3

Помимо того, что модуль Web3opens in a new tab является шлюзом в Ethereum, он предлагает несколько удобных функций. Давайте рассмотрим некоторые из них.

В приложении Ethereum часто возникает необходимость конвертировать номиналы валют. Модуль Web3 предоставляет для этого пару вспомогательных методов: from_weiopens in a new tab и to_weiopens in a new tab.

Примечание. Компьютеры печально известны плохой обработкой десятичной арифметики. Чтобы обойти это, разработчики часто хранят суммы в долларах в центах. Например, товар с ценой $5,99 может храниться в базе данных как 599.

Аналогичный подход используется при обработке транзакций в ether. Однако вместо двух десятичных знаков у ether их 18! Самая маленькая единица ether называется wei, и именно это значение указывается при отправке транзакций.

1 ether = 1000000000000000000 wei

1 wei = 0.000000000000000001 ether

Попробуйте конвертировать некоторые значения в wei и обратно. Обратите внимание, что многие из номиналовopens in a new tab между ether и wei имеют свои названия. Один из наиболее известных среди них — gwei, так как в нем часто указывают комиссию за транзакции.

1In [2]: Web3.to_wei(1, 'ether')
2Out[2]: 1000000000000000000
3
4In [3]: Web3.from_wei(500000000, 'gwei')
5Out[3]: Decimal('0.5')

Другие служебные методы модуля Web3 включают преобразователи форматов данных (например, toHexopens in a new tab), вспомогательные функции для адресов (например, isAddressopens in a new tab) и хэш-функции (например, keccakopens in a new tab). Многие из них будут рассмотрены далее в этой серии статей. Чтобы просмотреть все доступные методы и свойства, воспользуйтесь автодополнением IPython, набрав Web3. и дважды нажав клавишу Tab после точки.

Общение с блокчейном

Вспомогательные методы — это прекрасно, но давайте перейдем к блокчейну. Следующим шагом является настройка Web3.py для взаимодействия с узлом Ethereum. Здесь у нас есть возможность использовать провайдеры IPC, HTTP или Websocket.

Мы не будем идти по этому пути, но пример полного рабочего процесса с использованием HTTP-провайдера может выглядеть примерно так:

  • Скачайте узел Ethereum, например, Gethopens in a new tab.
  • Запустите Geth в одном окне терминала и дождитесь синхронизации с сетью. Порт HTTP по умолчанию — 8545, но его можно настроить.
  • Сообщите Web3.py, чтобы он подключался к узлу через HTTP, по адресу localhost:8545. w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))
  • Используйте экземпляр w3 для взаимодействия с узлом.

Хотя это один из «реальных» способов, процесс синхронизации занимает часы и не является необходимым, если вам просто нужна среда разработки. Для этой цели Web3.py предоставляет четвертый провайдер — EthereumTesterProvider. Этот тестовый провайдер подключается к симулированному узлу Ethereum с более мягкими правами доступа и поддельной валютой для экспериментов.

Диаграмма, показывающая EthereumTesterProvider, связывающий ваше приложение web3.py с симулированным узлом Ethereum

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

Этот симулированный узел называется eth-testeropens in a new tab, и мы установили его в рамках команды pip install web3[tester]. Настроить Web3.py для использования этого тестового провайдера очень просто:

1In [4]: w3 = Web3(Web3.EthereumTesterProvider())

Теперь вы готовы к серфингу по блокчейну! Вообще-то так не говорят. Я только что это выдумал. Давайте совершим краткий тур.

Краткий тур

Первым делом — проверка работоспособности:

1In [5]: w3.is_connected()
2Out[5]: True

Поскольку мы используем тестовый провайдер, эта проверка не очень информативна, но если она не удалась, скорее всего, вы что-то не так набрали при создании экземпляра переменной w3. Перепроверьте, что вы включили внутренние скобки, т. е. Web3.EthereumTesterProvider().

Остановка № 1: аккаунты

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

Сначала давайте посмотрим список этих аккаунтов:

1In [6]: w3.eth.accounts
2Out[6]: ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
3 '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF',
4 '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69', ...]

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

Как уже упоминалось, тестовый провайдер предварительно пополнил каждый из этих аккаунтов некоторым количеством тестового эфира. Давайте узнаем, сколько средств на первом аккаунте:

1In [7]: w3.eth.get_balance(w3.eth.accounts[0])
2Out[7]: 1000000000000000000000000

Как много нулей! Прежде чем вы побежите в поддельный банк, вспомните урок о номиналах валюты. Суммы в ether выражаются в наименьшем номинале — wei. Конвертируйте это в ether:

1In [8]: w3.from_wei(1000000000000000000000000, 'ether')
2Out[8]: Decimal('1000000')

Один миллион тестовых ether — тоже неплохо.

Остановка № 2: данные блока

Давайте посмотрим на состояние этого симулированного блокчейна:

1In [9]: w3.eth.get_block('latest')
2Out[9]: AttributeDict({
3 'number': 0,
4 'hash': HexBytes('0x9469878...'),
5 'parentHash': HexBytes('0x0000000...'),
6 ...
7 'transactions': []
8})

О блоке возвращается много информации, но здесь следует отметить лишь несколько моментов:

  • Номер блока — ноль, независимо от того, как давно вы настроили тестовый провайдер. В отличие от реальной сети Ethereum, которая добавляет новый блок каждые 12 секунд, эта симуляция будет ждать, пока вы не дадите ей какую-нибудь работу.
  • transactions — это пустой список по той же причине: мы еще ничего не делали. Этот первый блок — пустой блок, он нужен просто для того, чтобы запустить цепочку.
  • Обратите внимание, что parentHash — это просто набор пустых байтов. Это означает, что это первый блок в цепочке, также известный как генезис-блок.

Остановка № 3: транзакции

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

1In [10]: tx_hash = w3.eth.send_transaction({
2 'from': w3.eth.accounts[0],
3 'to': w3.eth.accounts[1],
4 'value': w3.to_wei(3, 'ether'),
5 'gas': 21000
6})

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

  1. Отправьте транзакцию и сохраните ее хэш. Пока блок, содержащий транзакцию, не будет создан и распространен, транзакция находится в состоянии «ожидания». tx_hash = w3.eth.send_transaction({ … })
  2. Дождитесь включения транзакции в блок: w3.eth.wait_for_transaction_receipt(tx_hash)
  3. Продолжите логику приложения. Чтобы просмотреть успешную транзакцию: w3.eth.get_transaction(tx_hash)

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

1In [11]: w3.eth.get_transaction(tx_hash)
2Out[11]: AttributeDict({
3 'hash': HexBytes('0x15e9fb95dc39...'),
4 'blockNumber': 1,
5 'transactionIndex': 0,
6 'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
7 'to': '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF',
8 'value': 3000000000000000000,
9 ...
10})
Показать все

Здесь вы увидите некоторые знакомые детали: поля from, to и value должны соответствовать входным данным нашего вызова send_transaction. Еще один обнадеживающий момент заключается в том, что эта транзакция была включена как первая транзакция ('transactionIndex': 0) в блок номер 1.

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

1In [12]: w3.eth.get_balance(w3.eth.accounts[0])
2Out[12]: 999996999979000000000000
3
4In [13]: w3.eth.get_balance(w3.eth.accounts[1])
5Out[13]: 1000003000000000000000000

Последний выглядит хорошо! Баланс изменился с 1 000 000 до 1 000 003 ether. Но что случилось с первым аккаунтом? Похоже, он потерял немного больше трех ether. Увы, в жизни ничто не дается бесплатно, и использование публичной сети Ethereum требует, чтобы вы компенсировали другим участникам их поддерживающую роль. С аккаунта, отправившего транзакцию, была списана небольшая комиссия за транзакцию. Эта комиссия представляет собой объем сожженного газа (21 000 единиц газа за перевод ETH), умноженный на базовую комиссию, которая варьируется в зависимости от активности сети, плюс чаевые, которые получает валидатор, включивший транзакцию в блок.

Подробнее о газе

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

И выдохнем

Мы уже довольно долго этим занимаемся, так что это хорошее место, чтобы сделать перерыв. Кроличья нора продолжается, и мы продолжим ее исследование во второй части этой серии статей. Некоторые из будущих тем: подключение к реальному узлу, смарт-контракты и токены. Остались вопросы? Дайте мне знать! Ваши отзывы повлияют на то, куда мы будем двигаться дальше. Запросы принимаются через Twitteropens in a new tab.

Последнее обновление страницы: 6 февраля 2025 г.

Было ли это руководство полезным?