Получение транзакций в режиме реального времени на адрес Ethereum

Недавно я работал над новым проектом, когда наткнулся на проблему: мне нужно было получать все транзакции для данной учетной записи более или менее в режиме реального времени. После просмотра документации API Web3.js, а также Stack Overflow стало ясно, что нет четко определенного способа сделать это, поэтому я попытался создать некоторые программы самостоятельно. Вот что из этого получилось. С полным текстом репо можно ознакомиться здесь.

Два сценария

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

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

Прежде чем мы начнем, вам понадобится пара вещей:

  • Работающий узел Ethereum, например Geth или Infura (который я буду использовать)
  • Node.js и npm
  • Новый каталог, инициализированный с помощью npm init

Нам понадобится только одна зависимость - web3.js (см. Документацию). API JavaScript для взаимодействия с сетью Ethereum. Поэтому обязательно npm install this.

Давайте сначала создадим модуль, который инициализирует наш клиент web3 для нас:

Этот модуль принимает пакет Web3 и возвращает инициализированного клиента. Я использовал узел Infura Ethereum в тестовой сети rinkeby, и если вы решите сделать это тоже (что я предлагаю), обязательно замените YOUR_INFURA_API_KEY правильным ключом.

Затем мы создадим собственно средство проверки транзакций:

Наш второй модуль использует этот клиент web3 для запроса реальной сети. У нас есть личная переменная учетной записи, которую вы должны заменить на интересующий вас адрес, а затем мы вернем функцию checkLastBlock. Сначала мы получаем последний блок и записываем номер в консоль. Вот как выглядит такой блок (я исключил некоторые поля, которые нам не нужны):

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

В строке 9 файла transactionChecker.js мы проверяем, не являются ли массивы block и block.transactions равными нулю, а в строке 10 мы зацикливаемся над указанным массивом. Для каждого хэша транзакции в массиве мы запрашиваем фактическую транзакцию. Транзакция выглядит так:

Если теперь мы обнаружим, что поле to, которое является адресом принимающей стороны транзакции, равно нашему адресу (не забудьте о функции toLowerCase ()), мы нашли то, что искали, и можем записать некоторые данные в консоль. (Если транзакция не содержит поля к, это было создание контракта)

Наконец, нам нужен файл index.js, чтобы собрать все это воедино:

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

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

Не забудьте вернуть и эту функцию.

Если вы совсем не понимаете, как я пишу свои модули: я экспортирую так называемую фабричную функцию, которая является отличным шаблоном проектирования для JavaScript. Если вы не понимаете, что происходит, это отличная статья.

Вторая программа использует Ethereum pub / sub. Pub / sub - это система, посредством которой издатель постоянно транслирует события, касающиеся определенных тем, в сеть, на которую клиент (подписчик) может подписаться. Это намного лучше и быстрее, чем необходимость постоянно опрашивать сеть, как мы это делали в первой программе. Однако есть некоторые аспекты, которые следует учитывать:

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

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

Во-первых, нам нужно создать нашего клиента. Для этой программы нам понадобится как обычный провайдер http, так и провайдер веб-сокетов. В нашем коде мы вернем оба значения в объекте: web3http будет клиентом http, web3 клиентом websocket:

А теперь о второй версии средства проверки транзакций:

Давайте разберемся с этим. Есть пара тем, на которые можно подписаться, например newBlockHeaders или журналы. Журналы были бы идеальным вариантом, но эта тема подписки пока не работает. Поэтому мы будем работать с подпиской pendingTransactions. Это происходит в строке 5. Наша подписка является источником событий, и в тот момент, когда кто-то отправляет новую транзакцию (таким образом, еще не подтвержденную), он отправляет нам хэш транзакции этой транзакции. Это происходит в функции watchTransactions, которую мы предоставляем в строке 11.

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

И, наконец, наш индексный файл, чтобы собрать все это воедино:

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

Вот и все! Если у вас есть вопросы, дайте мне знать в комментариях. С радостью отвечу.

Ресурсы:

Получайте лучшие предложения по программному обеспечению прямо в свой почтовый ящик