Что такое оракул?
Смарт-контракты по своей природе могут выполнять алгоритмические вычисления, а также хранить и извлекать данные. Поскольку каждый узел выполняет все вычисления, делать произвольные сетевые запросы из контракта 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] и укажите ссылку на этот пост.
Джон Велдон пишет внутренний код для различных проектов и имеет опыт работы с широким спектром технологий и языков программирования.