帮助更新此页面

🌏

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

此页不完整。如果您是这方面的专家,请编辑这一页,并将您的智慧撒在上面。

Oracle(预言机)

上次编辑: , Invalid DateTime
Edit page

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

前置要求

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

什么是预言机

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

为什么需要预言机?

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

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

安全性

预言机的安全性等同于其数据源。 如果一个 dapp 使用 Uniswap 作为其 ETH/DAI 价格的预言机,攻击者就可能在 Uniswap 上篡改价格,以操纵 dapp 对当前价格的理解。 如何对付这个隐患的一个例子是 MakerDAO 所使用的 price feed oracles 。它将来自若干外部数据源的价格数据相互核对,而不是仅仅依靠单一来源。

使用方法

预言机即服务

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
显示全部
📋 复制

查看文档

预言机服务

构建一个预言机智能合约

这是 Pedro Costa 编写的一个预言机智能合约范例。 你可以在他的文章中找到更多注释: Implementing a Blockchain Oracle on Ethereum

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
显示全部
📋 复制

我们希望有更多关于构建预言机智能合约的文档。 如果你能帮助我们,就创建一个 PR 吧!

延伸阅读

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

帮助我们处理此页面

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

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

自由发挥 文档模板

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

编辑页面