Vai al contenuto principale

Aiuta ad aggiornare questa pagina

🌏

C'è una nuova versione di questa pagina, ma al momento è solo in inglese. Aiutaci a tradurre l'ultima versione.

Traduci la pagina
Visualizza in inglese

Nessun bug qui!🐛

Questa pagina non è stata tradotta. Per il momento, è stata intenzionalmente lasciata in inglese.

Oracoli

Ultima modifica: , Invalid DateTime
Modifica la pagina

Gli oracoli sono feed di dati che connettono Ethereum all'esterno della catena, informazioni del mondo reale, così che tu possa interrogare i dati nei tuoi contratti intelligenti. Ad esempio, le dapp dei mercati predittivi utilizzano gli oracoli per effettuare i pagamenti in base a eventi. Un mercato predittivo potrebbe chiedere di puntare ETH sul prossimo presidente degli Stati Uniti. Userà un oracolo per confermare l'esito e pagare i vincitori.

Prerequisiti

Assicurati di esser familiare con i nodi, i meccanismi di consenso e l'anatomia dei contratti intelligenti e, nello specifico, con gli eventi.

Cos'è un oracolo

Un oracolo è un ponte tra la blockchain e il mondo reale. Agiscono come API sulla catena che puoi interrogare per ottenere informazioni nei tuoi contratti intelligenti. Si può trattare di qualsiasi cosa, da informazioni sui prezzi a previsioni meteo. Gli oracoli possono anche essere bidirezionali ed essere usati per "inviare" i dati al mondo reale.

Guarda Patrick spiegare gli oracoli:

Perché sono necessari?

Con una blockchain come Ethereum, necessiti di ogni nodo nella rete per riprodurre ogni transazione e terminare con lo stesso risultato, garantito. Le API introducono dati potenzialmente variabili. Se stavi inviando ETH in base a un valore stabilito in $USD usando un'API per i prezzi, la query restituirà un risultato diverso da un giorno all'altro. Per non parlare del fatto che l'API potrebbe essere oggetto di attacchi o diventare obsoleta. In tal caso, i nodi della rete non sarebbero in grado di combaciare con lo stato corrente di Ethereum e, di fatto, verrebbe meno il consenso.

Gli oracoli risolvono questo problema pubblicando i dati sulla blockchain. Quindi ogni nodo che riproduce la transazione utilizzerà gli stessi dati immutabili che vengono pubblicati affinché siano visibili a tutti. Per farlo, un oracolo è tipicamente composto da un contratto intelligente e alcuni componenti esterni alla catena che possono interrogare le API e che, periodicamente, inviano transazioni per aggiornare i dati del contratto intelligente.

Il problema dell'oracolo

Come menzionato, le transazioni di Ethereum non possono accedere direttamente ai dati esterni alla catena. Al contempo, affidarsi a una singola fonte di verità per fornire dati non è sicuro e invalida la decentralizzazione di un contratto intelligente. Questo è noto come il problema dell'oracolo.

Possiamo evitarlo, usando un oracolo decentralizzato che preleva da più fonti di dati; se una fonte di dati è violata o si guasta, il contratto intelligente continuerà a funzionare come previsto.

Sicurezza

La sicurezza di un oracolo è pari a quella della sua o delle sue fonti di dati. Se una dApp usa Uniswap come oracolo per il suo feed del prezzo di ETH/DAI, un malintenzionato può spostare il prezzo su Uniswap per manipolare la comprensione della dApp del prezzo corrente. Un esempio di come combattere ciò è un sistema di feed come quello usato da MakerDAO, che raccoglie i dati sui prezzi da molti feed di prezzo esterni anziché basarsi soltanto su una singola fonte.

Architettura

Questo è un esempio di un'architettura semplice di un oracolo, ma esistono altri modi per innescare il calcolo esterno alla catena.

  1. Emetti un registro con l'evento del tuo contratto intelligente
  2. Un servizio esterno alla catena si è iscritto (solitamente usando qualcosa come il comando eth_subscribe di JSON-RPC) a questi registri specifici.
  3. Il servizio esterno alla catena procede con l'esecuzione di alcune attività come definito dal registro.
  4. Il servizio esterno alla catena risponde coi dati richiesti in una transazione secondaria al contratto intelligente.

È così che si ottengono dati in un rapporto 1 a 1, tuttavia, per migliorare la sicurezza è possibile decentralizzare la raccolta dei dati esterni alla catena.

Il prossimo passaggio potrebbe consistere nel far sì che una rete di questi nodi effettuino le chiamate ad API e fonti diverse e aggreghino i dati nella catena.

Il Chainlink Off-Chain Reporting (Chainlink OCR) ha migliorato questo metodo facendo comunicare tra loro le reti dell'oracolo esterne alla catena, firmando crittograficamente le loro risposte, aggregandole all'esterno della catena e inviando solo una transazione con il risultato sulla catena. Così, si consuma meno gas, ma si può comunque garantire che i dati siano decentralizzati, poiché ogni nodo ha firmato la propria parte della transazione, rendendola immutabile dal nodo che la sta inviando. La politica di escalation subentra se il nodo non effettua la transazione e quello successivo la invia.

Utilizzo

Usando servizi come Chainlink, è possibile fare riferimento ai dati decentralizzati sulla catena che sono già stati prelevati dal mondo reale e aggregati. Una sorta di bene comune e pubblico, ma per i dati decentralizzati. È possibile anche creare le proprie reti dell'oracolo modulari per ottenere tutti i dati personalizzati desiderati. Inoltre, si può anche effettuare calcoli esterni alla catena e inviare le informazioni al mondo reale. Chainlink dispone dell'infrastruttura per:

Ecco un esempio di come ottenere l'ultimo prezzo di ETH nel tuo contratto intelligente, usando il feed dei prezzi di 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}
32
Mostra tutto
📋 Copia

Puoi testarlo nel remix con questo link

Consulta la documentazione

Chainlink VRF (Funzione Casuale Verificabile) è una fonte di casualità provatamente equa e verificabile progettata per i contratti intelligenti. Gli sviluppatori dei contratti intelligenti possono usare Chainlink VRF come generazione di numeri casuali a prova di manomissione (RNG) per creare contratti intelligenti affidabili basati su risultati imprevedibili:

  • Giochi e NFT della blockchain
  • Assegnazione casuale di doveri e risorse (es. assegnazione casuale di giudici ai casi)
  • Scelta di un campione rappresentativo per il meccanismo del consenso

I numeri casuali sono difficili perché le blockchain sono deterministiche.

Lavorare con gli oracoli di Chainlink al di fuori dei feed di dati segue il ciclo di richiesta e ricezione di lavoro con Chainlink. Usano il token LINK per inviare il gas ai fornitori dell'oracolo per restituire le risposte. Il token LINK è progettato specificamente per funzionare con gli oracoli e si basa sul token ERC-677 aggiornato, retrocompatibile con l'ERC-20. Il seguente codice, se sviluppato sulla testnet di Kovan recupererà un numero casuale provato crittograficamente. Per effettuare la richiesta, finanzia il contratto con token LINK della testnet che puoi ottenere da Kovan LINK Faucet.

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
Mostra tutto

I contratti intelligenti non possono innescare o avviare le proprie funzioni in momenti o in condizioni arbitrari. I cambiamenti di stato si verificheranno soltanto quando un altro conto avvia una transazione (come un utente, un oracolo o un contratto). La Rete Keeper di Chainlink fornisce opzioni per i contratti intelligenti, per esternalizzare le regolari attività di manutenzione in modo minimizzato e decentralizzato.

Per usare i Keeper di Chainlink, un contratto intelligente deve implementare KeeperCompatibleInterface, che consiste in due funzioni:

  • checkUpkeep - Controlla se il contratto richiede l'esecuzione di un lavoro.
  • performUpkeep - Esegue il lavoro sul contratto, se indicato da checkUpkeep.

L'esempio seguente è un semplice contratto Counter. La variabile counter è incrementata di uno a ogni chiamata a performUpkeep. controllare il seguente codice usando Remix

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. Il checkData è definito quando l'Upkeep è stato registrato.
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. Il performData è generato dalla chiamata del Keeper alla propria funzione checkUpkeep
36 }
37}
38
Mostra tutto

Dopo aver distribuito un contratto compatibile con Keeper, è necessario registrarlo per Upkeep e finanziarlo con LINK, per informare la Rete di Keeper del proprio contratto, così che il tuo lavoro sia eseguito continuamente.

Progetti dei Keeper

Le Chiamate all'API di Chainlink sono il metodo più facile per ottenere dati dal mondo esterno alla catena con il metodo tradizionale in cui funziona il web: le chiamate API. Eseguire una sola istanza e utilizzare un solo oracolo lo rende centralizzato per natura. Per mantenerlo veramente decentralizzato, una piattaforma di contratti intelligenti dovrebbe usare numerosi nodi trovati in un mercato di dati esterni.

Distribuisci il seguente codice nel remix sulla rete di kovan per testarlo

Ciò segue anche il ciclo di richiesta e ricezione degli oracoli e necessita che il contratto sia finanziato con Kovan LINK (il gas dell'oracolo) per funzionare.

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
Mostra tutto

Puoi scoprire di più sulle applicazioni di Chainlink leggendo il blog degli sviluppatori di Chainlink.

Servizi per oracoli

Creare un contratto intelligente dell'oracolo

Ecco un esempio di contratto oracolo di Pedro Costa. Puoi trovare ulteriori commenti nel suo articolo: 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
Mostra tutto
📋 Copia

Ci piacerebbe avere più documentazione sulla creazione di contratto intelligente dell'oracolo. Se vuoi contribuire, crea una PR!

Letture consigliate

Articoli

Video

Tutorial

Progetti di esempio

Questo articolo è stato utile?