10分钟内用Ezo和Python构建以太坊Oracle

上一篇,我写了用Web3.js构建以太坊Oracle。这个练习给了我一些新的Web3.js 1.0版本知识。许多新的好东西可供选择而且使用它实现一个简单的oracle非常容易。但是,显然必须有更好的方法。

Instant Oracles,只需添加处理程序!

Ezo(发音为eh-zoh)是用于构建和运行以太坊的离线事件响应器的工具。受到ServerLessGordon等AWS工具集以及Django,Rails和Truffle等开发工具的启发,Ezo开始将大多数oracle开发减少到几个命令并自定义生成的Python事件处理程序。当然,这不是空话,我在晚餐前几分钟,即可让我们在沙拉三明治到达之前建立一个简单的时间戳oracle。

为什么一个时间戳oracle?

在以太坊虚拟机(EVM)上,时间以(大约)15秒块为单位进行测量。你和我都没有看到时间的概念。在EVM上,你只能真正知道块计数(自链生效以来生成的块数),并且可能会在一秒,十秒或更长时间内发生变化。然而,以太坊合约的现实可以与外界牢固地联系在一起。它可能具有法律约束力的协议,必须在外部世界的时钟上执行。如果合约需要知道什么时候它是脱链的,以便完成它的工作,它必须问别人。我们简单的oracle将简单地抓住系统的纪元时间并将其发送回调用合约上的方法。

它是如何工作的?

在它最基本的形式中,Ezo使用简单模型与所有合约进行交互:

单个合约事件响应者的顺序很简单:

  • 1.oracle开始倾听事件。
  • 2.账户实体在合约上执行请求方法。
  • 3.该合约发出命名事件。
  • 4.监听每个新区块的oracle接收事件。
    1. oracle执行特定事件所需的处理。
  • 6.然后oracle打包处理结果并将响应发送给调用合约。

Ezo可以同时执行多个合约的复杂操作。但是,就我们的时间戳响应者而言,我们只需要一份简单的合约。幸运的是,Ezo带有Timestamp Oracle合约。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
pragma solidity ^0.4.21;

// Timestamp Oracle
// Generated by ezo
//
// use at your own risk
//

contract TimestampRequestOracle {

address public owner;
uint public _timestamp;

event TimeRequest(address sender);
event RequestFilled(address sender,uint timestamp);


function constructor() public {
_timestamp = 0;
owner = msg.sender;
}

function sendTimestampRequest() public {
emit TimeRequest(msg.sender);
}

function setTimestamp(uint timestamp) public {
_timestamp = timestamp;
emit RequestFilled(msg.sender, _timestamp);
}

function getTimestamp() public returns (uint) {
return _timestamp;
}

}

实体(合约/所有者帐户)运行不带参数的sendTimestampRequest()方法。我们的oracle将获取TimeRequest事件,获取当前的纪元时间并使用setTimestamp()方法将其发送回调用合约。只是为了好玩,setTimestamp()将触发一个RequestFilled事件,只是为了让Ezo的日志显示一些额外信息,让你知道该方法已执行。

安装Ezo

你需要先安装一些东西:

1
2
3
4
5
Python 3.6 or later — get the latest and greatest
Solidity compiler — (Vyper fans, Ezo will take care of you very soon.)
LevelDB — for OS/X, use brew install leveldb. Ubuntu users, see instructions for installation on the Ezo Github wiki.
Ganache GUI — Ezo comes with a preconfigured Ganache GUI target and account, which means you can use it right out of the box with only a configuration modification.
Virtualenv — always recommended

安装后,然后安装ezo:

1
pip install ezo

(注意:如果你收到丢失包的信息,请尝试使用pip3而不是pip)

构建

现在,我们准备好了。让我们创建Ezo项目。我们称之为TimestampOracle。创建一个Virtualenv实例,然后创建一个Ezo项目:

1
ezo create project TimestampOracle

此命令为基础项目创建工件:名为TimestampOracle的项目目录,handlerscontracts的目录以及ezo.conf文件。

对于本演示,我们将使用Ganache。启动它,然后将其中一个帐户地址从Ganache复制到剪贴板。现在,打开ezo.conf,你会在目标下看到一个测试条目。删除帐户地址并将其替换为你刚刚从Ganache客户端复制的帐户地址。这就是让Ezo在Ganache上工作所需的一切(我将做一篇关于使用测试网的后续文章,比如Ropsten,它有点复杂)。

contract目录中,有一个名为contract2.sol的合约文件。它包含一个名为TimestampRequestOracle的基本时间戳请求协定。我们来编译它:

1
ezo compile contract2.sol

Solidity编制合约并将其存储在Ezo中。如果没有收到错误消息,你应该可以看到它已使用Ezo的view contracts命令编译:

1
ezo view contracts

你应该看到时间戳oracle及其相关数据,类似于此,但只有一个TimestampRequestOracle的条目。

请注意哈希引用。Ezo通过对合并文件进行hash并将其与合约信息一起保存来区分同一合约文件的不同版本。这样可以避免在更新合约脚本时出现混乱,并使Ezo能够轻松跟踪同一合约的不同编译版本。

让我们生成合约事件处理程序并将它们与合约事件相关联:

1
ezo gen handlers TimestampRequestOracle

Ezo为每个事件生成一个Python处理程序,并在事件记录调用中删除。然后,它会从事件签名中找出事件主题的外观,并将处理程序与签名配对。这一步至关重要。如果你在不执行此步骤的情况下启动Ezo,你将收到日志消息,告知你在收到事件时找不到匹配的主题。

现在,让我们将合约部署到Ganache。确保你运行它:

1
ezo deploy TimestampRequestOracle --target=test

如果一切顺利,你应该看到确认消息。这会将合约部署到目标网络。你可以在这里查看:

1
ezo view deploys

你应该看到TimestampRequestOracle的一个部署。对于多个部署,它看起来像这样:

现在,我们准备以oracle模式启动Ezo:

1
ezo start TimestampRequestOracle --target=test

你应该得到一行如下所示的输出:

1
INFO: hello ezo::listening to address: 0x8cdaf0cd2....

现在,启动另一个终端窗口,导航到你的Ezo项目目录,并运行ezo测试客户端以在Ganache上运行合约:

1
ezo send tx TimestampRequestOracle sendTimestampRequest [] -t test

你应该在第一个终端窗口的Ezo oracle控制台日志中看到类似的内容:

1
INFO: event: time_request contract: TimestampRequestOracle address 0xCD1A21534E9bD2325c4f0587e89D9909147A1925 timestamp: 2018–06–18 01:03:52.263817

(注意:你也可以使用-t而不是target来指定目标节点。)

爽!一切正常。现在,让我们回顾一下EVM。

自定义处理程序

打开你喜欢的编辑器,并将其指向项目中的处理程序子目录。打开文件time_request_handler.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from core.views import event_output

# This code automatically generated by ezo. Only modify where suggested.
#
# data is an instance of ContractEvent
# contract is the calling instance of Contract - it is used to send a response

event_name = 'time_request'

def handler(data, contract):

### put your code here

event_output(contract, event_name, data)

### uncomment the code below to build a response object
# response = dict()
# response["address"] = data.address
# response["function"] = None
# response["params'] = None

### uncomment the code below to send a response
# _, err = contract.response(response)
# if err:
# return None, err

return None, None

无论你是否对事件感兴趣,Ezo都会为每个事件生成一个处理程序存根。event_output()发送生成的每个处理程序在控制台上显示的日志记录数据。如果你不关心特定事件的输出,只需删除该行代码即可。

注释掉是预建的响应字典,以及调用合约的响应处理程序。你可以使用此预制代码快速组合对事件的适当响应。

我们将在几行代码中填写详细信息,并删除一些注释,以使我们的oracle正常工作。记住我们的oracle只返回纪元时间,一个整数,所以这很容易。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from core.views import event_output
import time
# This code automatically generated by ezo.
#
# data is an instance of ContractEvent
# contract is the calling instance of Contract - it is used to send a response

event_name = 'time_request'

def handler(data, contract):

### put your code here

event_output(contract, event_name, data)

time_epoch = int(time.time())

### uncomment the code below to build a response object
response = dict()
response["address"] = data.address
response["function"] = "setTimestamp"
response["params"] = [time_epoch]

### uncomment the code below to send a response
_, err = contract.response(response)
if err:
return None, err

return None, None

我们添加了几行,一个用于导入Python时间库,另一行用于获取自纪元以来的时间。我们取消注释响应字典行以及返回响应的代码。我们希望通过setTimestamp合约方法将时间返回到合约,因为它设置了合约的状态。有一个时间参数,Ezo在列表中预期。参数始终在Ezo的列表中发送。

在我们测试之前,让我们先看一下合约存储中设置的epoch_time当前值。我们这样做:

1
ezo send call TimestampRequestOracle getTimestamp [] -t test

在控制台上:

现在让我们再次从第二个终端运行来自测试客户端的请求:

1
ezo send tx TimestampRequestOracle sendTimestampRequest [] -t test

…当我们从测试客户端运行send tx命令时,让我们看看第一个终端中的Ezo oracle输出:

不错!request_filled事件被触发,这意味着我们在处理程序中指定的方法被调用,并且在合约的存储中设置了纪元时间。

我们来看看合约存储。你可以调用getTimestamp()方法,而无需更改EVM状态:

1
ezo send call TimestampRequestOracle getTimestamp [] -t test

我们在控制台上的回应:

恭喜!你刚刚创建了你的第一个以太坊Oracle。只有一些命令和两行Python!美观,优雅,易于使用的Python。

这只是Ezo的开始。还有很多东西要来。然而,沙拉三明治刚出现,我得走了。你很快就会看到。我将做一篇关于使用Ezo使用本地Geth节点的后续文章。当我们摆脱Ganache的轻松时,事情会变得更复杂,而且速度要慢得多。

愉快的构建!

点击这里查看Ezo Github Repo

注意:Ezo 0.1 alpha已经在OS/X和Ubuntu 16.04上进行了测试。如果你遇到奇怪或堆栈痕迹,请报告Ezo Github问题。对于以太网,Web3.py,Python,LevelDB,Cement CLI库,PyPi,Linux,StackOverflow以及所有其他开源软件和社区的思想和开发人员,以及我多年来一直在深思熟虑和无耻地利用的所有其他开源软件和社区 事业:巨大的赫拉,谢谢你。如果它不适合你,那就仍使用blinkytext和WAIS。

======================================================================

分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:

  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
  • EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。

汇智网原创翻译,转载请标明出处。这里是10分钟内用Ezo和Python构建以太坊Oracle