Weiter zum Hauptinhalt
Change page

Oracles

Letzte Änderung: @Coram_Deo(opens in a new tab), 15. August 2023

Oracles sind Datenleitungen, die Ethereum mit Offchain-Informationen in der realen Welt verbinden, so dass Sie Daten in Ihren Smart Contracts abfragen können. Zum Beispiel nutzen Vorhersagemarkt-dApps Oracles zur Begleichung von Zahlungen auf der Grundlage von Events. Ein Vorhersagemarkt könnte Sie fragen, Ihre ETH auf den nächsten Präsidenten der Vereinigten Staaten zu wetten. Sie werden ein Oracle verwenden, um das Ergebnis zu bestätigen und entsprechend an die Gewinner zu zahlen.

Voraussetzungen

Stellen Sie sicher, dass Sie mit Knotenpunkten, Konsensmechanismenund Smart Contract-Anatomievertraut sind, im speziellen Smart Contract-Events.

Was ist ein Oracle

Ein Oracle ist eine Brücke zwischen der Blockchain und der realen Welt. Sie fungieren als on-chain APIs, die Sie abfragen können, um Informationen für Ihre Smart Contracts zu bekommen. Dies könnte theoretisch alles sein, von Preisinformationen bis hin zu Wetterberichten. Oracles können auch bidirektional sein, um Daten in die reale Welt zu „senden".

Schauen Sie sich an, wie Patrick Oracles erklärt:

Warum werden sie benötigt?

Bei einer Blockchain wie Ethereum muss jeder Knotenpunkt im Netzwerk jede Transaktion wiederholen und am Ende garantiert das gleiche Ergebnis erzielen. APIs führen potenziell variable Daten ein. Wenn Sie ETH auf Basis eines vereinbarten $USD-Werts über eine Preis-API senden würden, würde die Abfrage von einem Tag auf den anderen ein anderes Ergebnis liefern. Nicht zu vergessen, die API könnte gehackt oder veraltet sein. Wenn dies geschieht, könnten sich die Knotenpunkte im Netzwerk nicht auf den aktuellen Zustand von Ethereum einigen und damit den Konsens brechen.

Oracles lösen dieses Problem, indem die Daten auf der Blockchain veröffentlicht werden. Jeder Knotenpunkt, der die Transaktion wiedergibt, verwendet also die gleichen unveränderlichen Daten, die für alle sichtbar sind. Dazu besteht ein Oracle in der Regel aus einem Smart Contract und einigen Off-Chain-Komponenten, die APIs abfragen können. Es werden regelmäßig Transaktionen gesendet, um die Daten des Smart Contract zu aktualisieren.

Das Oracle-Problem

Wie wir bereits erwähnt haben, können Ethereum-Transaktionen nicht direkt auf Off-Chain-Daten zugreifen. Gleichzeitig ist es unsicher, sich bei der Bereitstellung von Daten auf eine einzige Quelle der Wahrheit zu verlassen, und es macht die Dezentralisierung eines Smart Contract zunichte. Dies ist als Oracle-Problem bekannt.

Wir können das Oracle-Problem vermeiden, indem wir ein dezentrales Oracle verwenden, das Daten aus mehreren Quellen schöpft. Wenn eine Datenquelle gehackt wird oder ausfällt, funktioniert der Smart Contract trotzdem wie vorgesehen.

Sicherheit

Ein Oracle ist nur so sicher wie seine Datenquelle(n). Wenn ein dApp Uniswap als Oracle für seinen ETH/DAI-Preis-Feed verwendet, kann ein Angreifer den Preis auf Uniswap verschieben, um das Verständnis des dApps für den aktuellen Preis zu manipulieren. Ein Beispiel dafür, wie man dem entgegenwirken kann, ist ein Feed-System(opens in a new tab) wie das von MakerDAO, das Preisdaten aus vielen externen Preis-Feeds zusammenführt, anstatt sich nur auf eine einzige Quelle zu verlassen.

Architektur

Dies ist ein Beispiel für eine einfache Oracle-Architektur, aber es gibt noch mehr Möglichkeiten, um Berechnungen außerhalb der Kette auszulösen.

  1. Senden Sie ein Protokoll mit Ihrem Smart-Contract-Event aus
  2. Ein Off-Chain-Dienst hat diese spezifischen Logs abonniert (normalerweise mit dem JSON-RPC eth_subscribe Befehl).
  3. Der Off-Chain-Dienst führt einige Aufgaben aus, die im Protokoll festgelegt sind.
  4. Der Off-Chain-Dienst gibt mit den angeforderten Daten in einer Sekundärtransaktion eine Antwort an den Smart Contract.

Auf diese Weise erhalten Sie die Daten 1 zu 1. Um die Sicherheit zu erhöhen, sollten Sie jedoch dezentralisieren, wie Sie Ihre Off-Chain-Daten sammeln.

Der nächste Schritt könnte ein Netzwerk mit diesen Knotenpunkten sein, die diese Aufrufe an verschiedene APIs und Quellen tätigen und die Daten in der Kette zusammenführen.

Chainlink Off-Chain Reporting(opens in a new tab) (Chainlink OCR) hat diese Methode verbessert, indem die Off-Chain-Oracles des Netzwerks miteinander kommunizieren, ihre Antworten kryptographisch signieren, ihre Antworten off-chain zusammenfassen und nur eine Transaktion mit dem Ergebnis on-chain senden. Auf diese Weise wird weniger Gas verbraucht, aber Sie erhalten trotzdem die Garantie dezentraler Daten, da jeder Knotenpunkt seinen Teil der Transaktion signiert hat, so dass er von dem Knotenpunkt, der die Transaktion sendet, nicht mehr geändert werden kann. Die Eskalationsrichtlinie tritt in Kraft, wenn der Knotenpunkt keine Transaktion durchführt, und der nächste Knotenpunkt eine Transaktion sendet.

Verwendung

Mit Diensten wie Chainlink können Sie dezentrale Daten auf der Kette referenzieren, die bereits aus der realen Welt gezogen und aggregiert wurden. Eine Art öffentliches Gemeingut, aber für dezentralisierte Daten. Sie können auch Ihre eigenen modularen Oracle Netzwerke aufbauen, um alle gewünschten Daten zu erhalten. Darüber hinaus können Sie auch Berechnungen außerhalb der Blockchain durchführen und Informationen an die reale Welt senden. Chainlink verfügt über die nötige Infrastruktur:

Hier ist ein Beispiel dafür, wie Sie den neuesten ETH-Preis in Ihrem Smart Contract mithilfe eines Chainlink-Preis-Feeds erhalten:

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}
Alles anzeigen
Kopieren

Sie können dies in Remix mit diesem Link testen(opens in a new tab)

Dokumentation ansehen(opens in a new tab)

Chainlink-VRF (Verifiable Random Function) ist eine nachweislich faire und überprüfbare Zufallsquelle, die für Smart Contracts entwickelt wurde. Entwickler von Smart Contracts können Chainlink-VRF als manipulationssichere Zufallszahlengenerierung (RNG) verwenden, um zuverlässige Smart Contracts für alle Anwendungen zu erstellen, die auf unvorhersehbare Ergebnisse angewiesen sind:

  • Blockchain-Spiele und NFTs
  • Zufällige Zuweisung von Aufgaben und Ressourcen (z. B. zufällige Zuweisung von Richtern zu Fällen)
  • Auswahl einer repräsentativen Stichprobe für Konsensmechanismen

Zufallszahlen sind schwierig, weil Blockchains deterministisch sind.

Die Arbeit mit Chainlink Oracles außerhalb von Datenleitungen folgt dem Anfrage- und Empfangszyklus(opens in a new tab) der Arbeit mit Chainlink. Sie verwenden den LINK-Token, um Oracle-Anbietern Oracle-Gas für Antworten zu senden. Der LINK-Token wurde speziell für die Arbeit mit Oracles entwickelt und basiert auf dem aktualisierten ERC-677 Token, der mit ERC-20 rückwärts kompatibel ist. Der folgende Code, falls er im Kovan-Testnet eingesetzt wird, wird eine kryptographisch erwiesene Zufallsnummer abrufen. Um den Antrag zu stellen, finanzieren Sie den Vertrag mit einem Testnet LINK-Token, den Sie über den Kovan LINK Faucet(opens in a new tab) erhalten können.

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}
Alles anzeigen

Smart Contracts können ihre eigenen Funktionen nicht zu beliebigen Zeiten oder unter beliebigen Bedingungen auslösen oder initiieren. Zustandsänderungen treten nur auf, wenn ein anderes Konto eine Transaktion initiiert (z. B. ein Benutzer, ein Oracle oder ein Vertrag). Das Chainlink Keeper Netzwerk(opens in a new tab) bietet Optionen für Smart Contracts, um regelmäßige Wartungsaufgaben auf eine vertrauensminimierte und dezentralisierte Weise auszulagern.

Um Chainlink Keeper zu verwenden, muss ein Smart Contract das Keeper-Compatible Interface(opens in a new tab) implementieren, das aus zwei Funktionen besteht:

  • checkUpkeep - Prüft, ob der Vertrag Arbeiten erfordert.
  • performUpkeep - Führt die Arbeit an dem Vertrag aus, wenn er von checkUpkeep angewiesen wurde.

Das folgende Beispiel ist ein einfacher Gegenvertrag. Die Variable Counter wird bei jeder Abfrage von performUpkeep um eins erhöht. Sie können den folgenden Code mit Remix(opens in a new tab) ausprobieren

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. Die checkData werden bei der Registrierung des Upkeep definiert.
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. Das performData wird durch die Abfrage der Funktion checkUpkeep durch den Keeper erzeugt
36 }
37}
Alles anzeigen

Nachdem Sie einen Keeper-kompatiblen Vertrag eingerichtet haben, müssen Sie den Vertrag für Upkeep(opens in a new tab) registrieren und mit LINK finanzieren, um das Keeper-Netzwerk über Ihren Vertrag zu informieren, damit Ihre Arbeit kontinuierlich ausgeführt wird.

Keeper-Projekte

Chainlink API Calls(opens in a new tab) sind der einfachste Weg, um Daten aus der Off-Chain-Welt auf die traditionelle Art und Weise zu erhalten, wie das Web funktioniert: API-Aufrufe. Da es nur eine einzige Instanz und nur ein Oracle gibt, ist es von Natur aus zentralisiert. Um wirklich dezentralisiert zu sein, müsste eine Smart-Contract-Plattform zahlreiche Knotenpunkte in einem externen Datenmarkt(opens in a new tab) verwenden.

Setzen Sie den folgenden Code in Remix auf dem Kovan-Netzwerk ein, um ihn zu testen(opens in a new tab)

Auch dies folgt dem Anfrage- und Empfangszyklus von Oracles und setzt voraus, dass der Vertrag mit Kovan LINK (dem Oracle-Gas) finanziert wird, um zu funktionieren.

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}
Alles anzeigen

Sie können mehr über die Anwendungen von Chainlink erfahren, indem Sie den Chainlink-Entwickler-Blog(opens in a new tab) lesen.

Oracle-Dienste

Erstellen Sie einen Oracle-Smart-Contract

Hier ist ein Oracle-Beispielvertrag von Pedro Costa. Weitere Anmerkungen finden Sie in seinem Artikel: Implementierung eines Blockchain Oracles auf 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}
Alles anzeigen
Kopieren

Wir würden gerne mehr Dokumentation über die Erstellung eines Oracle-Smart-Contract erhalten. Wenn Sie helfen können, erstellen Sie bitte eine PR!

Weiterführende Informationen

Artikel

Videos

Lernprogramme

War dieser Artikel hilfreich?