什么是以太坊预言机?智能合约就其性质而言,能够运行各种算法并存储查询数据。预言机可以监控以太坊区块链事件并能将监控结果发回智能合约。因为每个节点每次都需要大量计算,所以从Ethereum智能合约开发中进行频繁的网络请求是切不实际的。这样,智能合约就可以与链外的世界进行互动了。
但是这样有一个明显的信任问题。与信任单一外部数据源的分布式智能合约有些矛盾。不过这可以通过让多个独立的预言机来响应相同的查询最终形成共识来缓解这个问题。
有关预言机的更多信息,请查看在分布式应用程序之间提供“可靠连接”的FinTech公司Oraclize。 他们对预言机的解释很不错。
The Tinypay.co DNS Oracle
Tinypay的预言机做了三件简单的事情:
从合同中提取'ClientCreated'事件
使用来自事件的数据验证DNS记录
域名确认后,向合约发送'ConfirmClient'交易
我经历了几次迭代,最终实现了,我希望通过它们来引导您了解以太坊的发展。
你也可以直接用RPC,不过似乎不应这么干
我第一次写预言机,我用了Go-Ethereum。我想直接使用RPC API与Ethereum节点进行所有通信。
这很有趣,因为我能够学习很多关于以太坊协议如何进行存储和数据编码等较底层的内容。我必须手动重新在代码中创建ABI(应用程序二进制接口),并使用它来发送和解密消息。 ABI对于定义合约如何交互以及如何从线上的原始字节中提取数据是必需的。
从事件中实际提取数据证明比我想象的要复杂得多。Go-Ethereum的处理事件没完成。我被迫手动轮询RPC端点,并找出如何将来自原始事件的二进制数据解码。Go-Ethereum当然似乎是以太坊团队关注的焦点,他们应该很清楚Go-Ethereum在观看和解码事件方面的问题。我希望他们能很快有所提升,这会使得Go-Ethereum成为编写预言机和其他以太坊客户端应用程序的更好选择。
低级别的RPC API和解码API被证明是非常低效率的,并且他们正在更快地进行迭代,所以...
Web3 则是一个很好的抽象
对于第二次迭代,我切换到node.js并使用web3库与geth节点进行通信。 这给了我内置的抽象了的事件查询,数据提取和格式化,而且明显使开发变得更容易。
我开始使用Alex Beregszaszi非常有用的'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使用RPC接口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.// 该代码块加载ABI来解释合约数据。 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. //在任一种情况下,启动轮询事件循环}
Truffle 应该是你最想用的框架
最后,在第三次迭代中,我放弃了自己搞的这一切。 我们已经在我们的网络前端使用ConsenSys的优秀工具Truffle。 我只是将生成的构件复制到我的node.js项目中,并直接将其包含在内,然后就开始工作。
使用Truffle,我们能够将我们的Solidity合约编译成的一个JavaScript库,它可以确认各种重要的细节,如合同的部署地址,并完全代替低级RPC通信。 查看事件,发送交易和查询数据变成了直接从我们的合同中生成的简单API调用。
// This code extract shows the whole event loop abstracted behind the actual event name: ClientConfirmed and ClientCreated.// 这段代码显示了整个事件循环中的抽象后的实际事件:ClientConfirmed 和 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为使用智能合约并与之交互提供了一些非常好的抽象。这并不完美,也不能解决合约版本问题等问题。但是我们需要在其他文章中再介绍这些内容。