Oracoli
Ultima modifica: , Invalid DateTime
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(opens in a new tab) 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.
- Emetti un registro con l'evento del tuo contratto intelligente
- Un servizio esterno alla catena si è iscritto (solitamente usando qualcosa come il comando
eth_subscribe
di JSON-RPC) a questi registri specifici. - Il servizio esterno alla catena procede con l'esecuzione di alcune attività come definito dal registro.
- 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(opens in a new tab) (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:
- ricevere feed sul prezzo di una criptovaluta nel contratto(opens in a new tab)
- generare numeri casuali verificabili (utili per il gioco)(opens in a new tab)
- chiamare API esterne(opens in a new tab) – Un nuovo utilizzo riguarda il Controllo delle riserve di wBTC(opens in a new tab)
Ecco un esempio di come ottenere l'ultimo prezzo di ETH nel tuo contratto intelligente, usando il feed dei prezzi di Chainlink:
Feed di dati di Chainlink
1pragma solidity ^0.6.7;23import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol";45contract PriceConsumerV3 {67 AggregatorV3Interface internal priceFeed;89 /**10 * Network: Kovan11 * Aggregator: ETH/USD12 * Address: 0x9326BFA02ADD2366b30bacB125260Af64103133113 */14 constructor() public {15 priceFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331);16 }1718 /**19 * Returns the latest price20 */21 function getLatestPrice() public view returns (int) {22 (23 uint80 roundID,24 int price,25 uint startedAt,26 uint timeStamp,27 uint80 answeredInRound28 ) = priceFeed.latestRoundData();29 return price;30 }31}32Mostra tuttoCopia
Puoi testarlo nel remix con questo link(opens in a new tab)
Consulta la documentazione(opens in a new tab)
Chainlink VRF
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(opens in a new tab) 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(opens in a new tab).
12pragma solidity 0.6.6;34import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol";56contract RandomNumberConsumer is VRFConsumerBase {78 bytes32 internal keyHash;9 uint256 internal fee;1011 uint256 public randomResult;1213 /**14 * Constructor inherits VRFConsumerBase15 *16 * Network: Kovan17 * Chainlink VRF Coordinator address: 0xdD3782915140c8f3b190B5D67eAc6dc5760C46E918 * LINK token address: 0xa36085F69e2889c224210F603D836748e7dC008819 * Key Hash: 0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f420 */21 constructor()22 VRFConsumerBase(23 0xdD3782915140c8f3b190B5D67eAc6dc5760C46E9, // VRF Coordinator24 0xa36085F69e2889c224210F603D836748e7dC0088 // LINK Token25 ) public26 {27 keyHash = 0x6c3699283bda56ad74f6b855546325b68d482e983852a7a82979cc4807b641f4;28 fee = 0.1 * 10 ** 18; // 0.1 LINK (varies by network)29 }3031 /**32 * Requests randomness from a user-provided seed33 */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 }3839 /**40 * Callback function used by VRF Coordinator41 */42 function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {43 randomResult = randomness;44 }45}46Mostra tutto
Chainlink Keeper
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(opens in a new tab) 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(opens in a new tab), 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(opens in a new tab)
1// SPDX-License-Identifier: MIT2pragma solidity ^0.7.0;34// KeeperCompatible.sol imports the functions from both ./KeeperBase.sol and5// ./interfaces/KeeperCompatibleInterface.sol6import "@chainlink/contracts/src/v0.7/KeeperCompatible.sol";78contract Counter is KeeperCompatibleInterface {9 /**10 * Public counter variable11 */12 uint public counter;1314 /**15 * Use an interval in seconds and a timestamp to slow execution of Upkeep16 */17 uint public immutable interval;18 uint public lastTimeStamp;1920 constructor(uint updateInterval) {21 interval = updateInterval;22 lastTimeStamp = block.timestamp;2324 counter = 0;25 }2627 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 }3132 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 checkUpkeep36 }37}38Mostra tutto
Dopo aver distribuito un contratto compatibile con Keeper, è necessario registrarlo per Upkeep(opens in a new tab) e finanziarlo con LINK, per informare la Rete di Keeper del proprio contratto, così che il tuo lavoro sia eseguito continuamente.
Progetti dei Keeper
Chiamata all'API di Chainlink
Le Chiamate all'API di Chainlink(opens in a new tab) 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(opens in a new tab).
Distribuisci il seguente codice nel remix sulla rete di kovan per testarlo(opens in a new tab)
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;23import "@chainlink/contracts/src/v0.6/ChainlinkClient.sol";45contract APIConsumer is ChainlinkClient {67 uint256 public volume;89 address private oracle;10 bytes32 private jobId;11 uint256 private fee;1213 /**14 * Network: Kovan15 * Oracle: 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e16 * Job ID: 29fa9aa13bf1468788b7cc4a500a45b817 * Fee: 0.1 LINK18 */19 constructor() public {20 setPublicChainlinkToken();21 oracle = 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e;22 jobId = "29fa9aa13bf1468788b7cc4a500a45b8";23 fee = 0.1 * 10 ** 18; // 0.1 LINK24 }2526 /**27 * Create a Chainlink request to retrieve API response, find the target28 * 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);3334 // Set the URL to perform the GET request on35 request.add("get", "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD");3637 // 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");4849 // Multiply the result by 1000000000000000000 to remove decimals50 int timesAmount = 10**18;51 request.addInt("times", timesAmount);5253 // Sends the request54 return sendChainlinkRequestTo(oracle, request, fee);55 }5657 /**58 * Receive the response in the form of uint25659 */60 function fulfill(bytes32 _requestId, uint256 _volume) public recordChainlinkFulfillment(_requestId)61 {62 volume = _volume;63 }64}65Mostra tutto
Puoi scoprire di più sulle applicazioni di Chainlink leggendo il blog degli sviluppatori di Chainlink(opens in a new tab).
Servizi per oracoli
- Chainlink(opens in a new tab)
- Witnet(opens in a new tab)
- Provable(opens in a new tab)
- Paralink(opens in a new tab)
- Dos.Network(opens in a new tab)
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(opens in a new tab).
1pragma solidity >=0.4.21 <0.6.0;23contract Oracle {4 Request[] requests; //list of requests made to the contract5 uint currentId = 0; //increasing request id6 uint minQuorum = 2; //minimum number of responses to receive before declaring final result7 uint totalOracleCount = 3; // Hardcoded oracle count89 // defines a general api request10 struct Request {11 uint id; //request id12 string urlToQuery; //API url13 string attributeToFetch; //json attribute (key) to retrieve in the response14 string agreedValue; //value from key15 mapping(uint => string) anwers; //answers provided by the oracles16 mapping(address => uint) quorum; //oracles which will query the answer (1=oracle hasn't voted, 2=oracle has voted)17 }1819 //event that triggers oracle outside of the blockchain20 event NewRequest (21 uint id,22 string urlToQuery,23 string attributeToFetch24 );2526 //triggered when there's a consensus on the final result27 event UpdatedRequest (28 uint id,29 string urlToQuery,30 string attributeToFetch,31 string agreedValue32 );3334 function createRequest (35 string memory _urlToQuery,36 string memory _attributeToFetch37 )38 public39 {40 uint lenght = requests.push(Request(currentId, _urlToQuery, _attributeToFetch, ""));41 Request storage r = requests[lenght-1];4243 // Hardcoded oracles address44 r.quorum[address(0x6c2339b46F41a06f09CA0051ddAD54D1e582bA77)] = 1;45 r.quorum[address(0xb5346CF224c02186606e5f89EACC21eC25398077)] = 1;46 r.quorum[address(0xa2997F1CA363D11a0a35bB1Ac0Ff7849bc13e914)] = 1;4748 // launch an event to be detected by oracle outside of blockchain49 emit NewRequest (50 currentId,51 _urlToQuery,52 _attributeToFetch53 );5455 // increase request id56 currentId++;57 }5859 //called by the oracle to record its answer60 function updateRequest (61 uint _id,62 string memory _valueRetrieved63 ) public {6465 Request storage currRequest = requests[_id];6667 //check if oracle is in the list of trusted oracles68 //and if the oracle hasn't voted yet69 if(currRequest.quorum[address(msg.sender)] == 1){7071 //marking that this address has voted72 currRequest.quorum[msg.sender] = 2;7374 //iterate through "array" of answers until a position if free and save the retrieved value75 uint tmpI = 0;76 bool found = false;77 while(!found) {78 //find first empty slot79 if(bytes(currRequest.anwers[tmpI]).length == 0){80 found = true;81 currRequest.anwers[tmpI] = _valueRetrieved;82 }83 tmpI++;84 }8586 uint currentQuorum = 0;8788 //iterate through oracle list and check if enough oracles(minimum quorum)89 //have voted the same answer has the current one90 for(uint i = 0; i < totalOracleCount; i++){91 bytes memory a = bytes(currRequest.anwers[i]);92 bytes memory b = bytes(_valueRetrieved);9394 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.agreedValue103 );104 }105 }106 }107 }108 }109}110Mostra tuttoCopia
Ci piacerebbe avere più documentazione sulla creazione di contratto intelligente dell'oracolo. Se vuoi contribuire, crea una PR!
Letture consigliate
Articoli
- What Is a Blockchain Oracle?(opens in a new tab) - Chainlink
- What is a Blockchain Oracle?(opens in a new tab) - Patrick Collins
- Decentralised Oracles: a comprehensive overview(opens in a new tab) – Julien Thevenard
- Implementing a Blockchain Oracle on Ethereum(opens in a new tab) – Pedro Costa
- Perché i contratti intelligenti non possono effettuare le chiamate all'API?(opens in a new tab) - StackExchange
- Why we need decentralized oracles(opens in a new tab) - Bankless
- So you want to use a price oracle(opens in a new tab) -samczsun
Video
- Oracoli ed espansione dell'utilità della blockchain(opens in a new tab) - Real Vision Finance
Tutorial
Progetti di esempio