Lanjut ke konten utama
Change page

Oracle

Terakhir diedit: @fuji.anggara10(opens in a new tab), 25 Juli 2024

Oracle adalah pengumpan data yang menghubungkan Ethereum ke informasi dunia nyata off-chain, sehingga Anda dapat membuat kueri data dalam kontrak pintar Anda. Sebagai contoh, dapp pasar prediksi menggunakan oracle untuk menyelesaikan pembayaran berdasarkan kejadian. Pasar prediksi mungkin meminta Anda untuk mempertaruhkan ETH siapa yang akan menjadi presiden Amerika Serikat berikutnya. Mereka akan menggunakan oracle untuk mengonfirmasi hasilnya dan membayar kepada para pemenang.

Prasyarat

Pastikan Anda sudah familiar dengan node, mekanisme konsensus, dan anatomi kontrak pintar, khususnya aksi.

Apa itu oracle

Oracle merupakan sebuah jembatan yang menghubungkan antara blockchain dan dunia nyata. Oracle bertindak sebagai API on-chain yang dapat Anda tanya untuk mendapatkan informasi tentang kontrak pintar Anda. Ini bisa berupa apa saja mulai dari informasi harga hingga laporan cuaca. Oracle dapat juga bersifat dua arah, yang digunakan untuk "mengirim" data ke dunia nyata.

Tonton Patrick menjelaskan Oracle:

Mengapa oracle dibutuhkan?

Dengan blockchain seperti Ethereum, Anda membutuhkan setiap node di jaringan untuk dapat memutar ulang setiap transaksi dan berakhir dengan hasil yang sama, yang terjamin. API mengenalkan data yang berpotensi menjadi variabel. Jika Anda mengirimkan ETH sesuai nilai $USD yang disetujui menggunakan API harga, kueri akan kembali dengan hasil yang berbeda setiap harinya. Belum lagi, API bisa diretas atau tidak digunakan lagi. Jika ini terjadi, node di jaringan tidak akan bisa berkesesuaian dengan state Ethereum saat ini, yang secara efektif melanggar konsensus.

Oracle menyelesaikan masalah ini dengan mencatatkan data pada blockchain. Jadi node mana saja yang memutar ulang transaksi akan menggunakan data sama yang tidak bisa dirubah yang dicatatkan untuk dilihat semua orang. Untuk melakukan ini, oracle biasanya terdiri dari kontrak pintar dan beberapa komponen off-chain yang dapat meminta API, lalu mengirim transaksi secara berkala untuk memperbarui data kontrak pintar.

Masalah oracle

Seperti yang kami sebutkan, transaksi Ethereum tidak dapat mengakses data off-chain secara langsung. Pada saat yang sama, mengandalkan satu sumber kebenaran untuk menyediakan data tidaklah aman dan melanggar desentralisasi sebuah kontrak pintar. Ini dikenal sebagai masalah oracle.

Kita dapat menghindari masalah oracle dengan menggunakan oracle terdesentralisasi yang menarik data dari berbagai sumber data; jika sata sumber data diretas atau gagal, kontrak pintar akan tetap berfungsi sebagaimana mestinya.

Keamanan

Oracle seaman sumber datanya. Jika dapp menggunakan Uniswap sebagai oracle untuk pengumpan harga ETH/DAI-nya, penyerang dapat memindahkan harga di Uniswap untuk memanipulasi kemampuan dapp dalam memahami harga terkini. Contoh cara melawannya adalah sistem pengumpan(opens in a new tab) seperti yang digunakan oleh MakerDAO, yang menyusun data harga dari banyak pengumpan harga eksternal ketimbang hanya bergantung pada satu sumber.

Arsitektur

Ini adalah sebuah contoh arsitektur Oracle sederhana, tetapi masih ada lebih banyak cara dari pada hanya ini untuk memicu komputasi off-chain.

  1. Keluarkan log dengan aksi kontrak pintar Anda
  2. Layanan off-chain telah berlangganan (biasanya menggunakan sesuatu seperti perintah eth_subscribe JSON-RPC) untuk jenis log khusus ini.
  3. Layanan off-chain melanjutkan dengan melakukan beberapa tugas yang ditentukan oleh log.
  4. Layanan off-chain menanggapi dengan mengirimkan data yang diminta dalam transaksi sekunder ke kontrak pintar.

Inilah cara mendapatkan data dengan cara 1 lawan 1, namun untuk meningkatkan keamanan, Anda mungkin ingin mendesentralisasikan cara mengumpulkan data off-chain Anda.

Langkah berikutnya mungkin membuat jaringan node ini melakukan pemanggilan terhadap API dan sumber yang beragam, dan mengumpulkan datanya secara on-chain.

Chainlink Off-Chain Reporting(opens in a new tab) (Chainlink OCR) telah meningkatkan metodologi ini dengan membuat jaringan oracle off-chain berkomunikasi satu sama lain, secara kriptografi menandatangani respons mereka, mengumpulkan respons off-chain mereka, dan hanya mengirim satu transaksi on-chain dengan hasilnya. Dengan cara ini, lebih sedikit gas terpakai, tetapi Anda masih mendapatkan jaminan data terdesentralisasi karena setiap node telah menandatangani bagian transaksi mereka, yang membuatnya tidak dapat diubah oleh node yang mengirimkan transaksi. Kebijakan eskalasi melakukan fungsinya jika node tidak melakukan transaksi, dan node berikutnya mengirimkan transaksi.

Penggunaan

Dengan menggunakan layanan seperti Chainlink, Anda dapat memberi referensi data terdesentralisasi on-chain yang telah ditarik dari dunia nyata dan dikumpulkan. Ini seperti kepemilikan umum, tetapi untuk data terdesentralisasi. Anda dapat juga membangun jaringan oracle modular sendiri untuk mendapatkan data yang disesuaikan yang Anda cari. Selain itu, Anda dapat melakukan komputasi off-chain dan sekaligus mengirimkan informasi ke dunia nyata. Chainlink memiliki infrastruktur bawaan untuk:

Ini adalah contoh cara mendapatkan harga ETH terbaru di dalam kontrak pintar Anda dengan menggunakan sebuah pengumpan harga Chainlink:

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}
Tampilkan semua
Salin

Anda dapat menguji ini dalam remix dengan mengklik tautan ini(opens in a new tab)

Lihat dokumen(opens in a new tab)

Chainlink VRF (Verifiable Random Function) adalah sumber keacakan yang terbukti adil dan terverifikasi yang dirancang untuk kontrak pintar. Para pengembang kontrak pintar dapat menggunakan Chainlink VRF sebagai random number generation (RNG) yang tahan perubahan untuk membangun kontrak pintar handal untuk aplikasi apa pun yang mengandalkan hasil yang tidak terprediksi:

  • Game dan NFT blockchain
  • Penetapan acak untuk tugas dan sumber daya (misalnya secara acak menetapkan para hakim untuk menangani kasus)
  • Memilih sampel representatif untuk mekanisme konsensus

Angka acak sulit dihasilkan karena blockchain bersifat deterministik.

Bekerja dengan Oracle Chainlink di luar pengumpan data mengikuti siklus minta dan terima(opens in a new tab) dengan Chainlink. Ini menggunakan token LINK untuk mengirimkan gas oracle kepada para penyedia oracle sebagai respons pengembalian. Token LINK khususnya dirancang untuk berfungsi dengan oracle dan didasarkan pada token ERC-677 yang ditingkatkan, yang kompatibel ke belakang dengan ERC-20. Kode berikut ini, jika digunakan pada testnet Kovan akan mengambil angka acak yang terbukti secara kriptografis. Untuk membuat permintaan, taruhlah dana dalam kontrak dengan beberapa token LINK testnet yang bisa Anda dapatkan dari Keran LINK Kovan(opens in a new tab).

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}
Tampilkan semua

Kontrak pintar tidak dapat memicu atau memulai fungsinya sendiri pada waktu arbitrari atau dalam kondisi arbitrari. Perubahan state hanya akan terjadi ketika akun lainnya memulai transaksi (seperti pengguna, oracle, atau kontrak). Jaringan Penjaga Chainlink(opens in a new tab) menyediakan opsi bagi kontrak pintar untuk men-outsource tugas pemeliharaan reguler dalam cara yang terpercaya, sederhana, dan terdesentralisasi.

Untuk menggunakan Penjaga Chainlink, suatu kontrak pintar harus mengimplementasikan KeeperCompatibleInterface(opens in a new tab), yang terdiri dari dua fungsi:

  • checkUpkeep - Memeriksa apakah kontrak membutuhkan pemeliharaan yang perlu dilakukan.
  • performUpkeep - Melakukan pemeliharaan pada kontrak, jika diinstruksikan oleh checkUpkeep.

Contoh di bawah adalah suatu kontrak penghitung sederhana. Variabel counter ditambahkan sebanyak satu oleh setiap pemanggilan ke performUpkeep. Anda dapat melihat kode berikut ini dengan menggunakan Remix(opens in a new tab)

1// SPDX-License-Identifier: MIT
2pragma solidity ^0.7.0;
3
4// KeeperCompatible.sol imports the functions from both ./KeeperBase.sol and
5// ./interfaces/KeeperCompatibleInterface.sol
6import "@chainlink/contracts/src/v0.7/KeeperCompatible.sol";
7
8contract Counter is KeeperCompatibleInterface {
9 /**
10 * Public counter variable
11 */
12 uint public counter;
13
14 /**
15 * Use an interval in seconds and a timestamp to slow execution of Upkeep
16 */
17 uint public immutable interval;
18 uint public lastTimeStamp;
19
20 constructor(uint updateInterval) {
21 interval = updateInterval;
22 lastTimeStamp = block.timestamp;
23
24 counter = 0;
25 }
26
27 function checkUpkeep(bytes calldata /* checkData */) external override returns (bool upkeepNeeded, bytes memory /* performData */) {
28 upkeepNeeded = (block.timestamp - lastTimeStamp) > interval;
29 // We don't use the checkData in this example. checkData ditentukan ketika Upkeep didaftarkan.
30 }
31
32 function performUpkeep(bytes calldata /* performData */) external override {
33 lastTimeStamp = block.timestamp;
34 counter = counter + 1;
35 // We don't use the performData in this example. performData dibuat oleh pemanggilan Penjaga ke fungsi checkUpkeep Anda
36 }
37}
Tampilkan semua

Setelah menyebarkan kontrak yang kompatibel dengan Penjaga, Anda harus mendaftarkan kontrak untuk Upkeep(opens in a new tab) dan membiayainya dengan LINK, untuk memberitahu Jaringan Penjaga tentang kontrak Anda, sehingga pemeliharaan Anda dilakukan secara berkelanjutan.

Proyek Penjaga

Pemanggilan API Chainlink(opens in a new tab) merupakan cara termudah untuk mendapatkan data dari dunia off-chain dengan cara tradisional yang memfungsikan web: pemanggilan API. Melakukan satu instance ini dan hanya memiliki satu oracle akan membuatnya terpusat secara alami. Agar benar-benar terdesentralisasi, platform kontrak pintar perlu menggunakan banyak node yang ditemukan di pasar data eksternal(opens in a new tab).

Sebarkan kode berikut ini di remix pada jaringan kovan untuk mengujinya(opens in a new tab)

Ini juga mengikuti siklus minta dan terima oracle dan mengharuskan kontrak untuk didanai dengan LINK Kovan (gas oracle) agar berfungsi.

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}
Tampilkan semua

Anda dapat mempelajari lebih lanjut tentang penerapan Chainlink dengan membaca Blog pengembang Chainlink(opens in a new tab).

Layanan Oracle

Membuat sebuah oracle kontrak pintar

Ini adalah suatu contoh kontrak oracle oleh Pedro Costa. Anda dapat menemukan penjelasan lebih lanjut di artikelnya: Mengimplementasikan Oracle Blockchain di Ethereum(opens in a new tab).

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 length = requests.push(Request(currentId, _urlToQuery, _attributeToFetch, ""));
41 Request storage r = requests[length-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}
Tampilkan semua
Salin

Kami ingin lebih banyak dokumentasi tentang pembuatan sebuah oracle kontrak pintar. Jika Anda dapat membantu, buat sebuah PR!

Bacaan lebih lanjut

Artikel

Video

Tutorial

Apakah artikel ini membantu?