智能合约本身没有访问区块链外部数据的能力。而外部的数据对于大多数智能合约应用场景来说都是至关重要的,所以这一功能的缺失限制了智能合约的更进一步的发展。比如涉及金融,供应链,保险,安全等诸多领域的智能合约都依| ~ Q } I a赖于外部事件。智能合约无法获取关键的链下事件信息,比如价格变动,物流/ + : B M . ? 5日期,以及支付能力。没有这些外部的信息,大多数智能合约的应用都是没有实际应用价值的。
为什么智能合约无法自主获取外部数据?
因为区块链网络是确定性的。智能合约在区块链这种去中心化的,_ 1 7 q t B ? ` [自我调节的基础设施上运行,其中的任何信息都是确定的,可验证的。区块链可以正常运行,必须在各个参与方之间达成共识。为了实现这个目标,人们设B _ f I计了[各种]*共识机制*,比如工作量证明(Proof of Work),权益证明(Proof of Stake),行动证明(Proof ofv } [ - J u Activity)。这l Z A 0 4 T些共识机制使得区块链这一分布式的系统形成一个统一的状态。
有了这些共识机制,就可以验证网络上的交易,确定统一公开账本的状态。这种设计允许区块链以公平和安全的方式运行,而无需使3 n $ i ~ V r P用集中式身份验证。因此,区块链整体上是*确定性状态机*。
但是区块链外部的数据6 D X M v Y r G e是非确定性的,因为从某种意义上说,它是通过区块链的历史无法验证的值。外部数据会受各种因素的影响动态变化。价格的频繁变化,& d H /公司实时更新物流信息,物流变化的更新,等等。因为这些信息是不确定的,智能合约没有一种方式可以验证这些数据进而达成共识。因此,无法确认为真实的数据对区块链没有任何意义。
如何把外部数据提供给智能合约?
通过区块链中间件,特别是安全可靠的预言机可以实现。预言机扮演者数据代理人的角色,连接外部数据与智能T t 4合约。它充当区块链数据API之间的中间层,将数据转换为区块链可以读取的格式。此外,预言机还负责验证外部数据的正确性,因此可信赖的? H 7 | L %来源(信任最小化)至@ X d * / q Z关重要。
但是,在中心化的预言机服务中,^ I v预言机会有被攻击的可能性(被黑客攻击,服务停机,} , Z O K E ` c I数据篡改等),这导致智能合约丢失了确定性和可3 K h [靠性这一最关键的特性,从而使n 0 9 大l _ 2 X z n $ &多数基于现实场景的智能合约用例的不可用。如何解决这一问题呢,答案是去中心化的预言机网络。或者说是Chainlink。
Chainlink通N o } =过提供与智能合约开发者的安全性和可靠性相匹配的去中心化的预言机网络来解决联通性问题。通过外部适配器(也被称为chainlinks),区块链可以安全地与chainlinked= / C M API连接。开发人员可以方便地将他4 c f [ A _ / 9 Z们自己的智能合约与预先编写的Chainlink API套件连进行连接,从而建立一个链下的预言机连接。
例如,o ( b Q k B q假设您开发了一个智能合约,可以把代币发送到一个地址。Chainlink(输出预言机)通过PayPal发送离线支付。然后,预言机可以V w 9 G 8 ~ { [基于离线支付在链上提供收据,从而完成区块链系统中的交易循环。
有了Chainlink,智能合约现在能够通过一个去中心化的预言机网络A . U b t U l在大多数现实世界的应用场景中正常运行。Chainlin[ ; o M * Zk通过安全可靠得方式满足| p ? !智能合约的预设条件,因此所有相关方都可以从智能合约生态系统的巨大潜力中受益。
代码层面,预言机是如何工作的?
使用预言机需要由足够数量的LINK代币,以及一些基本的Solididy知识,Solidity是编写智& n e W z能合约的语言。请参考Chainlink的[Solidity接口文档]来了解Chainlink的所有方法。最后,wi* e j 5 4 0 9eld能从Chainlink的预言机请求数据,你需要首先在你的合约中继承ChainlinkClie& . 9 Z D k r (nt合约。你可以通过[这里]的例子作为指导来创建合约,也可以参考[文档]。
预言机可以帮助智能合约请求和^ M I i O获取区块链的外部数据。我们通过jobs来执行预言机任e Y W务来完成请求。这些jobs有与预言机地址相对应的JobID。这些Job由一系列任务,或称为[适配器],所组成,在指定JobID发送请求时, 这些任务或适配器定义了要完成的工) p % $作。
为了更好地展示预言机如何在代码层面运行,我们通过一个请求以太网价格的示例智能合约来解释:
contract Myo ~ ) ) Q y s BCi v y hontracF S x ! . h . @ Yt i{ j ! d O 5 3s ChainlinkClient {
address owner;
constructor public {
// Set the address for the LINK token on the pu? 1 6 3 B 6blic network
// 设置公共网络的LINK代币发行地址
setPublicChainlinkToken;
owner = msg.sender;
}
// Additional functions here...
// 其他的函数...
}
首先,为了能使用Chainlink网络,你需要在你的合约中继承v l j 6 T U OChainlinkClient合约。这是一个测试9 $ = p网和正式网通用的构造函数体。这是因为我们使用了setPublicU O . % Z P Z }Chainlinkr ) N 9 i | $Token方法,这个方法会根据合约部署的网络环境,自动的获取LINK代Y X e Z币的发行地址。
所有当前的预~ | 0 l H ?言机和LINK代币地址都可以在[这里]。存储LINK代币地址后,您可以指定预言机合约地址及其相应的JobID来创建请求。
// Creates a ChainG ( C : x Tlink requesE $ vt to the specified oracle with a given Job ID
// 通过给定预言机地址和JobID来创建Chaij znlink请求
function requestEthereumPrice(address _oracle, bytes32 _jobId)
public
onlyT , K 9Owner
{
// newRI 6 : g m Xequest takes a JobID, a callback address, and cB $ x ; E Y A %allbackv : F H w function as input
// 新的请求需要JobID,回调地址和回调函数作为输入
Chainlink.Request memory req8 r i 0 = buildCh5 I i w 9 *ainlinkRequest(_jobId, this, this.fulfill.selector);
// Adds a URL with the key \"get] Z 9 2 / o %\" to the request parameters
// 添加一个URL设置\"get\"作为key来请求参数n _ 5req.add(\"get\", \"https://min-api.cryptocompare.c/ # 9 ] s X K R Xom/data/price?fsym=ETH&amb - 5p;tsyms=USD\");
// Uses input param (dot-delimited string) as the \"path\" in the res s ~quest parameters
// 使用点分隔的字符串作为请c y % V求参数中的path
req.add(\"path\o x * m t | ", \"USD\");
// Adds an integer with the key \"times\" to the request parameters
// 为请求参数设置倍数
req.addInt(q I N I ( : m\"times\", 100);
// Sends the request with 1 LINK to the oracle contract
// 向预言机t v ( ) G + Z # /还有发送1 LINK
sendChainlinkRequestTo(_oracle, req, ORACLEU F 2 o s ^_PAYMENT);
}
请求通过buildChainlinkRequest方法创建,接受相应的参数填写- ~ p Y到Chainlink.Request结构体中作为负载。你可以使用req.add向请F N ) m 5 J求添加参数,比如URL。一旦你准备好了所有的必须参数,可以通过s1 a }endChainlinkRequestTo方法发送到特定的预言机| 0 3 ^ v k合约地址,并支付1 LINK的代币,作为给节点运营方的奖励。请注意,在主网上,支付金额是各不相同的,但是为了方便大家理解,我们目前设置了为每次R Z J R | E !请求花费1 LINK。由于测试网络上这些代币没有任何价值,所以我们可以通过[水龙头]来获取。
uint256 constant private EXPECTED_RESPONSES = 3;
uint256 private prices;
uint256 public avgPrice;
funcZ 7 h . dtion fulfil: / T [ x vlEthereumPrice(bytes32 _requestId, uint256 _price)X % = y R z g
public
recordChainlinkFulfillment(_requestId)
{
if(prices.push(_pri7 R 5 a ]ce) == EXPECTED_RESPONSES) {
uint256 sum;
for(uint i = 0; i sum = sum.add(prices[i]);
dz F Eelete price} t 9 J } _s[i];
}
avx N , C N ` agPrice = sum.div(EXPECTED_RESPONSES);
}
}
当Chainlink节点从指定的端点取回结果后,预言机合约会调用回填方法(fulfillment method)。回填方法应该通过recordr O n c ; AChainlinkFulfill_ , /ment修改器或validateChainlinkCallback方法保护起来。这样可以防止无关的人调用该方法,并且只能根据你的请求填写相应的结果。
将u e ^ H ? ]所有这V : v x & ? / n些组合到一起,就可以完成一个可以在以太坊测试网络上可以获取外部数据的预言机合约了。完整代码见[这# ~ ^ E里]。
最后,如果您有任何问题,请参阅我们的[技术文档]。