本文为智能合约设计模式系列的一部分。
目的
访问区块链外的数据
动机
以太坊上的每个计算需要网络中的每个参与节点验证,允许合约访问外部网络不切实际,因为每个节点都必须进行同样的访问以验证结果。这样不仅会导致小站点无法处理过量的访问请求,访问信息的更改也会破坏共识算法。因此,智能合约无法和外部世界通讯,这意味着它们无法从互联网等外部来源获得信息。但是对于许多合约来说,它们的核心功能依赖于外部信息,特别是针对一些复杂应用。已经有合约依赖于体育赛事结果、货币价格或航班信息等。这个问题的第一个解决方案是Orisi,发布于2014年,旨在成为比特币和外部世界之间的中介。从那时起,以预言机之名,各个区块链都出现了类似服务。预言机充当区块链上的代理,提供针对外部查询的响应。
区块链处理数据的一个重点是达成信任。由于没有中心节点,信任必须通过不变性或共识算法等方式达成。当依赖外部信息时,也必须有一种办法建立对该信息的信任。
适用性
在以下条件时使用预言机模式
- 所需的信息无法从区块链上获得
- 信任外部信息的提供者
参与者和协作
预言机模式由三部分组成:发起请求的合约,预言机和数据源。整个过程从一个合约发起外部查询请求开始,外部查询请求发给预言机,预言机也是一个位于区块链上的智能合约。查询请求包含一些可选参数,例如指定的外部数据源,或某个提供响应的未来时间。
然后,预言机将请求转发给数据源,因为数据源位于链下,所以不能同通过交易和其通信,而是通过其他方式。
数据源处理请求后将结果返回预言机。预言机可以将结果发给调用合约,或等到到达指定时间。调用合约提供回调方法处理返回结果。
实现
我们主要关注合约中的模式实现。预言机本身是在链下实现,这里不做介绍。互联网上有好几个资源介绍如何根据智能合约和业务逻辑实现自己的预言机。然而,用户可能会对采用自实现预言机合约望而却步,因为他们必须信任合约创建者,同时也是预言机操作者,并没有操控预言机结果以影响合约执行。这引入了新的信任需求,而这正是我们试图通过区块链来解决的。
更常见的方式是使用独立的服务作为预言机。这一领域的领导者是英国公司Oraclize。其他预言机服务有Town Crier、使用可信硬件或现实密钥。
无论预言机是自己实现还是使用外部服务,调用合约至少包含下面2个方法:
- 第一个方法生成查询,通过事务提交请求给预言机合约。根据不同预言机实现,请求可以增额外参数。通常,预言机会返回一个请求ID。
- 第二个方法是回调函数。预言机调用这个方法返回结果。回调方法可以保存结果也可以执行任何内部逻辑。第一个方法返回的ID作为参数传入。回调方法需要包含一个检查以确保只有预言机可以调用它,否则,恶意用户可能会调用它提供错误结果来影响合约执行。
代码示例
由于Oraclize服务被广泛使用,下面的示例代码将展示如何使用Oraclize服务接收欧元兑换美元汇率。其他的预言机集成也是类似方式。相关语法的细节,请参考相关文档。
// This code has not been professionally audited, therefore I cannot make any promises about
// safety or correctness. Use at own risk.
import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";
contract OracleExample is usingOraclize {
string public EURUSD;
function updatePrice() public payable {
if (oraclize_getPrice("URL") > this.balance) {
//Handle out of funds error
} else {
oraclize_query("URL", "json(http://api.fixer.io/latest?symbols=USD).rates.USD");
}
}
function __callback(bytes32 myid, string result) public {
require(msg.sender == oraclize_cbAddress());
EURUSD = result;
}
}
第3行从GitHub中导入Oraclize API,如果编译器不支持从GitHub直接导入,则需要改用本地导入。API提供地址访问以及与预言机交互的功能。第5行代码指定合约使用is
关键字从API继承。updatePrice
方法想预言机发送查询,这个方法需要payable
修饰器,因为Oraclize服务需要收费,收费标准可以在Oraclize文档中找到。第10行确保合约有足够的余额支付查询费用,如果不够,应通知用户,例如通过触发事件。如果余额足够,第13行发送查询给Oraclize合约。第一个参数指明查询URL,第二个参数包含具体url和响应JSON对象中感兴趣的部分。互联网上的任何API都可以通过这种方式访问。
预言机调用__callback
方法返回结果。第一个参数myid
对应查询时返回的请求id。第18行确保调用者是预言机,第19行保存结果到合约中。
结果
预言机模式最重要的结果是访问链上不能提供的数据,由此产生全新的业务模型和用例。除此之外,预言机还自动触发合约的方法通过在查询中提供触发时间,这可以解决区块链经常遇到的定时任务问题。它还可以 用来生成随机数,一个困难任务,在随机数模式中介绍。对开发人员来说,实现预言机模式很容易,特别是在使用已有服务时。使用已有服务的另个好处是,它们已经过了严格的审核,降低了出错的风险。
使用预言机的一个负面结果是引入了单一故障点。合约以及交互用户在很大程度上依赖预言机提供的信息。预言机或数据源曾经发生的错误,在将来很可能再次发生。不仅错误,即使数据格式的很小改动也会影响智能合约,就像reddit发布的案例,数据源将拳击比赛的输出格式从小写改为大写,导致不可变的智能合约无法工作。另个负面结果是必须信任预言机和数据源,依赖一个外部实体和区块链致力于去中心化的宗旨相矛盾。这个问题可以通过访问多个独立的预言机得到缓解。一种可能的策略是访问M个独立的预言机,采用至少N个返回的相同结果(N < M)。这个方法的一个缺点是每增加一个预言机就会增加成本,并且大多数情况下,得到结果的时间也会增加,因为必须等待至少N个返回。另一个解决方式是Oraclize采用的TLS证明。使用TLS证明,Oraclize可以证明他们在某个时间确实访问了指定网站,并确实收到了提供的结果。虽然这不能阻止Oraclize在获得结果前多次查询,但如果结果不会在短时间内波动,还是值得信赖。
未来可以通过采用去中心化的预言机来解决更多的信任问题,目前仍需大量的工作。
已知应用
区块链上有很多采用预言机的合约。一个使用Oraclize服务的例子是Etherisc合约,预言机用于获取航班延误数据,如果航班延误,合约将向用户支付赔偿。
另一个预言机的实现是体育博彩合约Ethersquares。合约所有人充当预言机,用户借助投票机制可以验证所有者提供的结果是否正确。
推而广之
智能合约或去中心化应用由于其确定性和运行时间限制等原因,无法访问或不适合访问链下数据,这极大地限制了它们的应用范围。目前解决这个问题的主流方式就是采用预言机模式。
预言机模式的另个重要作用就是定时执行。不像传统程序能够通过系统调用或后台线程容易实现主动定时执行,区块链应用自己无法实现主动执行,它的调用只能通过客户端发起。但是,复杂的合约或应用往往需要完成一些定时任务,这就需要引入预言机模式。特别是在联盟链这种具有身份机制,并且链下彼此有信任基础的系统,引入预言机完成定时任务更容易被各方接受。
完整内容请查看智能合约设计模式系列
网友评论