Что такое оракул?

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

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

Чтобы узнать больше об Oracles, посетите Oraclize, финтех-компанию, обеспечивающую надежное соединение между распределенными приложениями. Их объяснение оракула - хорошее начало.

DNS Oracle Tinypay.co

Оракул для Tinypay (более подробно описанный здесь) должен делать три простые вещи:

  • Выберите события «ClientCreated» из контракта.
  • Проверить записи DNS, используя данные события
  • Отправьте транзакцию ConfirmClient контракту после подтверждения домена

Я прошел через несколько итераций, чтобы получить правильную реализацию, и я хочу провести вас через них в качестве экскурсии по разработке на Ethereum.

Вы можете использовать RPC напрямую ... но, вероятно, не должны

В первый раз я писал оракул, я использовал Go. Я попытался осуществить всю связь с узлом Ethereum напрямую с помощью API RPC.

Это было интересно, потому что я смог много узнать о том, как протокол Ethereum хранит и упорядочивает данные на низком уровне. Мне пришлось вручную воссоздать ABI (двоичный интерфейс приложения) в коде и использовать его как для отправки, так и для расшифровки сообщений. ABI необходим для определения того, как можно взаимодействовать с контрактом и как данные извлекаются из необработанных байтов в сети.

Фактическое извлечение данных из событий оказалось более сложным, чем я был готов. Обработка событий в клиенте Go еще не завершена. Мне пришлось вручную опросить конечную точку RPC и выяснить, как декодировать двоичные данные из необработанных событий. Клиент Go, безусловно, кажется основным направлением работы команды Ethereum, и они хорошо осведомлены о дыре в клиенте, связанной с наблюдением и декодированием событий. Я ожидаю, что это скоро будет поддержано, что сделает клиент Go первоклассным вариантом для написания Oracles и других клиентских приложений Ethereum.

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

Web3 - хорошая абстракция

Для второй итерации я переключился на node.js и использовал библиотеку web3 для связи с узлом geth. Это дало мне встроенные абстракции для наблюдения за событиями, извлечения данных и форматирования и в целом значительно упростило жизнь.

Я начал с очень полезного руководства tinyoracle Алекса Берегсази, которое помогло мне на пути к хорошей второй версии.

Следующие отрывки кода несколько отредактированы, полный код можно найти в репозитории github (тег v0.0.2 для этой итерации)

var Web3 = require('web3');
var web3 = new Web3();
var contracts = require(path.join(__dirname, 'gen_contracts.json'));
// First we instruct web3 to use the RPC provider
web3.setProvider(
  new web3.providers.HttpProvider(
    'http://' + opts.rpc_host + ':' + opts.rpc_port));
// This isn't strictly necessary here, but goes to show the step required to "unlock" the account before sending transactions.
if (!web3.personal.unlockAccount(
  web3.eth.coinbase, opts.wallet_password)) {
  console.error('Could not unlock');
  process.exit();
}
// Here we register the filter with the ethereum node, and then begin polling for updates.
function runLoop(o) {
  var filter = web3.eth.filter({address: o.contract_address});
  filter.watch(function (err, results) {
    if (err) {
      console.log('WATCH ERROR: ', err);
      process.exit();
    }
    console.debug(results);
  });
}
// If the contract isn't deployed yet, we deploy it here
if (!opts.contract_address) {
// This block of code loads the ABI for interpreting contract data.
  var dmC = web3.eth.contract(JSON.parse(contracts.DomainMicropay.abi));
  var x = {
    from: web3.eth.coinbase,
    data: contracts.DomainMicropay.bin,
    gas: 1000000
  };
  // send the transaction for installing the contract.
  dmC.new(x, function (err, resp) {
    if (err) {
      console.error('Loading contract', err);
      process.exit();
    }
    var addr = resp.address;
    if (!addr) {
      console.log('Pending tx: ', resp.transactionHash);
    } else {
      console.log('Deployed Address: ', addr);
      opts.contract_address = addr;
      runLoop(opts);
    }
  });
} else {
  runLoop(opts); // in either case, start the polling event loop.
}

Трюфель - это то, что вы хотите использовать

Наконец, для третьей итерации я отказался от попыток свернуть все самостоятельно. Мы уже использовали отличный инструмент трюфель от ConsenSys в нашем веб-интерфейсе. Я просто скопировал сгенерированные артефакты в свой проект node.js и включил его напрямую, и я занялся бизнесом.

Используя Truffle, мы смогли скомпилировать наши контракты Solidity в библиотеку javascript, которая фиксировала важные детали, такие как развернутый адрес контракта, и полностью абстрагировала низкоуровневую связь RPC. Наблюдение за событиями, отправка транзакций и запрос данных превратились в простые вызовы API, генерируемые непосредственно из нашего контракта.

// This code extract shows the whole event loop abstracted behind the actual event name: ClientConfirmed and ClientCreated.
startWatcher: function (rpcUrl, unlockPass) {
        password = unlockPass || password;
web3.setProvider(new web3.providers.HttpProvider(rpcUrl));
        DomainMicropay.setProvider(web3.currentProvider);
contract.ClientConfirmed({}, eventOpts(), function (err, data) {
          if (err) {
            console.log('Error ClientConfirmed: ', err);
            return;
          }
          console.log('Event ClientConfirmed: ', data.args.domain);
        });
contract.ClientCreated({}, eventOpts(), function (err, data) {
          if (err) {
            console.log('Error ClientCreated: ', err);
            return;
          }
          console.log('Event ClientCreated: ', data.args.domain);
          contract.getPaymentContractForDomain
            .call(data.args.domain)
            .then(beginDomainVerification(data))
            .catch(errFn('Unhandled Error: '));
        });
      }

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

Must Win с радостью поможет разработать ваш следующий DApp. Если вам нужна помощь в понимании или использовании технологии цепочки блоков, напишите нам по адресу [email protected] и укажите ссылку на этот пост.

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