帮助更新此页面

🌏

本页面有新版本,但现在只有英文版。请帮助我们翻译最新版本。

我们期待您的共同协作来完善本页内容。欢迎您在此添加任何您认为对其他人有帮助的内容。

Oracle(预言机)

上次编辑: , Invalid DateTime
编辑页面

预言机是将以太坊连接到链外、真实世界信息的数据源,以便您可以在智能合约中查询数据。 例如,预测市场的 dapp 使用预言机来根据事件进行结算。 预测市场可能要求您使用 ETH 来打赌下一任美国总统是谁。 他们将使用一个预言机来确认结果并向获胜者付款。

前置要求

请确保您熟悉节点共识机制智能合约解析,尤其是事件。

什么是预言机

预言机是区块链与真实世界之间的桥梁。 他们充当在链上的 API,你可以查询以获取信息到你的智能合约。 可以是任何信息,从价格信息到天气预报。 Oracle 可以是双向的,用于向真实世界“发送”数据。

观看 Patrick 解释 Oracle:

为什么需要预言机?

使用以太坊这样的区块链,您需要确保:网络中每个节点都能够重放每个交易并最终产生同样的结果。 API 引入潜在的变量数据。 如果使用价格 API 根据商定的 $USD 值发送 ETH,查询将在不同日期返回不同的结果。 更不用说,API 可能会被破解或弃用。 一旦发生这种情况,网络中的节点不会接受以太坊的当前状态,从而打破共识

Oracle 通过发布数据到区块链来解决这个问题, 以便任何重放交易的节点使用所有人都能看到的相同且不变的数据。 要做到这一点,Oracle 通常由智能合约和某些可以查询 API 的链下组件组成,然后定期发送交易以更新智能合约的数据。

Oracle 问题

如前所述,以太坊交易无法直接获取链下数据。 同时,依靠单一的事实来源提供数据并不安全,会使智能合约的去中心化失效。 这便是 Oracle 问题。

我们可以使用去中心化 Oracle 拉取多个数据来源,以避免 Oracle 问题; 如果某个数据源被破解或失效,智能合约仍将按预期运行。

安全性

Oracle 的安全性等同于其数据源。 如果一个去中心化应用程序使用 Uniswap 作为其 ETH/DAI 价格的 Oracle,攻击者就可以在 Uniswap 上篡改价格,以操纵该去中心化应用程序对当前价格的理解。 如何对付这个隐患的示例包括一种推送系统,如 MakerDAO 所使用的推送系统。它会将来自若干外部数据源的价格数据进行比对,而不是仅仅依靠单一来源。

架构

这是一个简单 Oracle 架构的示例,但还有更多方法可用来触发链下计算。

  1. 通过您的智能合约事件发出一份日志
  2. 一个链下服务已经订阅了(通常使用类似 JSON-RPC eth_subscribe的命令)这些特定的日志。
  3. 该链下服务会继续做一些日志所定义的任务。
  4. 链下服务使用智能合约二级交易中要求的数据作出回应。

如此可以 1 对 1 的方式获取数据,但是为了提高安全性,您可能希望对链下数据的收集方式进行去中心化。

下一步可能是建立这些节点的网络,将这些节点调用到不同的 API 和源,并在链上汇总数据。

Chainlink 链下报告 (Chainlink OCR) 让链下 Oracle 网络相互通信、为它们的回应附加加密签名、汇总它们的链下回应,并且仅随结果发送一个交易,以改进这种方法。 这样可以减少消耗的 gas,但您仍然可以得到去中心化数据的保证,因为每个节点都对其负责的交易进行了签名,使得发送交易的节点无法更改该交易。 如果节点不进行交易处理,而由下一个节点发送交易,上报规则将会介入。

使用方法

使用诸如 Chainlink 等服务,您可以引用链上已经从现实世界中提取并汇总的去中心化数据。 这有点像公共空间,但仅用于去中心化数据。 您也可以构建自己的模块化 Oracle 网络来获取您想要的任意自定义数据。 此外,您可以执行链下运算并向真实世界发送信息。 Chainlink 已具备相关基础设施,以:

以下为如何从智能合约中获取最新 ETH 价格的示例:

1pragma solidity ^0.6.7;
2
3import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol";
4
5contract PriceConsumerV3 {
6
7 AggregatorV3Interface internal priceFeed;
8
9 /**
10 * Network: Kovan
11 * Aggregator: ETH/USD
12 * Address: 0x9326BFA02ADD2366b30bacB125260Af641031331
13 */
14 constructor() public {
15 priceFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331);
16 }
17
18 /**
19 * Returns the latest price
20 */
21 function getLatestPrice() public view returns (int) {
22 (
23 uint80 roundID,
24 int price,
25 uint startedAt,
26 uint timeStamp,
27 uint80 answeredInRound
28 ) = priceFeed.latestRoundData();
29 return price;
30 }
31}
32
显示全部
📋 复制

您可以结合此链接进行该测试

查看文档

Chainlink VRF(可验证随机函数)是为智能合约设计的可证明公平性和可验证的随机性来源。 智能合约开发者可以使用 Chainlink VRF 作为防篡改随机数生成 (RNG),为依赖不可预测结果的任何应用程序建立可靠的智能合约:

  • 区块链游戏和 NFT
  • 职责和资源的随机分配(例如随机分配审判者审理案件)
  • 为共识机制选择一个有代表性的示例

随机数很难,因为区块链是决定性的。

在数据源之外使用 Chainlink Oracle 需要遵循使用 Chainlink 的请求和接收周期。 它们使用 LINK 代币向 Oracle 供应商发送 Oracle gas,以获得响应。 LINK 代币专门设计用于与 Oracle 协同作用,基于升级后的 ERC-677 代币,此代币向后兼容 ERC-20。 以下代码如果部署到 Kovan 测试网,将获得一个经加密验证的随机数。 要提出请求,请使用您可以从 Kovan LINK Faucet 获得的测试网 LINK 代币,为合约提供资金。

1
2pragma solidity 0.6.6;
3
4import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol";
5
6contract RandomNumberConsumer is VRFConsumerBase {
7
8 bytes32 internal keyHash;
9 uint256 internal fee;
10
11 uint256 public randomResult;
12
13 /**
14 * Constructor inherits VRFConsumerBase
15 *
16 * Network: Kovan
17 * Chainlink VRF Coordinator address: 0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9
18 * LINK token address: 0xa36085F69e2889c224210F603D836748e7dC0088
19 * Key Hash: 0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4
20 */
21 constructor()
22 VRFConsumerBase(
23 0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9, // VRF Coordinator
24 0xa36085F69e2889c224210F603D836748e7dC0088 // LINK Token
25 ) public
26 {
27 keyHash = 0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4;
28 fee = 0.1 * 10 ** 18; // 0.1 LINK (varies by network)
29 }
30
31 /**
32 * Requests randomness from a user-provided seed
33 */
34 function getRandomNumber(uint256 userProvidedSeed) public returns (bytes32 requestId) {
35 require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
36 return requestRandomness(keyHash, fee, userProvidedSeed);
37 }
38
39 /**
40 * Callback function used by VRF Coordinator
41 */
42 function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
43 randomResult = randomness;
44 }
45}
46
显示全部

Chainlink API 调用 是以网络调用的传统方式从链下获取数据的最简单方式:API 调用。 做一个这样的实例,并且只有一个 oracle,使得它在本质上是集中的。 要保持它真正去中心化,智能合约平台需要使用在外部数据市场 中找到的许多节点。

在 kovan 网络上部署以下代码来测试

这也遵循了 Oracle 的请求和接收周期,并且需要从 Kovan LINK(Oracle gas)获得资金才能工作。

1pragma solidity ^0.6.0;
2
3import "@chainlink/contracts/src/v0.6/ChainlinkClient.sol";
4
5contract APIConsumer is ChainlinkClient {
6
7 uint256 public volume;
8
9 address private oracle;
10 bytes32 private jobId;
11 uint256 private fee;
12
13 /**
14 * Network: Kovan
15 * Oracle: 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e
16 * Job ID: 29fa9aa13bf1468788b7cc4a500a45b8
17 * Fee: 0.1 LINK
18 */
19 constructor() public {
20 setPublicChainlinkToken();
21 oracle = 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e;
22 jobId = "29fa9aa13bf1468788b7cc4a500a45b8";
23 fee = 0.1 * 10 ** 18; // 0.1 LINK
24 }
25
26 /**
27 * Create a Chainlink request to retrieve API response, find the target
28 * data, then multiply by 1000000000000000000 (to remove decimal places from data).
29 */
30 function requestVolumeData() public returns (bytes32 requestId)
31 {
32 Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
33
34 // Set the URL to perform the GET request on
35 request.add("get", "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD");
36
37 // Set the path to find the desired data in the API response, where the response format is:
38 // {"RAW":
39 // {"ETH":
40 // {"USD":
41 // {
42 // "VOLUME24HOUR": xxx.xxx,
43 // }
44 // }
45 // }
46 // }
47 request.add("path", "RAW.ETH.USD.VOLUME24HOUR");
48
49 // Multiply the result by 1000000000000000000 to remove decimals
50 int timesAmount = 10**18;
51 request.addInt("times", timesAmount);
52
53 // Sends the request
54 return sendChainlinkRequestTo(oracle, request, fee);
55 }
56
57 /**
58 * Receive the response in the form of uint256
59 */
60 function fulfill(bytes32 _requestId, uint256 _volume) public recordChainlinkFulfillment(_requestId)
61 {
62 volume = _volume;
63 }
64}
65
显示全部

您可以阅读 Chainlink 开发者博客,进一步了解 Chainlink 应用程序。

Oracle 服务

建立一个 Oracle 智能合约

此为 Pedro Costa 编写的 Oracle 合约示例。 您可以在他的以下文章中找到更多注解:Implementing a Blockchain Oracle on Ethereum(在以太网部署区块链 Oracle)

1pragma solidity >=0.4.21 <0.6.0;
2
3contract Oracle {
4 Request[] requests; //list of requests made to the contract
5 uint currentId = 0; //increasing request id
6 uint minQuorum = 2; //minimum number of responses to receive before declaring final result
7 uint totalOracleCount = 3; // Hardcoded oracle count
8
9 // defines a general api request
10 struct Request {
11 uint id; //request id
12 string urlToQuery; //API url
13 string attributeToFetch; //json attribute (key) to retrieve in the response
14 string agreedValue; //value from key
15 mapping(uint => string) anwers; //answers provided by the oracles
16 mapping(address => uint) quorum; //oracles which will query the answer (1=oracle hasn't voted, 2=oracle has voted)
17 }
18
19 //event that triggers oracle outside of the blockchain
20 event NewRequest (
21 uint id,
22 string urlToQuery,
23 string attributeToFetch
24 );
25
26 //triggered when there's a consensus on the final result
27 event UpdatedRequest (
28 uint id,
29 string urlToQuery,
30 string attributeToFetch,
31 string agreedValue
32 );
33
34 function createRequest (
35 string memory _urlToQuery,
36 string memory _attributeToFetch
37 )
38 public
39 {
40 uint lenght = requests.push(Request(currentId, _urlToQuery, _attributeToFetch, ""));
41 Request storage r = requests[lenght-1];
42
43 // Hardcoded oracles address
44 r.quorum[address(0x6c2339b46F41a06f09CA0051ddAD54D1e582bA77)] = 1;
45 r.quorum[address(0xb5346CF224c02186606e5f89EACC21eC25398077)] = 1;
46 r.quorum[address(0xa2997F1CA363D11a0a35bB1Ac0Ff7849bc13e914)] = 1;
47
48 // launch an event to be detected by oracle outside of blockchain
49 emit NewRequest (
50 currentId,
51 _urlToQuery,
52 _attributeToFetch
53 );
54
55 // increase request id
56 currentId++;
57 }
58
59 //called by the oracle to record its answer
60 function updateRequest (
61 uint _id,
62 string memory _valueRetrieved
63 ) public {
64
65 Request storage currRequest = requests[_id];
66
67 //check if oracle is in the list of trusted oracles
68 //and if the oracle hasn't voted yet
69 if(currRequest.quorum[address(msg.sender)] == 1){
70
71 //marking that this address has voted
72 currRequest.quorum[msg.sender] = 2;
73
74 //iterate through "array" of answers until a position if free and save the retrieved value
75 uint tmpI = 0;
76 bool found = false;
77 while(!found) {
78 //find first empty slot
79 if(bytes(currRequest.anwers[tmpI]).length == 0){
80 found = true;
81 currRequest.anwers[tmpI] = _valueRetrieved;
82 }
83 tmpI++;
84 }
85
86 uint currentQuorum = 0;
87
88 //iterate through oracle list and check if enough oracles(minimum quorum)
89 //have voted the same answer has the current one
90 for(uint i = 0; i < totalOracleCount; i++){
91 bytes memory a = bytes(currRequest.anwers[i]);
92 bytes memory b = bytes(_valueRetrieved);
93
94 if(keccak256(a) == keccak256(b)){
95 currentQuorum++;
96 if(currentQuorum >= minQuorum){
97 currRequest.agreedValue = _valueRetrieved;
98 emit UpdatedRequest (
99 currRequest.id,
100 currRequest.urlToQuery,
101 currRequest.attributeToFetch,
102 currRequest.agreedValue
103 );
104 }
105 }
106 }
107 }
108 }
109}
110
显示全部
📋 复制

我们希望有更多关于建立 Oracle 智能合约的相关文档。 如果您能帮助我们,就创建一个 PR 吧!

延伸阅读

文章

视频

教程

░░░░░░░░░▄░░░░░░░░░░░░░░▄░░░░ ░░░░░░░░▌▒█░░░░░░░░░░░▄▀▒▌░░░ ░░░░░░░░▌▒▒█░░░░░░░░▄▀▒▒▒▐░░░ ░░░░░░░▐▄▀▒▒▀▀▀▀▄▄▄▀▒▒▒▒▒▐░░░ ░░░░░▄▄▀▒░▒▒▒▒▒▒▒▒▒█▒▒▄█▒▐░░░ ░░░▄▀▒▒▒░░░▒▒▒░░░▒▒▒▀██▀▒▌░░░ ░░▐▒▒▒▄▄▒▒▒▒░░░▒▒▒▒▒▒▒▀▄▒▒▌░░ ░░▌░░▌█▀▒▒▒▒▒▄▀█▄▒▒▒▒▒▒▒█▒▐░░ ░▐░░░▒▒▒▒▒▒▒▒▌██▀▒▒░░░▒▒▒▀▄▌░ ░▌░▒▄██▄▒▒▒▒▒▒▒▒▒░░░░░░▒▒▒▒▌░ ▀▒▀▐▄█▄█▌▄░▀▒▒░░░░░░░░░░▒▒▒▐░ ▐▒▒▐▀▐▀▒░▄▄▒▄▒▒▒▒▒▒░▒░▒░▒▒▒▒▌ ▐▒▒▒▀▀▄▄▒▒▒▄▒▒▒▒▒▒▒▒░▒░▒░▒▒▐░ ░▌▒▒▒▒▒▒▀▀▀▒▒▒▒▒▒░▒░▒░▒░▒▒▒▌░ ░▐▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒░▒░▒▒▄▒▒▐░░ ░░▀▄▒▒▒▒▒▒▒▒▒▒▒░▒░▒░▒▄▒▒▒▒▌░░ ░░░░▀▄▒▒▒▒▒▒▒▒▒▒▄▄▄▀▒▒▒▒▄▀░░░ ░░░░░░▀▄▄▄▄▄▄▀▀▀▒▒▒▒▒▄▄▀░░░░░ ░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▀▀░░░░░░░░

帮助我们处理此页面

如果您是这方面的专家,并想发表意见,那么编辑此页分享您的智慧。

您将获得褒奖并会为以太坊社区提供帮助!

自由发挥 文档模板

有任何疑问? 请在我们的频道中进行询问 Discord服务器

编辑页面