Salt la conținutul principal
Change page

Oracole

Ultima modificare: @nicklcanada(opens in a new tab), 25 iulie 2024

Oracolele sunt fluxuri de date care conectează Ethereum la informații din lumea reală, în afara lanțului, astfel încât să puteţi interoga datele în contractele dvs. inteligente. De exemplu, aplicațiile dapp de predicție de piață utilizează oracole pentru a efectua plăți pe baza evenimentelor. O piață de predicție vă poate cere săpariaţi ETH pe următorul președinte al Statelor Unite. Va folosi un oracol pentru a confirma rezultatul și pentru a plăti câștigătorilor.

Condiții prealabile

Aveţi grijă să vă familiarizaţi cu nodurile, mecanismele de consens și anatomia contractelor inteligente, în special cu evenimentele.

Ce este un oracol

Un oracol este o punte între blockchain și lumea reală. Acestea acționează ca API-uri pe lanț pe care le puteţi interoga pentru a obține informații în contractele inteligente. Acest lucru ar putea fi orice, de la informații de preț la rapoarte meteorologice. Oracolele pot fi şi bidirecționale, adică folosite pentru a „trimite” date în lumea reală.

Urmăriți-l pe Patrick explicând Oracolele:

De ce este nevoie de oracole?

Cu un blockchain precum Ethereum, aveți nevoie de fiecare nod din rețea ca să repete fiecare tranzacție și să ajungă la același rezultat, garantat. API-urile introduc date potențial variabile. Dacă ați trimite ETH pe baza unei valori $USD convenite folosind un API de prețuri, interogarea ar răspunde printr-un rezultat diferit de la o zi la alta. Ca să nu mai spunem că API-ul ar putea fi piratat sau perimat. Dacă se întâmplă acest lucru, nodurile din rețea nu ar fi în măsură să se pună de acord asupra stării actuale a lui Ethereum, încălcând în mod efectiv consensul.

Oracolele rezolvă această problemă postând datele pe blockchain. De aceea, orice nod care redă tranzacția va utiliza aceleași date imuabile care sunt postate pentru ca toți să le vadă. Pentru a face acest lucru, un oracol este format de obicei dintr-un contract inteligent și unele componente din lanț care pot interoga API-urile, iar apoi trimit periodic tranzacții pentru a actualiza datele contractului inteligent.

Problema oracolului

După cum am menționat, tranzacțiile Ethereum nu pot accesa direct datele din afara lanțului. Totodată, dacă ne bizuim pe o singură sursă a adevărului pentru a furniza date, acest lucru este nesigur și invalidează descentralizarea unui contract inteligent. Aceasta este cunoscută sub numele de problema oracolului.

Putem evita problema oracolului prin utilizarea unui oracol descentralizat care preia date din mai multe surse; dacă una dintre sursele de date este piratată sau eșuează, contractul inteligent va funcționa în continuare așa cum a fost stabilit.

Securitate

Un oracol este la fel de securizat ca sursele sale de date. Dacă o aplicație dapp utilizează Uniswap ca oracol pentru alimentarea cu prețul ETH/DAI, un atacator poate muta prețul pe Uniswap pentru a manipula înțelegerea prețului curent de către aplicația dapp. Un exemplu de modalitate de combatere a acestui lucru este un sistem de alimentare(opens in a new tab) precum cel utilizat de MakerDAO, care colaționează datele despre prețuri din mai multe surse externe în loc să se bazeze doar pe una singură.

Arhitectură

Acesta este un exemplu de arhitectură Oracle simplă, însă există mai multe moduri de a declanșa calculul off-chain.

  1. Emiteți un jurnal cu evenimentul contractului dvs. inteligent
  2. Un serviciu off-chain s-a abonat (de regulă folosind ceva de genul comenzii JSON-RPC eth_subscribe) la aceste jurnale specifice.
  3. Serviciul off-chain trece la efectuarea unor sarcini definite de jurnal.
  4. Serviciul off-chain răspunde contractului inteligent prin datele solicitate într-o tranzacție secundară.

Aceasta este modalitatea de obținere a datelor într-o manieră 1 la 1, dar, pentru a îmbunătăți securitatea, poate doriți să descentralizați modul în care colectați datele off-chain.

Următoarea etapă ar putea fi aceea de a avea o rețea a acestor noduri care să efectueze acele apeluri către diferite API-uri și surse și să agrege datele pe lanț.

Chainlink Off-Chain Reporting(opens in a new tab) (Chainlink OCR) a îmbunătățit această metodologie prin faptul că rețelele de oracole off-chain comunică între ele, își semnează criptografic răspunsurile, agregă răspunsurile off-chain și trimit doar o singură tranzacție on-chain cu rezultatul. În acest mod se cheltuiește mai puțin gaz, dar se menține garanția datelor descentralizate, întrucât fiecare nod a semnat partea sa de tranzacție, prin aceasta nemaiputând fi modificată de către nodul care trimite tranzacția. Politica de escaladare intră în vigoare dacă nodul nu efectuează tranzacția, ci următorul nod trimite tranzacția.

Utilizare

Utilizând servicii precum Chainlink, puteți face referire la datele descentralizate on-chain care au fost deja extrase din lumea reală și agregate. Un fel de bunuri comune publice, dar pentru date descentralizate. De asemenea, vă puteți construi propriile rețele de oracole modulare pentru a obține datele personalizate pe care le căutați. În plus, puteți efectua calcule off-chain și de asemenea trimite informații în lumea reală. Chainlink dispune de infrastructura necesară pentru:

Iată un exemplu al modului de a obţine cel mai recent preț ETH în contractul dvs. inteligent folosind un flux de prețuri 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}
Afișează tot
Copiați

Puteți testa acest lucru în remix cu acest link(opens in a new tab)

Vedeţi documentația(opens in a new tab)

Chainlink VRF (Funcția aleatorie verificabilă) este o sursă de randomizare verificabilă și verificabilă, cu o corectitudine dovedită, concepută pentru contractele inteligente. Dezvoltatorii de contracte inteligente pot folosi Chainlink VRF ca generator de numere aleatorii (RNG) inviolabil ca să construiască contracte inteligente fiabile pentru orice aplicații care se bazează pe rezultate imprevizibile:

  • Jocuri blockchain și NFT-uri
  • Alocarea aleatorie de sarcini și resurse (de exemplu, alocarea aleatorie a judecătorilor la procese)
  • Alegerea unui eșantion reprezentativ pentru mecanismele de consens

Numerele aleatorii sunt dificile, deoarece blockchain-urile sunt deterministe.

Modul de lucru cu oracolele Chainlink în afara fluxurilor de date urmează ciclul de solicitare și primire(opens in a new tab) al modului de lucru cu Chainlink. Acesta foloseşte tokenul LINK pentru a trimite furnizorilor de oracole gazul oracolelor ca să dea răspunsuri. Tokenul LINK este conceput special pentru a lucra cu oracolele și se bazează pe tokenul ERC-677 actualizat, care este compatibil cu ERC-20. Dacă următorul cod este implementat pe testnet-ul Kovan, acesta va prelua un număr aleatoriu dovedit criptografic. Pentru a face cererea, finanțați contractul cu câteva tokenuri de testnet LINK, pe care le puteți obține de la faucet-ul Kovan LINK(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}
Afișează tot

Contractele inteligente nu își pot declanșa sau iniția propriile funcții în momente sau în condiții arbitrare. Starea contractelor se va modifica doar când un alt cont inițiază o tranzacție (cum ar fi un utilizator, un oracol sau un contract). Rețeaua Chainlink Keeper(opens in a new tab) oferă contractelor inteligente opțiuni de externalizare a sarcinilor regulate de întreținere, în mod descentralizat și necesitând încredere minimă.

Pentru a utiliza Chainlink Keepers, un contract inteligent trebuie să implementeze o interfață KeeperCompatibleInterface(opens in a new tab), care constă din două funcții:

  • checkUpkeep - Verifică dacă sunt prevăzute în contract lucrări de efectuat.
  • performUpkeep - Execută lucrările prevăzute în contract, dacă este instruit de checkUpkeep.

Exemplul de mai jos este un simplu contract counter. Variabila counter este incrementată cu unu la fiecare apel lcătre performUpkeep. Puteți examina codul următor folosind 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 este definită în momentul înregistrării Upkeep-ului.
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 este generată de apelul Keeeper-ului la funcția checkUpkeep.
36 }
37}
Afișează tot

După implementarea unui contract compatibil Keeper, trebuie să înregistrați contractul pentru Upkeep(opens in a new tab) (întreținere) și să îl finanțați cu LINK, pentru a anunţa rețeaua Keeper despre contract, astfel încât munca dvs. să fie efectuată în mod continuu.

Proiecte Keepers

Apelurile API Chainlink(opens in a new tab) sunt cea mai simplă modalitate de a obține date off-chain în modul tradițional în care funcționează web-ul: apelurile API. Realizarea unei singure astfel de instanțe și existența unui singur oracol îl face să devină centralizat prin natura sa. Pentru a-l menține cu adevărat descentralizat, o platformă de contracte inteligente ar trebui să utilizeze numeroase noduri găsite pe o piață de date externă(opens in a new tab).

Implementați următorul cod în remix pe rețeaua kovan pentru a testa(opens in a new tab)

Acesta urmează tot ciclul de solicitare și primire a oracolelor și are nevoie să fie finanţat contractul cu Kovan LINK (gazul oracolului) pentru a funcționa.

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}
Afișează tot

Puteți afla mai multe despre aplicațiile Chainlink consultând blogul dezvoltatorilor Chainlink(opens in a new tab).

Servicii Oracle

Construiește un contract inteligent oracol

Iată un exemplu de contract oracol al lui Pedro Costa. Puteţi găsi şi alte adnotări în articolul său: Implementarea unui blockchain oracol pe Ethereum(opens in a new tab).

1pragma solidity >=0.4.21 <0.6.0;
2
3contract Oracle {
4 Request[] requests; //lista cererilor adresate contractului
5 uint currentId = 0; //creșterea ID-ului solicitării
6 uint minQuorum = 2; //numărul minim de răspunsuri de primit înainte de declararea rezultatului final
7 uint totalOracleCount = 3; // Număr de oracol codificat greu
8
9 // definește o cerere API generală
10 struct Request {
11 uint id; //cerere id
12 string urlToQuery; //API url
13 string attributeToFetch; //atribut json (cheie) pentru a prelua în răspuns string agreedValue; //valoarea din cheie
14 mapping(uint => string) anwers; //răspunsuri oferite de oracole
15 mapping(address => uint) quorum; //oracole care vor interoga răspunsul (1 = oracolul nu a votat, 2 = oracolul a votat)
16 }
17
18 //eveniment care declanșează oracolul în afara blockchain-ului
19 event NewRequest (
20 uint id,
21 string urlToQuery,
22 string attributeToFetch
23 );
24
25 //declanșat atunci când există un consens cu privire la rezultatul final
26 event UpdatedRequest (
27 uint id,
28 string urlToQuery,
29 string attributeToFetch,
30 string agreedValue
31 );
32
33 function createRequest (
34 string memory _urlToQuery,
35 string memory _attributeToFetch
36 )
37 public
38 {
39 uint length = requests.push(Request(currentId, _urlToQuery, _attributeToFetch, ""));
40 Request storage r = requests[length-1];
41
42 // adresa oracole codificată greu
43 r.quorum[address(0x6c2339b46F41a06f09CA0051ddAD54D1e582bA77)] = 1;
44 r.quorum[address(0xb5346CF224c02186606e5f89EACC21eC25398077)] = 1;
45 r.quorum[address(0xa2997F1CA363D11a0a35bB1Ac0Ff7849bc13e914)] = 1;
46
47 //lansează un eveniment care va fi detectat de oracol în afara blockchain-ului
48 emit NewRequest (
49 currentId,
50 _urlToQuery,
51 _attributeToFetch
52 );
53
54 // crește cererea id
55 currentId++;
56 }
57
58 //chemat de oracol pentru a înregistra răspunsul său
59 function updateRequest (
60 uint _id,
61 string memory _valueRetrieved
62 ) public {
63
64 Request storage currRequest = requests[_id];
65
66 //verifică dacă oracolul se află în lista oracolelor de încredere
67 //și dacă oracolul nu a votat încă
68 if(currRequest.quorum[address(msg.sender)] == 1){
69
70 //marcând că această adresă a votat
71 currRequest.quorum[msg.sender] = 2;
72
73 //iterează prin „matricea” de răspunsuri până la o poziție dacă este liberă și salvează valoarea recuperată
74 uint tmpI = 0;
75 bool found = false;
76 while(!found) {
77 //găsește primul slot gol
78 if(bytes(currRequest.anwers[tmpI]).length == 0){
79 found = true;
80 currRequest.anwers[tmpI] = _valueRetrieved;
81 }
82 tmpI++;
83 }
84
85 uint currentQuorum = 0;
86
87 //iterează prin lista oracolelor și verifică dacă sunt suficiente oracole (cvorum minim)
88 //au votat același răspuns ca și cel actual
89 for(uint i = 0; i < totalOracleCount; i++){
90 bytes memory a = bytes(currRequest.anwers[i]);
91 bytes memory b = bytes(_valueRetrieved);
92
93 if(keccak256(a) == keccak256(b)){
94 currentQuorum++;
95 if(currentQuorum >= minQuorum){
96 currRequest.agreedValue = _valueRetrieved;
97 emit UpdatedRequest (
98 currRequest.id,
99 currRequest.urlToQuery,
100 currRequest.attributeToFetch,
101 currRequest.agreedValue
102 );
103 }
104 }
105 }
106 }
107 }
108}
Afișează tot
Copiați

Ne-ar plăcea să mai avem documentație privind crearea unui contract inteligent oracol. Dacă puteți ajuta, creați un PR!

Referințe suplimentare

Articole

Videoclipuri

Tutoriale

A fost util acest articol?