HTTP-запросы работают в Ethereum и в Solidity с так называемыми оракулами.
Создавать HTTP-запросы в Solidity просто с Chainlink. Если вы хотите получить больше информации о передовых методах, то после прочтения этой статьи обязательно прочтите Вызовы API в блокчейне.
Давайте сделаем шаг назад, чтобы начать.
Как работает запрос Chainlink?
Помните, что оракулы Chainlink являются промежуточным программным обеспечением блокчейна, поэтому нам нужно что-то, что программно скажет им сделать HTTP-запрос. Нам нужно что-то, чтобы инициировать их, чтобы они начали получать данные для нас. Эти инструкции для указания узлу звена цепи начать получение данных называются Инициаторами звена звена.
Узел Chainlink может иметь сколько угодно инициаторов. Каждый из них определит свой способ для узла начать получение данных.
Инициатор 1. Начните получать данные при наступлении x
Инициатор 2. Начните получать данные, когда наступит y
Инициатор 3. Начните получать данные, когда произойдет z
И т. д.
Вместе с инициаторами, а также набор инструкций, что делать, когда они инициированы. Этот набор инструкций о том, что делать, называется списком задач или адаптеров.
Адаптер 1. Выполните q для данных после получения
Адаптер 2: исправляйте полученные данные
Адаптер 3: что делать с данными после получения
И т. д.…
Комбинация инициаторов и адаптеров составляет единую работу на узле звена цепи. Узлы Chainlink могут иметь несколько заданий, так что они могут делать много разных вещей в простом интерфейсе.
Вот как выглядит пример определения задания: вы можете видеть, что он содержит список из initiators
, определенных вверху, а затем список из _2 _ / _ 3_, определенных после. Вы также можете видеть, что инициаторы могут принимать параметры, позволяющие им делать еще больше. Этот пример определения задания известен как jobspec
.
{ "initiators": [ { "type": "runlog", "params": { "address": "0xd8c819674b79c7372d56db03280a5695a9254894" } } ], "tasks": [ { "type": "httpget" }, { "type": "jsonparse" }, { "type": "multiply" }, { "type": "ethuint256" }, { "type": "ethtx" } ] }
У большинства узлов много одинаковых заданий, и одно из самых распространенных заданий в приведенном выше примере. Это задание определяет, как делать простые httpget
запросы в ваш смарт-контракт. Также имеется httppost
адаптер, если вы хотите отправить запрос POST вместо GET. Эта работа определяется:
- Инициатор Runlog
- Этот конкретный список адаптеров / задач: адаптеры httpget, jsonparse, multiply, ethuint256 и ethTx.
Инициатор Runlog - один из наиболее часто используемых инициаторов. Он определяет, что узел звена цепи будет следить за цепочкой блоков на предмет любых событий журнала, которые включают идентификатор этого задания. Как только он обнаруживает событие, он запускает адаптеры и отправляет данные в цепочку. Мы скоро рассмотрим, что делает каждый из этих адаптеров.
Вашему смарт-контракту придется немного подождать, пока узел Chainlink вернет ваши данные, но как только это произойдет, ваш смарт-контракт будет извлекать данные из этой транзакции.
Как это выглядит в твердости?
Чтобы показать, как это выглядит в коде, давайте посмотрим на получение числовой цены ETH. Вы можете использовать эту ссылку на ремикс, чтобы следовать, но вот подмножество этого кода контракта.
// Creates a Chainlink request with the uint256 multiplier job // Ideally, you'd want to pass the oracle payment, address, and jobID as function requestEthereumPrice() public onlyOwner { // newRequest takes a JobID, a callback address, and callback function as input Chainlink.Request memory req = buildChainlinkRequest(JOBID, address(this), this.fulfill.selector); req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD"); req.add("path", "USD"); req.addInt("times", 100); sendChainlinkRequestTo(ORACLE_ADDRESS, req, ORACLE_PAYMENT); } // fulfill receives a uint256 data type function fulfill(bytes32 _requestId, uint256 _price) public // Use recordChainlinkFulfillment to ensure only the requesting oracle can fulfill recordChainlinkFulfillment(_requestId) { currentPrice = _price; }
Чтобы начать делать запрос к узлу звена цепи, мы вызываем функцию buildChainlinkRequest
, которая импортируется из ChainlinkClient.sol
, эта функция возвращает структуру Chainlink.Request
. В этой функции мы передаем идентификатор задания, адрес возврата и функцию выполнения, когда начинаем ее строить.
Мы можем найти jobID независимого узла Chainlink, перейдя в службу листинга узлов, например market.link, и выполнив поиск того, что мы хотим. В сервисах списка узлов независимые узлы публикуют информацию о том, как подключиться к их узлу Chainlink. Вы можете использовать их для создания своей сети децентрализованных оракулов. Убедитесь, что вы подключены к правильной сети (ropsten, mainnet, kovan и т. Д.).
Для этого нам нужна работа, которая может делать httpget
вызов и возвращать uint256
, поэтому давайте попробуем найти это задание в сети ropsten.
Давайте выберем работу и просто убедимся, что jobspec
- это то, что мы ищем. Мы можем перейти на вкладку jobspec
, чтобы узнать, что такое инициаторы и адаптеры.
Чтобы убедиться, что у этого задания есть подходящие адаптеры, мы можем проверить их список задач / адаптеров. Изображение выше показывает, что у этого задания есть именно те инициаторы и адаптеры / задачи, которые нам нужны.
Убедившись, что у задания есть нужные нам адаптеры, мы можем скопировать идентификатор задания для использования в нашем коде. Вы можете увидеть другие узлы, выполняющие аналогичную работу, и этот список понадобится вам, когда вы сделаете свой контракт децентрализованным, но, опять же, для тестирования и разработки мы можем просто использовать один узел. Убедитесь, что вы также получили адрес оракула, так как он понадобится вам позже.
Chainlink.Request memory req = buildChainlinkRequest(JOBID, address(this), this.fulfill.selector);
Затем мы помещаем JOBID в первый параметр нашего buildChainlinkReqeust
. Второй параметр - это адрес контракта для возврата данных, также известного как callbackaddress
. Последний параметр - это функция, которая будет обрабатывать данные после сбора, также известная как thecallbackFunctionSignature
. Мы хотим вернуть данные в этот контракт, поэтому мы вводим address(this)
, и наша функция, которая будет обрабатывать данные, будет fulfill
. Мы определили функцию выполнения прямо под этой.
function fulfill(bytes32 _requestId, uint256 _price) public // Use recordChainlinkFulfillment to ensure only the requesting oracle can fulfill recordChainlinkFulfillment(_requestId) { currentPrice = _price; }
Параметр uint256 _price
- это то место, где узел Chainlink будет вводить цену, полученную при выполнении HTTP-запроса на получение. В нашем примере выше мы просто устанавливаем возвращаемое значение из узла в нашу currentPrice
variable.
Вернувшись к функции requestEthereumPrice
, мы можем добавить параметры в наши adatpers / tasks. Давайте рассмотрим, что делает каждый адаптер:
httpget
Узел Chainlink сделает HTTP-запрос GET; обычно это простой вызов API.
HTTP-запрос GET, который мы хотим сделать в параметрах, переданных в переменную req
.
req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD");
На момент написания возвращаемое значение этого HTTP-запроса GET:
{"USD":391.41}
jsonparse
Как только узел выполнит HTTP-запрос GET, он пройдет через json и найдет только то значение, которое нам нужно. Хранение всего возврата HTTP-запроса GET было бы очень дорогостоящим, поскольку чем больше вы храните в цепочке Ethereum, тем больше газа ETH вам придется заплатить. Итак, мы хотим вернуть как можно меньше.
req.add("path", "USD");
Это сократит возвращаемый HTTP-запрос GET только до значения 391.41
. Он проходит по json возвращаемого значения {“USD”:391.41}
. Для более длительных возвратов json вы также можете добавить индексы списков. Например, если у вас есть такой объект, как:
{"USD": [ "price": {"ETH": 391.41}]}
Мы могли бы пройти по пути к этому значению с помощью:
req.add("path", "USD.0.ETH");
умножить / раз
Как только мы получим значение 391.41
, мы должны превратить его в целое число. Десятичные дроби не работают в твердом состоянии, и нам нужно представлять десятичные дроби с использованием математики с фиксированной точкой.
req.addInt("times", 100);
Это еще раз изменит наш результат, чтобы он был 39141
(поскольку мы перемещаем десятичный разряд дважды, умножая его на 100).
ethuint256
Как вы заметили в нашем коде выше, мы не передаем никаких параметров этому адаптеру; это потому, что нам это не нужно. Этот адаптер просто преобразует наш ответ 39141
в понятный для солидности формат.
ethtx
Здесь тоже не нужны никакие параметры. Это адаптер, который фактически отправляет данные обратно в цепочку.
Теперь мы используем метод sendChainlinkRequestTo
с адресом оракула, который мы получили из market.link, самим запросом и oracle_payment.
sendChainlinkRequestTo(ORACLE_ADDRESS, req, ORACLE_PAYMENT);
Мы также получаем ORACLE_ADDRESS
и ORACLE_PAYMENT
с market.link. Плата Oracle - это количество LINK oracle gas, которое мы хотим отправить оракулу, каждое задание имеет свою минимальную стоимость для отправки.
Отправь это
А теперь давайте снова запустим! Давайте развернем контракт, пополним его ссылкой, а затем запросим цену ETH. Вы можете пройти пример пошагового руководства, если не знаете, как это сделать. Или более подробное пошаговое видео на YouTube:
Узел Chainlink получит запрос, прочитав его из блокчейна. Опять же, он может считывать его из блокчейна, потому что использует инициатор Runlog. Как только он берет задание, он проходит через адаптеры и отправляет его обратно в цепочку для использования вашим смарт-контрактом!
Оракулы также могут определять параметры прямо в своих определениях заданий (называемых jobspec
), и вы можете просто вызвать задание, не вводя никаких параметров вообще.
Это было довольно глубокое погружение в то, как именно работает работа с узлами Chainlink, но теперь у вас есть много основных инструментов для создания некоторых удивительных проектов в экосистеме блокчейна.
Как видите, получить данные вне сети в Ethereum и на любом блокчейне просто, безопасно и надежно с помощью цепочки ссылок. А теперь посмотрим какое-нибудь здание!