diff --git a/public/content/translations/de/developers/tutorials/how-to-mint-an-nft/index.md b/public/content/translations/de/developers/tutorials/how-to-mint-an-nft/index.md index a9b9884c8d5..89bc8e47a70 100644 --- a/public/content/translations/de/developers/tutorials/how-to-mint-an-nft/index.md +++ b/public/content/translations/de/developers/tutorials/how-to-mint-an-nft/index.md @@ -1,39 +1,36 @@ --- -title: So prägen Sie einen NFT (Teil 2/3 der NFT-Tutorialreihe) -description: In diesem Tutorial wird beschrieben, wie Sie ein NFT auf der Ethereum-Blockchain mit unserem Smart Contract und Web3 prägen können +title: "Wie man einen NFT prägt (Teil 2/3 der NFT-Tutorial-Reihe)" +description: "In diesem Tutorial wird beschrieben, wie Sie einen NFT auf der Ethereum-Blockchain mit unserem Smart Contract und Web3 prägen können." author: "Sumi Mudgil" -tags: - - "NFTs" - - "ERC-721" - - "Alchemy" - - "Solidity" - - "Smart Contracts" +tags: ["ERC-721", "alchemy", "solidity", "smart contracts"] skill: beginner lang: de published: 2021-04-22 --- -[Beeple](https://www.nytimes.com/2021/03/11/arts/design/nft-auction-christies-beeple.html): 69 Millionen USD [3LAU](https://www.forbes.com/sites/abrambrown/2021/03/03/3lau-nft-nonfungible-tokens-justin-blau/?sh=5f72ef64643b): 11 Millionen USD [Grimes](https://www.theguardian.com/music/2021/mar/02/grimes-sells-digital-art-collection-non-fungible-tokens): 6 Millionen USD +[Beeple](https://www.nytimes.com/2021/03/11/arts/design/nft-auction-christies-beeple.html): 69 Millionen Dollar +[3LAU](https://www.forbes.com/sites/abrambrown/2021/03/03/3lau-nft-nonfungible-tokens-justin-blau/?sh=5f72ef64643b): 11 Millionen Dollar +[Grimes](https://www.theguardian.com/music/2021/mar/02/grimes-sells-digital-art-collection-non-fungible-tokens): 6 Millionen Dollar Sie alle haben ihre NFTs mit der leistungsstarken API von Alchemy geprägt. In diesem Tutorial zeigen wir Ihnen, wie das in \<10 Minuten geht. -"NFTs prägen" – ist die Veröffentlichung einer einzigartigen Instanz Ihres ERC-721-Tokens auf der Blockchain. Mit unserem Smart Contract aus [Teil 1 dieser NFT-Tutorialserie](/developers/tutorials/how-to-write-and-deploy-an-nft/) möchten wir unsere Web3-Fähigkeiten unter Beweis stellen und einen NFT prägen. Am Ende dieses Tutorials sind Sie selbst in der Lage, so viele NFTs zu prägen, wie Ihr Herz (und Ihr Wallet) begehrt. +Das „Prägen eines NFT“ ist der Akt der Veröffentlichung einer einzigartigen Instanz Ihres ERC-721-Tokens auf der Blockchain. Nutzen wir unseren Smart Contract aus [Teil 1 dieser NFT-Tutorial-Reihe](/developers/tutorials/how-to-write-and-deploy-an-nft/), um unsere Web3-Fähigkeiten unter Beweis zu stellen und einen NFT zu prägen. Am Ende dieses Tutorials werden Sie in der Lage sein, so viele NFTs zu prägen, wie Ihr Herz (und Ihr Wallet) begehrt! -Los gehts! +Legen wir los! ## Schritt 1: Web3 installieren {#install-web3} -Wenn Sie das erste Tutorial zur Erstellung Ihres NFT-Smart Contracts verfolgt haben, haben Sie bereits Erfahrung mit Ethers.js gesammelt. Web3 ist ähnlich wie Ethers eine Bibliothek, die das Erstellen von Anfragen an die Ethereum-Blockchain erleichtert. In diesem Tutorial verwenden wir [Alchemy Web3](https://docs.alchemyapi.io/alchemy/documentation/alchemy-web3), eine erweiterte Web3-Bibliothek, die automatische Wiederholungen und ausgereifte WebSocket-Unterstützung bietet. +Wenn Sie dem ersten Tutorial zur Erstellung Ihres NFT-Smart-Contracts gefolgt sind, haben Sie bereits Erfahrung mit der Verwendung von Ethers.js. Web3 ist ähnlich wie Ethers eine Bibliothek, die das Erstellen von Anfragen an die Ethereum-Blockchain erleichtert. In diesem Tutorial verwenden wir [Alchemy Web3](https://docs.alchemyapi.io/alchemy/documentation/alchemy-web3), eine erweiterte Web3-Bibliothek, die automatische Wiederholungsversuche und robuste WebSocket-Unterstützung bietet. -Führen Sie folgenden Befehl im Startverzeichnis Ihres Projekts aus: +Führen Sie im Stammverzeichnis Ihres Projekts Folgendes aus: ``` npm install @alch/alchemy-web3 ``` -## Schritt 2: `mint-nft.js`-Datei erstellen {#create-mintnftjs} +## Schritt 2: Eine `mint-nft.js`-Datei erstellen {#create-mintnftjs} -Erstellen Sie die Datei `mint-nft.js` in Ihrem Skriptverzeichnis und fügen Sie die folgenden Codezeilen hinzu: +Erstellen Sie in Ihrem Skriptverzeichnis eine `mint-nft.js`-Datei und fügen Sie die folgenden Codezeilen hinzu: ```js require("dotenv").config() @@ -42,123 +39,123 @@ const { createAlchemyWeb3 } = require("@alch/alchemy-web3") const web3 = createAlchemyWeb3(API_URL) ``` -## Schritt 3: Vertrags-ABI öffnen {#contract-abi} +## Schritt 3: Die Vertrags-ABI abrufen {#contract-abi} -Unsere Vertrags-ABI (Application Binary Interface) ist die Schnittstelle, über die die Interaktion mit unserem Smart Contract erfolgt. [Hier](https://docs.alchemyapi.io/alchemy/guides/eth_getlogs#what-are-ab-is) erfahren Sie mehr über Vertrags-ABIs. Hardhat generiert automatisch eine ABI für uns und speichert sie in der Datei `MyNFT.json`. Um das zu nutzen, müssen wir den Inhalt auslesen. Dafür fügen wir die folgenden Codezeilen in unsere `mint-nft.js`-Datei ein: +Die ABI unseres Vertrags (Application Binary Interface) ist die Schnittstelle für die Interaktion mit unserem Smart-Contract. Mehr über Vertrags-ABIs erfahren Sie [hier](https://docs.alchemyapi.io/alchemy/guides/eth_getlogs#what-are-ab-is). Hardhat generiert automatisch eine ABI für uns und speichert sie in der Datei `MyNFT.json`. Um dies zu verwenden, müssen wir den Inhalt parsen, indem wir die folgenden Codezeilen zu unserer `mint-nft.js`-Datei hinzufügen: ```js const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json") ``` -Wenn Sie die ABI anzeigen möchten, können Sie sie auf Ihrer Konsole anzeigen: +Wenn Sie die ABI sehen möchten, können Sie sie auf Ihrer Konsole ausgeben: ```js console.log(JSON.stringify(contract.abi)) ``` -Um `mint-nft.js` auszuführen und die ABI auf der Konsole anzuzeigen, navigieren Sie zu Ihrem Terminal und führen folgenden befehl aus: +Um `mint-nft.js` auszuführen und Ihre ABI auf der Konsole ausgeben zu sehen, navigieren Sie zu Ihrem Terminal und führen Sie Folgendes aus: ```js node scripts/mint-nft.js ``` -## Schritt 4: Metadaten für den NFT mit IPFS konfigurieren {#config-meta} +## Schritt 4: Metadaten für Ihren NFT mit IPFS konfigurieren {#config-meta} -Wenn Sie sich an den ersten Teil unseres Tutorials erinnern, nimmt unsere `mintNFT`-Smart-Contract-Funktion einen tokenURI-Parameter entgegen, der in ein JSON-Dokument aufgelöst werden sollte, das die Metadaten des NFT beschreibt. Das ist es, was einen NFT wirklich zum Leben erweckt und ermöglicht, konfigurierbare Eigenschaften wie einen Namen, eine Beschreibung, ein Bild und andere Attribute zu haben. +Wenn Sie sich an unser Tutorial in Teil 1 erinnern, nimmt unsere `mintNFT`-Smart-Contract-Funktion einen tokenURI-Parameter entgegen, der zu einem JSON-Dokument aufgelöst werden sollte, das die Metadaten des NFT beschreibt – was den NFT erst richtig zum Leben erweckt und ihm konfigurierbare Eigenschaften wie Name, Beschreibung, Bild und andere Attribute verleiht. -> _Interplanetary File System (IPFS) ist ein dezentralisiertes Protokoll und Peer-to-Peer-Netzwerk, um Informationen in verteilten Dateisystemen zu speichern und zu teilen._ +> _Interplanetary File System (IPFS) ist ein dezentrales Protokoll und ein Peer-to-Peer-Netzwerk zum Speichern und Teilen von Daten in einem verteilten Dateisystem._ -Wir nutzen Pinata, eine praktische IPFS-API und ein Toolkit zum Speichern unserer NFT-Assets und Metadaten, um sicherzustellen, dass unser NFT wirklich dezentralisiert ist. Falls Sie noch kein Pinata-Konto haben, können Sie ein kostenloses Konto [hier](https://app.pinata.cloud) erstellen. Zur Vervollständigung müssen Sie anschließend noch Ihre E-Mail-Adresse verifizieren. +Wir werden Pinata, eine praktische IPFS-API und ein Toolkit, verwenden, um unser NFT-Asset und unsere Metadaten zu speichern und sicherzustellen, dass unser NFT wirklich dezentral ist. Wenn Sie kein Pinata-Konto haben, registrieren Sie sich [hier](https://app.pinata.cloud) für ein kostenloses Konto und führen Sie die Schritte zur Verifizierung Ihrer E-Mail-Adresse aus. Sobald Sie ein Konto erstellt haben: -- Navigieren Sie zur Seite "Files" (Dateien) und klicken Sie auf die blaue Schaltfläche "Upload" (Hochladen) oben links auf der Seite. +- Navigieren Sie zur Seite „Dateien“ und klicken Sie oben links auf die blaue Schaltfläche „Hochladen“. -- Laden Sie ein Bild bei Pinata hoch — diese Bild-Datei wird für Ihren NFT benötigt. Sie können die Datei beliebig benennen. +- Laden Sie ein Bild auf Pinata hoch – dies wird das Bild-Asset für Ihren NFT sein. Sie können das Asset benennen, wie Sie möchten. -- Nach dem Hochladen sehen Sie die Dateiinformationen in der Tabelle auf der Seite "Files" (Dateien). Zudem sehen Sie auch die Spalte "CID". Sie können die CID kopieren, indem Sie auf die Kopierschaltfläche direkt daneben klicken. Sie können ihren Upload einsehen unter: `https://gateway.pinata.cloud/ipfs/`. Das Bild, das wir auf IPFS verwendet haben, finden Sie zum Beispiel [hier](https://gateway.pinata.cloud/ipfs/QmarPqdEuzh5RsWpyH2hZ3qSXBCzC5RyK3ZHnFkAsk7u2f). +- Nach dem Hochladen sehen Sie die Datei-Informationen in der Tabelle auf der Seite „Dateien“. Sie werden auch eine CID-Spalte sehen. Sie können die CID kopieren, indem Sie auf die Schaltfläche zum Kopieren daneben klicken. Sie können Ihren Upload unter `https://gateway.pinata.cloud/ipfs/` ansehen. Das von uns verwendete Bild finden Sie zum Beispiel auf IPFS [hier](https://gateway.pinata.cloud/ipfs/QmZdd5KYdCFApWn7eTZJ1qgJu18urJrP9Yh1TZcZrZxxB5). -Für visuell Lernende sind die oben genannten Schritte hier zusammengefasst: +Für die eher visuell Lernenden sind die obigen Schritte hier zusammengefasst: -![So laden Sie ihr Bild bei Pinata hoch](https://gateway.pinata.cloud/ipfs/Qmcdt5VezYzAJDBc4qN5JbANy5paFg9iKDjq8YksRvZhtL) +![So laden Sie Ihr Bild auf Pinata hoch](./instructionsPinata.gif) -Jetzt laden wir ein weiteres Dokument in Pinata hoch. Doch bevor wir das machen, müssen wir es erst erstellen. +Jetzt wollen wir noch ein weiteres Dokument auf Pinata hochladen. Aber bevor wir das tun, müssen wir es erstellen! -Erstellen Sie in Ihrem Stammverzeichnis eine neue Datei namens `nft-metadata.json` und fügen Sie den folgenden json-Code hinzu: +Erstellen Sie in Ihrem Stammverzeichnis eine neue Datei mit dem Namen `nft-metadata.json` und fügen Sie den folgenden JSON-Code hinzu: ```json { "attributes": [ { - "trait_type": "Breed", + "trait_type": "Rasse", "value": "Maltipoo" }, { - "trait_type": "Eye color", - "value": "Mocha" + "trait_type": "Augenfarbe", + "value": "Mokka" } ], - "description": "The world's most adorable and sensitive pup.", + "description": "Der bezauberndste und sensibelste Welpe der Welt.", "image": "ipfs://QmWmvTJmJU3pozR9ZHFmQC2DNDwi2XJtf3QGyYiiagFSWb", "name": "Ramses" } ``` -Sie können die Daten in der json-Datei gerne ändern. Sie können den Attributabschnitt entfernen oder hinzufügen. Vergewissern Sie sich vor allem, dass das Bildfeld auf den Speicherort Ihres IPFS-Bildes verweist – andernfalls wird Ihr NFT ein Foto eines (sehr niedlichen!) Hundes enthalten. +Sie können die Daten in der JSON-Datei beliebig ändern. Sie können den Abschnitt mit den Attributen entfernen oder erweitern. Am wichtigsten ist, dass das Bildfeld auf den Speicherort Ihres IPFS-Bildes verweist – andernfalls enthält Ihr NFT ein Foto von einem (sehr süßen!) Hund. -Wenn Sie mit der Bearbeitung der JSON-Datei fertig sind, speichern Sie sie und laden Sie sie auf Pinata hoch. Führen Sie dafür die gleichen Schritte wie beim Hochladen des Bildes aus. +Wenn Sie mit der Bearbeitung der JSON-Datei fertig sind, speichern Sie sie und laden Sie sie auf Pinata hoch. Befolgen Sie dabei die gleichen Schritte wie beim Hochladen des Bildes. -![So laden Sie die Datei "nft-metadata.json" bei Pinata hoch](./uploadPinata.gif) +![So laden Sie Ihre nft-metadata.json auf Pinata hoch](./uploadPinata.gif) -## Schritt 5: Vertragsinstanz erstellen {#instance-contract} +## Schritt 5: Eine Instanz Ihres Vertrags erstellen {#instance-contract} -Um nun mit unserem Vertrag zu interagieren, müssen wir eine Instanz davon in unserem Code erstellen. Dazu benötigen wir unsere Vertragsadresse, die wir über die Bereitstellung oder [Etherscan](https://ropsten.etherscan.io/) erhalten können. Dafür fragen wir die Adresse ab, die Sie für die Bereitstellung des Vertrags verwendet haben. +Um mit unserem Vertrag zu interagieren, müssen wir nun eine Instanz davon in unserem Code erstellen. Dazu benötigen wir unsere Vertragsadresse, die wir bei der Bereitstellung oder von [Blockscout](https://eth-sepolia.blockscout.com/) erhalten können, indem wir die Adresse nachschlagen, die Sie zur Bereitstellung des Vertrags verwendet haben. -![Ihre Vertragsadresse auf Etherscan anzeigen](./viewContractEtherscan.png) +![Ihre Vertragsadresse auf Etherscan anzeigen](./view-contract-etherscan.png) -Im obigen Beispiel lautet unsere Vertragsadresse 0x81c587EB0fE773404c42c1d2666b5f557C470eED. +Im obigen Beispiel lautet unsere Vertragsadresse 0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778. -Als Nächstes werden wir die Web3-[Vertragsmethode](https://docs.web3js.org/api/web3-eth-contract/class/Contract) verwenden, um unseren Vertrag unter Verwendung der ABI und der Adresse zu erstellen. Fügen Sie in der Datei `mint-nft.js` Folgendes hinzu: +Als Nächstes verwenden wir die Web3-[Vertragsmethode](https://docs.web3js.org/api/web3-eth-contract/class/Contract), um unseren Vertrag mithilfe der ABI und der Adresse zu erstellen. Fügen Sie in Ihrer `mint-nft.js`-Datei Folgendes hinzu: ```js -const contractAddress = "0x81c587EB0fE773404c42c1d2666b5f557C470eED" +const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778" const nftContract = new web3.eth.Contract(contract.abi, contractAddress) ``` -## Schritt 6: `.env`-Datei aktualisieren {#update-env} +## Schritt 6: Die `.env`-Datei aktualisieren {#update-env} -Um nun Transaktionen zu erstellen und an die Ethereum-Chain zu senden, verwenden wir Ihre öffentliche Ethereum-Kontoadresse, um die Konto-Nonce zu erhalten (wird unten erklärt). +Um Transaktionen zu erstellen und an die Ethereum-Kette zu senden, verwenden wir nun Ihre öffentliche Ethereum-Kontoadresse, um die Konto-Nonce zu erhalten (Erklärung folgt unten). -Fügen Sie Ihren öffentlichen Schlüssel zu Ihrer `.env-`Datei hinzu. Wenn Sie den ersten Teil des Tutorials abgeschlossen haben, sollte die `.env`-Datei nun wie folgt aussehen: +Fügen Sie Ihren öffentlichen Schlüssel zu Ihrer `.env`-Datei hinzu – wenn Sie Teil 1 des Tutorials abgeschlossen haben, sollte unsere `.env`-Datei jetzt so aussehen: ```js -API_URL = "https://eth-ropsten.alchemyapi.io/v2/your-api-key" +API_URL = "https://eth-sepolia.g.alchemy.com/v2/your-api-key" PRIVATE_KEY = "your-private-account-address" PUBLIC_KEY = "your-public-account-address" ``` -## Schritt 7: Transaktion erstellen {#create-txn} +## Schritt 7: Ihre Transaktion erstellen {#create-txn} -Als Erstes definieren wir eine Funktion mit dem Namen `mintNFT(tokenData)` und erstellen dann wie folgt unsere Transaktion: +Definieren wir zunächst eine Funktion namens `mintNFT(tokenData)` und erstellen wir unsere Transaktion, indem wir Folgendes tun: -1. Nehmen Sie den _PRIVATE_KEY_ und _PUBLIC_KEY_ aus der `.env`-Datei. +1. Holen Sie sich Ihren _PRIVATE_KEY_ und _PUBLIC_KEY_ aus der `.env`-Datei. -1. Als Nächstes müssen wir die Konto-Nonce in Erfahrung bringen. Die Nonce-Spezifikation wird verwendet, um die Anzahl der von Ihrer Adresse aus gesendeten Transaktionen zu verfolgen. Das ist aus Sicherheitsgründen und zur Verhinderung von [Replay-Angriffen](https://docs.alchemyapi.io/resources/blockchain-glossary#account-nonce) erforderlich. Um die Anzahl der von Ihrer Adresse aus gesendeten Transaktionen in Erfahrung zu bringen, nutzen wir [getTransactionCount](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc#eth_gettransactioncount). +2. Als Nächstes müssen wir die Konto-Nonce ermitteln. Die Nonce-Spezifikation wird verwendet, um die Anzahl der von Ihrer Adresse gesendeten Transaktionen zu verfolgen – was wir aus Sicherheitsgründen und zur Verhinderung von [Replay-Angriffen](https://docs.alchemyapi.io/resources/blockchain-glossary#account-nonce) benötigen. Um die Anzahl der von Ihrer Adresse gesendeten Transaktionen zu erhalten, verwenden wir [getTransactionCount](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc#eth_gettransactioncount). -1. Abschließend richten wir eine Transaktion mit der folgenden Information ein: +3. Schließlich richten wir unsere Transaktion mit den folgenden Informationen ein: -- `'from': PUBLIC_KEY` – Der Ursprung der Transaktion ist unsere öffentliche Adresse +- `'from': PUBLIC_KEY` — Der Ursprung unserer Transaktion ist unsere öffentliche Adresse -- `'to': contractAddress` – Der Vertrag, mit dem wir interagieren und dem wir die Transaktion senden möchten +- `'to': contractAddress` — Der Vertrag, mit dem wir interagieren und an den wir die Transaktion senden wollen -- `'nonce': nonce` – Die Konto-Nonce mit der Anzahl der Transaktionen, die von unserer Adresse gesendet wurden +- `'nonce': nonce` — Die Konto-Nonce mit der Anzahl der von unserer Adresse gesendeten Transaktionen -- `'gas': estimatedGas` – Die geschätzten Ressourcen, die zum Abschließen der Transaktion erforderlich sind +- `'gas': estimatedGas` — Das geschätzte Gas, das für den Abschluss der Transaktion benötigt wird -- `'data': nftContract.methods.mintNFT(PUBLIC_KEY, md).encodeABI()` – Die Berechnung, die wir in dieser Transaktion durchführen wollen, in diesem Fall das Prägen eines NFT. +- `'data': nftContract.methods.mintNFT(PUBLIC_KEY, md).encodeABI()` — Die Berechnung, die wir in dieser Transaktion durchführen wollen — in diesem Fall das Prägen eines NFT -Unser mint-nft.js Datei sollte dann so aussehen: +Ihre `mint-nft.js`-Datei sollte jetzt so aussehen: ```js require('dotenv').config(); @@ -170,13 +167,13 @@ Unser mint-nft.js Datei sollte dann so aussehen: const web3 = createAlchemyWeb3(API_URL); const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json"); - const contractAddress = "0x81c587EB0fE773404c42c1d2666b5f557C470eED"; + const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778"; const nftContract = new web3.eth.Contract(contract.abi, contractAddress); async function mintNFT(tokenURI) { - const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce + const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //letzte Nonce holen - //the transaction + //die Transaktion const tx = { 'from': PUBLIC_KEY, 'to': contractAddress, @@ -187,11 +184,11 @@ Unser mint-nft.js Datei sollte dann so aussehen: }​ ``` -## Schritt 8: Transaktion signieren {#sign-txn} +## Schritt 8: Die Transaktion signieren {#sign-txn} -Jetzt, da wir unsere Transaktion erstellt haben, müssen wir sie signieren, um sie abzusenden. Dafür verwenden wir den privaten Schlüssel. +Nachdem wir unsere Transaktion erstellt haben, müssen wir sie signieren, um sie zu versenden. Hier werden wir unseren privaten Schlüssel verwenden. -`web3.eth.sendSignedTransaction` liefert uns den Transaktions-Hash, mit dem wir sicherstellen können, dass die Transaktion verarbeitet und nicht vom Netzwerk gelöscht wurde. Sie werden feststellen, dass wir im Abschnitt zur Transaktionssignierung eine Fehlerprüfung hinzugefügt haben, damit wir wissen, ob unsere Transaktion erfolgreich durchgeführt wurde. +`web3.eth.sendSignedTransaction` gibt uns den Transaktions-Hash, mit dem wir sicherstellen können, dass unsere Transaktion gemined und nicht vom Netzwerk verworfen wurde. Sie werden feststellen, dass wir im Abschnitt zur Signierung der Transaktion eine Fehlerprüfung hinzugefügt haben, damit wir wissen, ob unsere Transaktion erfolgreich war. ```js require("dotenv").config() @@ -203,13 +200,13 @@ const { createAlchemyWeb3 } = require("@alch/alchemy-web3") const web3 = createAlchemyWeb3(API_URL) const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json") -const contractAddress = "0x81c587EB0fE773404c42c1d2666b5f557C470eED" +const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778" const nftContract = new web3.eth.Contract(contract.abi, contractAddress) async function mintNFT(tokenURI) { - const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //get latest nonce + const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //letzte Nonce holen - //the transaction + //die Transaktion const tx = { from: PUBLIC_KEY, to: contractAddress, @@ -226,13 +223,13 @@ async function mintNFT(tokenURI) { function (err, hash) { if (!err) { console.log( - "The hash of your transaction is: ", + "Der Hash Ihrer Transaktion ist: ", hash, - "\nCheck Alchemy's Mempool to view the status of your transaction!" + "\nÜberprüfen Sie den Mempool von Alchemy, um den Status Ihrer Transaktion anzuzeigen!" ) } else { console.log( - "Something went wrong when submitting your transaction:", + "Beim Senden Ihrer Transaktion ist ein Fehler aufgetreten:", err ) } @@ -240,24 +237,24 @@ async function mintNFT(tokenURI) { ) }) .catch((err) => { - console.log(" Promise failed:", err) + console.log(" Promise fehlgeschlagen:", err) }) } ``` -## Schritt 9: `mintNFT` aufrufen und Node-`mint-nft.js` ausführen {#call-mintnft-fn} +## Schritt 9: `mintNFT` aufrufen und `node mint-nft.js` ausführen {#call-mintnft-fn} -Erinnern Sie sich noch an die Datei `metadata.json`, die Sie in Pinata hochgeladen haben? Holen Sie sich den Hashcode von Pinata und übermitteln Sie die Parameter an die `mintNFT`-Funktion: `https://gateway.pinata.cloud/ipfs/` +Erinnern Sie sich an die `metadata.json`, die Sie auf Pinata hochgeladen haben? Holen Sie sich den Hashcode von Pinata und übergeben Sie Folgendes als Parameter an die Funktion `mintNFT`: `https://gateway.pinata.cloud/ipfs/` So erhalten Sie den Hashcode: -![So erhalten Sie Ihren NFT-Metadaten-Hashcode auf Pinata](./metadataPinata.gif)_So bekommen Sie Ihren NFT-Metadata-Hashcode auf Pinata_ +![So erhalten Sie den Hashcode Ihrer NFT-Metadaten auf Pinata](./metadataPinata.gif)_So erhalten Sie den Hashcode Ihrer NFT-Metadaten auf Pinata_ -> Überprüfen Sie den Hashcode, den Sie zu Ihrer **metadata.json** verlinkt haben, indem Sie `https://gateway.pinata.cloud/ipfs/` in einem separaten Fenster öffnen. Die Seite sollte ähnlich wie der untenstehende Screenshot aussehen: +> Überprüfen Sie, ob der von Ihnen kopierte Hashcode auf Ihre **metadata.json** verweist, indem Sie `https://gateway.pinata.cloud/ipfs/` in einem separaten Fenster laden. Die Seite sollte ähnlich wie im folgenden Screenshot aussehen: -![Ihre Seite sollte die json-Metadata anzeigen](./metadataJSON.png)_Ihre Seite sollte die json-Metadaten anzeigen_ +![Ihre Seite sollte die JSON-Metadaten anzeigen](./metadataJSON.png)_Ihre Seite sollte die JSON-Metadaten anzeigen_ -Alles in allem sollte Ihr Code etwa wie folgt aussehen: +Insgesamt sollte Ihr Code etwa so aussehen: ```js require("dotenv").config() @@ -269,13 +266,13 @@ const { createAlchemyWeb3 } = require("@alch/alchemy-web3") const web3 = createAlchemyWeb3(API_URL) const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json") -const contractAddress = "0x81c587EB0fE773404c42c1d2666b5f557C470eED" +const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778" const nftContract = new web3.eth.Contract(contract.abi, contractAddress) async function mintNFT(tokenURI) { - const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //get latest nonce + const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //letzte Nonce holen - //the transaction + //die Transaktion const tx = { from: PUBLIC_KEY, to: contractAddress, @@ -292,13 +289,13 @@ async function mintNFT(tokenURI) { function (err, hash) { if (!err) { console.log( - "The hash of your transaction is: ", + "Der Hash Ihrer Transaktion ist: ", hash, - "\nCheck Alchemy's Mempool to view the status of your transaction!" + "\nÜberprüfen Sie den Mempool von Alchemy, um den Status Ihrer Transaktion anzuzeigen!" ) } else { console.log( - "Something went wrong when submitting your transaction:", + "Beim Senden Ihrer Transaktion ist ein Fehler aufgetreten:", err ) } @@ -306,25 +303,27 @@ async function mintNFT(tokenURI) { ) }) .catch((err) => { - console.log("Promise failed:", err) + console.log("Promise fehlgeschlagen:", err) }) } mintNFT("ipfs://QmYueiuRNmL4MiA2GwtVMm6ZagknXnSpQnB3z2gWbz36hP") ``` -Führen Sie nun `node scripts/mint-nft.js` aus, um Ihren NFT bereitzustellen. Nach ein paar Sekunden sollten Sie eine Antwort wie diese in Ihrem Terminal sehen: +Führen Sie nun `node scripts/mint-nft.js` aus, um Ihren NFT zu prägen. Nach einigen Sekunden sollten Sie eine Antwort wie diese in Ihrem Terminal sehen: - The hash of your transaction is: 0x10e5062309de0cd0be7edc92e8dbab191aa2791111c44274483fa766039e0e00 + ``` + Der Hash Ihrer Transaktion lautet: 0x301791fdf492001fcd9d5e5b12f3aa1bbbea9a88ed24993a8ab2cdae2d06e1e8 + + Überprüfen Sie den Mempool von Alchemy, um den Status Ihrer Transaktion anzuzeigen! + ``` - Check Alchemy's Mempool to view the status of your transaction! +Besuchen Sie als Nächstes Ihren [Alchemy-Mempool](https://dashboard.alchemyapi.io/mempool), um den Status Ihrer Transaktion zu sehen (ob sie ausstehend ist, gemined wurde oder vom Netzwerk verworfen wurde). Wenn Ihre Transaktion verworfen wurde, ist es auch hilfreich, [Blockscout](https://eth-sepolia.blockscout.com/) zu überprüfen und nach Ihrem Transaktions-Hash zu suchen. -Als Nächstes gehen Sie zu Ihrem [Alchemy-Mempool](https://dashboard.alchemyapi.io/mempool), um den Status Ihrer Transaktion zu sehen (ob sie ausstehend ist, geprägt oder vom Netzwerk abgebrochen wurde). Wenn Ihre Transaktion abgebrochen wurde, ist es auch hilfreich, [Ropsten-Etherscan](https://ropsten.etherscan.io/) zu überprüfen und nach Ihrem Transaktionshash zu suchen. +![Ihren NFT-Transaktions-Hash auf Etherscan anzeigen](./view-nft-etherscan.png)_Ihren NFT-Transaktions-Hash auf Etherscan anzeigen_ -![Ihren NFT-Transaktions-Hash auf Etherscan anzeigen](./viewNFTEtherscan.png)_NFT-Transaktionshash auf Etherscan ansehen_ +Und das war's! Sie haben nun einen NFT auf der Ethereum-Blockchain bereitgestellt UND geprägt -Das war's! Sie haben jetzt einen NFT auf der Ethereum-Blockchain veröffentlicht UND geprägt. +Mit `mint-nft.js` können Sie so viele NFTs prägen, wie Ihr Herz (und Ihr Wallet) begehrt! Stellen Sie nur sicher, dass Sie eine neue tokenURI übergeben, die die Metadaten des NFT beschreibt (andernfalls erstellen Sie nur einen Haufen identischer NFTs mit unterschiedlichen IDs). -Mit der `mint-nft.js` können Sie so viele NFTs prägen, wie Sie möchten (und Ihre Wallet zulässt). Vergewissern Sie sich nur, dass Sie eine neue tokenURI übermitteln, die die Metadaten der NFTs beschreibt (andernfalls würden Sie nur einen Haufen identischer Token mit unterschiedlichen IDs erstellen). - -Vermutlich möchten Sie, dass Ihre NFTs in Ihrer Wallet erscheinen. Lesen Sie also unbedingt [Teil 3: So können Sie Ihre NFTs in Ihrer Wallet anzeigen](/developers/tutorials/how-to-view-nft-in-metamask/). +Vermutlich möchten Sie Ihren NFT in Ihrem Wallet präsentieren können – schauen Sie sich also unbedingt [Teil 3: Wie Sie Ihren NFT in Ihrem Wallet anzeigen](/developers/tutorials/how-to-view-nft-in-metamask/) an! diff --git a/public/content/translations/de/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md b/public/content/translations/de/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md new file mode 100644 index 00000000000..26d32ddcba6 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md @@ -0,0 +1,102 @@ +--- +title: "So simulieren Sie Solidity Smart Contracts für Testzwecke" +description: "Warum Sie sich beim Testen über Ihre Verträge lustig machen sollten" +author: Markus Waas +lang: de +tags: ["solidity", "smart contracts", "testing", "mocking"] +skill: intermediate +published: 2020-05-02 +source: soliditydeveloper.com +sourceUrl: https://soliditydeveloper.com/mocking-contracts +--- + +[Mockobjekte](https://wikipedia.org/wiki/Mock_object) sind ein übliches Entwurfsmuster in der objektorientierten Programmierung. Das Wort stammt von dem französischen Wort "mocquer" ab, das so viel bedeutet wie "sich über etwas lustig machen". Es bedeutet aber auch "etwas nachbilden" was genau das ist, was in der Programmierung gemacht wird. Machen Sie sich bitte nur über Ihre Smart Contracts lustig, wenn Sie wollen, aber simulieren Sie sie, wann immer Sie können. Das macht Ihr Leben einfacher. + +## Unittests von Verträgen mit Mocks {#unit-testing-contracts-with-mocks} + +Das Mocking eines Vertrags bedeutet im Wesentlichen, eine zweite Version dieses Vertrags zu erstellen, die sich sehr ähnlich wie das Original verhält, jedoch auf eine Weise, die vom Entwickler leicht kontrolliert werden kann. Oft hat man es mit komplexen Verträgen zu tun, bei denen man nur [kleine Teile des Vertrags per Unittest testen](/developers/docs/smart-contracts/testing/) möchte. Das Problem dabei ist: Was ist, wenn das Testen dieses kleinen Teils einen ganz bestimmten Vertragszustand erfordert, der nur schwer zu erreichen ist? + +Sie könnten jedes Mal eine komplexe Test-Setup-Logik schreiben, die den Vertrag in den erforderlichen Zustand bringt, oder Sie schreiben einen Mock. Das Mocking eines Vertrags ist mit Vererbung einfach. Erstellen Sie einfach einen zweiten Mock-Vertrag, der vom ursprünglichen erbt. Jetzt können Sie Funktionen in Ihrem Mock überschreiben. Sehen wir uns ein Beispiel an. + +## Beispiel: Privates ERC20 {#example-private-erc20} + +Wir verwenden einen beispielhaften ERC-20-Vertrag, der anfänglich eine private Phase hat. Der Eigentümer kann private Benutzer verwalten und nur diesen ist es zu Beginn gestattet, Tokens zu erhalten. Sobald eine bestimmte Zeit vergangen ist, darf jeder die Tokens verwenden. Falls Sie neugierig sind: Wir verwenden den [`_beforeTokenTransfer`](https://docs.openzeppelin.com/contracts/5.x/extending-contracts#using-hooks)-Hook aus den neuen OpenZeppelin Contracts v3. + +```solidity +pragma solidity ^0.6.0; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract PrivateERC20 is ERC20, Ownable { + mapping (address => bool) public isPrivateUser; + uint256 private publicAfterTime; + + constructor(uint256 privateERC20timeInSec) ERC20("PrivateERC20", "PRIV") public { + publicAfterTime = now + privateERC20timeInSec; + } + + function addUser(address user) external onlyOwner { + isPrivateUser[user] = true; + } + + function isPublic() public view returns (bool) { + return now >= publicAfterTime; + } + + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override { + super._beforeTokenTransfer(from, to, amount); + + require(_validRecipient(to), "PrivateERC20: ungültiger Empfänger"); + } + + function _validRecipient(address to) private view returns (bool) { + if (isPublic()) { + return true; + } + + return isPrivateUser[to]; + } +} +``` + +Und nun werden wir ihn mocken. + +```solidity +pragma solidity ^0.6.0; +import "../PrivateERC20.sol"; + +contract PrivateERC20Mock is PrivateERC20 { + bool isPublicConfig; + + constructor() public PrivateERC20(0) {} + + function setIsPublic(bool isPublic) external { + isPublicConfig = isPublic; + } + + function isPublic() public view returns (bool) { + return isPublicConfig; + } +} +``` + +Sie erhalten eine der folgenden Fehlermeldungen: + +- `PrivateERC20Mock.sol: TypeError: Overriding function is missing "override" specifier.` +- `PrivateERC20.sol: TypeError: Trying to override non-virtual function. Did you forget to add "virtual"?.` + +Da wir die neue Solidity-Version 0.6 verwenden, müssen wir das Schlüsselwort `virtual` für Funktionen, die überschrieben werden können, und `override` für die überschreibende Funktion hinzufügen. Fügen wir diese also beiden `isPublic`-Funktionen hinzu. + +In Ihren Unittests können Sie jetzt stattdessen `PrivateERC20Mock` verwenden. Wenn Sie das Verhalten während der privaten Nutzungszeit testen möchten, verwenden Sie `setIsPublic(false)` und entsprechend `setIsPublic(true)` zum Testen der öffentlichen Nutzungszeit. Natürlich könnten wir in unserem Beispiel auch einfach [Zeithelfer](https://docs.openzeppelin.com/test-helpers/0.5/api#increase) verwenden, um die Zeiten entsprechend zu ändern. Aber die Idee des Mockings sollte jetzt klar sein und Sie können sich Szenarien vorstellen, in denen es nicht so einfach ist, wie nur die Zeit vorzustellen. + +## Mocking vieler Verträge {#mocking-many-contracts} + +Es kann unübersichtlich werden, wenn Sie für jeden einzelnen Mock einen weiteren Vertrag erstellen müssen. Wenn Sie das stört, können Sie sich die [MockContract](https://github.com/gnosis/mock-contract)-Bibliothek ansehen. Sie ermöglicht es Ihnen, das Verhalten von Verträgen zur Laufzeit zu überschreiben und zu ändern. Sie funktioniert jedoch nur für das Mocking von Aufrufen an einen anderen Vertrag, weshalb sie für unser Beispiel nicht funktionieren würde. + +## Mocking kann noch leistungsfähiger sein {#mocking-can-be-even-more-powerful} + +Die Möglichkeiten des Mockings enden hier nicht. + +- Hinzufügen von Funktionen: Nicht nur das Überschreiben einer bestimmten Funktion ist nützlich, sondern auch das einfache Hinzufügen zusätzlicher Funktionen. Ein gutes Beispiel für Tokens ist es, einfach eine zusätzliche `mint`-Funktion zu haben, die es jedem Benutzer erlaubt, kostenlos neue Tokens zu erhalten. +- Verwendung in Testnets: Wenn Sie Ihre Verträge zusammen mit Ihrer Dapp auf Testnets bereitstellen und testen, sollten Sie die Verwendung einer gemockten Version in Betracht ziehen. Vermeiden Sie das Überschreiben von Funktionen, es sei denn, es ist absolut notwendig. Schließlich wollen Sie ja die eigentliche Logik testen. Aber das Hinzufügen zum Beispiel einer Reset-Funktion, die den Vertragszustand einfach auf den Anfang zurücksetzt, kann nützlich sein, ohne dass eine neue Bereitstellung erforderlich ist. Offensichtlich würden Sie das nicht in einem Mainnet-Vertrag haben wollen. diff --git a/public/content/translations/de/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md b/public/content/translations/de/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md new file mode 100644 index 00000000000..a41f3d793e4 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md @@ -0,0 +1,702 @@ +--- +title: So verwenden Sie Echidna zum Testen von Smart Contracts +description: So verwenden Sie Echidna zum automatischen Testen von Smart Contracts +author: "Trailofbits" +lang: de +tags: ["solidity", "smart contracts", "security", "testing", "fuzzing"] +skill: advanced +published: 2020-04-10 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/echidna +--- + +## Installation {#installation} + +Echidna kann über Docker oder durch Verwendung des vorkompilierten Binärprogramms installiert werden. + +### Echidna über Docker {#echidna-through-docker} + +```bash +docker pull trailofbits/eth-security-toolbox +docker run -it -v "$PWD":/home/training trailofbits/eth-security-toolbox +``` + +_Der letzte Befehl führt die eth-security-toolbox in einem Docker aus, der Zugriff auf dein aktuelles Verzeichnis hat. Du kannst die Dateien von deinem Host aus ändern und die Tools für die Dateien aus dem Docker ausführen_ + +Führen Sie in Docker Folgendes aus: + +```bash +solc-select 0.5.11 +cd /home/training +``` + +### Binär {#binary} + +[https://github.com/crytic/echidna/releases/tag/v1.4.0.0](https://github.com/crytic/echidna/releases/tag/v1.4.0.0) + +## Einführung in das eigenschaftsbasierte Fuzzing {#introduction-to-property-based-fuzzing} + +Echidna ist ein eigenschaftsbasierter Fuzzer, den wir in unseren vorherigen Blogbeiträgen beschrieben haben ([1](https://blog.trailofbits.com/2018/03/09/echidna-a-smart-fuzzer-for-ethereum/), [2](https://blog.trailofbits.com/2018/05/03/state-machine-testing-with-echidna/), [3](https://blog.trailofbits.com/2020/03/30/an-echidna-for-all-seasons/)). + +### Fuzzing {#fuzzing} + +[Fuzzing](https://wikipedia.org/wiki/Fuzzing) ist eine bekannte Technik in der Sicherheits-Community. Sie besteht darin, mehr oder weniger zufällige Eingaben zu generieren, um Fehler im Programm zu finden. Fuzzer für traditionelle Software (wie [AFL](http://lcamtuf.coredump.cx/afl/) oder [LibFuzzer](https://llvm.org/docs/LibFuzzer.html)) sind als effiziente Werkzeuge zur Fehlersuche bekannt. + +Über die rein zufällige Generierung von Eingaben hinaus gibt es viele Techniken und Strategien, um gute Eingaben zu generieren, darunter: + +- Feedback aus jeder Ausführung einholen und die Generierung damit steuern. Wenn zum Beispiel eine neu generierte Eingabe zur Entdeckung eines neuen Pfades führt, kann es sinnvoll sein, neue Eingaben in dessen Nähe zu generieren. +- Generierung der Eingabe unter Einhaltung einer strukturellen Beschränkung. Wenn Ihre Eingabe zum Beispiel einen Header mit einer Prüfsumme enthält, ist es sinnvoll, den Fuzzer Eingaben generieren zu lassen, die die Prüfsumme validieren. +- Verwendung bekannter Eingaben zur Generierung neuer Eingaben: Wenn Sie Zugriff auf einen großen Datensatz gültiger Eingaben haben, kann Ihr Fuzzer daraus neue Eingaben generieren, anstatt die Generierung von Grund auf neu zu starten. Diese werden in der Regel _Seeds_ genannt. + +### Eigenschaftsbasiertes Fuzzing {#property-based-fuzzing} + +Echidna gehört zu einer bestimmten Familie von Fuzzern: dem eigenschaftsbasierten Fuzzing, das stark von [QuickCheck](https://wikipedia.org/wiki/QuickCheck) inspiriert ist. Im Gegensatz zu klassischen Fuzzern, die versuchen, Abstürze zu finden, wird Echidna versuchen, benutzerdefinierte Invarianten zu brechen. + +In Smart Contracts sind Invarianten Solidity-Funktionen, die jeden inkorrekten oder ungültigen Zustand, den der Vertrag erreichen kann, darstellen können, einschließlich: + +- Falsche Zugriffskontrolle: Der Angreifer wurde zum Eigentümer des Vertrags. +- Falsche Statusmaschine: Die Token können übertragen werden, während der Vertrag pausiert ist. +- Falsche Arithmetik: der Benutzer kann sein Guthaben unterlaufen lassen und unbegrenzt kostenlose Token erhalten. + +### Testen einer Eigenschaft mit Echidna {#testing-a-property-with-echidna} + +Wir werden sehen, wie man einen Smart Contract mit Echidna testet. Das Ziel ist der folgende Smart Contract [`token.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/token.sol): + +```solidity +contract Token{ + mapping(address => uint) public balances; + function airdrop() public{ + balances[msg.sender] = 1000; + } + function consume() public{ + require(balances[msg.sender]>0); + balances[msg.sender] -= 1; + } + function backdoor() public{ + balances[msg.sender] += 1; + } +} +``` + +Wir gehen davon aus, dass dieser Token die folgenden Eigenschaften haben muss: + +- Jeder kann maximal 1000 Token haben +- Der Token kann nicht übertragen werden (es ist kein ERC20-Token) + +### Eine Eigenschaft schreiben {#write-a-property} + +Echidna-Eigenschaften sind Solidity-Funktionen. Eine Eigenschaft muss: + +- Kein Argument haben +- `true` zurückgeben, wenn es erfolgreich ist +- Der Name muss mit `echidna` beginnen + +Echidna wird: + +- Automatisch beliebige Transaktionen generieren, um die Eigenschaft zu testen. +- Alle Transaktionen melden, die dazu führen, dass eine Eigenschaft `false` zurückgibt oder einen Fehler auslöst. +- Nebeneffekte beim Aufrufen einer Eigenschaft verwerfen (d. h., wenn die Eigenschaft eine Zustandsvariable ändert, wird sie nach dem Test verworfen) + +Die folgende Eigenschaft prüft, dass der Aufrufer nicht mehr als 1000 Token hat: + +```solidity +function echidna_balance_under_1000() public view returns(bool){ + return balances[msg.sender] <= 1000; +} +``` + +Verwenden Sie Vererbung, um Ihren Vertrag von Ihren Eigenschaften zu trennen: + +```solidity +contract TestToken is Token{ + function echidna_balance_under_1000() public view returns(bool){ + return balances[msg.sender] <= 1000; + } + } +``` + +[`token.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/token.sol) implementiert die Eigenschaft und erbt vom Token. + +### Einen Vertrag initiieren {#initiate-a-contract} + +Echidna benötigt einen [Konstruktor](/developers/docs/smart-contracts/anatomy/#constructor-functions) ohne Argument. Wenn Ihr Vertrag eine spezifische Initialisierung benötigt, müssen Sie dies im Konstruktor tun. + +Es gibt einige spezifische Adressen in Echidna: + +- `0x00a329c0648769A73afAc7F9381E08FB43dBEA72`, die den Konstruktor aufruft. +- `0x10000`, `0x20000`, und `0x00a329C0648769a73afAC7F9381e08fb43DBEA70`, die zufällig die anderen Funktionen aufrufen. + +In unserem aktuellen Beispiel benötigen wir keine besondere Initialisierung, daher ist unser Konstruktor leer. + +### Echidna ausführen {#run-echidna} + +Echidna wird gestartet mit: + +```bash +echidna-test contract.sol +``` + +Wenn contract.sol mehrere Verträge enthält, können Sie das Ziel angeben: + +```bash +echidna-test contract.sol --contract MyContract +``` + +### Zusammenfassung: Testen einer Eigenschaft {#summary-testing-a-property} + +Das Folgende fasst die Ausführung von Echidna an unserem Beispiel zusammen: + +```solidity +contract TestToken is Token{ + constructor() public {} + function echidna_balance_under_1000() public view returns(bool){ + return balances[msg.sender] <= 1000; + } + } +``` + +```bash +echidna-test testtoken.sol --contract TestToken +... + +echidna_balance_under_1000: failed!💥 + Call sequence, shrinking (1205/5000): + airdrop() + backdoor() + +... +``` + +Echidna hat herausgefunden, dass die Eigenschaft verletzt wird, wenn `backdoor` aufgerufen wird. + +## Filtern von Funktionen, die während einer Fuzzing-Kampagne aufgerufen werden sollen {#filtering-functions-to-call-during-a-fuzzing-campaign} + +Wir werden sehen, wie man die zu fuzzenden Funktionen filtert. +Das Ziel ist der folgende Smart Contract: + +```solidity +contract C { + bool state1 = false; + bool state2 = false; + bool state3 = false; + bool state4 = false; + + function f(uint x) public { + require(x == 12); + state1 = true; + } + + function g(uint x) public { + require(state1); + require(x == 8); + state2 = true; + } + + function h(uint x) public { + require(state2); + require(x == 42); + state3 = true; + } + + function i() public { + require(state3); + state4 = true; + } + + function reset1() public { + state1 = false; + state2 = false; + state3 = false; + return; + } + + function reset2() public { + state1 = false; + state2 = false; + state3 = false; + return; + } + + function echidna_state4() public returns (bool) { + return (!state4); + } +} +``` + +Dieses kleine Beispiel zwingt Echidna, eine bestimmte Sequenz von Transaktionen zu finden, um eine Zustandsvariable zu ändern. +Das ist schwierig für einen Fuzzer (es wird empfohlen, ein symbolisches Ausführungswerkzeug wie [Manticore](https://github.com/trailofbits/manticore) zu verwenden). +Wir können Echidna ausführen, um dies zu überprüfen: + +```bash +echidna-test multi.sol +... +echidna_state4: passed! 🎉 +Seed: -3684648582249875403 +``` + +### Filterfunktionen {#filtering-functions} + +Echidna hat Schwierigkeiten, die richtige Sequenz zum Testen dieses Vertrags zu finden, da die beiden Reset-Funktionen (`reset1` und `reset2`) alle Zustandsvariablen auf `false` setzen. +Wir können jedoch eine spezielle Echidna-Funktion verwenden, um entweder die Reset-Funktion auf eine schwarze Liste zu setzen oder nur die Funktionen `f`, `g`, +`h` und `i` auf eine weiße Liste zu setzen. + +Um Funktionen auf die schwarze Liste zu setzen, können wir diese Konfigurationsdatei verwenden: + +```yaml +filterBlacklist: true +filterFunctions: ["reset1", "reset2"] +``` + +Ein anderer Ansatz zum Filtern von Funktionen besteht darin, die auf der weißen Liste stehenden Funktionen aufzulisten. Dazu können wir diese Konfigurationsdatei verwenden: + +```yaml +filterBlacklist: false +filterFunctions: ["f", "g", "h", "i"] +``` + +- `filterBlacklist` ist standardmäßig `true`. +- Die Filterung erfolgt nur nach Namen (ohne Parameter). Wenn Sie `f()` und `f(uint256)` haben, wird der Filter `"f"` auf beide Funktionen passen. + +### Echidna ausführen {#run-echidna-1} + +Um Echidna mit einer Konfigurationsdatei `blacklist.yaml` auszuführen: + +```bash +echidna-test multi.sol --config blacklist.yaml +... +echidna_state4: failed!💥 + Call sequence: + f(12) + g(8) + h(42) + i() +``` + +Echidna wird die Sequenz der Transaktionen, um die Eigenschaft zu widerlegen, fast sofort finden. + +### Zusammenfassung: Filterfunktionen {#summary-filtering-functions} + +Echidna kann während einer Fuzzing-Kampagne entweder Funktionen auf eine schwarze oder eine weiße Liste setzen, indem es Folgendes verwendet: + +```yaml +filterBlacklist: true +filterFunctions: ["f1", "f2", "f3"] +``` + +```bash +echidna-test contract.sol --config config.yaml +... +``` + +Echidna startet eine Fuzzing-Kampagne, bei der `f1`, `f2` und `f3` entweder auf der schwarzen Liste stehen oder nur diese aufgerufen werden, je nach dem Wert des `filterBlacklist`-Booleans. + +## Wie man Soliditys `assert` mit Echidna testet {#how-to-test-soliditys-assert-with-echidna} + +In diesem kurzen Tutorial zeigen wir, wie man Echidna zum Testen der Assertionsprüfung in Verträgen verwendet. Nehmen wir an, wir haben einen Vertrag wie diesen: + +```solidity +contract Incrementor { + uint private counter = 2**200; + + function inc(uint val) public returns (uint){ + uint tmp = counter; + counter += val; + // tmp <= counter + return (counter - tmp); + } +} +``` + +### Eine Assertion schreiben {#write-an-assertion} + +Wir wollen sicherstellen, dass `tmp` kleiner oder gleich `counter` ist, nachdem die Differenz zurückgegeben wurde. Wir könnten eine +Echidna-Eigenschaft schreiben, aber wir müssten den `tmp`-Wert irgendwo speichern. Stattdessen könnten wir eine Assertion wie diese verwenden: + +```solidity +contract Incrementor { + uint private counter = 2**200; + + function inc(uint val) public returns (uint){ + uint tmp = counter; + counter += val; + assert (tmp <= counter); + return (counter - tmp); + } +} +``` + +### Echidna ausführen {#run-echidna-2} + +Um das Testen von Assertionsfehlern zu aktivieren, erstellen Sie eine [Echidna-Konfigurationsdatei](https://github.com/crytic/echidna/wiki/Config) `config.yaml`: + +```yaml +checkAsserts: true +``` + +Wenn wir diesen Vertrag in Echidna ausführen, erhalten wir die erwarteten Ergebnisse: + +```bash +echidna-test assert.sol --config config.yaml +Analyzing contract: assert.sol:Incrementor +assertion in inc: failed!💥 + Call sequence, shrinking (2596/5000): + inc(21711016731996786641919559689128982722488122124807605757398297001483711807488) + inc(7237005577332262213973186563042994240829374041602535252466099000494570602496) + inc(86844066927987146567678238756515930889952488499230423029593188005934847229952) + +Seed: 1806480648350826486 +``` + +Wie Sie sehen können, meldet Echidna einen Assertionsfehler in der `inc`-Funktion. Das Hinzufügen von mehr als einer Assertion pro Funktion ist möglich, aber Echidna kann nicht sagen, welche Assertion fehlgeschlagen ist. + +### Wann und wie man Assertions verwendet {#when-and-how-use-assertions} + +Assertions können als Alternative zu expliziten Eigenschaften verwendet werden, insbesondere wenn die zu prüfenden Bedingungen direkt mit der korrekten Verwendung einer Operation `f` zusammenhängen. Das Hinzufügen von Assertions nach einem Code erzwingt, dass die Prüfung unmittelbar nach dessen Ausführung stattfindet: + +```solidity +function f(..) public { + // some complex code + ... + assert (condition); + ... +} + +``` + +Im Gegenteil, die Verwendung einer expliziten Echidna-Eigenschaft führt zu einer zufälligen Ausführung von Transaktionen, und es gibt keine einfache Möglichkeit, genau zu erzwingen, wann sie überprüft wird. Es ist immer noch möglich, diesen Workaround zu verwenden: + +```solidity +function echidna_assert_after_f() public returns (bool) { + f(..); + return(condition); +} +``` + +Es gibt jedoch einige Probleme: + +- Es schlägt fehl, wenn `f` als `internal` oder `external` deklariert ist. +- Es ist unklar, welche Argumente zum Aufrufen von `f` verwendet werden sollen. +- Wenn `f` fehlschlägt, wird die Eigenschaft ebenfalls fehlschlagen. + +Im Allgemeinen empfehlen wir, [John Regehrs Empfehlung](https://blog.regehr.org/archives/1091) zur Verwendung von Assertions zu folgen: + +- Erzwingen Sie keine Nebeneffekte während der Assertionsprüfung. Zum Beispiel: `assert(ChangeStateAndReturn() == 1)` +- Behaupten Sie keine offensichtlichen Aussagen. Zum Beispiel `assert(var >= 0)`, wobei `var` als `uint` deklariert ist. + +Schließlich, bitte **verwenden Sie nicht** `require` anstelle von `assert`, da Echidna es nicht erkennen kann (aber der Vertrag wird trotzdem fehlschlagen). + +### Zusammenfassung: Assertionsprüfung {#summary-assertion-checking} + +Das Folgende fasst die Ausführung von Echidna an unserem Beispiel zusammen: + +```solidity +contract Incrementor { + uint private counter = 2**200; + + function inc(uint val) public returns (uint){ + uint tmp = counter; + counter += val; + assert (tmp <= counter); + return (counter - tmp); + } +} +``` + +```bash +echidna-test assert.sol --config config.yaml +Analyzing contract: assert.sol:Incrementor +assertion in inc: failed!💥 + Call sequence, shrinking (2596/5000): + inc(21711016731996786641919559689128982722488122124807605757398297001483711807488) + inc(7237005577332262213973186563042994240829374041602535252466099000494570602496) + inc(86844066927987146567678238756515930889952488499230423029593188005934847229952) + +Seed: 1806480648350826486 +``` + +Echidna hat herausgefunden, dass die Assertion in `inc` fehlschlagen kann, wenn diese Funktion mehrmals mit großen Argumenten aufgerufen wird. + +## Sammeln und Modifizieren eines Echidna-Korpus {#collecting-and-modifying-an-echidna-corpus} + +Wir werden sehen, wie man mit Echidna einen Korpus von Transaktionen sammelt und verwendet. Das Ziel ist der folgende Smart Contract [`magic.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/magic.sol): + +```solidity +contract C { + bool value_found = false; + function magic(uint magic_1, uint magic_2, uint magic_3, uint magic_4) public { + require(magic_1 == 42); + require(magic_2 == 129); + require(magic_3 == magic_4+333); + value_found = true; + return; + } + + function echidna_magic_values() public returns (bool) { + return !value_found; + } + +} +``` + +Dieses kleine Beispiel zwingt Echidna, bestimmte Werte zu finden, um eine Zustandsvariable zu ändern. Das ist schwierig für einen Fuzzer +(es wird empfohlen, ein symbolisches Ausführungswerkzeug wie [Manticore](https://github.com/trailofbits/manticore) zu verwenden). +Wir können Echidna ausführen, um dies zu überprüfen: + +```bash +echidna-test magic.sol +... + +echidna_magic_values: passed! 🎉 + +Seed: 2221503356319272685 +``` + +Wir können Echidna jedoch immer noch verwenden, um während dieser Fuzzing-Kampagne einen Korpus zu sammeln. + +### Einen Korpus sammeln {#collecting-a-corpus} + +Um die Korpus-Sammlung zu aktivieren, erstellen Sie ein Korpus-Verzeichnis: + +```bash +mkdir corpus-magic +``` + +Und eine [Echidna-Konfigurationsdatei](https://github.com/crytic/echidna/wiki/Config) `config.yaml`: + +```yaml +coverage: true +corpusDir: "corpus-magic" +``` + +Jetzt können wir unser Werkzeug ausführen und den gesammelten Korpus überprüfen: + +```bash +echidna-test magic.sol --config config.yaml +``` + +Echidna kann immer noch nicht die richtigen magischen Werte finden, aber wir können uns den Korpus ansehen, den es gesammelt hat. +Eine dieser Dateien war zum Beispiel: + +```json +[ + { + "_gas'": "0xffffffff", + "_delay": ["0x13647", "0xccf6"], + "_src": "00a329c0648769a73afac7f9381e08fb43dbea70", + "_dst": "00a329c0648769a73afac7f9381e08fb43dbea72", + "_value": "0x0", + "_call": { + "tag": "SolCall", + "contents": [ + "magic", + [ + { + "contents": [ + 256, + "93723985220345906694500679277863898678726808528711107336895287282192244575836" + ], + "tag": "AbiUInt" + }, + { + "contents": [256, "334"], + "tag": "AbiUInt" + }, + { + "contents": [ + 256, + "68093943901352437066264791224433559271778087297543421781073458233697135179558" + ], + "tag": "AbiUInt" + }, + { + "tag": "AbiUInt", + "contents": [256, "332"] + } + ] + ] + }, + "_gasprice'": "0xa904461f1" + } +] +``` + +Offensichtlich wird diese Eingabe den Fehler in unserer Eigenschaft nicht auslösen. Im nächsten Schritt werden wir jedoch sehen, wie wir sie dafür modifizieren können. + +### Einen Korpus mit Startwerten versehen {#seeding-a-corpus} + +Echidna benötigt etwas Hilfe, um mit der `magic`-Funktion umzugehen. Wir werden die Eingabe kopieren und modifizieren, um geeignete +Parameter dafür zu verwenden: + +```bash +cp corpus/2712688662897926208.txt corpus/new.txt +``` + +Wir werden `new.txt` modifizieren, um `magic(42,129,333,0)` aufzurufen. Jetzt können wir Echidna erneut ausführen: + +```bash +echidna-test magic.sol --config config.yaml +... +echidna_magic_values: failed!💥 + Call sequence: + magic(42,129,333,0) + + +Unique instructions: 142 +Unique codehashes: 1 +Seed: -7293830866560616537 + +``` + +Dieses Mal wurde sofort festgestellt, dass die Eigenschaft verletzt wird. + +## Transaktionen mit hohem Gasverbrauch finden {#finding-transactions-with-high-gas-consumption} + +Wir werden sehen, wie man mit Echidna die Transaktionen mit hohem Gasverbrauch findet. Das Ziel ist der folgende Smart Contract: + +```solidity +contract C { + uint state; + + function expensive(uint8 times) internal { + for(uint8 i=0; i < times; i++) + state = state + i; + } + + function f(uint x, uint y, uint8 times) public { + if (x == 42 && y == 123) + expensive(times); + else + state = 0; + } + + function echidna_test() public returns (bool) { + return true; + } + +} +``` + +Hier kann `expensive` einen hohen Gasverbrauch haben. + +Derzeit benötigt Echidna immer eine Eigenschaft zum Testen: hier gibt `echidna_test` immer `true` zurück. +Wir können Echidna ausführen, um dies zu überprüfen: + +``` +echidna-test gas.sol +... +echidna_test: passed! 🎉 + +Seed: 2320549945714142710 +``` + +### Gasverbrauch messen {#measuring-gas-consumption} + +Um den Gasverbrauch mit Echidna zu aktivieren, erstellen Sie eine Konfigurationsdatei `config.yaml`: + +```yaml +estimateGas: true +``` + +In diesem Beispiel werden wir auch die Größe der Transaktionssequenz reduzieren, um die Ergebnisse leichter verständlich zu machen: + +```yaml +seqLen: 2 +estimateGas: true +``` + +### Echidna ausführen {#run-echidna-3} + +Sobald wir die Konfigurationsdatei erstellt haben, können wir Echidna so ausführen: + +```bash +echidna-test gas.sol --config config.yaml +... +echidna_test: passed! 🎉 + +f used a maximum of 1333608 gas + Call sequence: + f(42,123,249) Gas price: 0x10d5733f0a Time delay: 0x495e5 Block delay: 0x88b2 + +Unique instructions: 157 +Unique codehashes: 1 +Seed: -325611019680165325 + +``` + +- Das angezeigte Gas ist eine Schätzung, die von [HEVM](https://github.com/dapphub/dapptools/tree/master/src/hevm#hevm-) bereitgestellt wird. + +### Gasreduzierende Aufrufe herausfiltern {#filtering-out-gas-reducing-calls} + +Das Tutorial zum **Filtern von Funktionen, die während einer Fuzzing-Kampagne aufgerufen werden** oben zeigt, wie man +einige Funktionen aus dem Test entfernt. +Dies kann entscheidend sein, um eine genaue Gasschätzung zu erhalten. +Betrachte das folgende Beispiel: + +```solidity +contract C { + address [] addrs; + function push(address a) public { + addrs.push(a); + } + function pop() public { + addrs.pop(); + } + function clear() public{ + addrs.length = 0; + } + function check() public{ + for(uint256 i = 0; i < addrs.length; i++) + for(uint256 j = i+1; j < addrs.length; j++) + if (addrs[i] == addrs[j]) + addrs[j] = address(0x0); + } + function echidna_test() public returns (bool) { + return true; + } +} +``` + +Wenn Echidna alle Funktionen aufrufen kann, wird es nicht leicht Transaktionen mit hohen Gaskosten finden: + +``` +echidna-test pushpop.sol --config config.yaml +... +pop used a maximum of 10746 gas +... +check used a maximum of 23730 gas +... +clear used a maximum of 35916 gas +... +push used a maximum of 40839 gas +``` + +Das liegt daran, dass die Kosten von der Größe von `addrs` abhängen und zufällige Aufrufe dazu neigen, das Array fast leer zu lassen. +Das Setzen von `pop` und `clear` auf die schwarze Liste liefert uns jedoch viel bessere Ergebnisse: + +```yaml +filterBlacklist: true +filterFunctions: ["pop", "clear"] +``` + +``` +echidna-test pushpop.sol --config config.yaml +... +push used a maximum of 40839 gas +... +check used a maximum of 1484472 gas +``` + +### Zusammenfassung: Finden von Transaktionen mit hohem Gasverbrauch {#summary-finding-transactions-with-high-gas-consumption} + +Echidna kann Transaktionen mit hohem Gasverbrauch finden, indem es die Konfigurationsoption `estimateGas` verwendet: + +```yaml +estimateGas: true +``` + +```bash +echidna-test contract.sol --config config.yaml +... +``` + +Echidna wird nach Abschluss der Fuzzing-Kampagne für jede Funktion eine Sequenz mit dem maximalen Gasverbrauch melden. diff --git a/public/content/translations/de/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md b/public/content/translations/de/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md new file mode 100644 index 00000000000..25bed43b6be --- /dev/null +++ b/public/content/translations/de/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md @@ -0,0 +1,518 @@ +--- +title: So nutzt du Manticore, um Fehler in Smart Contracts zu finden +description: So nutzt du Manticore, um automatisiert Fehler in Smart Contracts zu finden +author: Trailofbits +lang: de +tags: + ["solidity", "smart contracts", "security", "testing", "formal verification"] +skill: advanced +published: 2020-01-13 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore +--- + +Das Ziel dieses Tutorials ist es zu zeigen, wie du Manticore nutzt, um automatisch Fehler in Smart Contracts zu finden. + +## Installation {#installation} + +Manticore erfordert Python >= 3.6. Die Installation kann über pip oder Docker erfolgen. + +### Manticore über Docker {#manticore-through-docker} + +```bash +docker pull trailofbits/eth-security-toolbox +docker run -it -v "$PWD":/home/training trailofbits/eth-security-toolbox +``` + +_Der letzte Befehl führt die eth-security-toolbox in einem Docker aus, der Zugriff auf dein aktuelles Verzeichnis hat. Du kannst die Dateien von deinem Host aus ändern und die Tools für die Dateien aus dem Docker ausführen_ + +Führe im Docker aus: + +```bash +solc-select 0.5.11 +cd /home/trufflecon/ +``` + +### Manticore über pip {#manticore-through-pip} + +```bash +pip3 install --user manticore +``` + +solc 0.5.11 wird empfohlen. + +### Ausführen eines Skripts {#running-a-script} + +So führst du ein Python-Skript mit Python 3 aus: + +```bash +python3 script.py +``` + +## Einführung in die dynamische symbolische Ausführung {#introduction-to-dynamic-symbolic-execution} + +### Dynamische symbolische Ausführung – kurz erklärt {#dynamic-symbolic-execution-in-a-nutshell} + +Die dynamische symbolische Ausführung (DSE) ist eine Programmanalysetechnik, die einen Zustandsraum mit einem hohen Grad an semantischem Bewusstsein untersucht. Diese Technik basiert auf der Entdeckung von „Programmpfaden“, die als mathematische Formeln, sogenannte `path predicates`, dargestellt werden. Konzeptionell arbeitet diese Technik in zwei Schritten mit Pfadprädikaten: + +1. Sie werden unter Verwendung von Einschränkungen für die Programmeingabe konstruiert. +2. Sie werden verwendet, um Programmeingaben zu generieren, die die Ausführung der zugehörigen Pfade bewirken. + +Dieser Ansatz erzeugt keine Falsch-Positiv-Meldungen, da alle identifizierten Programmzustände während einer konkreten Ausführung ausgelöst werden können. Findet die Analyse beispielsweise einen Integer-Überlauf, ist dieser garantiert reproduzierbar. + +### Beispiel für ein Pfadprädikat {#path-predicate-example} + +Um einen Einblick in die Funktionsweise von DSE zu erhalten, sieh dir das folgende Beispiel an: + +```solidity +function f(uint a){ + + if (a == 65) { + // Ein Fehler ist vorhanden + } + +} +``` + +Da `f()` zwei Pfade enthält, konstruiert eine DSE zwei verschiedene Pfadprädikate: + +- Pfad 1: `a == 65` +- Pfad 2: `Not (a == 65)` + +Jedes Pfadprädikat ist eine mathematische Formel, die an einen sogenannten [SMT-Solver](https://wikipedia.org/wiki/Satisfiability_modulo_theories) übergeben werden kann, der versucht, die Gleichung zu lösen. Für `Pfad 1` wird der Solver ausgeben, dass der Pfad mit `a = 65` untersucht werden kann. Für `Pfad 2` kann der Solver für `a` einen beliebigen anderen Wert als 65 angeben, zum Beispiel `a = 0`. + +### Überprüfen von Eigenschaften {#verifying-properties} + +Manticore ermöglicht die vollständige Kontrolle über die gesamte Ausführung jedes Pfades. Dadurch kannst du beliebige Einschränkungen für fast alles hinzufügen. Diese Kontrolle ermöglicht das Erstellen von Eigenschaften für den Vertrag. + +Betrachte das folgende Beispiel: + +```solidity +function unsafe_add(uint a, uint b) returns(uint c){ + c = a + b; // kein Überlaufschutz + return c; +} +``` + +Hier gibt es in der Funktion nur einen Pfad zu untersuchen: + +- Pfad 1: `c = a + b` + +Mit Manticore kannst du auf einen Überlauf prüfen und dem Pfadprädikat Einschränkungen hinzufügen: + +- `c = a + b AND (c < a OR c < b)` + +Wenn es möglich ist, eine Bewertung für `a` und `b` zu finden, für die das obige Pfadprädikat erfüllbar ist, bedeutet das, dass du einen Überlauf gefunden hast. Der Solver kann beispielsweise die Eingabe `a = 10 , b = MAXUINT256` generieren. + +Wenn du eine korrigierte Version betrachtest: + +```solidity +function safe_add(uint a, uint b) returns(uint c){ + c = a + b; + require(c>=a); + require(c>=b); + return c; +} +``` + +Die zugehörige Formel mit Überlaufprüfung wäre: + +- `c = a + b AND (c >= a) AND (c=>b) AND (c < a OR c < b)` + +Diese Formel kann nicht gelöst werden; mit anderen Worten, das ist ein **Beweis** dafür, dass in `safe_add` der Wert `c` immer größer wird. + +DSE ist somit ein leistungsfähiges Tool, das beliebige Einschränkungen in deinem Code überprüfen kann. + +## Ausführung mit Manticore {#running-under-manticore} + +Wir werden sehen, wie man einen Smart Contract mit der Manticore-API untersucht. Das Ziel ist der folgende Smart Contract [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol): + +```solidity +pragma solidity >=0.4.24 <0.6.0; + +contract Simple { + function f(uint a) payable public{ + if (a == 65) { + revert(); + } + } +} +``` + +### Eine eigenständige Untersuchung ausführen {#run-a-standalone-exploration} + +Du kannst Manticore mit dem folgenden Befehl direkt auf dem Smart Contract ausführen (`project` kann eine Solidity-Datei oder ein Projektverzeichnis sein): + +```bash +$ manticore project +``` + +Du erhältst die Ausgabe von Testfällen wie diesem (die Reihenfolge kann sich ändern): + +``` +... +... m.c.manticore:INFO: Generated testcase No. 0 - STOP +... m.c.manticore:INFO: Generated testcase No. 1 - REVERT +... m.c.manticore:INFO: Generated testcase No. 2 - RETURN +... m.c.manticore:INFO: Generated testcase No. 3 - REVERT +... m.c.manticore:INFO: Generated testcase No. 4 - STOP +... m.c.manticore:INFO: Generated testcase No. 5 - REVERT +... m.c.manticore:INFO: Generated testcase No. 6 - REVERT +... m.c.manticore:INFO: Results in /home/ethsec/workshops/Automated Smart Contracts Audit - TruffleCon 2018/manticore/examples/mcore_t6vi6ij3 +... +``` + +Ohne zusätzliche Informationen untersucht Manticore den Vertrag mit neuen symbolischen Transaktionen, bis es keine neuen Pfade mehr auf dem Vertrag untersucht. Manticore führt nach einer fehlgeschlagenen Transaktion (z. B. nach einem Revert) keine neuen Transaktionen aus. + +Manticore gibt die Informationen in einem `mcore_*`-Verzeichnis aus. Unter anderem findest du in diesem Verzeichnis: + +- `global.summary`: Abdeckung und Compiler-Warnungen +- `test_XXXXX.summary`: Abdeckung, letzte Anweisung, Kontostände pro Testfall +- `test_XXXXX.tx`: detaillierte Liste der Transaktionen pro Testfall + +Hier findet Manticore 7 Testfälle, die Folgendem entsprechen (die Reihenfolge der Dateinamen kann sich ändern): + +| | Transaktion 0 | Transaktion 1 | Transaktion 2 | Ergebnis | +| :-------------------------------------------------------: | :----------------: | :------------------------: | -------------------------- | :------: | +| **test_00000000.tx** | Vertragserstellung | f(!=65) | f(!=65) | STOP | +| **test_00000001.tx** | Vertragserstellung | Fallback-Funktion | | REVERT | +| **test_00000002.tx** | Vertragserstellung | | | RETURN | +| **test_00000003.tx** | Vertragserstellung | f(65) | | REVERT | +| **test_00000004.tx** | Vertragserstellung | f(!=65) | | STOP | +| **test_00000005.tx** | Vertragserstellung | f(!=65) | f(65) | REVERT | +| **test_00000006.tx** | Vertragserstellung | f(!=65) | Fallback-Funktion | REVERT | + +_Zusammenfassung der Untersuchung: f(!=65) bezeichnet einen Aufruf von f mit einem beliebigen Wert, der nicht 65 ist._ + +Wie du sehen kannst, generiert Manticore für jede erfolgreiche oder rückgängig gemachte (reverted) Transaktion einen eindeutigen Testfall. + +Verwende das `--quick-mode`-Flag, wenn du eine schnelle Code-Untersuchung wünschst (es deaktiviert Fehlerdetektoren, Gas-Berechnung, ...) + +### Einen Smart Contract über die API manipulieren {#manipulate-a-smart-contract-through-the-api} + +Dieser Abschnitt beschreibt im Detail, wie man einen Smart Contract über die Manticore-Python-API manipuliert. Du kannst eine neue Datei mit der Python-Erweiterung `*.py` erstellen und den notwendigen Code schreiben, indem du die API-Befehle (deren Grundlagen unten beschrieben werden) in diese Datei einfügst und sie dann mit dem Befehl `$ python3 *.py` ausführst. Du kannst die folgenden Befehle auch direkt in der Python-Konsole ausführen. Um die Konsole zu starten, verwende den Befehl `$ python3`. + +### Erstellen von Konten {#creating-accounts} + +Als Erstes solltest du mit den folgenden Befehlen eine neue Blockchain initialisieren: + +```python +from manticore.ethereum import ManticoreEVM + +m = ManticoreEVM() +``` + +Ein Nicht-Vertragskonto wird mit [m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account) erstellt: + +```python +user_account = m.create_account(balance=1000) +``` + +Ein Solidity-Vertrag kann mit [m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract) bereitgestellt werden: + +```solidity +source_code = ''' +pragma solidity >=0.4.24 <0.6.0; +contract Simple { + function f(uint a) payable public{ + if (a == 65) { + revert(); + } + } +} +''' +# Initiate the contract +contract_account = m.solidity_create_contract(source_code, owner=user_account) +``` + +#### Zusammenfassung {#summary} + +- Du kannst Benutzer- und Vertragskonten mit [m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account) und [m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract) erstellen. + +### Ausführen von Transaktionen {#executing-transactions} + +Manticore unterstützt zwei Arten von Transaktionen: + +- Raw-Transaktion: Alle Funktionen werden untersucht +- Benannte Transaktion: Es wird nur eine Funktion untersucht + +#### Raw-Transaktion {#raw-transaction} + +Eine Raw-Transaktion wird mit [m.transaction](https://manticore.readthedocs.io/en/latest/evm.html?highlight=transaction#manticore.ethereum.ManticoreEVM.transaction) ausgeführt: + +```python +m.transaction(caller=user_account, + address=contract_account, + data=data, + value=value) +``` + +Der Aufrufer, die Adresse, die Daten oder der Wert der Transaktion können entweder konkret oder symbolisch sein: + +- [m.make_symbolic_value](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_value#manticore.ethereum.ManticoreEVM.make_symbolic_value) erstellt einen symbolischen Wert. +- [m.make_symbolic_buffer(size)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_buffer#manticore.ethereum.ManticoreEVM.make_symbolic_buffer) erstellt ein symbolisches Byte-Array. + +Beispiel: + +```python +symbolic_value = m.make_symbolic_value() +symbolic_data = m.make_symbolic_buffer(320) +m.transaction(caller=user_account, + address=contract_address, + data=symbolic_data, + value=symbolic_value) +``` + +Wenn die Daten symbolisch sind, untersucht Manticore während der Transaktionsausführung alle Funktionen des Vertrags. Es ist hilfreich, die Erklärung der Fallback-Funktion im Artikel [Hands on the Ethernaut CTF](https://blog.trailofbits.com/2017/11/06/hands-on-the-ethernaut-ctf/) zu lesen, um zu verstehen, wie die Funktionsauswahl funktioniert. + +#### Benannte Transaktion {#named-transaction} + +Funktionen können über ihren Namen ausgeführt werden. +Um `f(uint var)` mit einem symbolischen Wert, von `user_account` und mit 0 Ether auszuführen, verwende: + +```python +symbolic_var = m.make_symbolic_value() +contract_account.f(symbolic_var, caller=user_account, value=0) +``` + +Wenn der `value` der Transaktion nicht angegeben ist, ist er standardmäßig 0. + +#### Zusammenfassung {#summary-1} + +- Argumente einer Transaktion können konkret oder symbolisch sein +- Eine Raw-Transaktion untersucht alle Funktionen +- Funktionen können über ihren Namen aufgerufen werden + +### Arbeitsbereich {#workspace} + +`m.workspace` ist das Verzeichnis, das als Ausgabeverzeichnis für alle erzeugten Dateien verwendet wird: + +```python +print("Ergebnisse sind in {}".format(m.workspace)) +``` + +### Die Untersuchung beenden {#terminate-the-exploration} + +Um die Untersuchung zu beenden, verwende [m.finalize()](https://manticore.readthedocs.io/en/latest/evm.html?highlight=finalize#manticore.ethereum.ManticoreEVM.finalize). Sobald diese Methode aufgerufen wird, sollten keine weiteren Transaktionen gesendet werden. Manticore generiert dann Testfälle für jeden untersuchten Pfad. + +### Zusammenfassung: Ausführung mit Manticore {#summary-running-under-manticore} + +Wenn wir alle vorherigen Schritte zusammenfassen, erhalten wir: + +```python +from manticore.ethereum import ManticoreEVM + +m = ManticoreEVM() + +with open('example.sol') as f: + source_code = f.read() + +user_account = m.create_account(balance=1000) +contract_account = m.solidity_create_contract(source_code, owner=user_account) + +symbolic_var = m.make_symbolic_value() +contract_account.f(symbolic_var) + +print("Ergebnisse sind in {}".format(m.workspace)) +m.finalize() # die Untersuchung anhalten +``` + +Den gesamten obigen Code findest du in [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) + +## Pfade erhalten, die eine Ausnahme auslösen {#getting-throwing-paths} + +Wir generieren nun spezifische Eingaben für die Pfade, die in `f()` eine Ausnahme auslösen. Das Ziel ist nach wie vor der folgende Smart Contract [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol): + +```solidity +pragma solidity >=0.4.24 <0.6.0; +contract Simple { + function f(uint a) payable public{ + if (a == 65) { + revert(); + } + } +} +``` + +### Verwenden von Zustandsinformationen {#using-state-information} + +Jeder ausgeführte Pfad hat seinen eigenen Zustand der Blockchain. Ein Zustand ist entweder bereit (ready) oder beendet (killed), was bedeutet, dass er eine THROW- oder REVERT-Anweisung erreicht: + +- [m.ready_states](https://manticore.readthedocs.io/en/latest/states.html#accessing): die Liste der Zustände, die bereit sind (sie haben kein REVERT/INVALID ausgeführt) +- [m.killed_states](https://manticore.readthedocs.io/en/latest/states.html#accessings): die Liste der beendeten Zustände +- [m.all_states](https://manticore.readthedocs.io/en/latest/states.html#accessings): alle Zustände + +```python +for state in m.all_states: + # etwas mit dem Zustand tun +``` + +Du kannst auf Zustandsinformationen zugreifen. Beispiel: + +- `state.platform.get_balance(account.address)`: der Kontostand des Kontos +- `state.platform.transactions`: die Liste der Transaktionen +- `state.platform.transactions[-1].return_data`: die von der letzten Transaktion zurückgegebenen Daten + +Die von der letzten Transaktion zurückgegebenen Daten sind ein Array, das beispielsweise mit `ABI.deserialize` in einen Wert umgewandelt werden kann: + +```python +data = state.platform.transactions[0].return_data +data = ABI.deserialize("uint", data) +``` + +### So generierst du einen Testfall {#how-to-generate-testcase} + +Verwende [m.generate_testcase(state, name)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=generate_testcase#manticore.ethereum.ManticoreEVM.generate_testcase), um einen Testfall zu generieren: + +```python +m.generate_testcase(state, 'BugFound') +``` + +### Zusammenfassung {#summary-2} + +- Du kannst mit `m.all_states` über die Zustände iterieren +- `state.platform.get_balance(account.address)` gibt den Kontostand des Kontos zurück +- `state.platform.transactions` gibt die Liste der Transaktionen zurück +- `transaction.return_data` sind die zurückgegebenen Daten +- `m.generate_testcase(state, name)` generiert Eingaben für den Zustand + +### Zusammenfassung: Pfad erhalten, der eine Ausnahme auslöst {#summary-getting-throwing-path} + +```python +from manticore.ethereum import ManticoreEVM + +m = ManticoreEVM() + +with open('example.sol') as f: + source_code = f.read() + +user_account = m.create_account(balance=1000) +contract_account = m.solidity_create_contract(source_code, owner=user_account) + +symbolic_var = m.make_symbolic_value() +contract_account.f(symbolic_var) + +## Prüfen, ob eine Ausführung mit REVERT oder INVALID endet + +for state in m.terminated_states: + last_tx = state.platform.transactions[-1] + if last_tx.result in ['REVERT', 'INVALID']: + print('Ausnahme gefunden {}'.format(m.workspace)) + m.generate_testcase(state, 'ThrowFound') +``` + +Den gesamten obigen Code findest du in [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) + +_Hinweis: Wir hätten ein viel einfacheres Skript generieren können, da alle von `terminated_state` zurückgegebenen Zustände REVERT oder INVALID in ihrem Ergebnis haben: Dieses Beispiel sollte nur demonstrieren, wie man die API manipuliert._ + +## Hinzufügen von Einschränkungen {#adding-constraints} + +Wir werden sehen, wie man die Untersuchung einschränken kann. Wir gehen von der Annahme aus, dass die Dokumentation von `f()` besagt, dass die Funktion niemals mit `a == 65` aufgerufen wird, sodass jeder Fehler bei `a == 65` kein echter Fehler ist. Das Ziel ist nach wie vor der folgende Smart Contract [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol): + +```solidity +pragma solidity >=0.4.24 <0.6.0; +contract Simple { + function f(uint a) payable public{ + if (a == 65) { + revert(); + } + } +} +``` + +### Operatoren {#operators} + +Das [Operators](https://github.com/trailofbits/manticore/blob/master/manticore/core/smtlib/operators.py)-Modul erleichtert die Bearbeitung von Einschränkungen und bietet unter anderem Folgendes: + +- Operators.AND, +- Operators.OR, +- Operators.UGT (unsigned greater than), +- Operators.UGE (unsigned greater than or equal to), +- Operators.ULT (unsigned lower than), +- Operators.ULE (unsigned lower than or equal to). + +Verwende Folgendes, um das Modul zu importieren: + +```python +from manticore.core.smtlib import Operators +``` + +`Operators.CONCAT` wird verwendet, um ein Array mit einem Wert zu verketten. Zum Beispiel muss `return_data` einer Transaktion in einen Wert geändert werden, um ihn mit einem anderen Wert zu vergleichen: + +```python +last_return = Operators.CONCAT(256, *last_return) +``` + +### Einschränkungen {#state-constraint} + +Du kannst Einschränkungen global oder für einen bestimmten Zustand verwenden. + +#### Globale Einschränkung {#state-constraint} + +Verwende `m.constrain(constraint)`, um eine globale Einschränkung hinzuzufügen. +Du kannst zum Beispiel einen Vertrag von einer symbolischen Adresse aus aufrufen und diese Adresse auf bestimmte Werte beschränken: + +```python +symbolic_address = m.make_symbolic_value() +m.constraint(Operators.OR(symbolic == 0x41, symbolic_address == 0x42)) +m.transaction(caller=user_account, + address=contract_account, + data=m.make_symbolic_buffer(320), + value=0) +``` + +#### Zustandsbeschränkung {#state-constraint} + +Verwende [state.constrain(constraint)](https://manticore.readthedocs.io/en/latest/states.html?highlight=StateBase#manticore.core.state.StateBase.constrain), um eine Einschränkung zu einem bestimmten Zustand hinzuzufügen. +Dies kann verwendet werden, um den Zustand nach seiner Untersuchung einzuschränken, um eine Eigenschaft darin zu überprüfen. + +### Einschränkung prüfen {#checking-constraint} + +Verwende `solver.check(state.constraints)`, um zu erfahren, ob eine Einschränkung noch erfüllbar ist. +Das folgende Beispiel schränkt beispielsweise `symbolic_value` so ein, dass es sich von 65 unterscheidet, und prüft, ob der Zustand noch erfüllbar ist: + +```python +state.constrain(symbolic_var != 65) +if solver.check(state.constraints): + # Zustand ist erfüllbar +``` + +### Zusammenfassung: Hinzufügen von Einschränkungen {#summary-adding-constraints} + +Wenn wir dem vorherigen Code eine Einschränkung hinzufügen, erhalten wir: + +```python +from manticore.ethereum import ManticoreEVM +from manticore.core.smtlib.solver import Z3Solver + +solver = Z3Solver.instance() + +m = ManticoreEVM() + +with open("example.sol") as f: + source_code = f.read() + +user_account = m.create_account(balance=1000) +contract_account = m.solidity_create_contract(source_code, owner=user_account) + +symbolic_var = m.make_symbolic_value() +contract_account.f(symbolic_var) + +no_bug_found = True + +## Prüfen, ob eine Ausführung mit REVERT oder INVALID endet + +for state in m.terminated_states: + last_tx = state.platform.transactions[-1] + if last_tx.result in ['REVERT', 'INVALID']: + # wir betrachten den Pfad nicht, in dem a == 65 ist + condition = symbolic_var != 65 + if m.generate_testcase(state, name="BugFound", only_if=condition): + print(f'Fehler gefunden, Ergebnisse sind in {m.workspace}') + no_bug_found = False + +if no_bug_found: + print(f'Kein Fehler gefunden') +``` + +Den gesamten obigen Code findest du in [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) diff --git a/public/content/translations/de/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md b/public/content/translations/de/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md new file mode 100644 index 00000000000..b6f719b059a --- /dev/null +++ b/public/content/translations/de/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md @@ -0,0 +1,233 @@ +--- +title: So verwenden Sie Slither, um Bugs in Smart Contracts zu finden +description: So verwenden Sie Slither, um automatisch Fehler in Smart Contracts zu finden +author: Trailofbits +lang: de +tags: ["solidity", "smart contracts", "security", "testing"] +skill: advanced +published: 2020-06-09 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither +--- + +## So verwenden Sie Slither {#how-to-use-slither} + +Das Ziel dieses Tutorials ist es zu zeigen, wie Sie Slither verwenden, um automatisch Fehler in Smart Contracts zu finden. + +- [Installation](#installation) +- [Verwendung der Befehlszeile](#command-line) +- [Einführung in die statische Analyse](#static-analysis): Kurze Einführung in die statische Analyse +- [API](#api-basics): Python-API-Beschreibung + +## Installation {#installation} + +Slither erfordert Python >= 3.6. Die Installation kann über pip oder Docker erfolgen. + +Slither über pip: + +```bash +pip3 install --user slither-analyzer +``` + +Slither über Docker: + +```bash +docker pull trailofbits/eth-security-toolbox +docker run -it -v "$PWD":/home/trufflecon trailofbits/eth-security-toolbox +``` + +_Der letzte Befehl führt die eth-security-toolbox in einem Docker aus, der Zugriff auf dein aktuelles Verzeichnis hat. Du kannst die Dateien von deinem Host aus ändern und die Tools für die Dateien aus dem Docker ausführen_ + +Führe im Docker aus: + +```bash +solc-select 0.5.11 +cd /home/trufflecon/ +``` + +### Ausführen eines Skripts {#running-a-script} + +So führst du ein Python-Skript mit Python 3 aus: + +```bash +python3 script.py +``` + +### Befehlszeile {#command-line} + +**Befehlszeile versus benutzerdefinierte Skripte.** Slither wird mit einer Reihe von vordefinierten Detektoren geliefert, die viele häufige Fehler finden. Wenn Sie Slither von der Befehlszeile aus aufrufen, werden alle Detektoren ausgeführt, ohne dass detaillierte Kenntnisse der statischen Analyse erforderlich sind: + +```bash +slither project_paths +``` + +Zusätzlich zu den Detektoren verfügt Slither über Code-Review-Funktionen durch seine [Printers](https://github.com/crytic/slither#printers) und [Tools](https://github.com/crytic/slither#tools). + +Verwenden Sie [crytic.io](https://github.com/crytic), um Zugang zu privaten Detektoren und zur GitHub-Integration zu erhalten. + +## Statische Analyse {#static-analysis} + +Die Fähigkeiten und das Design des statischen Analyse-Frameworks von Slither wurden in Blog-Beiträgen ([1](https://blog.trailofbits.com/2018/10/19/slither-a-solidity-static-analysis-framework/), [2](https://blog.trailofbits.com/2019/05/27/slither-the-leading-static-analyzer-for-smart-contracts/)) und einem [wissenschaftlichen Artikel](https://github.com/trailofbits/publications/blob/master/papers/wetseb19.pdf) beschrieben. + +Statische Analyse gibt es in verschiedenen Varianten. Ihnen ist wahrscheinlich bewusst, dass Compiler wie [clang](https://clang-analyzer.llvm.org/) und [gcc](https://lwn.net/Articles/806099/) auf diesen Forschungstechniken basieren, aber sie bilden auch die Grundlage für ([Infer](https://fbinfer.com/), [CodeClimate](https://codeclimate.com/), [FindBugs](http://findbugs.sourceforge.net/) und auf formalen Methoden basierende Werkzeuge wie [Frama-C](https://frama-c.com/) und [Polyspace](https://www.mathworks.com/products/polyspace.html)). + +Wir werden hier nicht erschöpfend auf statische Analysetechniken und Forscher eingehen. Stattdessen konzentrieren wir uns darauf, was nötig ist, um zu verstehen, wie Slither funktioniert, damit Sie es effektiver nutzen können, um Fehler zu finden und den Code zu verstehen. + +- [Code-Darstellung](#code-representation) +- [Code-Analyse](#analysis) +- [Zwischendarstellung](#intermediate-representation) + +### Code-Darstellung {#code-representation} + +Im Gegensatz zu einer dynamischen Analyse, die einen einzelnen Ausführungspfad betrachtet, betrachtet die statische Analyse alle Pfade auf einmal. Dazu stützt es sich auf eine andere Code-Darstellung. Die beiden häufigsten sind der abstrakte Syntaxbaum (AST) und der Kontrollflussgraph (CFG). + +### Abstrakte Syntaxbäume (AST) {#abstract-syntax-trees-ast} + +ASTs werden jedes Mal verwendet, wenn der Compiler Code parst. Es ist wahrscheinlich die grundlegendste Struktur, auf der statische Analysen durchgeführt werden können. + +Kurz gesagt, ein AST ist ein strukturierter Baum, in dem normalerweise jedes Blatt eine Variable oder eine Konstante enthält und interne Knoten Operanden oder Kontrollflussoperationen sind. Betrachten Sie den folgenden Code: + +```solidity +function safeAdd(uint a, uint b) pure internal returns(uint){ + if(a + b <= a){ + revert(); + } + return a + b; +} +``` + +Der entsprechende AST ist hier dargestellt: + +![AST](./ast.png) + +Slither verwendet den von solc exportierten AST. + +Obwohl der AST einfach zu erstellen ist, handelt es sich um eine verschachtelte Struktur. Manchmal ist dies nicht am einfachsten zu analysieren. Um beispielsweise die vom Ausdruck `a + b <= a` verwendeten Operationen zu identifizieren, müssen Sie zuerst `<=` und dann `+` analysieren. Ein gängiger Ansatz ist die Verwendung des sogenannten Visitor-Patterns, das rekursiv durch den Baum navigiert. Slither enthält einen generischen Visitor in [`ExpressionVisitor`](https://github.com/crytic/slither/blob/master/slither/visitors/expression/expression.py). + +Der folgende Code verwendet `ExpressionVisitor`, um zu erkennen, ob der Ausdruck eine Addition enthält: + +```python +from slither.visitors.expression.expression import ExpressionVisitor +from slither.core.expressions.binary_operation import BinaryOperationType + +class HasAddition(ExpressionVisitor): + + def result(self): + return self._result + + def _post_binary_operation(self, expression): + if expression.type == BinaryOperationType.ADDITION: + self._result = True + +visitor = HasAddition(expression) # expression ist der zu testende Ausdruck +print(f'Der Ausdruck {expression} enthält eine Addition: {visitor.result()}') +``` + +### Kontrollflussgraph (CFG) {#control-flow-graph-cfg} + +Die zweithäufigste Code-Darstellung ist der Kontrollflussgraph (CFG). Wie der Name schon sagt, handelt es sich um eine graphbasierte Darstellung, die alle Ausführungspfade aufzeigt. Jeder Knoten enthält eine oder mehrere Anweisungen. Kanten im Graphen stellen die Kontrollflussoperationen dar (if/then/else, Schleife usw.). Der CFG unseres vorherigen Beispiels lautet: + +![CFG](./cfg.png) + +Der CFG ist die Darstellung, auf der die meisten Analysen aufbauen. + +Es gibt viele andere Code-Darstellungen. Jede Darstellung hat Vor- und Nachteile, je nachdem, welche Analyse Sie durchführen möchten. + +### Analyse {#analysis} + +Die einfachste Art von Analysen, die Sie mit Slither durchführen können, sind syntaktische Analysen. + +### Syntaktische Analyse {#syntax-analysis} + +Slither kann durch die verschiedenen Komponenten des Codes und deren Darstellung navigieren, um Inkonsistenzen und Fehler mit einem Ansatz zu finden, der dem Pattern-Matching ähnelt. + +Die folgenden Detektoren suchen beispielsweise nach syntaxbezogenen Problemen: + +- [Zustandsvariablen-Shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing): iteriert über alle Zustandsvariablen und prüft, ob eine davon eine Variable aus einem geerbten Vertrag verschattet ([state.py#L51-L62](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/shadowing/state.py#L51-L62)) + +- [Inkorrektes ERC20-Interface](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface): sucht nach inkorrekten ERC20-Funktionssignaturen ([incorrect_erc20_interface.py#L34-L55](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/erc/incorrect_erc20_interface.py#L34-L55)) + +### Semantische Analyse {#semantic-analysis} + +Im Gegensatz zur Syntaxanalyse geht eine semantische Analyse tiefer und analysiert die "Bedeutung" des Codes. Diese Familie umfasst einige allgemeine Arten von Analysen. Sie führen zu aussagekräftigeren und nützlicheren Ergebnissen, sind aber auch komplexer zu schreiben. + +Semantische Analysen werden für die fortschrittlichsten Schwachstellenerkennungen verwendet. + +#### Datenabhängigkeitsanalyse {#fixed-point-computation} + +Eine Variable `variable_a` gilt als datenabhängig von `variable_b`, wenn es einen Pfad gibt, auf dem der Wert von `variable_a` durch `variable_b` beeinflusst wird. + +Im folgenden Code ist `variable_a` von `variable_b` abhängig: + +```solidity +// ... +variable_a = variable_b + 1; +``` + +Slither verfügt über integrierte [Datenabhängigkeits](https://github.com/crytic/slither/wiki/data-dependency)-Fähigkeiten, dank seiner Zwischendarstellung (die in einem späteren Abschnitt besprochen wird). + +Ein Beispiel für die Verwendung der Datenabhängigkeit findet sich im [Detektor für gefährliche strikte Gleichheit](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities). Hier sucht Slither nach einem strikten Gleichheitsvergleich mit einem gefährlichen Wert ([incorrect_strict_equality.py#L86-L87](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L86-L87)) und informiert den Benutzer, dass er `>=` oder `<=` anstelle von `==` verwenden sollte, um zu verhindern, dass ein Angreifer den Vertrag in eine Falle lockt. Unter anderem wird der Detektor den Rückgabewert eines Aufrufs von `balanceOf(address)` ([incorrect_strict_equality.py#L63-L64](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L63-L64)) als gefährlich einstufen und die Datenabhängigkeits-Engine verwenden, um seine Verwendung zu verfolgen. + +#### Fixpunktberechnung {#fixed-point-computation} + +Wenn Ihre Analyse durch den CFG navigiert und den Kanten folgt, werden Sie wahrscheinlich bereits besuchte Knoten sehen. Wenn zum Beispiel eine Schleife wie unten dargestellt ist: + +```solidity +for(uint i; i < range; ++){ + variable_a += 1 +} +``` + +Ihre Analyse muss wissen, wann sie anhalten muss. Hier gibt es zwei Hauptstrategien: (1) jeden Knoten eine endliche Anzahl von Malen durchlaufen, (2) einen sogenannten _Fixpunkt_ berechnen. Ein Fixpunkt bedeutet im Grunde, dass die Analyse dieses Knotens keine aussagekräftigen Informationen mehr liefert. + +Ein Beispiel für die Verwendung eines Fixpunkts findet sich in den Reentrancy-Detektoren: Slither untersucht die Knoten und sucht nach externen Aufrufen sowie Schreib- und Lesezugriffen auf den Speicher. Sobald ein Fixpunkt erreicht ist ([reentrancy.py#L125-L131](https://github.com/crytic/slither/blob/master/slither/detectors/reentrancy/reentrancy.py#L125-L131)), wird die Untersuchung gestoppt und die Ergebnisse werden anhand verschiedener Reentrancy-Muster ([reentrancy_benign.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_benign.py), [reentrancy_read_before_write.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_read_before_write.py), [reentrancy_eth.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_eth.py)) analysiert, um festzustellen, ob eine Reentrancy vorliegt. + +Das Schreiben von Analysen mit effizienter Fixpunktberechnung erfordert ein gutes Verständnis dafür, wie die Analyse ihre Informationen propagiert. + +### Zwischendarstellung {#intermediate-representation} + +Eine Zwischendarstellung (Intermediate Representation, IR) ist eine Sprache, die sich besser für die statische Analyse eignen soll als die ursprüngliche. Slither übersetzt Solidity in seine eigene IR: [SlithIR](https://github.com/crytic/slither/wiki/SlithIR). + +Das Verständnis von SlithIR ist nicht notwendig, wenn Sie nur einfache Prüfungen schreiben wollen. Es wird sich jedoch als nützlich erweisen, wenn Sie vorhaben, fortgeschrittene semantische Analysen zu schreiben. Die [SlithIR](https://github.com/crytic/slither/wiki/Printer-documentation#slithir)- und [SSA](https://github.com/crytic/slither/wiki/Printer-documentation#slithir-ssa)-Printers helfen Ihnen zu verstehen, wie der Code übersetzt wird. + +## API-Grundlagen {#api-basics} + +Slither hat eine API, mit der Sie die grundlegenden Attribute des Vertrags und seiner Funktionen untersuchen können. + +So laden Sie eine Codebasis: + +```python +from slither import Slither +slither = Slither('/path/to/project') + +``` + +### Untersuchen von Verträgen und Funktionen {#exploring-contracts-and-functions} + +Ein `Slither`-Objekt hat: + +- `contracts (list(Contract)`: Liste von Verträgen +- `contracts_derived (list(Contract)`: Liste von Verträgen, die nicht von einem anderen Vertrag geerbt werden (Teilmenge von `contracts`) +- `get_contract_from_name (str)`: Gibt einen Vertrag anhand seines Namens zurück + +Ein `Contract`-Objekt hat: + +- `name (str)`: Name des Vertrags +- `functions (list(Function))`: Liste von Funktionen +- `modifiers (list(Modifier))`: Liste von Modifikatoren +- `all_functions_called (list(Function/Modifier))`: Liste aller internen Funktionen, die vom Vertrag aus erreichbar sind +- `inheritance (list(Contract))`: Liste der geerbten Verträge +- `get_function_from_signature (str)`: Gibt eine Funktion anhand ihrer Signatur zurück +- `get_modifier_from_signature (str)`: Gibt einen Modifikator anhand seiner Signatur zurück +- `get_state_variable_from_name (str)`: Gibt eine Zustandsvariable anhand ihres Namens zurück + +Ein `Function`- oder ein `Modifier`-Objekt hat: + +- `name (str)`: Name der Funktion +- `contract (contract)`: der Vertrag, in dem die Funktion deklariert ist +- `nodes (list(Node))`: Liste der Nodes, aus denen der CFG der Funktion/des Modifikators besteht +- `entry_point (Node)`: Einstiegspunkt des CFG +- `variables_read (list(Variable))`: Liste der gelesenen Variablen +- `variables_written (list(Variable))`: Liste der geschriebenen Variablen +- `state_variables_read (list(StateVariable))`: Liste der gelesenen Zustandsvariablen (Teilmenge von `variables_read`) +- `state_variables_written (list(StateVariable))`: Liste der geschriebenen Zustandsvariablen (Teilmenge von `variables_written`) diff --git a/public/content/translations/de/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md b/public/content/translations/de/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md new file mode 100644 index 00000000000..a5a54f51591 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md @@ -0,0 +1,81 @@ +--- +title: Wie Sie Tellor als Ihr Orakel einrichten +description: "Eine Anleitung für den Einstieg in die Integration des Tellor-Orakels in Ihr Protokoll" +author: "Tellor" +lang: de +tags: ["solidity", "smart contracts", "oracles"] +skill: beginner +published: 2021-06-29 +source: Tellor Docs +sourceUrl: https://docs.tellor.io/tellor/ +--- + +Quizfrage: Ihr Protokoll ist fast fertig, aber es braucht ein Orakel, um Zugang zu Offchain-Daten zu erhalten... Was tun Sie? + +## (Optionale) Voraussetzungen {#soft-prerequisites} + +Dieser Beitrag soll den Zugriff auf einen Orakel-Feed so einfach und unkompliziert wie möglich gestalten. Davon abgesehen gehen wir von den folgenden Programmierkenntnissen aus, um uns auf den Orakel-Aspekt zu konzentrieren. + +Annahmen: + +- Sie können in einem Terminal navigieren +- Sie haben npm installiert +- Sie wissen, wie man npm zur Verwaltung von Abhängigkeiten verwendet + +Tellor ist ein live und quelloffenes Orakel, das zur Implementierung bereit ist. Diese Anleitung für Anfänger soll zeigen, wie einfach man mit Tellor loslegen kann, um Ihr Projekt mit einem vollständig dezentralen und zensurresistenten Orakel auszustatten. + +## Überblick {#overview} + +Tellor ist ein Orakelsystem, bei dem Parteien den Wert eines Offchain-Datenpunkts (z. B. BTC/USD) anfordern können und Reporter darum konkurrieren, diesen Wert einer Onchain-Datenbank hinzuzufügen, auf die alle Smart Contracts von Ethereum zugreifen können. Die Eingaben in diese Datenbank werden durch ein Netzwerk von gestakten Reportern gesichert. Tellor nutzt krypto-ökonomische Anreizmechanismen, die ehrliche Dateneinreichungen von Reportern belohnen und schlechte Akteure durch die Emission des Tellor-Tokens, Tributes (TRB), und einen Streitbeilegungsmechanismus bestrafen. + +In diesem Tutorial werden wir Folgendes behandeln: + +- Einrichten des anfänglichen Toolkits, das Sie für den Start benötigen. +- Durchgehen eines einfachen Beispiels. +- Auflisten der Testnet-Adressen von Netzwerken, auf denen Sie Tellor derzeit testen können. + +## UsingTellor {#usingtellor} + +Als Erstes sollten Sie die grundlegenden Tools installieren, die für die Verwendung von Tellor als Ihr Orakel erforderlich sind. Verwenden Sie [dieses Paket](https://github.com/tellor-io/usingtellor), um die Tellor User Contracts zu installieren: + +`npm install usingtellor` + +Nach der Installation können Ihre Verträge die Funktionen aus dem Vertrag „UsingTellor“ erben. + +Großartig! Jetzt, wo Sie die Tools bereit haben, lassen Sie uns eine einfache Übung durchgehen, bei der wir den Bitcoin-Preis abrufen: + +### BTC/USD-Beispiel {#btcusd-example} + +Erben Sie den UsingTellor-Vertrag und übergeben Sie die Tellor-Adresse als Konstruktorargument: + +Hier ein Beispiel: + +```solidity +import "usingtellor/contracts/UsingTellor.sol"; + +contract PriceContract is UsingTellor { + uint256 public btcPrice; + + //Dieser Vertrag hat jetzt Zugriff auf alle Funktionen in UsingTellor + +constructor(address payable _tellorAddress) UsingTellor(_tellorAddress) public {} + +function setBtcPrice() public { + bytes memory _b = abi.encode("SpotPrice",abi.encode("btc","usd")); + bytes32 _queryId = keccak256(_b); + + uint256 _timestamp; + bytes _value; + + (_value, _timestamp) = getDataBefore(_queryId, block.timestamp - 15 minutes); + + btcPrice = abi.decode(_value,(uint256)); + } +} +``` + +Eine vollständige Liste der Vertragsadressen finden Sie [hier](https://docs.tellor.io/tellor/the-basics/contracts-reference). + +Zur Vereinfachung der Nutzung wird das UsingTellor-Repo mit einer Version des [Tellor Playground](https://github.com/tellor-io/TellorPlayground)-Vertrags für eine einfachere Integration geliefert. Eine Liste hilfreicher Funktionen finden Sie [hier](https://github.com/tellor-io/sampleUsingTellor#tellor-playground). + +Für eine robustere Implementierung des Tellor-Orakels sehen Sie sich die vollständige Liste der verfügbaren Funktionen [hier](https://github.com/tellor-io/usingtellor/blob/master/README.md) an. diff --git a/public/content/translations/de/developers/tutorials/how-to-view-nft-in-metamask/index.md b/public/content/translations/de/developers/tutorials/how-to-view-nft-in-metamask/index.md index 7810e36f7c0..a4b9d9270f8 100644 --- a/public/content/translations/de/developers/tutorials/how-to-view-nft-in-metamask/index.md +++ b/public/content/translations/de/developers/tutorials/how-to-view-nft-in-metamask/index.md @@ -1,38 +1,33 @@ --- -title: So zeigen Sie Ihren NFT in Ihrem Wallet an (Teil 3/3 der NFT-Tutorialreihe) -description: In diesem Tutorial wird beschrieben, wie Sie einen existierenden NFT auf MetaMask einsehen können. +title: So zeigen Sie Ihr NFT in Ihrem Wallet an (Teil 3/3 der NFT-Tutorialreihe) +description: "Dieses Tutorial beschreibt, wie Sie ein bestehendes NFT auf MetaMask anzeigen können!" author: "Sumi Mudgil" -tags: - - "NFTs" - - "ERC-721" - - "Alchemy" - - "Non Fungible Token" - - "Solidity" +tags: ["ERC-721", "Alchemy", "Solidity"] skill: beginner lang: de published: 2021-04-22 --- -Dieses Tutorial ist Teil 3/3 der NFT-Tutorialreihe, in dem wir unseren neu geprägten NFT betrachten. Die allgemeine Anleitung ist für alle ERC-721-Token auf MetaMask anwendbar, auch im Mainnet oder einem Testnet. Wenn Sie lernen möchten, wie Sie Ihren eigenen NFT auf Ethereum prägen können, sollten Sie sich [Teil 1 zum Schreiben und Bereitstellen eines NFT-Smart-Contracts](/developers/tutorials/how-to-write-and-deploy-an-nft) ansehen. +Dieses Tutorial ist Teil 3/3 der NFT-Tutorialreihe, in dem wir unser neu geprägtes NFT betrachten. Sie können dieses allgemeine Tutorial jedoch für jeden ERC-721-Token mit MetaMask verwenden, einschließlich auf dem Mainnet oder einem beliebigen Testnet. Wenn Sie lernen möchten, wie Sie Ihr eigenes NFT auf Ethereum prägen können, sollten Sie sich [Teil 1 zum Schreiben und Bereitstellen eines NFT-Smart-Contracts](/developers/tutorials/how-to-write-and-deploy-an-nft) ansehen! -Herzlichen Glückwunsch! Sie haben es zum kürzesten und einfachsten Teil unserer NFT-Tutorialreihe geschafft. In diesem Teil erfahren Sie, wie Sie Ihren frisch geprägten NFT in einer virtuellen Geldbörse (Wallet) anzeigen können. Für dieses Beispiel verwenden wir MetaMask, da wir es bereits in den beiden vorangegangenen Teilen verwendet haben. +Glückwunsch! Sie haben es zum kürzesten und einfachsten Teil unserer NFT-Tutorialreihe geschafft – wie Sie Ihr frisch geprägtes NFT in einer virtuellen Wallet anzeigen können. Für dieses Beispiel verwenden wir MetaMask, da wir es bereits in den beiden vorangegangenen Teilen verwendet haben. -Als Voraussetzung sollten Sie MetaMask bereits auf ihrem Handy oder in Ihrem Browser installiert haben und es sollte das Konto enthalten, für die Sie Ihre NFTs geprägt haben. Die App können Sie kostenlos auf [iOS](https://apps.apple.com/us/app/metamask-blockchain-wallet/id1438144202) oder [Android](https://play.google.com/store/apps/details?id=io.metamask&hl=de_US&gl=US) erhalten. +Voraussetzung ist, dass Sie MetaMask auf Ihrem Mobilgerät installiert haben und dass die App das Konto enthält, auf das Sie Ihr NFT geprägt haben – Sie können die App kostenlos für [iOS](https://apps.apple.com/us/app/metamask-blockchain-wallet/id1438144202) oder [Android](https://play.google.com/store/apps/details?id=io.metamask&hl=en_US&gl=US) herunterladen. -## Schritt 1: Das Netzwerk auf Ropsten festlegen {#set-network-to-ropsten} +## Schritt 1: Netzwerk auf Sepolia einstellen {#set-network-to-sepolia} -Drücken Sie oben in der App auf die Schaltfläche "Wallet". Daraufhin werden Sie aufgefordert, ein Netzwerk auszuwählen. Da unser NFT im Ropsten-Netzwerk geprägt wurde, sollten Sie als Netzwerk Ropsten auswählen. +Drücken Sie oben in der App auf die Schaltfläche "Wallet", woraufhin Sie aufgefordert werden, ein Netzwerk auszuwählen. Da unser NFT auf dem Sepolia-Netzwerk geprägt wurde, sollten Sie Sepolia als Ihr Netzwerk auswählen. -![So legen Sie Ropsten als Netzwerk auf MetaMask fest](./goerliMetamask.gif) +![So stellen Sie Sepolia als Ihr Netzwerk auf MetaMask Mobile ein](./goerliMetamask.gif) -## Schritt 2: Kollektion zu MetaMask hinzufügen {#add-nft-to-metamask} +## Schritt 2: Ihr Sammelobjekt zu MetaMask hinzufügen {#add-nft-to-metamask} -Sobald Sie sich im Ropsten-Netzwerk befinden, wählen Sie die Registerkarte "Collectibles" (Sammelbare Elemente) auf der rechten Seite und fügen Sie die NFT-Smart-Contract-Adresse und die ERC-721-Token-ID Ihres NFT hinzu. Sie können sie anhand des Transaktions-Hashes Ihres NFT, der im zweiten Teil unseres Tutorials bereitgestellt wurde, auf Etherscan finden. +Sobald Sie sich im Sepolia-Netzwerk befinden, wählen Sie rechts die Registerkarte "Collectibles" aus und fügen Sie die NFT-Smart-Contract-Adresse und die ERC-721-Token-ID Ihres NFT hinzu. Diese finden Sie auf Etherscan über den Transaktions-Hash Ihres in Teil II unseres Tutorials bereitgestellten NFT. -![So finden Sie Ihren Transaktions-Hash und die ERC-721-Token-ID](./findNFTEtherscan.png) +![So finden Sie Ihren Transaktions-Hash und Ihre ERC-721-Token-ID](./findNFTEtherscan.png) -Möglicherweise müssen Sie die Seite ein paar Mal aktualisieren, bis Sie den NFT sehen können. Aber keine Sorge, er wird da sein. +Sie müssen möglicherweise ein paar Mal aktualisieren, um Ihr NFT zu sehen – aber es wird da sein ! -![So laden Sie Ihren NFT in MetaMask hoch](./findNFTMetamask.gif) +![So laden Sie Ihr NFT auf MetaMask hoch](./findNFTMetamask.gif) -Glückwunsch! Sie haben erfolgreich einen NFT gepräft und können ihn jetzt sehen. Wir können es kaum erwarten zu sehen, wie Sie die NFT-Welt im Sturm erobern werden! +Glückwunsch! Sie haben erfolgreich ein NFT geprägt und können es jetzt ansehen! Wir können es kaum erwarten zu sehen, wie Sie die NFT-Welt im Sturm erobern werden! diff --git a/public/content/translations/de/developers/tutorials/how-to-write-and-deploy-an-nft/index.md b/public/content/translations/de/developers/tutorials/how-to-write-and-deploy-an-nft/index.md index bee38e63e41..8e52454172d 100644 --- a/public/content/translations/de/developers/tutorials/how-to-write-and-deploy-an-nft/index.md +++ b/public/content/translations/de/developers/tutorials/how-to-write-and-deploy-an-nft/index.md @@ -1,86 +1,88 @@ --- -title: So erstellen und veröffentlichen Sie einen NFT (Teil 1/3 von unserer NFT-Tutorialreihe) -description: Dieses Tutorial ist Teil 1 einer Serie über NFTs, die Ihnen Schritt für Schritt zeigt, wie Sie einen Non Fungible Token (ERC-721 Token) Smart Contract mit Ethereum und Inter Planetary File System (IPFS) erstellen und veröffentlichen. +title: "So erstellen und veröffentlichen Sie einen NFT (Teil 1/3 der NFT-Tutorial-Reihe)" +description: "Dieses Tutorial ist Teil 1 einer Serie über NFTs, die Ihnen Schritt für Schritt zeigt, wie Sie einen Non Fungible Token (ERC-721 Token) Smart Contract mit Ethereum und Inter Planetary File System (IPFS) erstellen und veröffentlichen." author: "Sumi Mudgil" -tags: - - "NFTs" - - "ERC-721" - - "Alchemy" - - "Solidity" - - "Smart Contracts" +tags: ["ERC-721", "Alchemy", "Solidity", "smart contracts"] skill: beginner lang: de published: 2021-04-22 --- -Mit NFTs ist die Blockchain ins Auge der Öffentlichkeit gerückt. Das ist nun eine ausgezeichnete Gelegenheit, sich selbst ein Bild über diesen Hype zu machen. Veröffentlichen Sie dafür Ihren eigenen NFT (ERC-721 Token) auf der Ethereum-Blockchain. +Da NFTs die Blockchain ins Rampenlicht der Öffentlichkeit rücken, ist dies eine hervorragende Gelegenheit, den Hype selbst zu verstehen, indem Sie Ihren eigenen NFT-Vertrag (ERC-721-Token) auf der Ethereum-Blockchain veröffentlichen! Alchemy ist sehr stolz darauf, die größten Namen im NFT-Bereich zu unterstützen, darunter Makersplace (kürzlich wurde ein Rekordverkauf digitaler Kunstwerke bei Christie's für 69 Millionen USD verzeichnet), Dapper Labs (Entwickler von NBA Top Shot & Crypto Kitties), OpenSea (der weltweit größte NFT-Marktplatz), Zora, Super Rare, NFTfi, Foundation, Enjin, Origin Protocol, Immutable und viele mehr. -In diesem Tutorial erfahren Sie, wie Sie im Ropsten-Testnet mithilfe von [MetaMask](https://metamask.io/), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/), [Pinata](https://pinata.cloud/) und [Alchemy](https://alchemy.com/signup/eth) einen ERC-721-Smart Contract erstellen und bereitstellen (keine Sorge, wenn Sie jetzt noch nicht wissen, was das alles bedeutet, wir werden Ihnen das erklären). +In diesem Tutorial führen wir Sie durch die Erstellung und Bereitstellung eines ERC-721-Smart-Contracts im Sepolia-Testnet unter Verwendung von [MetaMask](https://metamask.io/), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/), [Pinata](https://pinata.cloud/) und [Alchemy](https://alchemy.com/signup/eth) (keine Sorge, wenn Sie noch nicht verstehen, was das alles bedeutet – wir werden es erklären!). In Teil 2 dieses Tutorials erläutern wir, wie Sie mit diesem Smart Contract einen NFT prägen können, in Teil 3 wird behandelt, wie Sie Ihren NFT auf MetaMask anzeigen können. -Wenn Sie zu irgendeinem Zeitpunkt Fragen haben, melden Sie sich gerne im [Alchemy Discord](https://discord.gg/gWuC7zB) oder rufen Sie die [NFT-API-Dokumentation von Alchemy](https://docs.alchemy.com/alchemy/enhanced-apis/nft-api). +Wenn Sie an irgendeiner Stelle Fragen haben, können Sie sich natürlich jederzeit im [Alchemy Discord](https://discord.gg/gWuC7zB) melden oder die [NFT-API-Dokumentation von Alchemy](https://docs.alchemy.com/alchemy/enhanced-apis/nft-api) besuchen! -## Schritt 1: Verbindung mit dem Ethereum-Netzwerk {#connect-to-ethereum} +## Schritt 1: Mit dem Ethereum-Netzwerk verbinden {#connect-to-ethereum} -Es gibt eine Reihe von Möglichkeiten, Anfragen an die Ethereum Blockchain zu stellen, der Einfachheit halber verwenden wir ein kostenloses Konto bei [Alchemy](https://alchemy.com/signup/eth), einer Blockchain-Entwicklerplattform und API, die es uns ermöglicht, mit der Ethereum-Chain zu kommunizieren, ohne dass wir unsere eigenen Nodes betreiben müssen. +Es gibt eine Reihe von Möglichkeiten, Anfragen an die Ethereum-Blockchain zu stellen, aber der Einfachheit halber verwenden wir ein kostenloses Konto bei [Alchemy](https://alchemy.com/signup/eth), einer Blockchain-Entwicklerplattform und API, die es uns ermöglicht, mit der Ethereum-Chain zu kommunizieren, ohne unsere eigenen Nodes betreiben zu müssen. -In diesem Tutorial werden wir auch die Alchemy-Entwicklertools für die Überwachung und Analyse nutzen, um zu verstehen, was sich hinter unserer Smart-Contract-Bereitstellung verbirgt. Wenn Sie noch kein Alchemy-Konto haben, können Sie sich [hier](https://alchemy.com/signup/eth) kostenlos registrieren. +In diesem Tutorial werden wir auch die Alchemy-Entwicklertools für die Überwachung und Analyse nutzen, um zu verstehen, was sich hinter unserer Smart-Contract-Bereitstellung verbirgt. Wenn Sie noch kein Alchemy-Konto haben, können Sie sich [hier](https://alchemy.com/signup/eth) kostenlos anmelden. -## Schritt 2: App (und den API-Schlüssel) erstellen {#make-api-key} +## Schritt 2: Ihre App (und Ihren API-Schlüssel) erstellen {#make-api-key} -Sobald Sie ein Alchemy-Konto erstellt haben, können Sie einen API-Schlüssel generieren, indem Sie eine App erstellen. Dadurch können wir Anfragen an das Ropsten-Testnet stellen. In [diesem Leitfaden](https://docs.alchemyapi.io/guides/choosing-a-network) erfahren Sie mehr über Testnetzwerke. +Sobald Sie ein Alchemy-Konto erstellt haben, können Sie einen API-Schlüssel generieren. Erstellen Sie dafür eine App. Dies ermöglicht es uns, Anfragen an das Sepolia-Testnet zu senden. Sehen Sie sich [diesen Leitfaden](https://docs.alchemyapi.io/guides/choosing-a-network) an, wenn Sie mehr über Testnetze erfahren möchten. 1. Klicken Sie in Ihrem Alchemy-Dashboard in der Navigationsleiste unter "Apps" auf "Create App" (App erstellen), um auf die Seite "Create App" (App erstellen) zu gelangen. -![App erstellen](./create-your-app.png) +![Erstellen Sie Ihre App](./create-your-app.png) -2. Geben Sie Ihrer App einen Namen (wir haben uns für "My First NFT!" entschieden), eine kurze Beschreibung, wählen Sie "Staging" für die Umgebung (für die Buchhaltung Ihrer App) und "Ropsten" als Netzwerk. +2. Benennen Sie Ihre App (wir haben „Mein erster NFT!“ gewählt), geben Sie eine kurze Beschreibung an, wählen Sie „Ethereum“ als Chain und „Sepolia“ als Ihr Netzwerk. Seit The Merge sind die anderen Testnetze veraltet. -![App konfigrurieren und veröffentlichen](./configure-and-publish-your-app.png) +![Ihre App konfigurieren und veröffentlichen](./alchemy-explorer-sepolia.png) 3. Klicken Sie auf “Create app” (App erstellen) und schon sind Sie fertig. Die App sollte in der untenstehenden Tabelle erscheinen. ## Schritt 3: Ethereum-Konto (Adresse) erstellen {#create-eth-address} -Zum Versenden und Empfangen von Transaktionen benötigen Sie ein Ethereum-Konto. In diesem Tutorial verwenden wir MetaMask, eine virtuelle Wallet im Browser, mit der Sie Ihre Ethereum-Kontoadresse verwalten können. Wenn Sie mehr über Transaktionen auf Ethereum erfahren möchten, besuchen Sie [diese Seite](/developers/docs/transactions/) von der Ethereum Foundation. +Zum Versenden und Empfangen von Transaktionen benötigen Sie ein Ethereum-Konto. In diesem Tutorial verwenden wir MetaMask, eine virtuelle Wallet im Browser, mit der Sie Ihre Ethereum-Kontoadresse verwalten können. Wenn Sie mehr darüber erfahren möchten, wie Transaktionen auf Ethereum funktionieren, sehen Sie sich [diese Seite](/developers/docs/transactions/) der Ethereum Foundation an. -Sie können [hier](https://metamask.io/download) MetaMask kostenlos herunterladen und ein Konto erstellen. Wie Sie ein neues Konto erstellen oder wenn Sie bereits ein Konto haben, stellen Sie bitte sicher, dass Sie zum Ropsten-Testnet oben rechts wechseln (um sicherzustellen, dass Sie nicht mit echtem Geld handeln). +Sie können MetaMask [hier](https://metamask.io/download) kostenlos herunterladen und ein Konto erstellen. Wenn Sie ein Konto erstellen oder bereits eines haben, stellen Sie sicher, dass Sie oben rechts zum „Sepolia Test Network“ wechseln (damit wir nicht mit echtem Geld arbeiten). -![Ropsten als Netzwerk festlegen](./metamask-goerli.png) +![Sepolia als Ihr Netzwerk festlegen](./metamask-goerli.png) ## Schritt 4: Ether von einem Faucet hinzufügen {#step-4-add-ether-from-a-faucet} -Um unseren Smart Contract in das Testnetzwerk integrieren zu können, benötigen wir ein paar Fake-ETH. Um ETH zu erhalten, können Sie zu [FaucETH](https://fauceth.komputing.org) navigieren und Ihre Ropsten-Kontoadresse eingeben. Klicken Sie dort auf "Request funds" (Geld anfordern), wählen Sie im Dropdown-Menü "Ethereum Testnet Ropsten" (Ethereum-Testnet Ropsten) und klicken Sie dann nochmals auf die Schaltfläche "Request funds" (Geld anfordern). Sie sollten kurz darauf ETH in Ihrem MetaMask-Konto sehen. +Um unseren Smart Contract in das Testnetzwerk integrieren zu können, benötigen wir ein paar Fake-ETH. Um ETH zu erhalten, können Sie zum von Alchemy gehosteten [Sepolia Faucet](https://sepoliafaucet.com/) gehen, sich anmelden, Ihre Kontoadresse eingeben und auf „Send Me ETH“ klicken. Sie sollten kurz darauf ETH in Ihrem MetaMask-Konto sehen. ## Schritt 5: Kontostand überprüfen {#check-balance} -Um zu überprüfen, ob Sie das Guthaben erhalten haben, stellen wir eine [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)-Anfrage über das [Composer-Tool von Alchemy](https://composer.alchemyapi.io?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D). Das gibt den ETH-Betrag in unserem Wallet wieder. Nachdem Sie die Adresse Ihres MetaMask-Kontos eingegeben und auf “Send Request” (Anforderung senden) geklickt haben, sollten Sie eine Antwort ähnlich der Folgenden erhalten: +Um zu überprüfen, ob unser Guthaben vorhanden ist, stellen wir eine [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)-Anfrage mit dem [Composer-Tool von Alchemy](https://composer.alchemyapi.io?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D). Das gibt den ETH-Betrag in unserem Wallet wieder. Nachdem Sie die Adresse Ihres MetaMask-Kontos eingegeben und auf “Send Request” (Anforderung senden) geklickt haben, sollten Sie eine Antwort ähnlich der Folgenden erhalten: - `{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}` + ``` + `{\"jsonrpc\": \"2.0\", \"id\": 0, \"result\": \"0xde0b6b3a7640000\"}` + ``` -**HINWEIS: **Dieses Ergebnis ist in Wei, nicht in ETH. Wei ist die kleinste Einheit von Ether. Die Umrechnung von Wei auf ETH ist: 1 ETH = 1018 Wei. Wenn wir also 0xde0b6b3a7640000 in eine Dezimalzahl konvertieren, erhalten wir 1\*1018 Wei und das entspricht 1 ETH. +> **Hinweis:** Dieses Ergebnis ist in Wei, nicht in ETH. Wei ist die kleinste Einheit von Ether. Die Umrechnung von Wei auf ETH ist: 1 ETH = 1018 Wei. Wenn wir also 0xde0b6b3a7640000 in eine Dezimalzahl konvertieren, erhalten wir 1\*1018 Wei und das entspricht 1 ETH. Puh! Unser Falschgeld ist da. -## Schritt 6: Projekt initialisieren {#initialize-project} +## Schritt 6: Unser Projekt initialisieren {#initialize-project} Zunächst müssen wir einen Ordner für unser Projekt erstellen. Navigieren Sie zur Befehlszeile und geben Sie Folgendes ein: + ``` mkdir my-nft cd my-nft + ``` -Jetzt, da wir uns in unserem Projektordner befinden, verwenden wir "npm init" um das Projekt zu starten. Wenn Sie npm noch nicht installiert haben, folgen Sie [dieser Anleitung](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm) (wir brauchen auch [Node.js](https://nodejs.org/en/download/), also laden Sie das auch herunter). +Jetzt, da wir uns in unserem Projektordner befinden, verwenden wir "npm init" um das Projekt zu starten. Wenn Sie npm noch nicht installiert haben, befolgen Sie [diese Anweisungen](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm) (wir benötigen auch [Node.js](https://nodejs.org/en/download/), also laden Sie das auch herunter!). + ``` npm init + ``` Es spielt keine Rolle, wie Sie die Fragen zur Installation beantworten, aber wir haben es folgendermaßen gemacht: + ```json package name: (my-nft) version: (1.0.0) - description: My first NFT! + description: Mein erster NFT! entry point: (index.js) test command: git repository: @@ -92,7 +94,7 @@ Es spielt keine Rolle, wie Sie die Fragen zur Installation beantworten, aber wir { "name": "my-nft", "version": "1.0.0", - "description": "My first NFT!", + "description": "Mein erster NFT!", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" @@ -101,26 +103,32 @@ Es spielt keine Rolle, wie Sie die Fragen zur Installation beantworten, aber wir "license": "ISC" } ``` + Genehmigen Sie die Datei "package.json" und schon kann es losgehen. ## Schritt 7: [Hardhat](https://hardhat.org/getting-started/#overview) installieren {#install-hardhat} -Hardhat ist eine Entwicklungsumgebung zum Kompilieren, Bereitstellen, Testen und Debuggen Ihrer Ethereum-Software. Es hilft Entwicklern bei der lokalen Erstellung von Smart Contracts und dApps, bevor diese auf der Live-Chain bereitgestellt werden. +Hardhat ist eine Entwicklungsumgebung zum Kompilieren, Bereitstellen, Testen und Debuggen Ihrer Ethereum-Software. Es hilft Entwicklern Smart Contracts und dApps lokal zu erstellen, bevor diese auf der Live-Chain bereitgestellt werden. Innerhalb unseres my-nft-Projektlaufs: + ``` npm install --save-dev hardhat + ``` -Auf dieser Seite finden Sie weitere Informationen zur [Installationsanleitung](https://hardhat.org/getting-started/#overview). +Weitere Details zu den [Installationsanweisungen](https://hardhat.org/getting-started/#overview) finden Sie auf dieser Seite. ## Schritt 8: Hardhat-Projekt erstellen {#create-hardhat-project} Führen Sie folgeden Befehl in unserem Projektordner aus: + ``` npx hardhat + ``` Sie sollten dann eine Willkommensnachricht sehen und die Möglichkeit haben, auszuwählen, wie Sie fortfahren möchten. Wählen Sie "create an empty hardhat.config.js" (Leere hardhat.config.js erstellen) aus: + ``` 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 @@ -131,9 +139,10 @@ Sie sollten dann eine Willkommensnachricht sehen und die Möglichkeit haben, aus 888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 👷 Welcome to Hardhat v2.0.11 👷‍ ? Was möchten Sie tun? … - Create a sample project - ❯ Create an empty hardhat.config.js - Quit + Ein Beispielprojekt erstellen + ❯ Eine leere hardhat.config.js erstellen + Beenden + ``` Darüber wird eine hardhat.config.js-Datei für uns generiert, in der alle Einstellungen für unser Projekt angeben werden (in Schritt 13). @@ -141,25 +150,27 @@ Darüber wird eine hardhat.config.js-Datei für uns generiert, in der alle Einst Um unser Projekt zu organisieren, erstellen wir zwei neue Ordner. Navigieren Sie in der Befehlszeile zum Stammverzeichnis Ihres Projekts und geben Sie Folgendes ein: + ``` mkdir contracts mkdir scripts + ``` - contracts/ ist der Ort, an dem wir unseren NFT-Smart-Contract-Code aufbewahren werden. - scripts/ ist der Ort, an dem wir Skripte veröffentlichen und mit unseren Smart Contract interagieren. -## Schritt 10: Vertrag schreiben {#write-contract} +## Schritt 10: Unseren Vertrag schreiben {#write-contract} -Nachdem unsere Umgebung nun eingerichtet ist, kommen wir zu spannenderen Dingen: _Wir schreiben unseren Smart-Contract-Code._ +Jetzt, da unsere Umgebung eingerichtet ist, geht es an die aufregenderen Dinge: _das Schreiben unseres Smart-Contract-Codes!_ -Öffnen sie das my-nft-Projekt in ihrem favorisierten Ordner (wir bevorzugen [VSCode](https://code.visualstudio.com/)). Smart Contracts werden in einer Sprache namens Solidity geschrieben. Damit werden wir auch unseren Smart Contract MyNFT.sol schreiben. +Öffnen Sie das my-nft-Projekt in Ihrem bevorzugten Editor (wir mögen [VSCode](https://code.visualstudio.com/)). Smart Contracts werden in einer Sprache namens Solidity geschrieben. Damit werden wir auch unseren Smart Contract MyNFT.sol schreiben. -1. Navigieren Sie zum Ordner `Contracts` (Verträge) und erstellen Sie eine neue Datei namens MyNFT.sol. +1. Navigieren Sie zum `contracts`-Ordner und erstellen Sie eine neue Datei namens MyNFT.sol. -2. Im Folgenden finden Sie den NFT-Smart-Contract-Code, der auf der ERC-721-Implementierung der [OpenZeppelin](https://docs.openzeppelin.com/contracts/3.x/erc721)-Bibliothek basiert. Kopieren Sie folgenden Inhalt und fügen Sie ihn in die Datei MyNFT.sol ein. +2. Nachfolgend finden Sie unseren NFT-Smart-Contract-Code, der auf der ERC-721-Implementierung der [OpenZeppelin](https://docs.openzeppelin.com/contracts/3.x/erc721)-Bibliothek basiert. Kopieren Sie folgenden Inhalt und fügen Sie ihn in die Datei MyNFT.sol ein. ```solidity - //Contract based on [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721) + //Vertrag basiert auf [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721) // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; @@ -189,74 +200,74 @@ Nachdem unsere Umgebung nun eingerichtet ist, kommen wir zu spannenderen Dingen: } ``` -3. Weil wir Klassen der OpenZeppelin-Vertragsbibliothek erben, geben Sie `npm install @openzeppelin/contracts` in die Befehlszeile ein, um die Bibliothek in Ihrem Ordner zu installieren. +3. Da wir Klassen aus der OpenZeppelin-contracts-Bibliothek erben, führen Sie in Ihrer Kommandozeile `npm install @openzeppelin/contracts^4.0.0` aus, um die Bibliothek in unserem Ordner zu installieren. -Doch was _macht_ dieser Code denn genau? Sehen wir uns das gemeinsam Zeil für Zeile an. +Was also _tut_ dieser Code genau? Sehen wir uns das gemeinsam Zeil für Zeile an. -Am Anfang unseres Smart Contracts importieren wir drei [OpenZeppelin](https://openzeppelin.com/)-Smart-Contract-Klassen: +Ganz oben in unserem Smart-Contract importieren wir drei [OpenZeppelin](https://openzeppelin.com/)-Smart-Contract-Klassen: -- @openzeppelin/contracts/token/ERC721/ERC721.sol enthält eine Implementierung des ERC-721-Standards, den unser NFT-Smart-Contract erben wird. (Damit der NFT auch Gültikeit erlangt, muss Ihr Smart Contract alle Methoden des ERC-721-Standards implementieren.) In der Schnittstellendefinition [hier](https://eips.ethereum.org/EIPS/eip-721) erfahren Sie mehr über die vererbten ERC-721-Funktionen. +- @openzeppelin/contracts/token/ERC721/ERC721.sol enthält eine Implementierung des ERC-721-Standards, den unser NFT-Smart-Contract erben wird. (Damit der NFT auch Gültikeit erlangt, muss Ihr Smart Contract alle Methoden des ERC-721-Standards implementieren.) Um mehr über die geerbten ERC-721-Funktionen zu erfahren, sehen Sie sich die Schnittstellendefinition [hier](https://eips.ethereum.org/EIPS/eip-721) an. - @openzeppelin/contracts/utils/Counters.sol stellt Zähler zur Verfügung, die jeweils nur um eins erhöht oder verringert werden können. Unser Smart Contract benutzt einen Zähler, um die Gesamtanzahl der geprägten NFTs zu überprüfen und eine eindeutige ID für unseren neuen NFT festzulegen. (Jedem NFT, der durch die Benutzung eines Smart Contracts geprägt wird, muss eine eindeutige ID zugewiesen werden. In diesem Beispiel wird unsere eindeutige ID einfach deterministisch über die Gesamtanzahl der existierenden NFTs bestimmt. Zum Beispiel hat der erste NFT, der mit unserem Smart Contract geprägt wird, die ID "1", unser zweiter NFT hat die ID "2" usw.) -- @openzeppelin/contracts/access/Ownable.sol richtet eine [Zugriffskontrolle](https://docs.openzeppelin.com/contracts/3.x/access-control) in unserem Smart Contract ein, so dass nur der Besitzer des Smart Contracts (also Sie) NFTs prägen kann. (Hinweis, die Einbeziehung der Zugriffskontrolle ist optional. Wenn Sie möchten, dass mit Ihrem Smart Contract jeder NFTs prägen kann, entfernen Sie das Wort "Ownable" in Zeile 10 und "onlyOwner" in Zeile 17.) +- @openzeppelin/contracts/access/Ownable.sol richtet eine [Zugriffskontrolle](https://docs.openzeppelin.com/contracts/3.x/access-control) für unseren Smart-Contract ein, sodass nur der Eigentümer des Smart-Contracts (also Sie) NFTs prägen kann. (Hinweis, die Einbeziehung der Zugriffskontrolle ist optional. Wenn Sie möchten, dass mit Ihrem Smart Contract jeder NFTs prägen kann, entfernen Sie das Wort "Ownable" in Zeile 10 und "onlyOwner" in Zeile 17.) -Nach unseren Importanweisungen haben wir unseren benutzerdefinierten Smart Contract, der überraschend kurz ist , denn er enthält nur einen Zähler, einen Konstruktor und eine einzige Funktion. Das ist unseren vererbten OpenZeppelin-Contracts zu verdanken, die einen Großteil der Methoden implementieren, die wir zur Erstellung eines NFT benötigen, wie `ownerOf`, was den Besitzer des NFT zurückgibt, und `transferFrom`, was das Eigentum an einem NFT von einem Konto zu einem anderen überträgt. +Nach unseren Importanweisungen haben wir unseren benutzerdefinierten Smart Contract, der überraschend kurz ist , denn er enthält nur einen Zähler, einen Konstruktor und eine einzige Funktion. Dies ist den von uns geerbten OpenZeppelin-Contracts zu verdanken, die die meisten der Methoden implementieren, die wir zum Erstellen eines NFT benötigen, wie z. B. `ownerOf`, das den Eigentümer des NFT zurückgibt, und `transferFrom`, das das Eigentum am NFT von einem Konto auf ein anderes überträgt. Sie werden feststellen, dass wir in unserem ERC-721-Konstruktor zwei Zeichenfolgen übergeben: "MyNFT" und "NFT". Die erste Variable ist der Name des Smart Contracts und die zweite ist sein Symbol. Sie können jede der beiden Variablen benennen wie sie möchten. -Schließlich haben wir unsere Funktion `mintNFT(address recipient, string memory tokenURI)`, mit der wir einen NFT prägen können. Sie werden bemerken, dass diese Funktion zwei Variablen benötigt: +Schließlich haben wir unsere Funktion `mintNFT(address recipient, string memory tokenURI)`, die es uns ermöglicht, einen NFT zu prägen! Sie werden bemerken, dass diese Funktion zwei Variablen benötigt: -- `address recipient` gibt die Adresse an, die den frisch geprägten NFT erhalten soll. +- `address recipient` gibt die Adresse an, die Ihren frisch geprägten NFT erhalten wird -- `string memory tokenURI` ist eine Zeichenfolge, die auf ein JSON-Dokument zeigt, das die Metadaten des NFT beschreibt. Die Metadaten eines NFT, sind das Element, das den NFT wirklich zum Leben erwecken. Sie schaffen die Grundlage, dass ein NFT konfigurierbare Eigenschaften wie einen Namen, eine Beschreibung ein Bild und andere Attribute haben kann. In Teil 2 dieses Tutorials wird die Konfiguration dieser Metadaten beschrieben. +- `string memory tokenURI` ist ein String, der in ein JSON-Dokument aufgelöst werden sollte, das die Metadaten des NFTs beschreibt. Die Metadaten eines NFT, sind das Element, das den NFT wirklich zum Leben erwecken. Sie schaffen die Grundlage, dass ein NFT konfigurierbare Eigenschaften wie einen Namen, eine Beschreibung ein Bild und andere Attribute haben kann. In Teil 2 dieses Tutorials wird die Konfiguration dieser Metadaten beschrieben. -`mintNFT` ruft bestimmte Methoden der vererbten ERC-721-Bibliothek auf und gibt eine Zahl zurück, die für die ID des frisch geprägten NFT steht. +`mintNFT` ruft einige Methoden aus der geerbten ERC-721-Bibliothek auf und gibt schließlich eine Zahl zurück, die die ID des frisch geprägten NFT darstellt. -## Schritt 11: MetaMask und Alchemy mit ihrem Projekt mit Ihrem Projekt verbinden {#connect-metamask-and-alchemy} +## Schritt 11: MetaMask & Alchemy mit Ihrem Projekt verbinden {#connect-metamask-and-alchemy} Nachdem wir nun eine MetaMask-Wallet und ein Alchemy-Konto erstellt uns unseren Smart Contract geschrieben haben, ist es an der Zeit, die drei Elemente miteinander zu verbinden. Jede Transaktion, die von Ihrer virtuellen Wallet gesendet wird, benötigt eine Signatur mit ihrem eindeutigen privaten Schlüssel. Um unser Programm mit dieser Berechtigung auszustatten, können wir unseren privaten Schlüssel (und Alchemy-API-Schlüssel) in einer Umgebungsdatei sicher abspeichern. -Wenn Sie mehr über das Senden von Transaktionen erfahren möchten, schauen Sie sich [dieses Tutorial](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) über das senden von Transaktionen mit Web3 an. +Um mehr über das Senden von Transaktionen zu erfahren, sehen Sie sich [dieses Tutorial](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) über das Senden von Transaktionen mit web3 an. Installieren Sie zuerst das dotenv-Paket in Ihrem Projektverzeichnis: + ``` npm install dotenv --save + ``` -Danach erstellen Sie eine `.env`-Datei im Hauptverzeichnis des Projekts und fügen den privaten Schlüssel von MetaMask und die HTTP-URL der Alchemy-API hinzu. +Erstellen Sie dann eine `.env`-Datei im Stammverzeichnis unseres Projekts und fügen Sie Ihren privaten MetaMask-Schlüssel und Ihre HTTP-Alchemy-API-URL hinzu. -- Befolgen Sie [diese Anweisungen](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key), um Ihren privaten Schlüssel aus MetaMask zu importieren. +- Befolgen Sie [diese Anweisungen](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key), um Ihren privaten Schlüssel aus MetaMask zu exportieren - Unten wird erläutert, wie Sie die HTTP-URL der Alchemy-API erhalten und in die Zwischenablage kopieren. -![Alchemy-API-URL kopieren](./copy-alchemy-api-url.gif) +![Kopieren Sie Ihre Alchemy-API-URL](./copy-alchemy-api-url.gif) -Ihre `.env`-Datei sollte nun wie folgt aussehen: +Ihre `.env`-Datei sollte jetzt so aussehen: - API_URL="https://eth-ropsten.alchemyapi.io/v2/your-api-key" + ``` + API_URL="https://eth-sepolia.g.alchemy.com/v2/your-api-key" PRIVATE_KEY="your-metamask-private-key" + ``` -Um nun die Verbindung mit unserem Code zu erstellen, werden wir diese Variablen in der Datei hardhat.config.js in Schritt 13 referenzieren. +Um diese tatsächlich mit unserem Code zu verbinden, werden wir in Schritt 13 in unserer Datei hardhat.config.js auf diese Variablen verweisen. - - - -Führen Sie keinen Commit für .env aus. Stellen Sie sicher, dass Sie Ihre .env-Datei niemals an andere weitergeben, denn damit würden Sie Ihre geheimen Daten weitergeben. Wenn Sie die Versionskontrolle verwenden, fügen Sie Ihre Env-Datei zu einer Datei gitignore hinzu. - - - + ## Schritt 12: Ethers.js installieren {#install-ethers} -Ethers.js ist eine Bibliothek, die es einfacher macht, mit Ethereum zu interagieren und Anfragen zu stellen. Dafür schließt sie [Standard-JSON-RPC-Methoden](/developers/docs/apis/json-rpc/) in benutzerfreundlichere Methoden ein. +Ethers.js ist eine Bibliothek, die die Interaktion und das Stellen von Anfragen an Ethereum erleichtert, indem sie [standardmäßige JSON-RPC-Methoden](/developers/docs/apis/json-rpc/) in benutzerfreundlichere Methoden verpackt. -Hardhat macht es sehr einfach [Plug-ins](https://hardhat.org/plugins/) für zusätzliche Tools und erweiterte Funktionen zu integrieren. Wir werden das [Ethers-Plug-in](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) für die Bereitstellung von Verträgen nutzen ([Ethers.js](https://github.com/ethers-io/ethers.js/) bietet einige sehr saubere Methoden zur Bereitstellung von Verträgen). +Hardhat macht es sehr einfach, [Plugins](https://hardhat.org/plugins/) für zusätzliche Tools und erweiterte Funktionalität zu integrieren. Wir werden das [Ethers-Plugin](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) für die Vertragsbereitstellung nutzen ([Ethers.js](https://github.com/ethers-io/ethers.js/) verfügt über einige sehr saubere Methoden zur Vertragsbereitstellung). Geben Sie Folgendes in Ihrem Projektverzeichnis ein: + ``` npm install --save-dev @nomiclabs/hardhat-ethers ethers@^5.0.0 + ``` Im nächsten Schritt benötigen wir auch Ether in unserer hardhat.config.js. @@ -275,10 +286,10 @@ Aktualisieren Sie Ihre hardhat.config.js so, dass sie wie folgt aussieht: const { API_URL, PRIVATE_KEY } = process.env; module.exports = { solidity: "0.8.1", - defaultNetwork: "ropsten", + defaultNetwork: "sepolia", networks: { hardhat: {}, - ropsten: { + sepolia: { url: API_URL, accounts: [`0x${PRIVATE_KEY}`] } @@ -286,27 +297,29 @@ Aktualisieren Sie Ihre hardhat.config.js so, dass sie wie folgt aussieht: } ``` -## Schritt 14: Vertrag kompilieren {#compile-contract} +## Schritt 14: Unseren Vertrag kompilieren {#compile-contract} Um sicherzugehen, dass so weit alles funktioniert, sollten wir unseren Vertrag erstellen. Die Aufgabe compile ist eine der integrierten Hardhat-Aufgaben. Führen Sie folgenden Befehl in der Befehlszeile aus: + ``` npx hardhat compile + ``` -Möglicherweise erhalten Sie eine Warnung, dass die SPDX-Lizenzkennung nicht in Quelldatei angegeben sei. Doch darüber brauchen Sie sich keine Sorgen zu machen. Alles andere sieht hoffentlich gut aus. Falls nicht, können Sie jederzeit eine Nachricht im [Alchemy Discord](https://discord.gg/u72VCg3) hinterlassen. +Möglicherweise erhalten Sie eine Warnung, dass die SPDX-Lizenzkennung nicht in Quelldatei angegeben sei. Doch darüber brauchen Sie sich keine Sorgen zu machen. Alles andere sieht hoffentlich gut aus. Wenn nicht, können Sie jederzeit eine Nachricht im [Alchemy-Discord](https://discord.gg/u72VCg3) schreiben. -## Schritt 15: Bereitstellungsskript schreiben {#write-deploy} +## Schritt 15: Unser Bereitstellungsskript schreiben {#write-deploy} Nun, da unser Vertrag geschrieben und unsere Konfigurationsdatei einsatzbereit ist, ist es an der Zeit, das Skript zur Bereitstellung des Vertrags zu schreiben. -Navigieren Sie zum Ordner `scripts/` und erstellen Sie eine neue Datei namens `deploy.js`. Fügen Sie folgende Inhalte hinzu: +Navigieren Sie zum `scripts/`-Ordner und erstellen Sie eine neue Datei namens `deploy.js`, indem Sie die folgenden Inhalte hinzufügen: ```js async function main() { const MyNFT = await ethers.getContractFactory("MyNFT") - // Start deployment, returning a promise that resolves to a contract object + // Bereitstellung starten, die ein Promise zurückgibt, das zu einem Vertragsobjekt aufgelöst wird const myNFT = await MyNFT.deploy() await myNFT.deployed() console.log("Contract deployed to address:", myNFT.address) @@ -320,40 +333,48 @@ main() }) ``` -Hardhat erklärt in seinem [Vertragstutorial](https://hardhat.org/tutorial/testing-contracts.html#writing-tests) sehr gut, was die einzelnen Codezeilen bewirken. Wir haben diese Erklärungen hier übernommen. +Hardhat erklärt in seinem [Contracts-Tutorial](https://hardhat.org/tutorial/testing-contracts.html#writing-tests) hervorragend, was jede dieser Codezeilen bewirkt; wir haben die Erklärungen hier übernommen. + ``` const MyNFT = await ethers.getContractFactory("MyNFT"); + ``` Eine ContractFactory in ethers.js ist eine Abstraktion, die dazu dient, neue Smart Contracts einzusetzen. So ist MyNFT eine Factory für Instanzen von unseren NFT-Vertrag. Wenn Sie das hardhat-ethers-Plug-in verwenden, werden die Instanzen ContractFactory und Contract standardmäßig mit dem ersten Unterzeichner verbunden. + ``` const myNFT = await MyNFT.deploy(); + ``` Mit dem Aufruf von deploy() über eine ContractFactory wird die Bereitstellung gestartet. Zurückgegeben wird eine Referenz, die auf einen Vertrag zeigt. Das ist das Objekt, das eine Methode für jede unserer Smart-Contract-Funktionen enthält. -## Schritt 16: Vertragsbereitstellung {#deploy-contract} +## Schritt 16: Unseren Vertrag bereitstellen {#deploy-contract} Nun sind wir endlich bereit, unseren Smart Contract bereitzustellen. Navigieren Sie zurück zu Ihrem Stammverzeichnis und führen Sie Folgendes über die Befehlszeile aus: - npx hardhat --network ropsten run scripts/deploy.js + ``` + npx hardhat --network sepolia run scripts/deploy.js + ``` Sie sollten dann etwas sehen wie: - Contract deployed to address: 0x81c587EB0fE773404c42c1d2666b5f557C470eED + ``` + Vertrag bereitgestellt für Adresse: 0x4C5266cCc4b3F426965d2f51b6D910325a0E7650 + ``` -Wenn wir den [Ropsten-Etherscan](https://ropsten.etherscan.io/) aufrufen und nach unserer Vertragsadresse suchen, sollten wir sehen, dass sie erfolgreich bereitgestellt wurde. Wenn sie nicht sofort angezeigt wird, haben Sie etwas Geduld, denn dieser Vorgang kann einige Zeit in Anspruch nehmen. Die Transaktion wird ungefähr so aussehen: +Wenn wir zum [Sepolia Etherscan](https://sepolia.etherscan.io/) gehen und nach unserer Vertragsadresse suchen, sollten wir sehen können, dass sie erfolgreich bereitgestellt wurde. Wenn sie nicht sofort angezeigt wird, haben Sie etwas Geduld, denn dieser Vorgang kann einige Zeit in Anspruch nehmen. Die Transaktion wird ungefähr so aussehen: -![Transaktionsadresse auf Etherscan einsehen](./etherscan-sepolia-tx-details.png) +![Ihre Transaktionsadresse auf Etherscan ansehen](./etherscan-sepoila-contract-creation.png) -Die Absenderadresse sollte mit der Adresse ihres MetaMask-Kontos übereinstimmen und in der Empfängeradresse sollte "Contract Creation" (Vertragserstellung) stehen. Wenn wir auf die Transaktion klicken ,sehen wir unsere Vertragsadresse im Empfängerfeld: +Die `From`-Adresse sollte mit Ihrer MetaMask-Kontoadresse übereinstimmen und die `To`-Adresse lautet „Contract Creation“. Wenn wir auf die Transaktion klicken ,sehen wir unsere Vertragsadresse im Empfängerfeld: -![Vertragsadresse auf Etherscan anzeigen](./etherscan-sepoila-contract-creation.png) +![Ihre Vertragsadresse auf Etherscan ansehen](./etherscan-sepolia-tx-details.png) -Großartig! Sie haben soeben Ihren NFT-Smart-Contract auf der Ethereum-Chain bereitgestellt. +Großartig! Sie haben soeben Ihren NFT-Smart-Contract in der Ethereum-Chain (Testnet) bereitgestellt! -Um zu verstehen, was im Verborgenen vor sich geht, navigieren wir zur Explorer-Registerkarte in unserem [Alchemy-Dashboard](https://dashboard.alchemyapi.io/explorer). Wenn Sie mehrere Alchemy-Apps besitzen, filtern Sie nach Apps und wählen Sie "MyNFT" aus. +Um zu verstehen, was unter der Haube vor sich geht, navigieren wir zum Explorer-Tab in unserem [Alchemy-Dashboard](https://dashboard.alchemyapi.io/explorer). Wenn Sie mehrere Alchemy-Apps besitzen, filtern Sie nach Apps und wählen Sie "MyNFT" aus. -![Mit dem Explorer-Dashboard von Alchemy Aufrufe einsehen, die im Verborgenen erfolgen](./alchemy-explorer-goerli.png) +![Anzeigen von Aufrufen, die „unter der Haube“ mit dem Explorer-Dashboard von Alchemy gemacht wurden](./alchemy-explorer-goerli.png) -Hier sehen Sie eine Handvoll JSON-RPC-Aufrufe, die Hardhat/Ethers implementiert hat, als wir die .deploy()-Funktion aufgerufen haben. Zwei wichtige Funktionen, die hier aufzuführen sind, ist die [eth_sendRawTransaction](/developers/docs/apis/json-rpc/#eth_sendrawtransaction), die eine Anforderung zum Schreiben unseres Smart Contracts auf der Ropsten-Chain ist, und [eth_getTranscationByHash](/developers/docs/apis/json-rpc/#eth_gettransactionbyhash), die eine Anforderung ist, um Informationen über unsere Transaktion zu lesen, die vom Hash gegeben werden (ein typisches Muster beim Senden von Transaktionen). Wenn Sie mehr über das Senden von Transaktionen erfahren möchten, schauen Sie sich diese Anleitung an: [Transaktionen mit Web3 senden](/developers/tutorials/sending-transactions-using-web3-and-alchemy/). +Hier sehen Sie eine Handvoll JSON-RPC-Aufrufe, die Hardhat/Ethers implementiert hat, als wir die .deploy()-Funktion aufgerufen haben. Zwei wichtige Aufrufe sind hier [eth_sendRawTransaction](/developers/docs/apis/json-rpc/#eth_sendrawtransaction), das ist die Anfrage, um unseren Smart-Contract tatsächlich in die Sepolia-Chain zu schreiben, und [eth_getTransactionByHash](/developers/docs/apis/json-rpc/#eth_gettransactionbyhash), eine Anfrage zum Lesen von Informationen über unsere Transaktion anhand des Hashes (ein typisches Muster beim Senden von Transaktionen). Um mehr über das Senden von Transaktionen zu erfahren, lesen Sie dieses Tutorial über das [Senden von Transaktionen mit Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/). -Damit sind wir am Ende vom ersten Teil dieses Tutorials. In [Teil 2 werden wir mit unserem Smart Contract interagieren, indem wir einen NFT prägen](/developers/tutorials/how-to-mint-an-nft/). In [Teil 3 werden wir Ihnen zeigen, wie Sie Ihren NFT in Ihrer Ethereum-Wallet sehen können](/developers/tutorials/how-to-view-nft-in-metamask/). +Damit sind wir am Ende vom ersten Teil dieses Tutorials. In [Teil 2 werden wir tatsächlich mit unserem Smart-Contract interagieren, indem wir einen NFT prägen](/developers/tutorials/how-to-mint-an-nft/), und in [Teil 3 zeigen wir Ihnen, wie Sie Ihren NFT in Ihrer Ethereum-Wallet ansehen können](/developers/tutorials/how-to-view-nft-in-metamask/)! diff --git a/public/content/translations/de/developers/tutorials/interact-with-other-contracts-from-solidity/index.md b/public/content/translations/de/developers/tutorials/interact-with-other-contracts-from-solidity/index.md new file mode 100644 index 00000000000..191b0b5739d --- /dev/null +++ b/public/content/translations/de/developers/tutorials/interact-with-other-contracts-from-solidity/index.md @@ -0,0 +1,172 @@ +--- +title: Mit anderen Contracts von Solidity aus interagieren +description: Wie man einen Smart Contract von einem bestehenden Contract aus bereitstellt und mit ihm interagiert +author: "jdourlens" +tags: ["smart contracts", "solidity", "remix", "deploying", "composability"] +skill: advanced +lang: de +published: 2020-04-05 +source: EthereumDev +sourceUrl: https://ethereumdev.io/interact-with-other-contracts-from-solidity/ +address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +--- + +In den vorherigen Tutorials haben wir viel gelernt, [wie man seinen ersten Smart Contract bereitstellt](/developers/tutorials/deploying-your-first-smart-contract/) und ihm einige Funktionen hinzufügt, wie [Zugriffskontrolle mit Modifikatoren](https://ethereumdev.io/organize-your-code-and-control-access-to-your-smart-contract-with-modifiers/) oder [Fehlerbehandlung in Solidity](https://ethereumdev.io/handle-errors-in-solidity-with-require-and-revert/). In diesem Tutorial lernen wir, wie man einen Smart Contract von einem bestehenden Contract aus bereitstellt und mit ihm interagiert. + +Wir erstellen einen Contract, der es jedem ermöglicht, seinen eigenen `Counter`-Smart-Contract zu haben. Dafür erstellen wir eine Factory, die `CounterFactory` heißen wird. Hier ist zunächst der Code unseres ursprünglichen `Counter`-Smart-Contracts: + +```solidity +pragma solidity 0.5.17; + +contract Counter { + + uint256 private _count; + address private _owner; + address private _factory; + + + modifier onlyOwner(address caller) { + require(caller == _owner, "Du bist nicht der Eigentümer des Vertrags"); + _; + } + + modifier onlyFactory() { + require(msg.sender == _factory, "Du musst die Factory verwenden"); + _; + } + + constructor(address owner) public { + _owner = owner; + _factory = msg.sender; + } + + function getCount() public view returns (uint256) { + return _count; + } + + function increment(address caller) public onlyFactory onlyOwner(caller) { + _count++; + } + +} +``` + +Beachte, dass wir den Contract-Code geringfügig geändert haben, um die Adresse der Factory und die Adresse des Contract-Eigentümers zu speichern. Wenn du einen Contract-Code von einem anderen Contract aus aufrufst, verweist `msg.sender` auf die Adresse unserer Contract-Factory. Dies ist **ein sehr wichtiger Punkt, den man verstehen sollte**, da die Verwendung eines Contracts zur Interaktion mit anderen Contracts gängige Praxis ist. Daher solltest du in komplexen Fällen darauf achten, wer der Absender ist. + +Dafür haben wir auch einen `onlyFactory`-Modifikator hinzugefügt, der sicherstellt, dass die zustandsändernde Funktion nur von der Factory aufgerufen werden kann, die den ursprünglichen Aufrufer als Parameter übergibt. + +Innerhalb unserer neuen `CounterFactory`, die alle anderen Counter verwalten wird, fügen wir ein Mapping hinzu, das einem Eigentümer die Adresse seines Counter-Contracts zuordnet: + +```solidity +mapping(address => Counter) _counters; +``` + +In Ethereum sind Mappings das Äquivalent zu Objekten in Javascript; sie ermöglichen es, einen Schlüssel vom Typ A einem Wert vom Typ B zuzuordnen. In diesem Fall ordnen wir die Adresse eines Eigentümers der Instanz seines Counters zu. + +Das Instanziieren eines neuen `Counter` für jemanden sieht wie folgt aus: + +```solidity + function createCounter() public { + require (_counters[msg.sender] == Counter(0)); + _counters[msg.sender] = new Counter(msg.sender); + } +``` + +Zuerst prüfen wir, ob die Person bereits einen `Counter` besitzt. Wenn die Person keinen `Counter` besitzt, instanziieren wir einen neuen, indem wir ihre Adresse an den `Counter`-Konstruktor übergeben und die neu erstellte Instanz dem Mapping zuweisen. + +Um den Zählerstand eines bestimmten `Counter` abzurufen, sieht es wie folgt aus: + +```solidity +function getCount(address account) public view returns (uint256) { + require (_counters[account] != Counter(0)); + return (_counters[account].getCount()); +} + +function getMyCount() public view returns (uint256) { + return (getCount(msg.sender)); +} +``` + +Die erste Funktion prüft, ob der `Counter`-Contract für eine bestimmte Adresse existiert, und ruft dann die `getCount`-Methode von der Instanz auf. Die zweite Funktion, `getMyCount`, ist nur eine Abkürzung, um `msg.sender` direkt an die `getCount`-Funktion zu übergeben. + +Die `increment`-Funktion ist recht ähnlich, übergibt aber den ursprünglichen Transaktionssender an den `Counter`-Contract: + +```solidity +function increment() public { + require (_counters[msg.sender] != Counter(0)); + Counter(_counters[msg.sender]).increment(msg.sender); + } +``` + +Beachte, dass unser `Counter` bei zu häufigen Aufrufen Opfer eines Überlaufs werden könnte. Du solltest die [SafeMath-Bibliothek](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/) so oft wie möglich verwenden, um dich vor diesem möglichen Fall zu schützen. + +Um unseren Contract bereitzustellen, musst du sowohl den Code der `CounterFactory` als auch den des `Counter` angeben. Bei der Bereitstellung in Remix musst du zum Beispiel `CounterFactory` auswählen. + +Hier ist der vollständige Code: + +```solidity +pragma solidity 0.5.17; + +contract Counter { + + uint256 private _count; + address private _owner; + address private _factory; + + + modifier onlyOwner(address caller) { + require(caller == _owner, "Du bist nicht der Eigentümer des Vertrags"); + _; + } + + modifier onlyFactory() { + require(msg.sender == _factory, "Du musst die Factory verwenden"); + _; + } + + constructor(address owner) public { + _owner = owner; + _factory = msg.sender; + } + + function getCount() public view returns (uint256) { + return _count; + } + + function increment(address caller) public onlyFactory onlyOwner(caller) { + _count++; + } + +} + +contract CounterFactory { + + mapping(address => Counter) _counters; + + function createCounter() public { + require (_counters[msg.sender] == Counter(0)); + _counters[msg.sender] = new Counter(msg.sender); + } + + function increment() public { + require (_counters[msg.sender] != Counter(0)); + Counter(_counters[msg.sender]).increment(msg.sender); + } + + function getCount(address account) public view returns (uint256) { + require (_counters[account] != Counter(0)); + return (_counters[account].getCount()); + } + + function getMyCount() public view returns (uint256) { + return (getCount(msg.sender)); + } + +} +``` + +Nach dem Kompilieren wählst du im Bereitstellungsbereich von Remix die Factory aus, die bereitgestellt werden soll: + +![Auswahl der in Remix bereitzustellenden Factory](./counterfactory-deploy.png) + +Danach kannst du mit deiner Contract Factory interagieren und die Wertänderung überprüfen. Wenn du den Smart Contract von einer anderen Adresse aus aufrufen möchtest, musst du die Adresse in der Kontoauswahl von Remix ändern. diff --git a/public/content/translations/de/developers/tutorials/ipfs-decentralized-ui/index.md b/public/content/translations/de/developers/tutorials/ipfs-decentralized-ui/index.md new file mode 100644 index 00000000000..c75b1125099 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/ipfs-decentralized-ui/index.md @@ -0,0 +1,73 @@ +--- +title: "IPFS für dezentralisierte Benutzeroberflächen" +description: "Dieses Tutorial zeigt dem Leser, wie man IPFS nutzt, um die Benutzeroberfläche für eine Dapp zu speichern. Obwohl die Daten und die Geschäftslogik der Anwendung dezentralisiert sind, könnten Benutzer ohne eine zensurresistente Benutzeroberfläche trotzdem den Zugriff darauf verlieren." +author: Ori Pomerantz +tags: [ "ipfs" ] +skill: beginner +lang: de +published: 2024-06-29 +--- + +Sie haben eine unglaubliche neue Dapp geschrieben. Sie haben sogar eine [Benutzeroberfläche](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/) dafür geschrieben. Aber jetzt befürchten Sie, dass jemand versuchen könnte, sie zu zensieren, indem er Ihre Benutzeroberfläche, die sich nur auf einem einzigen Server in der Cloud befindet, lahmlegt. In diesem Tutorial lernen Sie, wie Sie Zensur vermeiden können, indem Sie Ihre Benutzeroberfläche im **[Interplanetary File System (IPFS)](https://ipfs.tech/developers/)** ablegen, sodass jeder Interessierte sie für den zukünftigen Zugriff auf einem Server pinnen kann. + +Sie könnten einen Drittanbieterdienst wie [Fleek](https://resources.fleek.xyz/docs/) nutzen, um die ganze Arbeit zu erledigen. Dieses Tutorial richtet sich an Personen, die genug tun wollen, um zu verstehen, was sie tun, auch wenn es mehr Arbeit bedeutet. + +## Lokale erste Schritte {#getting-started-locally} + +Es gibt mehrere [Drittanbieter von IPFS](https://docs.ipfs.tech/how-to/work-with-pinning-services/#use-a-third-party-pinning-service), aber für Testzwecke ist es am besten, IPFS zunächst lokal auszuführen. + +1. Installieren Sie die [IPFS-Benutzeroberfläche](https://docs.ipfs.tech/install/ipfs-desktop/#install-instructions). + +2. Erstellen Sie ein Verzeichnis mit Ihrer Website. Wenn Sie [Vite](https://vite.dev/) verwenden, nutzen Sie diesen Befehl: + + ```sh + pnpm vite build + ``` + +3. Klicken Sie in IPFS Desktop auf **Importieren > Ordner** und wählen Sie das Verzeichnis aus, das Sie im vorherigen Schritt erstellt haben. + +4. Wählen Sie den Ordner aus, den Sie gerade hochgeladen haben, und klicken Sie auf **Umbenennen**. Geben Sie ihm einen aussagekräftigeren Namen. + +5. Wählen Sie ihn erneut aus und klicken Sie auf **Link teilen**. Kopieren Sie die URL in die Zwischenablage. Der Link wäre ähnlich wie `https://ipfs.io/ipfs/QmaCuQ7yN6iyBjLmLGe8YiFuCwnePoKfVu6ue8vLBsLJQJ`. + +6. Klicken Sie auf **Status**. Erweitern Sie den Tab **Erweitert**, um die Gateway-Adresse zu sehen. Auf meinem System lautet die Adresse beispielsweise `http://127.0.0.1:8080`. + +7. Kombinieren Sie den Pfad aus dem Link-Schritt mit der Gateway-Adresse, um Ihre Adresse zu finden. Für das obige Beispiel lautet die URL `http://127.0.0.1:8080/ipfs/QmaCuQ7yN6iyBjLmLGe8YiFuCwnePoKfVu6ue8vLBsLJQJ`. Öffnen Sie diese URL in einem Browser, um Ihre Website zu sehen. + +## Hochladen {#uploading} + +Jetzt können Sie IPFS also verwenden, um Dateien lokal bereitzustellen, was nicht sehr aufregend ist. Der nächste Schritt besteht darin, sie für die ganze Welt verfügbar zu machen, wenn Sie offline sind. + +Es gibt eine Reihe von bekannten [Pinning-Diensten](https://docs.ipfs.tech/concepts/persistence/#pinning-services). Wählen Sie einen davon aus. Egal welchen Dienst Sie nutzen, Sie müssen ein Konto erstellen und ihm den **Content Identifier (CID)** von Ihrem IPFS-Desktop bereitstellen. + +Ich persönlich fand [4EVERLAND](https://docs.4everland.org/storage/4ever-pin/guides) am einfachsten zu bedienen. Hier ist die Anleitung dafür: + +1. Navigieren Sie zum [Dashboard](https://dashboard.4everland.org/overview) und melden Sie sich mit Ihrer Wallet an. + +2. Klicken Sie in der linken Seitenleiste auf **Speicher > 4EVER Pin**. + +3. Klicken Sie auf **Hochladen > Ausgewählte CID**. Geben Sie Ihrem Inhalt einen Namen und geben Sie die CID aus dem IPFS-Desktop an. Derzeit ist eine CID eine Zeichenkette, die mit `Qm` beginnt, gefolgt von 44 Buchstaben und Ziffern, die einen [Base58-kodierten](https://medium.com/bootdotdev/base64-vs-base58-encoding-c25553ff4524) Hash darstellen, wie z. B. `QmaCuQ7yN6iyBjLmLGe8YiFuCwnePoKfVu6ue8vLBsLJQJ`, aber [das wird sich wahrscheinlich ändern](https://docs.ipfs.tech/concepts/content-addressing/#version-1-v1). + +4. Der anfängliche Status ist **In Warteschlange**. Laden Sie neu, bis er sich auf **Gepinnt** ändert. + +5. Klicken Sie auf Ihre CID, um den Link zu erhalten. Sie können meine Anwendung [hier](https://bafybeifqka2odrne5b6l5guthqvbxu4pujko2i6rx2zslvr3qxs6u5o7im.ipfs.dweb.link/) sehen. + +6. Möglicherweise müssen Sie Ihr Konto aktivieren, damit es länger als einen Monat gepinnt bleibt. Die Aktivierung des Kontos kostet etwa 1 $. Wenn Sie es geschlossen haben, melden Sie sich ab und wieder an, um erneut zur Aktivierung aufgefordert zu werden. + +## Verwendung von IPFS {#using-from-ipfs} + +An diesem Punkt haben Sie einen Link zu einem zentralisierten Gateway, das Ihre IPFS-Inhalte bereitstellt. Kurz gesagt, Ihre Benutzeroberfläche ist vielleicht etwas sicherer, aber immer noch nicht zensurresistent. Für echte Zensurresistenz müssen Benutzer IPFS [direkt aus einem Browser](https://docs.ipfs.tech/install/ipfs-companion/#prerequisites) verwenden. + +Sobald Sie das installiert haben (und der Desktop-IPFS funktioniert), können Sie auf jeder Website zu [/ipfs/``](https://any.site/ipfs/bafybeifqka2odrne5b6l5guthqvbxu4pujko2i6rx2zslvr3qxs6u5o7im) gehen und Sie erhalten diesen Inhalt auf dezentralisierte Weise. + +## Nachteile {#drawbacks} + +Sie können IPFS-Dateien nicht zuverlässig löschen. Solange Sie also Ihre Benutzeroberfläche ändern, ist es wahrscheinlich am besten, sie entweder zentralisiert zu belassen oder das [Interplanetary Name System (IPNS)](https://docs.ipfs.tech/concepts/ipns/#mutability-in-ipfs) zu verwenden, ein System, das Veränderbarkeit auf IPFS bietet. Natürlich kann alles, was veränderbar ist, zensiert werden, im Fall von IPNS, indem man die Person unter Druck setzt, die den zugehörigen privaten Schlüssel besitzt. + +Außerdem haben einige Pakete ein Problem mit IPFS. Wenn Ihre Website also sehr kompliziert ist, ist das möglicherweise keine gute Lösung. Und natürlich kann alles, was auf Server-Integration angewiesen ist, nicht dezentralisiert werden, nur weil die Client-Seite auf IPFS liegt. + +## Fazit {#conclusion} + +So wie Ethereum es Ihnen ermöglicht, die Datenbank- und Geschäftslogikaspekte Ihrer Dapp zu dezentralisieren, ermöglicht IPFS die Dezentralisierung der Benutzeroberfläche. Damit können Sie einen weiteren Angriffsvektor gegen Ihre Dapp ausschalten. + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). diff --git a/public/content/translations/de/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md b/public/content/translations/de/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md new file mode 100644 index 00000000000..e7c0847d970 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md @@ -0,0 +1,105 @@ +--- +title: Starte deine Dapp-Frontend-Entwicklung mit create-eth-app +description: "Ein Überblick über die Verwendung und Funktionen von create-eth-app" +author: "Markus Waas" +tags: + ["frontend", "javascript", "ethers.js", "the graph", "defi"] +skill: beginner +lang: de +published: 2020-04-27 +source: soliditydeveloper.com +sourceUrl: https://soliditydeveloper.com/create-eth-app +--- + +Zuletzt haben wir uns [das große Ganze von Solidity](https://soliditydeveloper.com/solidity-overview-2020) angesehen und bereits [create-eth-app](https://github.com/PaulRBerg/create-eth-app) erwähnt. Jetzt erfährst du, wie du sie verwenden kannst, welche Funktionen integriert sind und wie du sie weiter ausbauen kannst. Diese App, die von Paul Razvan Berg, dem Gründer von [Sablier](http://sablier.com/), gestartet wurde, wird deine Frontend-Entwicklung in Schwung bringen und bietet mehrere optionale Integrationen zur Auswahl. + +## Installation {#installation} + +Die Installation erfordert Yarn 0.25 oder höher (`npm install yarn --global`). Die Ausführung ist denkbar einfach: + +```bash +yarn create eth-app my-eth-app +cd my-eth-app +yarn react-app:start +``` + +Es verwendet [create-react-app](https://github.com/facebook/create-react-app) unter der Haube. Um deine App zu sehen, öffne `http://localhost:3000/`. Wenn du bereit für den Einsatz in der Produktion bist, erstelle mit yarn build ein minifiziertes Bundle. Eine einfache Möglichkeit, dies zu hosten, wäre [Netlify](https://www.netlify.com/). Du kannst ein GitHub-Repo erstellen, es zu Netlify hinzufügen, den Build-Befehl einrichten und schon bist du fertig! Deine App wird gehostet und ist für alle nutzbar. Und all das kostenlos. + +## Funktionen {#features} + +### React & create-react-app {#react--create-react-app} + +Zunächst einmal das Herzstück der App: React und all die zusätzlichen Funktionen, die mit _create-react-app_ kommen. Die alleinige Nutzung ist eine gute Option, wenn du Ethereum nicht integrieren möchtest. [React](https://react.dev/) selbst macht die Erstellung interaktiver UIs wirklich einfach. Es mag nicht so einsteigerfreundlich sein wie [Vue](https://vuejs.org/), aber es wird immer noch am häufigsten verwendet, hat mehr Funktionen und, was am wichtigsten ist, es stehen Tausende von zusätzlichen Bibliotheken zur Auswahl. Die _create-react-app_ macht den Einstieg ebenfalls sehr einfach und umfasst: + +- Unterstützung für React-, JSX-, ES6-, TypeScript- und Flow-Syntax. +- Spracherweiterungen über ES6 hinaus, wie der Object-Spread-Operator. +- CSS mit automatischen Präfixen, sodass du keine -webkit- oder andere Präfixe benötigst. +- Ein schneller, interaktiver Unit-Test-Runner mit integrierter Unterstützung für Coverage-Reporting. +- Ein Live-Entwicklungsserver, der vor häufigen Fehlern warnt. +- Ein Build-Skript zum Bündeln von JS, CSS und Bildern für die Produktion, mit Hashes und Sourcemaps. + +Die _create-eth-app_ macht insbesondere von den neuen [Hooks-Effekten](https://legacy.reactjs.org/docs/hooks-effect.html) Gebrauch. Eine Methode, um leistungsstarke und dennoch sehr kleine sogenannte funktionale Komponenten zu schreiben. Wie sie in _create-eth-app_ verwendet werden, steht im folgenden Abschnitt über Apollo. + +### Yarn Workspaces {#yarn-workspaces} + +[Yarn Workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) ermöglichen es, mehrere Pakete zu haben, diese aber alle vom Stammverzeichnis aus zu verwalten und die Abhängigkeiten für alle auf einmal mit `yarn install` zu installieren. Dies ist vor allem bei kleineren Zusatzpaketen wie dem Management von Smart-Contract-Adressen/ABIs (die Information darüber, wo man welche Smart Contracts eingesetzt hat und wie man mit ihnen kommuniziert) oder der Graph-Integration sinnvoll, die beide Teil von `create-eth-app` sind. + +### ethers.js {#ethersjs} + +Obwohl [Web3](https://docs.web3js.org/) immer noch am häufigsten verwendet wird, hat [ethers.js](https://docs.ethers.io/) im letzten Jahr als Alternative viel Anklang gefunden und ist in _create-eth-app_ integriert. Du kannst damit arbeiten, zu Web3 wechseln oder ein Upgrade auf [ethers.js v5](https://docs.ethers.org/v5/) in Erwägung ziehen, das fast die Beta-Phase abgeschlossen hat. + +### The Graph {#the-graph} + +[GraphQL](https://graphql.org/) ist im Vergleich zu einer [RESTful-API](https://restfulapi.net/) eine alternative Methode für den Umgang mit Daten. Sie haben mehrere Vorteile gegenüber RESTful-APIs, insbesondere für dezentrale Blockchain-Daten. Wenn du an der Begründung dafür interessiert bist, schau dir [GraphQL Will Power the Decentralized Web](https://medium.com/graphprotocol/graphql-will-power-the-decentralized-web-d7443a69c69a) an. + +Normalerweise würdest du Daten direkt von deinem Smart Contract abrufen. Willst du die Zeit des letzten Handels auslesen? Rufe einfach `MyContract.methods.latestTradeTime().call()` auf, was die Daten von einem Ethereum-Node in deine Dapp holt. Aber was ist, wenn du Hunderte verschiedener Datenpunkte benötigst? Das würde zu Hunderten von Datenabrufen beim Node führen, die jedes Mal eine [RTT](https://wikipedia.org/wiki/Round-trip_delay_time) erfordern, was deine Dapp langsam und ineffizient machen würde. Eine Problemumgehung könnte eine Abruffunktion in deinem Contract sein, die mehrere Daten auf einmal zurückgibt. Das ist aber nicht immer ideal. + +Und dann bist du vielleicht auch an historischen Daten interessiert. Du willst nicht nur die Zeit des letzten Handels wissen, sondern die Zeiten aller Handelsgeschäfte, die du jemals selbst getätigt hast. Verwende das Subgraph-Paket von _create-eth-app_, lies die [Dokumentation](https://thegraph.com/docs/en/subgraphs/developing/creating/starting-your-subgraph) und passe es an deine eigenen Contracts an. Wenn du nach beliebten Smart Contracts suchst, gibt es möglicherweise sogar bereits einen Subgraph. Sieh dir den [Subgraph-Explorer](https://thegraph.com/explorer/) an. + +Sobald du einen Subgraph hast, kannst du eine einfache Abfrage in deiner Dapp schreiben, die alle wichtigen Blockchain-Daten, einschließlich historischer Daten, die du benötigst, abruft; dafür ist nur ein Abruf erforderlich. + +### Apollo {#apollo} + +Dank der [Apollo Boost](https://www.apollographql.com/docs/react/get-started/)-Integration kannst du den Graphen einfach in deine React-Dapp integrieren. Besonders bei der Verwendung von React Hooks und Apollo ist das Abrufen von Daten so einfach wie das Schreiben einer einzigen GraphQL-Abfrage in deiner Komponente: + +```js +const { loading, error, data } = useQuery(myGraphQlQuery) + +React.useEffect(() => { + if (!loading && !error && data) { + console.log({ data }) + } +}, [loading, error, data]) +``` + +## Vorlagen {#templates} + +Darüber hinaus kannst du aus verschiedenen Vorlagen wählen. Bisher kannst du eine Aave-, Compound-, UniSwap- oder Sablier-Integration verwenden. Sie alle fügen wichtige Smart-Contract-Adressen für Dienste zusammen mit vorgefertigten Subgraph-Integrationen hinzu. Füge einfach die Vorlage zum Erstellungsbefehl hinzu, wie `yarn create eth-app my-eth-app --with-template aave`. + +### Aave {#aave} + +[Aave](https://aave.com/) ist ein dezentraler Geldleihmarkt. Die Einleger stellen dem Markt Liquidität zur Verfügung, um passives Einkommen zu generieren, während Kreditnehmer sich mithilfe von Sicherheiten Geld leihen können. Eine einzigartige Funktion von Aave sind die [Flash Loans](https://aave.com/docs/developers/flash-loans), die es dir ermöglichen, Geld ohne jegliche Sicherheiten zu leihen, solange du das Darlehen innerhalb einer einzigen Transaktion zurückzahlst. Das kann zum Beispiel nützlich sein, um zusätzliches Geld für das Arbitrage-Trading zu erhalten. + +Gehandelte Token, die dir Zinsen einbringen, werden _aTokens_ genannt. + +Wenn du dich für die Integration von Aave mit _create-eth-app_ entscheidest, erhältst du eine [Subgraph-Integration](https://docs.aave.com/developers/getting-started/using-graphql). Aave verwendet The Graph und stellt dir bereits mehrere einsatzbereite Subgraphs auf [Ropsten](https://thegraph.com/explorer/subgraph/aave/protocol-ropsten) und [Mainnet](https://thegraph.com/explorer/subgraph/aave/protocol) in [roher](https://thegraph.com/explorer/subgraph/aave/protocol-raw) oder [formatierter](https://thegraph.com/explorer/subgraph/aave/protocol) Form zur Verfügung. + +![Aave Flash-Loan-Meme – „Yeahhh, wenn ich meinen Flash Loan länger als eine Transaktion behalten könnte, wäre das großartig“](./flashloan-meme.png) + +### Compound {#compound} + +[Compound](https://compound.finance/) ist ähnlich wie Aave. Die Integration enthält bereits den neuen [Compound v2 Subgraph](https://medium.com/graphprotocol/https-medium-com-graphprotocol-compound-v2-subgraph-highlight-a5f38f094195). Die zinsbringenden Token werden hier überraschenderweise _cTokens_ genannt. + +### Uniswap {#uniswap} + +[Uniswap](https://uniswap.exchange/) ist eine dezentralisierte Börse (DEX). Liquiditätsanbieter können Gebühren verdienen, indem sie die erforderlichen Token oder Ether für beide Seiten eines Handels bereitstellen. Es ist weit verbreitet und hat daher eine der höchsten Liquiditäten für eine sehr breite Palette von Token. Du kannst es einfach in deine Dapp integrieren, um beispielsweise Benutzern zu ermöglichen, ihre ETH gegen DAI zu tauschen. + +Leider ist die Integration zum Zeitpunkt der Erstellung dieses Artikels nur für Uniswap v1 und nicht für die [gerade veröffentlichte v2](https://uniswap.org/blog/uniswap-v2/) möglich. + +### Sablier {#sablier} + +[Sablier](https://sablier.com/) ermöglicht Benutzern das Streamen von Geldzahlungen. Anstelle eines einzelnen Zahltages erhältst du dein Geld nach der anfänglichen Einrichtung kontinuierlich und ohne weitere Verwaltung. Die Integration umfasst einen [eigenen Subgraph](https://thegraph.com/explorer/subgraph/sablierhq/sablier). + +## Was kommt als Nächstes? {#whats-next} + +Wenn du Fragen zu _create-eth-app_ hast, besuche den [Sablier Community-Server](https://discord.gg/bsS8T47), wo du mit den Autoren von _create-eth-app_ in Kontakt treten kannst. Als erste nächste Schritte könntest du ein UI-Framework wie [Material UI](https://mui.com/material-ui/) integrieren, GraphQL-Abfragen für die Daten schreiben, die du tatsächlich benötigst, und das Deployment einrichten. diff --git a/public/content/translations/de/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md b/public/content/translations/de/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md new file mode 100644 index 00000000000..f3ef6b9c6b3 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md @@ -0,0 +1,269 @@ +--- +title: Grundlegende Ethereum-Themen mit SQL lernen +description: "Dieses Tutorial hilft Lesern, grundlegende Ethereum-Konzepte wie Transaktionen, Blöcke und Gas zu verstehen, indem sie On-Chain-Daten mit der Structured Query Language (SQL) abfragen." +author: "Paul Apivat" +tags: ["SQL", "Querying", "Transactions"] +skill: beginner +lang: de +published: 2021-05-11 +source: paulapivat.com +sourceUrl: https://paulapivat.com/post/query_ethereum/ +--- + +Viele Ethereum-Tutorials richten sich an Entwickler, aber es mangelt an Lernressourcen für Datenanalysten oder für Leute, die On-Chain-Daten einsehen möchten, ohne einen Client oder einen Knoten zu betreiben. + +Dieses Tutorial hilft Lesern, grundlegende Ethereum-Konzepte wie Transaktionen, Blöcke und Gas zu verstehen, indem sie On-Chain-Daten mit der Structured Query Language (SQL) über eine von [Dune Analytics](https://dune.com/) bereitgestellte Schnittstelle abfragen. + +On-Chain-Daten können uns helfen, Ethereum, das Netzwerk und seine Wirtschaftlichkeit in Bezug auf die Rechenleistung zu verstehen. Sie sollten als Grundlage für das Verständnis der Herausforderungen dienen, mit denen Ethereum heute konfrontiert ist (z. B. steigende Gaspreise) und, was noch wichtiger ist, für Diskussionen über Skalierungslösungen. + +### Transaktionen {#transactions} + +Die Reise eines Nutzers auf Ethereum beginnt mit der Initialisierung eines nutzergesteuerten Kontos oder einer Entität mit einem ETH-Guthaben. Es gibt zwei Kontotypen – benutzergesteuert oder ein Smart Contract (siehe [ethereum.org](/developers/docs/accounts/)). + +Jedes Konto kann in einem Block-Explorer wie [Etherscan](https://etherscan.io/) oder [Blockscout](https://eth.blockscout.com/) eingesehen werden. Block-Explorer sind ein Portal zu den Daten von Ethereum. Sie zeigen in Echtzeit Daten zu Blöcken, Transaktionen, Minern, Konten und anderen On-Chain-Aktivitäten an (siehe [hier](/developers/docs/data-and-analytics/block-explorers/)). + +Ein Benutzer möchte die Daten jedoch möglicherweise direkt abfragen, um die von externen Block-Explorern bereitgestellten Informationen abzugleichen. [Dune Analytics](https://dune.com/) bietet diese Möglichkeit jedem, der über SQL-Kenntnisse verfügt. + +Als Referenz kann das Smart-Contract-Konto der Ethereum Foundation (EF) auf [Blockscout](https://eth.blockscout.com/address/0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe) eingesehen werden. + +Es ist zu beachten, dass alle Konten, einschließlich desjenigen der EF, eine öffentliche Adresse haben, die zum Senden und Empfangen von Transaktionen verwendet werden kann. + +Der Kontostand auf Etherscan umfasst reguläre Transaktionen und interne Transaktionen. Interne Transaktionen sind, trotz des Namens, keine _tatsächlichen_ Transaktionen, die den Zustand der Chain ändern. Es handelt sich um Wertübertragungen, die durch die Ausführung eines Vertrags initiiert werden ([Quelle](https://ethereum.stackexchange.com/questions/3417/how-to-get-contract-internal-transactions)). Da interne Transaktionen keine Signatur haben, sind sie **nicht** in der Blockchain enthalten und können nicht mit Dune Analytics abgefragt werden. + +Daher wird sich dieses Tutorial auf reguläre Transaktionen konzentrieren. Dies kann wie folgt abgefragt werden: + +```sql +WITH temp_table AS ( +SELECT + hash, + block_number, + block_time, + "from", + "to", + value / 1e18 AS ether, + gas_used, + gas_price / 1e9 AS gas_price_gwei +FROM ethereum."transactions" +WHERE "to" = '\xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe' +ORDER BY block_time DESC +) +SELECT + hash, + block_number, + block_time, + "from", + "to", + ether, + (gas_used * gas_price_gwei) / 1e9 AS txn_fee +FROM temp_table +``` + +Dies liefert die gleichen Informationen wie auf der Transaktionsseite von Etherscan. Zum Vergleich, hier die beiden Quellen: + +#### Etherscan {#etherscan} + +![](./etherscan_view.png) + +[Vertragsseite der EF auf Blockscout.](https://eth.blockscout.com/address/0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe) + +#### Dune Analytics {#dune-analytics} + +![](./dune_view.png) + +Das Dashboard finden Sie [hier](https://dune.com/paulapivat/Learn-Ethereum). Klicken Sie auf die Tabelle, um die Abfrage zu sehen (siehe auch oben). + +### Transaktionen im Detail {#breaking_down_transactions} + +Eine übermittelte Transaktion enthält mehrere Informationen, darunter ([Quelle](/developers/docs/transactions/)): + +- **Empfänger**: Die Empfangsadresse (abgefragt als „to“) +- **Signatur**: Während die privaten Schlüssel eines Absenders eine Transaktion signieren, können wir mit SQL die öffentliche Adresse eines Absenders abfragen („from“). +- **Wert**: Dies ist der Betrag an transferiertem ETH (siehe Spalte `ether`). +- **Daten**: Das sind beliebige Daten, die gehasht wurden (siehe Spalte `data`) +- **gasLimit** – die maximale Menge an Gaseinheiten, die von der Transaktion verbraucht werden kann. Gaseinheiten stellen Rechenschritte dar. +- **maxPriorityFeePerGas** – die maximale Gasmenge, die als Trinkgeld für den Miner enthalten sein soll +- **maxFeePerGas** – die maximale Menge an Gas, die für die Transaktion gezahlt werden soll (einschließlich baseFeePerGas und maxPriorityFeePerGas) + +Wir können diese spezifischen Informationen für Transaktionen an die öffentliche Adresse der Ethereum Foundation abfragen: + +```sql +SELECT + "to", + "from", + value / 1e18 AS ether, + data, + gas_limit, + gas_price / 1e9 AS gas_price_gwei, + gas_used, + ROUND(((gas_used / gas_limit) * 100),2) AS gas_used_pct +FROM ethereum."transactions" +WHERE "to" = '\xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe' +ORDER BY block_time DESC +``` + +### Blöcke {#blocks} + +Jede Transaktion ändert den Zustand der Ethereum Virtual Machine ([EVM](/developers/docs/evm/)) ([Quelle](/developers/docs/transactions/)). Transaktionen werden an das Netzwerk gesendet, um verifiziert und in einen Block aufgenommen zu werden. Jede Transaktion ist mit einer Blocknummer verknüpft. Um die Daten zu sehen, können wir eine bestimmte Blocknummer abfragen: 12396854 (der jüngste Block der Ethereum-Foundation-Transaktionen zum Zeitpunkt des Schreibens dieses Tutorials am 5.11.2021). + +Wenn wir die nächsten beiden Blöcke abfragen, können wir außerdem sehen, dass jeder Block den Hash des vorherigen Blocks (d. h. den übergeordneten Hash) enthält, was zeigt, wie die Blockchain aufgebaut ist. + +Jeder Block enthält einen Verweis auf seinen übergeordneten Block. Dies wird unten zwischen den Spalten `hash` und `parent_hash` gezeigt ([Quelle](/developers/docs/blocks/)): + +![parent_hash](./parent_hash.png) + +Hier ist die [Abfrage](https://dune.com/queries/44856/88292) auf Dune Analytics: + +```sql +SELECT + time, + number, + hash, + parent_hash, + nonce +FROM ethereum."blocks" +WHERE "number" = 12396854 OR "number" = 12396855 OR "number" = 12396856 +LIMIT 10 +``` + +Wir können einen Block untersuchen, indem wir Zeit, Blocknummer, Schwierigkeitsgrad, Hash, übergeordneten Hash und die Nonce abfragen. + +Das Einzige, was diese Abfrage nicht abdeckt, ist die _Liste der Transaktionen_, die eine separate Abfrage unten erfordert, und der _State-Root_. Ein Full- oder Archiv-Node speichert alle Transaktionen und Zustandsübergänge, sodass Clients den Zustand der Chain jederzeit abfragen können. Da dies viel Speicherplatz erfordert, können wir Chain-Daten von Zustandsdaten trennen: + +- Chain-Daten (Liste von Blöcken, Transaktionen) +- Zustandsdaten (Ergebnis des Zustandsübergangs jeder Transaktion) + +Der State-Root fällt unter Letzteres und ist _implizite_ Daten (nicht on-chain gespeichert), während Chain-Daten explizit sind und auf der Chain selbst gespeichert werden ([Quelle](https://ethereum.stackexchange.com/questions/359/where-is-the-state-data-stored)). + +In diesem Tutorial konzentrieren wir uns auf On-Chain-Daten, die mit SQL über Dune Analytics abgefragt werden _können_. + +Wie oben erwähnt, enthält jeder Block eine Liste von Transaktionen. Wir können diese abfragen, indem wir nach einem bestimmten Block filtern. Wir versuchen es mit dem letzten Block, 12396854: + +```sql +SELECT * FROM ethereum."transactions" +WHERE block_number = 12396854 +ORDER BY block_time DESC` +``` + +Hier ist die SQL-Ausgabe auf Dune: + +![](./list_of_txn.png) + +Dieser einzelne Block, der der Chain hinzugefügt wird, ändert den Zustand der Ethereum Virtual Machine ([EVM](/developers/docs/evm/)). Dutzende, manchmal Hunderte von Transaktionen werden auf einmal verifiziert. In diesem speziellen Fall waren 222 Transaktionen enthalten. + +Um zu sehen, wie viele tatsächlich erfolgreich waren, fügen wir einen weiteren Filter hinzu, um erfolgreiche Transaktionen zu zählen: + +```sql +WITH temp_table AS ( + SELECT * FROM ethereum."transactions" + WHERE block_number = 12396854 AND success = true + ORDER BY block_time DESC +) +SELECT + COUNT(success) AS num_successful_txn +FROM temp_table +``` + +Für Block 12396854 wurden von insgesamt 222 Transaktionen 204 erfolgreich verifiziert: + +![](./successful_txn.png) + +Transaktionsanfragen kommen Dutzende Male pro Sekunde vor, aber Blöcke werden etwa alle 15 Sekunden committet ([Quelle](/developers/docs/blocks/)). + +Um zu sehen, dass ungefähr alle 15 Sekunden ein Block produziert wird, können wir die Anzahl der Sekunden pro Tag (86.400) durch 15 teilen, um eine geschätzte durchschnittliche Anzahl von Blöcken pro Tag (~ 5.760) zu erhalten. + +Das Diagramm der täglich erzeugten Ethereum-Blöcke (2016 – heute) ist: + +![](./daily_blocks.png) + +Die durchschnittliche Anzahl der in diesem Zeitraum täglich produzierten Blöcke beträgt ~5.874: + +![](./avg_daily_blocks.png) + +Die Abfragen lauten: + +```sql +# Abfrage zur Visualisierung der täglich produzierten Blöcke seit 2016 + +SELECT + DATE_TRUNC('day', time) AS dt, + COUNT(*) AS block_count +FROM ethereum."blocks" +GROUP BY dt +OFFSET 1 + +# durchschnittliche Anzahl der pro Tag produzierten Blöcke + +WITH temp_table AS ( +SELECT + DATE_TRUNC('day', time) AS dt, + COUNT(*) AS block_count +FROM ethereum."blocks" +GROUP BY dt +OFFSET 1 +) +SELECT + AVG(block_count) AS avg_block_count +FROM temp_table +``` + +Die durchschnittliche Anzahl der seit 2016 pro Tag produzierten Blöcke liegt mit 5.874 leicht über diesem Wert. Alternativ ergeben 86.400 Sekunden geteilt durch 5.874 durchschnittliche Blöcke 14,7 Sekunden oder ungefähr einen Block alle 15 Sekunden. + +### Gas {#gas} + +Blöcke sind in ihrer Größe begrenzt. Die maximale Blockgröße ist dynamisch und variiert je nach Netzwerknachfrage zwischen 12.500.000 und 25.000.000 Einheiten. Limits sind erforderlich, um zu verhindern, dass willkürlich große Blöcke die Full Nodes in Bezug auf Speicherplatz- und Geschwindigkeitsanforderungen belasten ([Quelle](/developers/docs/blocks/)). + +Eine Möglichkeit, sich das Block-Gaslimit vorzustellen, ist, es als das **Angebot** an verfügbarem Blockspace zu betrachten, in dem Transaktionen gebündelt werden. Das Block-Gaslimit kann von 2016 bis heute abgefragt und visualisiert werden: + +![](./avg_gas_limit.png) + +```sql +SELECT + DATE_TRUNC('day', time) AS dt, + AVG(gas_limit) AS avg_block_gas_limit +FROM ethereum."blocks" +GROUP BY dt +OFFSET 1 +``` + +Dann gibt es das tatsächlich täglich verwendete Gas, um für Berechnungen auf der Ethereum-Chain zu bezahlen (d. h. das Senden von Transaktionen, das Aufrufen eines Smart Contracts, das Prägen eines NFT). Dies ist die **Nachfrage** nach verfügbarem Ethereum-Blockspace: + +![](./daily_gas_used.png) + +```sql +SELECT + DATE_TRUNC('day', time) AS dt, + AVG(gas_used) AS avg_block_gas_used +FROM ethereum."blocks" +GROUP BY dt +OFFSET 1 +``` + +Wir können diese beiden Diagramme auch einander gegenüberstellen, um zu sehen, wie sich **Angebot und Nachfrage** beeinflussen: + +![gas_demand_supply](./gas_demand_supply.png) + +Daher können wir Gaspreise als eine Funktion der Nachfrage nach Ethereum-Blockspace bei gegebenem Angebot verstehen. + +Schließlich möchten wir vielleicht die durchschnittlichen täglichen Gaspreise für die Ethereum-Chain abfragen. Dies würde jedoch zu einer besonders langen Abfragezeit führen, also filtern wir unsere Abfrage auf die durchschnittliche Gasmenge, die von der Ethereum Foundation pro Transaktion bezahlt wird. + +![](./ef_daily_gas.png) + +Wir können die Gaspreise sehen, die über die Jahre für alle an die Adresse der Ethereum Foundation getätigten Transaktionen gezahlt wurden. Hier ist die Abfrage: + +```sql +SELECT + block_time, + gas_price / 1e9 AS gas_price_gwei, + value / 1e18 AS eth_sent +FROM ethereum."transactions" +WHERE "to" = '\xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe' +ORDER BY block_time DESC +``` + +### Zusammenfassung {#summary} + +Mit diesem Tutorial verstehen wir grundlegende Ethereum-Konzepte und wie die Ethereum-Blockchain funktioniert, indem wir On-Chain-Daten abfragen und ein Gefühl für sie bekommen. + +Das Dashboard mit dem gesamten in diesem Tutorial verwendeten Code finden Sie [hier](https://dune.com/paulapivat/Learn-Ethereum). + +Für weitere Datennutzung zur Erkundung von Web3 [finden Sie mich auf Twitter](https://twitter.com/paulapivat). diff --git a/public/content/translations/de/developers/tutorials/logging-events-smart-contracts/index.md b/public/content/translations/de/developers/tutorials/logging-events-smart-contracts/index.md new file mode 100644 index 00000000000..26a9503924a --- /dev/null +++ b/public/content/translations/de/developers/tutorials/logging-events-smart-contracts/index.md @@ -0,0 +1,62 @@ +--- +title: Protokollierung von Daten aus Smart Contracts mit Ereignissen +description: "Eine Einführung in Smart-Contract-Ereignisse und wie Sie diese zur Datenprotokollierung verwenden können" +author: "jdourlens" +tags: ["smart contracts", "remix", "solidity", "events"] +skill: intermediate +lang: de +published: 2020-04-03 +source: EthereumDev +sourceUrl: https://ethereumdev.io/logging-data-with-events/ +address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +--- + +In Solidity sind [Ereignisse](/developers/docs/smart-contracts/anatomy/#events-and-logs) Signale, die von Smart Contracts ausgegeben werden können. Dapps oder alles, was mit der JSON-RPC-API von Ethereum verbunden ist, können auf diese Ereignisse lauschen und entsprechend reagieren. Ein Ereignis kann auch indiziert werden, sodass der Ereignisverlauf später durchsuchbar ist. + +## Ereignisse {#events} + +Das häufigste Ereignis auf der Ethereum-Blockchain ist zum Zeitpunkt der Erstellung dieses Artikels das Transfer-Ereignis, das von ERC20-Tokens ausgegeben wird, wenn jemand Token überträgt. + +```solidity +event Transfer(address indexed from, address indexed to, uint256 value); +``` + +Die Ereignissignatur wird innerhalb des Vertragscodes deklariert und kann mit dem Schlüsselwort emit ausgegeben werden. Zum Beispiel protokolliert das Transfer-Ereignis, wer die Übertragung gesendet hat (_from_), an wen (_to_) und wie viele Token übertragen wurden (_value_). + +Wenn wir zu unserem Counter-Smart-Contract zurückkehren und beschließen, jedes Mal zu protokollieren, wenn der Wert geändert wird. Da dieser Vertrag nicht zur Bereitstellung gedacht ist, sondern als Grundlage für die Erstellung eines anderen Vertrags durch Erweiterung dient, wird er als abstrakter Vertrag bezeichnet. Im Fall unseres Counter-Beispiels würde es so aussehen: + +```solidity +pragma solidity 0.5.17; + +contract Counter { + + event ValueChanged(uint oldValue, uint256 newValue); + + // Private Variable vom Typ unsigned int zur Speicherung der Anzahl der Zählungen + uint256 private count = 0; + + // Funktion, die unseren Zähler inkrementiert + function increment() public { + count += 1; + emit ValueChanged(count - 1, count); + } + + // Getter zum Abrufen des Zählwerts + function getCount() public view returns (uint256) { + return count; + } + +} +``` + +Beachten Sie Folgendes: + +- **Zeile 5**: Wir deklarieren unser Ereignis und was es enthält: den alten Wert und den neuen Wert. + +- **Zeile 13**: Wenn wir unsere Zählvariable inkrementieren, geben wir das Ereignis aus. + +Wenn wir nun den Vertrag bereitstellen und die increment-Funktion aufrufen, sehen wir, dass Remix es automatisch anzeigt, wenn Sie auf die neue Transaktion in einem Array namens logs klicken. + +![Remix-Screenshot](./remix-screenshot.png) + +Logs sind sehr nützlich für das Debugging Ihrer Smart Contracts, aber sie sind auch wichtig, wenn Sie Anwendungen erstellen, die von verschiedenen Personen genutzt werden. Sie erleichtern die Analyse, um zu verfolgen und zu verstehen, wie Ihr Smart Contract genutzt wird. Die durch Transaktionen erzeugten Logs werden in gängigen Block-Explorern angezeigt und Sie können sie beispielsweise auch verwenden, um Off-Chain-Skripte zu erstellen, die auf bestimmte Ereignisse lauschen und bei deren Auftreten Maßnahmen ergreifen. diff --git a/public/content/translations/de/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md b/public/content/translations/de/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md new file mode 100644 index 00000000000..a09025edccf --- /dev/null +++ b/public/content/translations/de/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md @@ -0,0 +1,249 @@ +--- +title: "Merkle-Beweise für Offline Datenintegrität" +description: "Sicherstellung der Datenintegrität onchain für Daten, die größtenteils offchain gespeichert sind" +author: Ori Pomerantz +tags: ["storage"] +skill: advanced +lang: de +published: 2021-12-30 +--- + +## Einführung {#introduction} + +Idealerweise möchten wir alles in einem Ethereum Speicher ablegen, der auf Tausenden von Computern gespeichert ist und eine extrem hohe Verfügbarkeit (die Daten können nicht zensiert werden) und Integrität (die Daten können nicht unbefugt verändert werden) aufweist, aber die Speicherung eines 32-Byte-Wortes kostet normalerweise 20.000 Gas. Während ich das schreibe, entsprechen +diese Kosten 6,60 USD. Mit 21 Cent pro Byte ist das für viele Anwendungen zu teuer. + +Um dieses Problem zu lösen, hat das Ethereum-Ökosystem viele alternative Wege entwickelt, um Daten dezentral +zu speichern. In der Regel muss dabei ein Kompromiss zwischen Verfügbarkeit und Preis eingegangen werden. Die Integrität ist jedoch in der Regel gewährleistet. + +In diesem Artikel erfahren Sie, **wie** Sie die Datenintegrität gewährleisten, ohne die Daten auf der Blockchain zu speichern, indem Sie +[Merkle-Beweise](https://computersciencewiki.org/index.php/Merkle_proof) verwenden. + +## Wie funktioniert das? {#how-does-it-work} + +Theoretisch könnten wir einfach den Hash der Daten onchain speichern und alle Daten in Transaktionen senden, die sie benötigen. Allerdings ist das immer noch zu teuer. Ein Byte Daten zu einer Transaktion kostet etwa 16 Gas, derzeit etwa einen halben Cent, oder etwa 5 USD pro Kilobyte. Mit 5000 USD pro Megabyte ist dies für viele Anwendungen immer noch zu teuer, selbst ohne die zusätzlichen Kosten für das Hashing der Daten. + +Die Lösung ist, verschiedene Teilmengen der Daten wiederholt zu hashen, so dass Sie für die Daten, die Sie nicht zu senden brauchen, nur einen Hash senden können. Dazu verwenden Sie einen Merkle Tree, eine Baum Datenstruktur, bei der jeder Knoten ein Hash der darunter liegenden Knoten ist: + +![Merkle-Baum](tree.png) + +Der Root-Hash ist der einzige Teil, der onchain gespeichert werden muss. Um einen bestimmten Wert zu beweisen, geben Sie alle Hashes an, die damit kombiniert werden müssen, um die Wurzel (Hash-Root) zu erhalten. Um zum Beispiel `C` zu beweisen, stellen Sie `D`, `H(A-B)` und `H(E-H)` zur Verfügung. + +![Beweis für den Wert von C](proof-c.png) + +## Implementierung {#implementation} + +[Der Beispielcode wird hier zur Verfügung gestellt](https://github.com/qbzzt/merkle-proofs-for-offline-data-integrity). + +### Offchain-Code {#offchain-code} + +In diesem Artikel verwenden wir JavaScript für die Offchain-Berechnungen. Die meisten dezentralisierten Anwendungen haben ihre Offchain-Komponente in JavaScript. + +#### Erstellen der Merkle-Root {#creating-the-merkle-root} + +Als erstes müssen wir die Merkle-Wurzel für die Chain bereitstellen. + +```javascript +const ethers = require("ethers") +``` + +[Wir verwenden die Hash-Funktion aus dem Ethers-Paket](https://docs.ethers.io/v5/api/utils/hashing/#utils-keccak256). + +```javascript +// Die Rohdaten, deren Integrität wir überprüfen müssen. Die ersten beiden Bytes +// sind eine Benutzerkennung, und die letzten beiden Bytes sind die Menge der Tokens, die +// der Benutzer derzeit besitzt. +const dataArray = [ + 0x0bad0010, 0x60a70020, 0xbeef0030, 0xdead0040, 0xca110050, 0x0e660060, + 0xface0070, 0xbad00080, 0x060d0091, +] +``` + +Die Kodierung jedes Eintrags in eine einzelne 256-Bit-Integerzahl führt zu weniger lesbarem Code als z. B. die Verwendung von JSON. Allerdings bedeutet dies einen deutlich geringeren Verarbeitungsaufwand für die Abfrage der Vertragsdaten und damit deutlich niedrigere Gaskosten. [Man kann JSON onchain lesen](https://github.com/chrisdotn/jsmnSol), es ist nur eine schlechte Idee, wenn es vermeidbar ist. + +```javascript +// Das Array der Hash-Werte als BigInts +const hashArray = dataArray +``` + +In diesem Fall handelt es sich bei unseren Daten um 256 Bit-Werte, so dass keine Verarbeitung erforderlich ist. Wenn wir eine kompliziertere Datenstruktur verwenden, wie z. B. Strings, müssen wir sicherstellen, dass wir die Daten zuerst hashen, um ein Array von Hashes zu erhalten. Das liegt auch daran, weil es uns nicht interessiert, ob Benutzer die Informationen anderer Benutzer kennen. Andernfalls hätten wir einen Hash verwenden müssen, damit Benutzer 1 den Wert für Benutzer 0 nicht kennt, Benutzer 2 den Wert für Benutzer 3 nicht kennt usw. + +```javascript +// Konvertieren zwischen dem String, den die Hash-Funktion erwartet, und dem +// BigInt, das wir überall sonst verwenden. +const hash = (x) => + BigInt(ethers.utils.keccak256("0x" + x.toString(16).padStart(64, 0))) +``` + +Die Ethers-Hash-Funktion erwartet einen JavaScript-String mit einer hexadezimalen Zahl, wie z. B. `0x60A7`, und gibt einen anderen String mit der gleichen Struktur zurück. Für den Rest des Codes ist es jedoch einfacher, `BigInt` zu verwenden, also konvertieren wir es in einen hexadezimalen String und wieder zurück. + +```javascript +// Symmetrischer Hash eines Paares, sodass die umgekehrte Reihenfolge keine Rolle spielt. +const pairHash = (a, b) => hash(hash(a) ^ hash(b)) +``` + +Diese Funktion ist symmetrisch (Hash von a [xor](https://en.wikipedia.org/wiki/Exclusive_or) b). Das bedeutet, dass wir uns bei der Überprüfung des Merkle-Beweises keine Gedanken darüber machen müssen, ob wir den Wert aus dem Beweis vor oder nach dem berechneten Wert setzen sollen. Die Überprüfung von Merkle-Beweisen wird onchain durchgeführt. Je weniger wir dort tun müssen, desto besser. + +Warnung: +Kryptographie ist schwieriger als es aussieht. +Die ursprüngliche Version dieses Artikels hatte die Hash-Funktion `hash(a^b)`. +Das war eine **schlechte** Idee, denn es bedeutete, dass man, wenn man die legitimen Werte von `a` und `b` kannte, `b' = a^b^a'` verwenden konnte, um einen beliebigen `a'`-Wert zu beweisen. +Mit dieser Funktion müsste man `b'` so berechnen, dass `hash(a') ^ hash(b')` gleich einem bekannten Wert ist (der nächste Zweig auf dem Weg zur Root), was viel schwieriger ist. + +```javascript +// Der Wert, der anzeigt, dass ein bestimmter Zweig leer ist und keinen +// Wert hat +const empty = 0n +``` + +Wenn die Anzahl der Werte keine ganzzahlige Zweierpotenz ist, müssen wir leere Zweige behandeln. Bei diesem Programm wird die Null als Platzhalter eingesetzt. + +![Merkle-Baum mit fehlenden Zweigen](merkle-empty-hash.png) + +```javascript +// Berechnet eine Ebene im Baum eines Hash-Arrays, indem der Hash +// jedes Paares in der Sequenz genommen wird +const oneLevelUp = (inputArray) => { + var result = [] + var inp = [...inputArray] // Um das Überschreiben der Eingabe zu vermeiden // Fügt bei Bedarf einen leeren Wert hinzu (wir benötigen alle Blätter // als Paare) + + if (inp.length % 2 === 1) inp.push(empty) + + for (var i = 0; i < inp.length; i += 2) + result.push(pairHash(inp[i], inp[i + 1])) + + return result +} // oneLevelUp +``` + +Diese Funktion "klettert" eine Ebene im Merkle-Baum hoch, indem sie die Wertepaare auf der aktuellen Ebene hasht. Beachten Sie, dass dies nicht die effizienteste Implementierung ist. Wir hätten das Kopieren der Eingabe vermeiden und einfach `hashEmpty` an der entsprechenden Stelle in der Schleife hinzufügen können, aber dieser Code ist auf Lesbarkeit optimiert. + +```javascript +const getMerkleRoot = (inputArray) => { + var result + + result = [...inputArray] // Klettere den Baum hinauf, bis es nur noch einen Wert gibt, das ist die // Root. // // Wenn eine Ebene eine ungerade Anzahl von Einträgen hat, fügt der // Code in oneLevelUp einen leeren Wert hinzu. Wenn wir also zum Beispiel // 10 Blätter haben, haben wir 5 Zweige in der zweiten Ebene, 3 // Zweige in der dritten, 2 in der vierten und die Root ist die fünfte + + while (result.length > 1) result = oneLevelUp(result) + + return result[0] +} +``` + +Um die Wurzel zu bekommen, mache so lange, bis nur noch ein Wert übrig ist. + +#### Erstellen eines Merkle-Beweises {#creating-a-merkle-proof} + +Ein Merkle-Beweis besteht aus den Werten, die zusammen mit dem zu beweisenden Wert gehasht werden müssen, um die Merkle-Wurzel zu erhalten. Der zu beweisende Wert ist oft aus anderen Daten verfügbar, daher ziehe ich es vor, ihn separat und nicht als Teil des Codes bereitzustellen. + +```javascript +// Ein Merkle-Beweis besteht aus dem Wert der Liste der Einträge, mit denen +// gehasht werden soll. Da wir eine symmetrische Hash-Funktion verwenden, benötigen wir +// den Speicherort des Elements nicht, um den Beweis zu verifizieren, sondern nur, um ihn zu erstellen +const getMerkleProof = (inputArray, n) => { +    var result = [], currentLayer = [...inputArray], currentN = n + +    // Bis wir die Spitze erreichen +    while (currentLayer.length > 1) { +        // Keine Ebenen mit ungerader Länge +        if (currentLayer.length % 2) +            currentLayer.push(empty) + +        result.push(currentN % 2 +               // Wenn currentN ungerade ist, füge den Wert davor zum Beweis hinzu +            ? currentLayer[currentN-1] +               // Wenn es gerade ist, füge den Wert danach hinzu +            : currentLayer[currentN+1]) + +``` + +Wir hashen `(v[0],v[1])`, `(v[2],v[3])` usw. Für gerade Werte benötigen wir also den nächsten, für ungerade Werte den vorherigen Wert. + +```javascript +        // Auf die nächste Ebene aufsteigen +        currentN = Math.floor(currentN/2) +        currentLayer = oneLevelUp(currentLayer) +    }   // while currentLayer.length > 1 + +    return result +}   // getMerkleProof +``` + +### Onchain-Code {#onchain-code} + +Schließlich haben wir den Code, der den Beweis überprüft. Der Onchain-Code ist in [Solidity](https://docs.soliditylang.org/en/v0.8.11/) geschrieben. Die Optimierung ist hier sehr viel wichtiger, weil Gas relativ teuer ist. + +```solidity +//SPDX-License-Identifier: Public Domain +pragma solidity ^0.8.0; + +import "hardhat/console.sol"; +``` + +Ich habe dies mit der [Hardhat-Entwicklungsumgebung](https://hardhat.org/) geschrieben, die es uns ermöglicht, während der Entwicklung [Konsolenausgaben von Solidity](https://hardhat.org/docs/cookbook/debug-logs) zu haben. + +```solidity + +contract MerkleProof { +    uint merkleRoot; + +    function getRoot() public view returns (uint) { +      return merkleRoot; +    } + +    // Äußerst unsicher, im Produktionscode muss der Zugriff auf +    // diese Funktion STRENG limitiert sein, wahrscheinlich auf einen +    // Besitzer +    function setRoot(uint _merkleRoot) external { +      merkleRoot = _merkleRoot; +    }   // setRoot +``` + +Set- und Get-Funktionen für die Merkle-Wurzel. Jeden die Merkle-Root aktualisieren zu lassen, ist eine _extrem schlechte Idee_ in einem Produktionssystem. Ich tue es hier der Einfachheit halber für den Beispielcode. **Tun Sie das nicht auf einem System, bei dem die Datenintegrität wirklich wichtig ist**. + +```solidity +    function hash(uint _a) internal pure returns(uint) { +      return uint(keccak256(abi.encode(_a))); +    } + +    function pairHash(uint _a, uint _b) internal pure returns(uint) { +      return hash(hash(_a) ^ hash(_b)); +    } +``` + +Diese Funktion erzeugt einen Paar-Hash. Es ist nur die Solidity-Übersetzung des JavaScript-Codes für `hash` und `pairHash`. + +**Hinweis:** Dies ist ein weiterer Fall der Optimierung auf Lesbarkeit. Basierend auf [der Funktionsdefinition](https://www.tutorialspoint.com/solidity/solidity_cryptographic_functions.htm) wäre es möglich, die Daten als [`bytes32`](https://docs.soliditylang.org/en/v0.5.3/types.html#fixed-size-byte-arrays)-Wert zu speichern und die Konvertierungen zu vermeiden. + +```solidity +    // Überprüfen eines Merkle-Beweises +    function verifyProof(uint _value, uint[] calldata _proof) +        public view returns (bool) { +      uint temp = _value; +      uint i; + +      for(i=0; i<_proof.length; i++) { +        temp = pairHash(temp, _proof[i]); +      } + +      return temp == merkleRoot; +    } + +}  // MarkleProof +``` + +In mathematischer Notation sieht die Verifizierung eines Merkle-Beweises so aus: `H(proof_n, H(proof_n-1, H(proof_n-2, ...` H(proof_1, H(proof_0, value))...)))`. Dieser Code implementiert sie. + +## Merkle-Beweise und Rollups vertragen sich nicht {#merkle-proofs-and-rollups} + +Merkle-Beweise funktionieren nicht gut mit [Rollups](/developers/docs/scaling/#rollups). Der Grund dafür ist, dass Rollups alle Transaktionsdaten auf L1 schreiben, aber auf L2 verarbeiten. Die Kosten für die Übermittlung eines Merkle-Beweises mit einer Transaktion belaufen sich auf durchschnittlich 638 Gas pro Ebene (derzeit kostet ein Byte in Call Data 16 Gas, wenn es nicht Null ist, und 4, wenn es Null ist). Bei einer Datenmenge von 1024 Wörtern sind für einen Merkle-Beweis zehn Ebenen erforderlich, also insgesamt 6380 Gas. + +Wenn man sich zum Beispiel [Optimism](https://public-grafana.optimism.io/d/9hkhMxn7z/public-dashboard?orgId=1&refresh=5m) ansieht, kostet das Schreiben von L1-Gas etwa 100 Gwei und L2-Gas 0,001 Gwei (das ist der normale Preis, er kann bei Überlastung steigen). Für die Kosten von einem L1 Gas können wir also hunderttausend Gas für die L2 Verarbeitung ausgeben. Wenn wir davon ausgehen, dass wir den Speicher nicht überschreiben, bedeutet das, dass wir für den Preis von einem L1 Gas etwa fünf Wörter in den L2 Speicher schreiben können. Für einen einzelnen Merkle-Beweis können wir die gesamten 1024 Wörter in den Speicher schreiben (vorausgesetzt, sie können von Anfang an onchain berechnet werden, anstatt in einer Transaktion bereitgestellt zu werden) und haben immer noch den größten Teil des Gases übrig. + +## Fazit {#conclusion} + +Im echten Leben werden Sie Merkle-Bäume vielleicht nie selbst implementieren. Es gibt bekannte und geprüfte Bibliotheken, die Sie verwenden können, wobei es im Allgemeinen nicht ratsam ist, kryptographische Primitive selbst zu implementieren. Aber ich hoffe, dass Sie jetzt die Merkle-Beweise besser verstehen und entscheiden können, wann es sich lohnt, sie einzusetzen. + +Beachten Sie, dass Merkle-Beweise zwar die _Integrität_, nicht aber die _Verfügbarkeit_ erhalten. Zu wissen, dass niemand sonst Ihre Assets nehmen kann, ist ein schwacher Trost, wenn der Datenspeicher beschließt, den Zugriff zu verweigern, und Sie auch keinen Merkle-Baum erstellen können, um darauf zuzugreifen. Merkle-Bäume werden daher am besten mit einer Art dezentralem Speicher wie IPFS verwendet. + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). diff --git a/public/content/translations/de/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md b/public/content/translations/de/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md new file mode 100644 index 00000000000..5d3ac6b05f7 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md @@ -0,0 +1,151 @@ +--- +title: "Überwachung von Geth mit InfluxDB und Grafana" +description: "Richten Sie die Überwachung für Ihren Geth-Node mit InfluxDB und Grafana ein, um die Leistung zu verfolgen und Probleme zu identifizieren." +author: "Mario Havel" +tags: ["clients", "nodes"] +skill: intermediate +lang: de +published: 2021-01-13 +--- + +Dieses Tutorial hilft Ihnen dabei, die Überwachung für Ihren Geth-Node einzurichten, damit Sie dessen Leistung besser verstehen und potenzielle Probleme erkennen können. + +## Voraussetzungen {#prerequisites} + +- Sie sollten bereits eine Instanz von Geth ausführen. +- Die meisten Schritte und Beispiele sind für eine Linux-Umgebung, grundlegende Terminalkenntnisse sind dabei hilfreich. +- Sehen Sie sich diesen Videoüberblick über die Metrik-Suite von Geth an: [Monitoring an Ethereum infrastructure by Péter Szilágyi](https://www.youtube.com/watch?v=cOBab8IJMYI). + +## Überwachungs-Stack {#monitoring-stack} + +Ein Ethereum-Client sammelt eine Menge Daten, die in Form einer chronologischen Datenbank gelesen werden können. Um die Überwachung zu erleichtern, können Sie diese in eine Datenvisualisierungssoftware einspeisen. Es gibt mehrere verfügbare Optionen: + +- [Prometheus](https://prometheus.io/) (Pull-Modell) +- [InfluxDB](https://www.influxdata.com/get-influxdb/) (Push-Modell) +- [Telegraf](https://www.influxdata.com/get-influxdb/) +- [Grafana](https://www.grafana.com/) +- [Datadog](https://www.datadoghq.com/) +- [Chronograf](https://www.influxdata.com/time-series-platform/chronograf/) + +Es gibt auch [Geth Prometheus Exporter](https://github.com/hunterlong/gethexporter), eine mit InfluxDB und Grafana vorkonfigurierte Option. + +In diesem Tutorial richten wir Ihren Geth-Client ein, um Daten an InfluxDB zur Erstellung einer Datenbank zu senden und mit Grafana eine Diagrammvisualisierung der Daten zu erstellen. Die manuelle Durchführung wird Ihnen helfen, den Prozess besser zu verstehen, ihn zu ändern und in verschiedenen Umgebungen bereitzustellen. + +## Einrichten von InfluxDB {#setting-up-influxdb} + +Lassen Sie uns zunächst InfluxDB herunterladen und installieren. Verschiedene Download-Optionen finden Sie auf der [Influxdata-Release-Seite](https://portal.influxdata.com/downloads/). Wählen Sie die für Ihre Umgebung passende aus. +Sie können es auch aus einem [Repository](https://repos.influxdata.com/) installieren. Zum Beispiel in einer Debian-basierten Distribution: + +``` +curl -tlsv1.3 --proto =https -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add +source /etc/lsb-release +echo "deb https://repos.influxdata.com/${DISTRIB_ID,,} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list +sudo apt update +sudo apt install influxdb -y +sudo systemctl enable influxdb +sudo systemctl start influxdb +sudo apt install influxdb-client +``` + +Nach der erfolgreichen Installation von InfluxDB, stellen Sie sicher, dass es im Hintergrund läuft. Standardmäßig ist es unter `localhost:8086` erreichbar. +Bevor Sie den `Influx`-Client verwenden, müssen Sie einen neuen Benutzer mit Administratorrechten erstellen. Dieser Benutzer dient der übergeordneten Verwaltung, der Erstellung von Datenbanken und Benutzern. + +``` +curl -XPOST "http://localhost:8086/query" --data-urlencode "q=CREATE USER username WITH PASSWORD 'password' WITH ALL PRIVILEGES" +``` + +Jetzt können Sie den Influx-Client verwenden, um mit diesem Benutzer die [InfluxDB-Shell](https://docs.influxdata.com/influxdb/v1.8/tools/shell/) zu betreten. + +``` +influx -username 'username' -password 'password' +``` + +Indem Sie direkt mit InfluxDB in seiner Shell kommunizieren, können Sie eine Datenbank und einen Benutzer für Geth-Metriken erstellen. + +``` +create database geth +create user geth with password choosepassword +``` + +Überprüfen Sie die erstellten Einträge mit: + +``` +show databases +show users +``` + +Verlassen Sie die InfluxDB-Shell. + +``` +exit +``` + +InfluxDB läuft und ist konfiguriert, um Metriken von Geth zu speichern. + +## Geth vorbereiten {#preparing-geth} + +Nachdem die Datenbank eingerichtet ist, müssen wir die Metrikerfassung in Geth aktivieren. Achten Sie auf `METRICS AND STATS OPTIONS` in `geth --help`. Dort finden Sie mehrere Optionen, in diesem Fall möchten wir, dass Geth Daten in InfluxDB pusht. +Die Grundeinrichtung gibt den Endpunkt an, an dem InfluxDB erreichbar ist, und die Authentifizierung für die Datenbank. + +``` +geth --metrics --metrics.influxdb --metrics.influxdb.endpoint "http://0.0.0.0:8086" --metrics.influxdb.username "geth" --metrics.influxdb.password "chosenpassword" +``` + +Diese Flags können an einen Befehl zum Starten des Clients angehängt oder in der Konfigurationsdatei gespeichert werden. + +Sie können überprüfen, ob Geth erfolgreich Daten pusht, indem Sie zum Beispiel die Metriken in der Datenbank auflisten. In der InfluxDB-Shell: + +``` +use geth +show measurements +``` + +## Einrichten von Grafana {#setting-up-grafana} + +Der nächste Schritt ist die Installation von Grafana, das die Daten grafisch interpretieren wird. Folgen Sie dem Installationsprozess für Ihre Umgebung in der Grafana-Dokumentation. Stellen Sie sicher, dass Sie die OSS-Version installieren, wenn nicht anders gewünscht. +Beispielhafte Installationsschritte für Debian-Distributionen unter Verwendung des Repositorys: + +``` +curl -tlsv1.3 --proto =https -sL https://packages.grafana.com/gpg.key | sudo apt-key add - +echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee -a /etc/apt/sources.list.d/grafana.list +sudo apt update +sudo apt install grafana +sudo systemctl enable grafana-server +sudo systemctl start grafana-server +``` + +Wenn Grafana läuft, sollte es unter `localhost:3000` erreichbar sein. +Verwenden Sie Ihren bevorzugten Browser, um auf diesen Pfad zuzugreifen, und melden Sie sich dann mit den Standard-Anmeldeinformationen an (Benutzer: `admin` und Passwort: `admin`). Wenn Sie dazu aufgefordert werden, ändern Sie das Standardpasswort und speichern Sie es. + +![](./grafana1.png) + +Sie werden zur Grafana-Startseite weitergeleitet. Richten Sie zunächst Ihre Quelldaten ein. Klicken Sie auf das Konfigurationssymbol in der linken Leiste und wählen Sie "Datenquellen" aus. + +![](./grafana2.png) + +Es sind noch keine Datenquellen erstellt worden. Klicken Sie auf "Datenquelle hinzufügen", um eine zu definieren. + +![](./grafana3.png) + +Wählen Sie für dieses Setup "InfluxDB" aus und fahren Sie fort. + +![](./grafana4.png) + +Die Konfiguration der Datenquelle ist ziemlich einfach, wenn Sie die Tools auf demselben Rechner ausführen. Sie müssen die InfluxDB-Adresse und die Details für den Zugriff auf die Datenbank festlegen. Beachten Sie die Abbildung unten. + +![](./grafana5.png) + +Wenn alles vollständig ist und InfluxDB erreichbar ist, klicken Sie auf "Speichern und testen" und warten Sie, bis die Bestätigung erscheint. + +![](./grafana6.png) + +Grafana ist jetzt so eingerichtet, dass es Daten aus InfluxDB lesen kann. Jetzt müssen Sie ein Dashboard erstellen, das sie interpretiert und anzeigt. Dashboard-Eigenschaften sind in JSON-Dateien kodiert, die von jedermann erstellt und einfach importiert werden können. Klicken Sie in der linken Leiste auf "Erstellen und Importieren". + +![](./grafana7.png) + +Für ein Geth-Überwachungs-Dashboard kopieren Sie die ID von [diesem Dashboard](https://grafana.com/grafana/dashboards/13877/) und fügen Sie sie auf der "Importseite" in Grafana ein. Nach dem Speichern des Dashboards sollte es so aussehen: + +![](./grafana8.png) + +Sie können Ihre Dashboards ändern. Jedes Panel kann bearbeitet, verschoben, entfernt oder hinzugefügt werden. Sie können Ihre Konfigurationen ändern. Es liegt ganz bei Ihnen! Um mehr darüber zu erfahren, wie Dashboards funktionieren, lesen Sie die [Dokumentation von Grafana](https://grafana.com/docs/grafana/latest/dashboards/). +Sie könnten auch an [Alerting](https://grafana.com/docs/grafana/latest/alerting/) interessiert sein. Damit können Sie Warnmeldungen für den Fall einrichten, dass Metriken bestimmte Werte erreichen. Verschiedene Kommunikationskanäle werden unterstützt. diff --git a/public/content/translations/de/developers/tutorials/nft-minter/index.md b/public/content/translations/de/developers/tutorials/nft-minter/index.md new file mode 100644 index 00000000000..4453f4a7027 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/nft-minter/index.md @@ -0,0 +1,868 @@ +--- +title: NFT-Minter Tutorial +description: In diesem Tutorial erstellst du einen NFT-Minter und lernst, wie man eine Full-Stack-Dapp erstellt, indem du deinen Smart Contract mit einem React-Frontend unter Verwendung von MetaMask und Web3-Tools verbindest. +author: "smudgil" +tags: ["solidity", "NFT", "alchemy", "smart contracts", "frontend", "Pinata"] +skill: intermediate +lang: de +published: 2021-10-06 +--- + +Eine der größten Herausforderungen für Entwickler mit einem Web2-Hintergrund ist herauszufinden, wie sie ihren Smart Contract mit einem Frontend Projekt verbinden und mit ihm interagieren können. + +Durch den Bau eines NFT-Minters - eine einfache Benutzeroberfläche, auf welcher ein Link den Nutzer zu seinen digitalen Vermögenswerten führt, ein Titel und eine Beschreibung - werden Sie Folgendes lernen: + +- Verbinden von MetaMask mit Ihrem Frontend-Projekt +- Rufen von Smart-Contract Methoden von Ihrem Frontend +- Transaktionen mit MetaMask signieren + +In diesem Tutorial werden wir [React](https://react.dev/) als unser Frontend-Framework verwenden. Da sich dieses Tutorial in erster Linie auf die Web3-Entwicklung konzentriert, werden wir nicht viel Zeit darauf verwenden, die React Grundlagen zu behandeln. Stattdessen konzentrieren wir uns darauf, Funktionalität in unser Projekt zu bringen. + +Als Voraussetzung solltest du ein grundlegendes Verständnis von React haben – du solltest wissen, wie Komponenten, Props, useState/useEffect und grundlegende Funktionsaufrufe funktionieren. Wenn du noch nie von diesen Begriffen gehört hast, solltest du dir dieses [Einführungstutorial zu React](https://react.dev/learn/tutorial-tic-tac-toe) ansehen. Für diejenigen, die eher visuell lernen, empfehlen wir diese ausgezeichnete Videoreihe [Full Modern React Tutorial](https://www.youtube.com/playlist?list=PL4cUxeGkcC9gZD-Tvwfod2gaISzfRiP9d) von Net Ninja. + +Und wenn Sie sich noch nicht angemeldet haben, dann benötigen Sie auf jeden Fall einen Alchemy Account, um dieses Tutorial zu beenden und um etwas auf der Blockchain bauen zu können. Registriere dich [hier](https://alchemy.com/) für ein kostenloses Konto. + +Lass uns ohne weiteres starten! + +## NFTs erstellen 101 {#making-nfts-101} + +Bevor wir uns überhaupt einen Code anschauen, ist es wichtig zu verstehen, wie das erstellen von NFTs überhaupt funktioniert. Es benötigt dafür zwei Schritte: + +### Einen NFT-Smart-Contract auf der Ethereum-Blockchain veröffentlichen {#publish-nft} + +Der größte Unterschied zwischen den beiden NFT smart Kontrakt Standards ist, dass der ERC-1155 ein Multi-Token Standard ist und eine Handvoll Funktionalitäten beinhaltet, wobei man mit dem ERC-721, welcher ein Single-Token Standard ist, nur eine Token-Transaktion gleichzeitig ausführen kann. + +### Die Minting-Funktion aufrufen {#minting-function} + +Normalerweise verlangt diese Minting-Funktion, dass du zwei Variablen als Parameter übergibst: erstens den `recipient` (Empfänger), der die Adresse angibt, die deinen frisch geminteten NFT erhält, und zweitens die `tokenURI` des NFTs, eine Zeichenfolge, die zu einem JSON-Dokument aufgelöst wird, das die Metadaten des NFTs beschreibt. + +Die NFT Metadaten sind das, was Leben hineinbringt, welches Ihnen erlaubt Eigenschaften zu haben, wie zum Beispiel: Namen, eine Beschreibung, ein Bild (oder andere digitale Vermögenswerte), und andere Attribute. [Hier ist ein Beispiel für eine tokenURI](https://gateway.pinata.cloud/ipfs/QmSvBcb4tjdFpajGJhbFAWeK3JAxCdNQLQtr6ZdiSi42V2), die die Metadaten eines NFT enthält. + +In diesem Tutorial werden wir uns auf den 2 Teil fokussieren, das Aufrufen von einer bereits existierenden NFT smart Kontrakt Prägungs Funktion, indem wir unsere React UI benutzen. + +[Hier ist ein Link](https://ropsten.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE) zu dem ERC-721-NFT-Smart-Contract, den wir in diesem Tutorial aufrufen werden. Wenn du lernen möchtest, wie wir ihn erstellt haben, empfehlen wir dir dringend unser anderes Tutorial [„Wie man einen NFT erstellt“](https://www.alchemy.com/docs/how-to-create-an-nft). + +Cool, nun verstehen wir wie die Erstellung von NFTS funktioniert, lass uns nun unsere Startdateien klonen! + +## Die Starter-Dateien klonen {#clone-the-starter-files} + +Gehe zuerst zum [nft-minter-tutorial GitHub-Repository](https://github.com/alchemyplatform/nft-minter-tutorial), um die Starter-Dateien für dieses Projekt zu erhalten. Klone dieses Repository in deine lokale Umgebung. + +Wenn du dieses geklonte `nft-minter-tutorial`-Repository öffnest, wirst du feststellen, dass es zwei Ordner enthält: `minter-starter-files` und `nft-minter`. + +- `minter-starter-files` enthält die Starter-Dateien (im Wesentlichen die React-UI) für dieses Projekt. In diesem Tutorial **werden wir in diesem Verzeichnis arbeiten**, während du lernst, wie du diese Benutzeroberfläche zum Leben erweckst, indem du sie mit deiner Ethereum-Wallet und einem NFT-Smart-Contract verbindest. +- `nft-minter` enthält das gesamte, vollständige Tutorial und dient dir als **Referenz**, **falls du nicht weiterkommst.** + +Öffne als Nächstes deine Kopie von `minter-starter-files` in deinem Code-Editor und navigiere dann in deinen `src`-Ordner. + +Der gesamte Code, den wir schreiben werden, befindet sich im Ordner `src`. Wir werden die `Minter.js`-Komponente bearbeiten und zusätzliche JavaScript-Dateien schreiben, um unserem Projekt Web3-Funktionalität zu verleihen. + +## Schritt 2: Unsere Starter-Dateien ansehen {#step-2-check-out-our-starter-files} + +Bevor wir mit dem Programmieren beginnen, ist es wichtig, sich anzusehen, was uns in den Starter-Dateien bereits zur Verfügung gestellt wird. + +### Dein React-Projekt zum Laufen bringen {#get-your-react-project-running} + +Beginnen wir damit, das React-Projekt in unserem Browser auszuführen. Das Schöne an React ist, dass alle Änderungen, die wir speichern, live in unserem Browser aktualisiert werden, sobald unser Projekt im Browser läuft. + +Um das Projekt zum Laufen zu bringen, navigiere zum Stammverzeichnis des Ordners `minter-starter-files` und führe `npm install` in deinem Terminal aus, um die Abhängigkeiten des Projekts zu installieren: + +```bash +cd minter-starter-files +npm install +``` + +Sobald die Installation abgeschlossen ist, führe `npm start` in deinem Terminal aus: + +```bash +npm start +``` + +Dadurch sollte sich http://localhost:3000/ in deinem Browser öffnen, wo du das Frontend für unser Projekt siehst. Es sollte aus 3 Feldern bestehen: einem Feld zur Eingabe eines Links zum Asset deines NFTs, einem Feld zur Eingabe des Namens deines NFTs und einem Feld zur Angabe einer Beschreibung. + +Wenn du versuchst, auf die Schaltflächen „Wallet verbinden“ oder „NFT minten“ zu klicken, wirst du feststellen, dass sie nicht funktionieren – das liegt daran, dass wir ihre Funktionalität noch programmieren müssen! :\) + +### Die Minter.js-Komponente {#minter-js} + +**HINWEIS:** Stelle sicher, dass du dich im Ordner `minter-starter-files` und nicht im Ordner `nft-minter` befindest! + +Gehen wir zurück in den `src`-Ordner in unserem Editor und öffnen die Datei `Minter.js`. Es ist sehr wichtig, dass wir alles in dieser Datei verstehen, da es sich um die primäre React-Komponente handelt, an der wir arbeiten werden. + +Oben in dieser Datei befinden sich unsere Zustandsvariablen, die wir nach bestimmten Ereignissen aktualisieren werden. + +```javascript +//Zustandsvariablen +const [walletAddress, setWallet] = useState("") +const [status, setStatus] = useState("") +const [name, setName] = useState("") +const [description, setDescription] = useState("") +const [url, setURL] = useState("") +``` + +Noch nie von React-Zustandsvariablen oder State-Hooks gehört? Sieh dir [diese](https://legacy.reactjs.org/docs/hooks-state.html) Dokumentation an. + +Hier ist, was die einzelnen Variablen darstellen: + +- `walletAddress` - eine Zeichenfolge, die die Wallet-Adresse des Benutzers speichert +- `status` - eine Zeichenfolge, die eine Nachricht enthält, die am unteren Rand der Benutzeroberfläche angezeigt wird +- `name` - eine Zeichenfolge, die den Namen des NFT speichert +- `description` - eine Zeichenfolge, die die Beschreibung des NFT speichert +- `url` - eine Zeichenfolge, die ein Link zum digitalen Asset des NFT ist + +Nach den Zustandsvariablen siehst du drei nicht implementierte Funktionen: `useEffect`, `connectWalletPressed` und `onMintPressed`. Du wirst feststellen, dass alle diese Funktionen `async` sind, weil wir in ihnen asynchrone API-Aufrufe tätigen werden! Ihre Namen sind gleichbedeutend mit ihren Funktionalitäten: + +```javascript +useEffect(async () => { + //TODO: implementieren +}, []) + +const connectWalletPressed = async () => { + //TODO: implementieren +} + +const onMintPressed = async () => { + //TODO: implementieren +} +``` + +- [`useEffect`](https://legacy.reactjs.org/docs/hooks-effect.html) – dies ist ein React-Hook, der aufgerufen wird, nachdem deine Komponente gerendert wurde. Da ihm ein leeres Array `[]` als Prop übergeben wird (siehe Zeile 3), wird er nur beim _ersten_ Rendern der Komponente aufgerufen. Hier rufen wir unseren Wallet-Listener und eine weitere Wallet-Funktion auf, um unsere Benutzeroberfläche zu aktualisieren und anzuzeigen, ob eine Wallet bereits verbunden ist. +- `connectWalletPressed` – diese Funktion wird aufgerufen, um die MetaMask-Wallet des Benutzers mit unserer Dapp zu verbinden. +- `onMintPressed` – diese Funktion wird aufgerufen, um den NFT des Benutzers zu minten. + +Gegen Ende dieser Datei haben wir die Benutzeroberfläche unserer Komponente. Wenn du diesen Code sorgfältig durchgehst, wirst du feststellen, dass wir unsere `url`-, `name`- und `description`-Zustandsvariablen aktualisieren, wenn sich die Eingabe in den entsprechenden Textfeldern ändert. + +Du wirst auch sehen, dass `connectWalletPressed` und `onMintPressed` aufgerufen werden, wenn die Schaltflächen mit den IDs `mintButton` bzw. `walletButton` angeklickt werden. + +```javascript +//die Benutzeroberfläche unserer Komponente +return ( +
+ + +

+

🧙‍♂️ Alchemy NFT Minter

+

+ Füge einfach den Link, den Namen und die Beschreibung deines Assets hinzu und drücke dann auf „Minten“. +

+
+

🖼 Link zum Asset:

+ setURL(event.target.value)} + /> +

🤔 Name:

+ setName(event.target.value)} + /> +

✍️ Beschreibung:

+ setDescription(event.target.value)} + /> +
+ +

{status}

+
+) +``` + +Schließlich wollen wir uns ansehen, wo diese Minter-Komponente hinzugefügt wird. + +Wenn du zur Datei `App.js` gehst, der Hauptkomponente in React, die als Container für alle anderen Komponenten fungiert, siehst du, dass unsere Minter-Komponente in Zeile 7 eingefügt wird. + +**In diesem Tutorial werden wir nur die `Minter.js`-Datei bearbeiten und Dateien in unserem `src`-Ordner hinzufügen.** + +Nachdem wir nun verstanden haben, womit wir arbeiten, richten wir unsere Ethereum-Wallet ein! + +## Richte deine Ethereum-Wallet ein {#set-up-your-ethereum-wallet} + +Damit Benutzer mit deinem Smart Contract interagieren können, müssen sie ihre Ethereum-Wallet mit deiner Dapp verbinden. + +### MetaMask herunterladen {#download-metamask} + +In diesem Tutorial verwenden wir MetaMask, eine virtuelle Wallet im Browser, mit der Sie Ihre Ethereum-Kontoadresse verwalten können. Wenn du mehr darüber erfahren möchtest, wie Transaktionen auf Ethereum funktionieren, sieh dir [diese Seite](/developers/docs/transactions/) an. + +Sie können MetaMask [hier](https://metamask.io/download) kostenlos herunterladen und ein Konto erstellen. Wenn du ein Konto erstellst oder bereits eines hast, wechsle unbedingt zum „Ropsten Test Network“ oben rechts (damit wir nicht mit echtem Geld hantieren). + +### Ether von einem Faucet hinzufügen {#add-ether-from-faucet} + +Um unsere NFTs zu minten (oder Transaktionen auf der Ethereum-Blockchain zu signieren), benötigen wir einige gefälschte ETH. Um ETH zu erhalten, kannst du zum [Ropsten-Faucet](https://faucet.ropsten.be/) gehen und deine Ropsten-Kontoadresse eingeben und dann auf „Send Ropsten ETH“ klicken. Kurz darauf solltest du ETH in deinem MetaMask-Konto sehen! + +### Deinen Kontostand überprüfen {#check-your-balance} + +Um zu überprüfen, ob unser Guthaben vorhanden ist, machen wir eine [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)-Anfrage mit dem [Composer-Tool von Alchemy](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D). Dies gibt den Betrag an ETH in unserer Wallet zurück. Nachdem Sie die Adresse Ihres MetaMask-Kontos eingegeben und auf “Send Request” (Anforderung senden) geklickt haben, sollten Sie eine Antwort ähnlich der Folgenden erhalten: + +```text +{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"} +``` + +**HINWEIS:** Dieses Ergebnis ist in Wei, nicht in ETH. Wei ist die kleinste Einheit von Ether. Die Umrechnung von Wei in ETH ist: 1 ETH = 10¹⁸ Wei. Wenn wir also 0xde0b6b3a7640000 in eine Dezimalzahl umwandeln, erhalten wir 1\*10¹⁸, was 1 ETH entspricht. + +Puh! Unser Falschgeld ist da! + +## MetaMask mit deiner Benutzeroberfläche verbinden {#connect-metamask-to-your-UI} + +Nachdem unsere MetaMask-Wallet nun eingerichtet ist, verbinden wir unsere Dapp damit! + +Da wir uns an das [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)-Paradigma halten wollen, werden wir eine separate Datei erstellen, die unsere Funktionen zur Verwaltung der Logik, der Daten und der Regeln unserer Dapp enthält, und diese Funktionen dann an unser Frontend (unsere Minter.js-Komponente) übergeben. + +### Die `connectWallet`-Funktion {#connect-wallet-function} + +Dazu erstellen wir einen neuen Ordner namens `utils` in deinem `src`-Verzeichnis und fügen eine Datei namens `interact.js` hinzu, die alle unsere Interaktionsfunktionen für Wallets und Smart Contracts enthalten wird. + +In unserer `interact.js`-Datei schreiben wir eine `connectWallet`-Funktion, die wir dann in unsere `Minter.js`-Komponente importieren und aufrufen. + +Füge in deine `interact.js`-Datei Folgendes ein + +```javascript +export const connectWallet = async () => { + if (window.ethereum) { + try { + const addressArray = await window.ethereum.request({ + method: "eth_requestAccounts", + }) + const obj = { + status: "👆🏽 Schreibe eine Nachricht in das Textfeld oben.", + address: addressArray[0], + } + return obj + } catch (err) { + return { + address: "", + status: "😥 " + err.message, + } + } + } else { + return { + address: "", + status: ( + +

+ {" "} + 🦊 + Du musst MetaMask, eine virtuelle Ethereum-Wallet, in deinem + Browser installieren. + +

+
+ ), + } + } +} +``` + +Schauen wir uns an, was dieser Code bewirkt: + +Zuerst prüft unsere Funktion, ob `window.ethereum` in deinem Browser aktiviert ist. + +`window.ethereum` ist eine globale API, die von MetaMask und anderen Wallet-Anbietern eingeschleust wird und es Websites ermöglicht, die Ethereum-Konten von Benutzern anzufordern. Wenn dies genehmigt wird, kann sie Daten aus den Blockchains lesen, mit denen der Benutzer verbunden ist, und dem Benutzer vorschlagen, Nachrichten und Transaktionen zu signieren. Weitere Informationen findest du in der [MetaMask-Dokumentation](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents)! + +Wenn `window.ethereum` _nicht_ vorhanden ist, bedeutet das, dass MetaMask nicht installiert ist. Dies führt dazu, dass ein JSON-Objekt zurückgegeben wird, bei dem die zurückgegebene `address` eine leere Zeichenfolge ist und das `status`-JSX-Objekt meldet, dass der Benutzer MetaMask installieren muss. + +**Die meisten Funktionen, die wir schreiben, geben JSON-Objekte zurück, mit denen wir unsere Zustandsvariablen und die Benutzeroberfläche aktualisieren können.** + +Wenn `window.ethereum` jedoch vorhanden _ist_, dann wird es interessant. + +Mithilfe einer try/catch-Schleife versuchen wir, eine Verbindung zu MetaMask herzustellen, indem wir [`window.ethereum.request({ method: "eth_requestAccounts" });`](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts) aufrufen. Der Aufruf dieser Funktion öffnet MetaMask im Browser, woraufhin der Benutzer aufgefordert wird, seine Wallet mit deiner Dapp zu verbinden. + +- Wenn der Benutzer die Verbindung herstellt, gibt `method: "eth_requestAccounts"` ein Array zurück, das alle Adressen der Benutzerkonten enthält, die mit der Dapp verbunden sind. Insgesamt gibt unsere `connectWallet`-Funktion ein JSON-Objekt zurück, das die _erste_ `address` in diesem Array (siehe Zeile 9) und eine `status`-Nachricht enthält, die den Benutzer auffordert, eine Nachricht an den Smart Contract zu schreiben. +- Wenn der Benutzer die Verbindung ablehnt, enthält das JSON-Objekt eine leere Zeichenfolge für die zurückgegebene `address` und eine `status`-Nachricht, die widerspiegelt, dass der Benutzer die Verbindung abgelehnt hat. + +### Hinzufügen der connectWallet-Funktion zur Minter.js-UI-Komponente {#add-connect-wallet} + +Nachdem wir diese `connectWallet`-Funktion geschrieben haben, verbinden wir sie mit unserer `Minter.js.`-Komponente. + +Zuerst müssen wir unsere Funktion in unsere `Minter.js`-Datei importieren, indem wir `import { connectWallet } from "./utils/interact.js";` am Anfang der `Minter.js`-Datei hinzufügen. Deine ersten 11 Zeilen von `Minter.js` sollten nun so aussehen: + +```javascript +import { useEffect, useState } from "react"; +import { connectWallet } from "./utils/interact.js"; + +const Minter = (props) => { + + //Zustandsvariablen + const [walletAddress, setWallet] = useState(""); + const [status, setStatus] = useState(""); + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const [url, setURL] = useState(""); +``` + +Dann rufen wir in unserer `connectWalletPressed`-Funktion unsere importierte `connectWallet`-Funktion auf, wie folgt: + +```javascript +const connectWalletPressed = async () => { + const walletResponse = await connectWallet() + setStatus(walletResponse.status) + setWallet(walletResponse.address) +} +``` + +Fällt dir auf, wie der größte Teil unserer Funktionalität von unserer `Minter.js`-Komponente in die `interact.js`-Datei abstrahiert wird? Dies geschieht, damit wir dem M-V-C-Paradigma entsprechen! + +In `connectWalletPressed` machen wir einfach einen Await-Aufruf an unsere importierte `connectWallet`-Funktion, und mit ihrer Antwort aktualisieren wir unsere `status`- und `walletAddress`-Variablen über ihre State-Hooks. + +Speichern wir nun beide Dateien, `Minter.js` und `interact.js`, und testen unsere Benutzeroberfläche. + +Öffne deinen Browser unter localhost:3000 und drücke die Schaltfläche „Wallet verbinden“ oben rechts auf der Seite. + +Wenn du MetaMask installiert hast, solltest du aufgefordert werden, deine Wallet mit deiner Dapp zu verbinden. Akzeptiere die Aufforderung, eine Verbindung herzustellen. + +Du solltest sehen, dass die Wallet-Schaltfläche nun anzeigt, dass deine Adresse verbunden ist. + +Als Nächstes versuche, die Seite neu zu laden ... Das ist seltsam. Unsere Wallet-Schaltfläche fordert uns auf, MetaMask zu verbinden, obwohl es bereits verbunden ist ... + +Aber keine Sorge! Das können wir leicht beheben, indem wir eine Funktion namens `getCurrentWalletConnected` implementieren, die prüft, ob eine Adresse bereits mit unserer Dapp verbunden ist, und unsere Benutzeroberfläche entsprechend aktualisiert! + +### Die getCurrentWalletConnected-Funktion {#get-current-wallet} + +Füge in deine `interact.js`-Datei die folgende `getCurrentWalletConnected`-Funktion ein: + +```javascript +export const getCurrentWalletConnected = async () => { + if (window.ethereum) { + try { + const addressArray = await window.ethereum.request({ + method: "eth_accounts", + }) + if (addressArray.length > 0) { + return { + address: addressArray[0], + status: "👆🏽 Schreibe eine Nachricht in das Textfeld oben.", + } + } else { + return { + address: "", + status: "🦊 Verbinde dich mit MetaMask über die Schaltfläche oben rechts.", + } + } + } catch (err) { + return { + address: "", + status: "😥 " + err.message, + } + } + } else { + return { + address: "", + status: ( + +

+ {" "} + 🦊 + Du musst MetaMask, eine virtuelle Ethereum-Wallet, in deinem + Browser installieren. + +

+
+ ), + } + } +} +``` + +Dieser Code ist der `connectWallet`-Funktion, die wir gerade geschrieben haben, _sehr_ ähnlich. + +Der Hauptunterschied besteht darin, dass wir anstelle der Methode `eth_requestAccounts`, die MetaMask für den Benutzer öffnet, um seine Wallet zu verbinden, hier die Methode `eth_accounts` aufrufen, die einfach ein Array zurückgibt, das die MetaMask-Adressen enthält, die derzeit mit unserer Dapp verbunden sind. + +Um diese Funktion in Aktion zu sehen, rufen wir sie in der `useEffect`-Funktion unserer `Minter.js`-Komponente auf. + +Wie bei `connectWallet` müssen wir diese Funktion aus unserer `interact.js`-Datei in unsere `Minter.js`-Datei importieren, und zwar so: + +```javascript +import { useEffect, useState } from "react" +import { + connectWallet, + getCurrentWalletConnected, //hier importieren +} from "./utils/interact.js" +``` + +Jetzt rufen wir sie einfach in unserer `useEffect`-Funktion auf: + +```javascript +useEffect(async () => { + const { address, status } = await getCurrentWalletConnected() + setWallet(address) + setStatus(status) +}, []) +``` + +Beachte, dass wir die Antwort unseres Aufrufs an `getCurrentWalletConnected` verwenden, um unsere `walletAddress`- und `status`-Zustandsvariablen zu aktualisieren. + +Nachdem du diesen Code hinzugefügt hast, versuche, unser Browserfenster zu aktualisieren. Die Schaltfläche sollte anzeigen, dass du verbunden bist, und eine Vorschau der Adresse deiner verbundenen Wallet anzeigen – auch nach dem Aktualisieren! + +### addWalletListener implementieren {#implement-add-wallet-listener} + +Der letzte Schritt in der Einrichtung unserer Dapp-Wallet ist die Implementierung des Wallet-Listeners, damit unsere Benutzeroberfläche aktualisiert wird, wenn sich der Zustand unserer Wallet ändert, z. B. wenn der Benutzer die Verbindung trennt oder das Konto wechselt. + +Füge in deine `Minter.js`-Datei eine Funktion `addWalletListener` hinzu, die wie folgt aussieht: + +```javascript +function addWalletListener() { + if (window.ethereum) { + window.ethereum.on("accountsChanged", (accounts) => { + if (accounts.length > 0) { + setWallet(accounts[0]) + setStatus("👆🏽 Schreibe eine Nachricht in das Textfeld oben.") + } else { + setWallet("") + setStatus("🦊 Verbinde dich mit MetaMask über die Schaltfläche oben rechts.") + } + }) + } else { + setStatus( +

+ {" "} + 🦊 + Du musst MetaMask, eine virtuelle Ethereum-Wallet, in deinem Browser installieren. + +

+ ) + } +} +``` + +Lass uns kurz aufschlüsseln, was hier passiert: + +- Zuerst prüft unsere Funktion, ob `window.ethereum` aktiviert ist (d. h. MetaMask ist installiert). + - Wenn nicht, setzen wir einfach unsere `status`-Zustandsvariable auf eine JSX-Zeichenfolge, die den Benutzer auffordert, MetaMask zu installieren. + - Wenn es aktiviert ist, richten wir den Listener `window.ethereum.on("accountsChanged")` in Zeile 3 ein, der auf Zustandsänderungen in der MetaMask-Wallet lauscht. Dazu gehören, wenn der Benutzer ein zusätzliches Konto mit der Dapp verbindet, Konten wechselt oder ein Konto trennt. Wenn mindestens ein Konto verbunden ist, wird die Zustandsvariable `walletAddress` als das erste Konto im `accounts`-Array aktualisiert, das vom Listener zurückgegeben wird. Andernfalls wird `walletAddress` als leere Zeichenfolge festgelegt. + +Schließlich müssen wir sie in unserer `useEffect`-Funktion aufrufen: + +```javascript +useEffect(async () => { + const { address, status } = await getCurrentWalletConnected() + setWallet(address) + setStatus(status) + + addWalletListener() +}, []) +``` + +Und voilà! Wir haben die Programmierung all unserer Wallet-Funktionalitäten abgeschlossen! Nachdem unsere Wallet nun eingerichtet ist, finden wir heraus, wie wir unser NFT minten können! + +## NFT-Metadaten 101 {#nft-metadata-101} + +Erinnerst du dich an die NFT-Metadaten, über die wir in Schritt 0 dieses Tutorials gesprochen haben? Sie erwecken einen NFT zum Leben und ermöglichen es ihm, Eigenschaften wie ein digitales Asset, einen Namen, eine Beschreibung und andere Attribute zu haben. + +Wir müssen diese Metadaten als JSON-Objekt konfigurieren und speichern, damit wir sie als `tokenURI`-Parameter übergeben können, wenn wir die `mintNFT`-Funktion unseres Smart Contracts aufrufen. + +Der Text in den Feldern „Link zum Asset“, „Name“, „Beschreibung“ wird die verschiedenen Eigenschaften der Metadaten unseres NFTs umfassen. Wir werden diese Metadaten als JSON-Objekt formatieren, aber es gibt ein paar Optionen, wo wir dieses JSON-Objekt speichern können: + +- Wir könnten sie auf der Ethereum-Blockchain speichern, was jedoch sehr teuer wäre. +- Wir könnten sie auf einem zentralisierten Server wie AWS oder Firebase speichern. Aber das würde unserem Dezentralisierungs-Ethos widersprechen. +- Wir könnten IPFS verwenden, ein dezentralisiertes Protokoll und Peer-to-Peer-Netzwerk zum Speichern und Teilen von Daten in einem verteilten Dateisystem. Da dieses Protokoll dezentralisiert und kostenlos ist, ist es unsere beste Option! + +Um unsere Metadaten auf IPFS zu speichern, verwenden wir [Pinata](https://pinata.cloud/), eine praktische IPFS-API und ein Toolkit. Im nächsten Schritt erklären wir genau, wie das geht! + +## Pinata verwenden, um deine Metadaten auf IPFS zu pinnen {#use-pinata-to-pin-your-metadata-to-IPFS} + +Wenn du noch kein [Pinata](https://pinata.cloud/)-Konto hast, registriere dich [hier](https://app.pinata.cloud/auth/signup) für ein kostenloses Konto und führe die Schritte zur Verifizierung deiner E-Mail und deines Kontos durch. + +### Erstelle deinen Pinata-API-Schlüssel {#create-pinata-api-key} + +Navigiere zur Seite [https://pinata.cloud/keys](https://pinata.cloud/keys), wähle dann oben die Schaltfläche „New Key“ (Neuer Schlüssel), setze das Admin-Widget auf „Enabled“ (Aktiviert) und benenne deinen Schlüssel. + +Dir wird dann ein Popup mit deinen API-Informationen angezeigt. Bewahre diese an einem sicheren Ort auf. + +Nachdem unser Schlüssel nun eingerichtet ist, fügen wir ihn zu unserem Projekt hinzu, damit wir ihn verwenden können. + +### Eine .env-Datei erstellen {#create-a-env} + +Wir können unseren Pinata-Schlüssel und das Geheimnis sicher in einer Umgebungsdatei speichern. Installieren wir das [dotenv-Paket](https://www.npmjs.com/package/dotenv) in deinem Projektverzeichnis. + +Öffne einen neuen Tab in deinem Terminal (getrennt von dem, auf dem der Localhost läuft) und stelle sicher, dass du dich im Ordner `minter-starter-files` befindest. Führe dann den folgenden Befehl in deinem Terminal aus: + +```text +npm install dotenv --save +``` + +Als Nächstes erstelle eine `.env`-Datei im Stammverzeichnis deiner `minter-starter-files`, indem du Folgendes in deiner Befehlszeile eingibst: + +```javascript +vim.env +``` + +Dadurch wird deine `.env`-Datei in vim (einem Texteditor) geöffnet. Um sie zu speichern, drücke „esc“ + „:“ + „q“ in dieser Reihenfolge auf deiner Tastatur. + +Navigiere als Nächstes in VSCode zu deiner `.env`-Datei und füge deinen Pinata-API-Schlüssel und dein API-Geheimnis hinzu, und zwar so: + +```text +REACT_APP_PINATA_KEY = +REACT_APP_PINATA_SECRET = +``` + +Speichere die Datei, und dann kannst du damit beginnen, die Funktion zum Hochladen deiner JSON-Metadaten auf IPFS zu schreiben! + +### pinJSONToIPFS implementieren {#pin-json-to-ipfs} + +Glücklicherweise hat Pinata eine [API speziell für das Hochladen von JSON-Daten auf IPFS](https://docs.pinata.cloud/api-reference/endpoint/ipfs/pin-json-to-ipfs#pin-json) und ein praktisches JavaScript-Beispiel mit Axios, das wir mit leichten Änderungen verwenden können. + +Erstellen wir in deinem `utils`-Ordner eine weitere Datei namens `pinata.js` und importieren dann unser Pinata-Geheimnis und den Schlüssel aus der .env-Datei wie folgt: + +```javascript +require("dotenv").config() +const key = process.env.REACT_APP_PINATA_KEY +const secret = process.env.REACT_APP_PINATA_SECRET +``` + +Füge als Nächstes den zusätzlichen Code von unten in deine `pinata.js`-Datei ein. Keine Sorge, wir werden aufschlüsseln, was alles bedeutet! + +```javascript +require("dotenv").config() +const key = process.env.REACT_APP_PINATA_KEY +const secret = process.env.REACT_APP_PINATA_SECRET + +const axios = require("axios") + +export const pinJSONToIPFS = async (JSONBody) => { + const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS` + //axios-POST-Anfrage an Pinata stellen ⬇️ + return axios + .post(url, JSONBody, { + headers: { + pinata_api_key: key, + pinata_secret_api_key: secret, + }, + }) + .then(function (response) { + return { + success: true, + pinataUrl: + "https://gateway.pinata.cloud/ipfs/" + response.data.IpfsHash, + } + }) + .catch(function (error) { + console.log(error) + return { + success: false, + message: error.message, + } + }) +} +``` + +Was genau macht dieser Code also? + +Zuerst importiert er [axios](https://www.npmjs.com/package/axios), einen Promise-basierten HTTP-Client für den Browser und node.js, den wir verwenden werden, um eine Anfrage an Pinata zu stellen. + +Dann haben wir unsere asynchrone Funktion `pinJSONToIPFS`, die einen `JSONBody` als Eingabe und den Pinata-API-Schlüssel und das Geheimnis in ihrem Header entgegennimmt, um eine POST-Anfrage an ihre `pinJSONToIPFS`-API zu stellen. + +- Wenn diese POST-Anfrage erfolgreich ist, gibt unsere Funktion ein JSON-Objekt zurück, bei dem der `success`-Boole’sche Wert auf „true“ gesetzt ist und die `pinataUrl` angibt, wo unsere Metadaten angeheftet wurden. Wir werden diese zurückgegebene `pinataUrl` als `tokenURI`-Eingabe für die Mint-Funktion unseres Smart Contracts verwenden. +- Wenn diese Post-Anfrage fehlschlägt, gibt unsere Funktion ein JSON-Objekt zurück, bei dem der `success`-Boole’sche Wert auf „false“ gesetzt ist und eine `message`-Zeichenfolge unseren Fehler weitergibt. + +Wie bei unseren `connectWallet`-Funktionsrückgabetypen geben wir JSON-Objekte zurück, damit wir ihre Parameter zur Aktualisierung unserer Zustandsvariablen und der Benutzeroberfläche verwenden können. + +## Lade deinen Smart Contract {#load-your-smart-contract} + +Nachdem wir nun eine Möglichkeit haben, unsere NFT-Metadaten über unsere `pinJSONToIPFS`-Funktion auf IPFS hochzuladen, benötigen wir eine Möglichkeit, eine Instanz unseres Smart Contracts zu laden, damit wir seine `mintNFT`-Funktion aufrufen können. + +Wie bereits erwähnt, werden wir in diesem Tutorial [diesen bestehenden NFT-Smart-Contract](https://ropsten.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE) verwenden. Wenn du jedoch lernen möchtest, wie wir ihn erstellt haben oder selbst einen erstellen möchtest, empfehlen wir dir dringend, unser anderes Tutorial [„Wie man einen NFT erstellt“](https://www.alchemy.com/docs/how-to-create-an-nft) anzusehen. + +### Das Contract-ABI {#contract-abi} + +Wenn du unsere Dateien genau untersucht hast, wirst du bemerkt haben, dass es in unserem `src`-Verzeichnis eine `contract-abi.json`-Datei gibt. Ein ABI ist notwendig, um anzugeben, welche Funktion ein Vertrag aufrufen wird, und um sicherzustellen, dass die Funktion Daten in dem von dir erwarteten Format zurückgibt. + +Wir werden auch einen Alchemy-API-Schlüssel und die Alchemy-Web3-API benötigen, um uns mit der Ethereum-Blockchain zu verbinden und unseren Smart Contract zu laden. + +### Erstelle deinen Alchemy-API-Schlüssel {#create-alchemy-api} + +Wenn du noch kein Alchemy-Konto hast, [registriere dich hier kostenlos](https://alchemy.com/?a=eth-org-nft-minter). + +Sobald Sie ein Alchemy-Konto erstellt haben, können Sie einen API-Schlüssel generieren. Erstellen Sie dafür eine App. Dadurch können wir Anfragen an das Ropsten-Testnet stellen. + +Navigiere zur Seite „App erstellen“ in deinem Alchemy-Dashboard, indem du in der Navigationsleiste über „Apps“ fährst und auf „App erstellen“ klickst. + +Benenne deine App – wir haben „Mein erster NFT!“ gewählt –, gib eine kurze Beschreibung, wähle „Staging“ für die Umgebung, die für die Buchführung deiner App verwendet wird, und wähle „Ropsten“ als dein Netzwerk. + +Klicken Sie auf “Create app” (App erstellen) und schon sind Sie fertig. Die App sollte in der untenstehenden Tabelle erscheinen. + +Super, jetzt, da wir unsere HTTP-Alchemy-API-URL erstellt haben, kopiere sie in deine Zwischenablage ... + +… und fügen wir sie zu unserer `.env`-Datei hinzu. Insgesamt sollte deine .env-Datei so aussehen: + +```text +REACT_APP_PINATA_KEY = +REACT_APP_PINATA_SECRET = +REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/ +``` + +Jetzt, da wir unser Contract-ABI und unseren Alchemy-API-Schlüssel haben, sind wir bereit, unseren Smart Contract mit [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) zu laden. + +### Richte deinen Alchemy-Web3-Endpunkt und deinen Vertrag ein {#setup-alchemy-endpoint} + +Wenn du es noch nicht hast, musst du zuerst [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) installieren, indem du im Terminal zum Stammverzeichnis navigierst: `nft-minter-tutorial`: + +```text +cd .. +npm install @alch/alchemy-web3 +``` + +Als Nächstes kehren wir zu unserer `interact.js`-Datei zurück. Füge am Anfang der Datei den folgenden Code hinzu, um deinen Alchemy-Schlüssel aus deiner .env-Datei zu importieren und deinen Alchemy-Web3-Endpunkt einzurichten: + +```javascript +require("dotenv").config() +const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(alchemyKey) +``` + +[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) ist ein Wrapper um [Web3.js](https://docs.web3js.org/) und bietet erweiterte API-Methoden und andere entscheidende Vorteile, um dir das Leben als Web3-Entwickler zu erleichtern. Es ist so konzipiert, dass es eine minimale Konfiguration erfordert, sodass du es sofort in deiner App verwenden kannst! + +Als Nächstes fügen wir unser Contract-ABI und unsere Contract-Adresse zu unserer Datei hinzu. + +```javascript +require("dotenv").config() +const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(alchemyKey) + +const contractABI = require("../contract-abi.json") +const contractAddress = "0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE" +``` + +Sobald wir beides haben, sind wir bereit, unsere Mint-Funktion zu programmieren! + +## Die mintNFT-Funktion implementieren {#implement-the-mintnft-function} + +Definieren wir in deiner `interact.js`-Datei unsere Funktion `mintNFT`, die gleichnamig unseren NFT minten wird. + +Da wir zahlreiche asynchrone Aufrufe machen werden (an Pinata, um unsere Metadaten auf IPFS zu pinnen, an Alchemy Web3, um unseren Smart Contract zu laden, und an MetaMask, um unsere Transaktionen zu signieren), wird unsere Funktion ebenfalls asynchron sein. + +Die drei Eingaben für unsere Funktion sind die `url` unseres digitalen Assets, der `name` und die `description`. Füge die folgende Funktionssignatur unter der `connectWallet`-Funktion hinzu: + +```javascript +export const mintNFT = async (url, name, description) => {} +``` + +### Fehlerbehandlung bei der Eingabe {#input-error-handling} + +Natürlich ist es sinnvoll, am Anfang der Funktion eine Art Fehlerbehandlung für die Eingabe zu haben, damit wir diese Funktion beenden, wenn unsere Eingabeparameter nicht korrekt sind. Fügen wir in unserer Funktion den folgenden Code hinzu: + +```javascript +export const mintNFT = async (url, name, description) => { + //Fehlerbehandlung + if (url.trim() == "" || name.trim() == "" || description.trim() == "") { + return { + success: false, + status: "❗Bitte stelle sicher, dass alle Felder vor dem Minten ausgefüllt sind.", + } + } +} +``` + +Im Wesentlichen, wenn einer der Eingabeparameter eine leere Zeichenfolge ist, geben wir ein JSON-Objekt zurück, bei dem der `success`-Boole'sche Wert auf „false“ gesetzt ist und die `status`-Zeichenfolge meldet, dass alle Felder in unserer Benutzeroberfläche vollständig sein müssen. + +### Hochladen der Metadaten auf IPFS {#upload-metadata-to-ipfs} + +Sobald wir wissen, dass unsere Metadaten korrekt formatiert sind, besteht der nächste Schritt darin, sie in ein JSON-Objekt zu verpacken und über die von uns geschriebene `pinJSONToIPFS`-Funktion auf IPFS hochzuladen! + +Dazu müssen wir zuerst die `pinJSONToIPFS`-Funktion in unsere `interact.js`-Datei importieren. Ganz oben in der `interact.js` fügen wir hinzu: + +```javascript +import { pinJSONToIPFS } from "./pinata.js" +``` + +Erinnere dich daran, dass `pinJSONToIPFS` einen JSON-Körper entgegennimmt. Bevor wir sie aufrufen, müssen wir also unsere `url`-, `name`- und `description`-Parameter in ein JSON-Objekt formatieren. + +Aktualisieren wir unseren Code, um ein JSON-Objekt namens `metadata` zu erstellen und dann einen Aufruf an `pinJSONToIPFS` mit diesem `metadata`-Parameter zu machen: + +```javascript +export const mintNFT = async (url, name, description) => { + //Fehlerbehandlung + if (url.trim() == "" || name.trim() == "" || description.trim() == "") { + return { + success: false, + status: "❗Bitte stelle sicher, dass alle Felder vor dem Minten ausgefüllt sind.", + } + } + + //Metadaten erstellen + const metadata = new Object() + metadata.name = name + metadata.image = url + metadata.description = description + + //Pinata-Aufruf durchführen + const pinataResponse = await pinJSONToIPFS(metadata) + if (!pinataResponse.success) { + return { + success: false, + status: "😢 Etwas ist beim Hochladen deiner tokenURI schiefgelaufen.", + } + } + const tokenURI = pinataResponse.pinataUrl +} +``` + +Beachte, dass wir die Antwort unseres Aufrufs an `pinJSONToIPFS(metadata)` im `pinataResponse`-Objekt speichern. Dann analysieren wir dieses Objekt auf Fehler. + +Wenn ein Fehler auftritt, geben wir ein JSON-Objekt zurück, bei dem der `success`-Boole’sche Wert auf „false“ gesetzt ist und unsere `status`-Zeichenfolge meldet, dass unser Aufruf fehlgeschlagen ist. Andernfalls extrahieren wir die `pinataURL` aus der `pinataResponse` und speichern sie als unsere `tokenURI`-Variable. + +Jetzt ist es an der Zeit, unseren Smart Contract mit der Alchemy Web3-API zu laden, die wir am Anfang unserer Datei initialisiert haben. Füge die folgende Codezeile am Ende der `mintNFT`-Funktion hinzu, um den Vertrag auf die globale Variable `window.contract` zu setzen: + +```javascript +window.contract = await new web3.eth.Contract(contractABI, contractAddress) +``` + +Das Letzte, was wir in unserer `mintNFT`-Funktion hinzufügen müssen, ist unsere Ethereum-Transaktion: + +```javascript +//Deine Ethereum-Transaktion einrichten +const transactionParameters = { + to: contractAddress, // Erforderlich, außer bei Vertragsveröffentlichungen. + from: window.ethereum.selectedAddress, // muss mit der aktiven Adresse des Benutzers übereinstimmen. + data: window.contract.methods + .mintNFT(window.ethereum.selectedAddress, tokenURI) + .encodeABI(), //Aufruf des NFT-Smart-Contracts durchführen +} + +//die Transaktion über MetaMask signieren +try { + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [transactionParameters], + }) + return { + success: true, + status: + "✅ Sieh dir deine Transaktion auf Etherscan an: https://ropsten.etherscan.io/tx/" + + txHash, + } +} catch (error) { + return { + success: false, + status: "😥 Etwas ist schiefgelaufen: " + error.message, + } +} +``` + +Wenn du bereits mit Ethereum-Transaktionen vertraut bist, wirst du feststellen, dass die Struktur ziemlich ähnlich zu dem ist, was du bereits gesehen hast. + +- Zuerst richten wir unsere Transaktionsparameter ein. + - `to` gibt die Empfängeradresse an (unser Smart Contract) + - `from` gibt den Unterzeichner der Transaktion an (die mit MetaMask verbundene Adresse des Benutzers: `window.ethereum.selectedAddress`) + - `data` enthält den Aufruf unserer Smart-Contract-`mintNFT`-Methode, die unsere `tokenURI` und die Wallet-Adresse des Benutzers, `window.ethereum.selectedAddress`, als Eingaben erhält +- Dann machen wir einen Await-Aufruf, `window.ethereum.request,`, bei dem wir MetaMask bitten, die Transaktion zu signieren. Beachte, dass wir in dieser Anfrage unsere eth-Methode (eth_SentTransaction) angeben und unsere `transactionParameters` übergeben. An diesem Punkt öffnet sich MetaMask im Browser und fordert den Benutzer auf, die Transaktion zu signieren oder abzulehnen. + - Wenn die Transaktion erfolgreich ist, gibt die Funktion ein JSON-Objekt zurück, bei dem der Boole’sche Wert `success` auf „true“ gesetzt ist und die `status`-Zeichenfolge den Benutzer auffordert, Etherscan für weitere Informationen zu seiner Transaktion zu besuchen. + - Wenn die Transaktion fehlschlägt, gibt die Funktion ein JSON-Objekt zurück, bei dem der `success`-Boole’sche Wert auf „false“ gesetzt ist und die `status`-Zeichenfolge die Fehlermeldung weitergibt. + +Insgesamt sollte unsere `mintNFT`-Funktion so aussehen: + +```javascript +export const mintNFT = async (url, name, description) => { + //Fehlerbehandlung + if (url.trim() == "" || name.trim() == "" || description.trim() == "") { + return { + success: false, + status: "❗Bitte stelle sicher, dass alle Felder vor dem Minten ausgefüllt sind.", + } + } + + //Metadaten erstellen + const metadata = new Object() + metadata.name = name + metadata.image = url + metadata.description = description + + //Pinata-Pin-Anfrage + const pinataResponse = await pinJSONToIPFS(metadata) + if (!pinataResponse.success) { + return { + success: false, + status: "😢 Etwas ist beim Hochladen deiner tokenURI schiefgelaufen.", + } + } + const tokenURI = pinataResponse.pinataUrl + + //Smart Contract laden + window.contract = await new web3.eth.Contract(contractABI, contractAddress) //loadContract(); + + //Deine Ethereum-Transaktion einrichten + const transactionParameters = { + to: contractAddress, // Erforderlich, außer bei Vertragsveröffentlichungen. + from: window.ethereum.selectedAddress, // muss mit der aktiven Adresse des Benutzers übereinstimmen. + data: window.contract.methods + .mintNFT(window.ethereum.selectedAddress, tokenURI) + .encodeABI(), //Aufruf des NFT-Smart-Contracts durchführen + } + + //Transaktion über MetaMask signieren + try { + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [transactionParameters], + }) + return { + success: true, + status: + "✅ Sieh dir deine Transaktion auf Etherscan an: https://ropsten.etherscan.io/tx/" + + txHash, + } + } catch (error) { + return { + success: false, + status: "😥 Etwas ist schiefgelaufen: " + error.message, + } + } +} +``` + +Das ist eine riesige Funktion! Jetzt müssen wir nur noch unsere `mintNFT`-Funktion mit unserer `Minter.js`-Komponente verbinden ... + +## Verbinde mintNFT mit unserem Minter.js-Frontend {#connect-our-frontend} + +Öffne deine `Minter.js`-Datei und aktualisiere die Zeile `import { connectWallet, getCurrentWalletConnected } from "./utils/interact.js";` am Anfang zu: + +```javascript +import { + connectWallet, + getCurrentWalletConnected, + mintNFT, +} from "./utils/interact.js" +``` + +Implementiere schließlich die `onMintPressed`-Funktion, um einen Await-Aufruf an deine importierte `mintNFT`-Funktion zu machen und die `status`-Zustandsvariable zu aktualisieren, um widerzuspiegeln, ob unsere Transaktion erfolgreich war oder fehlgeschlagen ist: + +```javascript +const onMintPressed = async () => { + const { status } = await mintNFT(url, name, description) + setStatus(status) +} +``` + +## Stelle deinen NFT auf einer Live-Website bereit {#deploy-your-NFT} + +Bereit, dein Projekt live zu schalten, damit Benutzer damit interagieren können? Sieh dir [dieses Tutorial](https://docs.alchemy.com/alchemy/tutorials/nft-minter/how-do-i-deploy-nfts-online) an, um deinen Minter auf einer Live-Website bereitzustellen. + +Ein letzter Schritt ... + +## Erobere die Blockchain-Welt im Sturm {#take-the-blockchain-world-by-storm} + +Nur ein Scherz, du hast es bis zum Ende des Tutorials geschafft! + +Zusammenfassend lässt sich sagen, dass du durch den Bau eines NFT-Minters erfolgreich gelernt hast, wie man: + +- Verbinden von MetaMask mit Ihrem Frontend-Projekt +- Rufen von Smart-Contract Methoden von Ihrem Frontend +- Transaktionen mit MetaMask signieren + +Vermutlich möchtest du die über deine Dapp geminteten NFTs in deiner Wallet präsentieren können – sieh dir also unbedingt unser kurzes Tutorial [So zeigst du dein NFT in deiner Wallet an](https://www.alchemy.com/docs/how-to-view-your-nft-in-your-mobile-wallet) an! + +Und wie immer, wenn du Fragen hast, sind wir hier, um im [Alchemy Discord](https://discord.gg/gWuC7zB) zu helfen. Wir können es kaum erwarten zu sehen, wie du die Konzepte aus diesem Tutorial auf deine zukünftigen Projekte anwendest! diff --git a/public/content/translations/de/developers/tutorials/optimism-std-bridge-annotated-code/index.md b/public/content/translations/de/developers/tutorials/optimism-std-bridge-annotated-code/index.md new file mode 100644 index 00000000000..980457337c5 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/optimism-std-bridge-annotated-code/index.md @@ -0,0 +1,1357 @@ +--- +title: "Walkthrough zum Vertrag der Optimism-Standard-Brücke" +description: "Wie funktioniert die Standard-Brücke für Optimism? Warum funktioniert sie auf diese Weise?" +author: Ori Pomerantz +tags: ["solidity", "bridge", "layer 2"] +skill: intermediate +published: 2022-03-30 +lang: de +--- + +[Optimism](https://www.optimism.io/) ist ein [Optimistic Rollup](/developers/docs/scaling/optimistic-rollups/). +Optimistic Rollups können Transaktionen zu einem viel niedrigeren Preis als das Ethereum Mainnet (auch bekannt als Layer 1 oder L1) verarbeiten, da Transaktionen nur von einigen wenigen Knoten anstatt von jedem Knoten im Netzwerk verarbeitet werden. +Gleichzeitig werden alle Daten auf L1 geschrieben, sodass alles mit der Integrität und Verfügbarkeit des Mainnets nachgewiesen und rekonstruiert werden kann. + +Um L1-Assets auf Optimism (oder einem anderen L2) zu verwenden, müssen die Assets [überbrückt](/bridges/#prerequisites) werden. +Eine Möglichkeit, dies zu erreichen, besteht darin, dass Benutzer Assets (ETH und [ERC-20-Tokens](/developers/docs/standards/tokens/erc-20/) sind die häufigsten) auf L1 sperren und gleichwertige Assets zur Verwendung auf L2 erhalten. +Schließlich möchte derjenige, der sie am Ende besitzt, sie vielleicht wieder auf L1 zurückbrücken. +Dabei werden die Assets auf L2 verbrannt und dann auf L1 wieder an den Benutzer freigegeben. + +So funktioniert die [Optimism Standard-Brücke](https://docs.optimism.io/app-developers/bridging/standard-bridge). +In diesem Artikel gehen wir den Quellcode für diese Brücke durch, um zu sehen, wie sie funktioniert, und um sie als Beispiel für gut geschriebenen Solidity-Code zu studieren. + +## Kontrollflüsse {#control-flows} + +Die Brücke hat zwei Hauptabläufe: + +- Einzahlung (von L1 nach L2) +- Auszahlung (von L2 nach L1) + +### Einzahlungsablauf {#deposit-flow} + +#### Layer 1 {#deposit-flow-layer-1} + +1. Bei der Einzahlung eines ERC-20 erteilt der Einzahler der Brücke eine Freigabe, den eingezahlten Betrag auszugeben. +2. Der Einzahler ruft die L1-Brücke auf (`depositERC20`, `depositERC20To`, `depositETH` oder `depositETHTo`) +3. Die L1-Brücke nimmt das überbrückte Asset in Besitz. + - ETH: Das Asset wird vom Einzahler als Teil des Aufrufs übertragen. + - ERC-20: Das Asset wird von der Brücke unter Verwendung der vom Einzahler erteilten Freigabe an sich selbst übertragen. +4. Die L1-Brücke verwendet den domänenübergreifenden Nachrichtenmechanismus, um `finalizeDeposit` auf der L2-Brücke aufzurufen. + +#### Layer 2 {#deposit-flow-layer-2} + +5. Die L2-Brücke überprüft, ob der Aufruf von `finalizeDeposit` rechtmäßig ist: + - Kam vom domänenübergreifenden Nachrichtenvertrag + - War ursprünglich von der Brücke auf L1 +6. Die L2-Brücke prüft, ob der ERC-20-Token-Vertrag auf L2 der richtige ist: + - Der L2-Vertrag meldet, dass sein L1-Gegenstück dasselbe ist wie das, von dem die Tokens auf L1 kamen + - Der L2-Vertrag meldet, dass er die richtige Schnittstelle unterstützt ([unter Verwendung von ERC-165](https://eips.ethereum.org/EIPS/eip-165)). +7. Wenn der L2-Vertrag der richtige ist, rufen Sie ihn auf, um die entsprechende Anzahl von Tokens an die entsprechende Adresse zu prägen. Wenn nicht, starten Sie einen Auszahlungsprozess, damit der Benutzer die Tokens auf L1 beanspruchen kann. + +### Auszahlungsablauf {#withdrawal-flow} + +#### Layer 2 {#withdrawal-flow-layer-2} + +1. Der Auszahlende ruft die L2-Brücke auf (`withdraw` oder `withdrawTo`) +2. Die L2-Brücke verbrennt die entsprechende Anzahl von Tokens, die `msg.sender` gehören. +3. Die L2-Brücke verwendet den domänenübergreifenden Nachrichtenmechanismus, um `finalizeETHWithdrawal` oder `finalizeERC20Withdrawal` auf der L1-Brücke aufzurufen. + +#### Layer 1 {#withdrawal-flow-layer-1} + +4. Die L1-Brücke überprüft, ob der Aufruf von `finalizeETHWithdrawal` oder `finalizeERC20Withdrawal` rechtmäßig ist: + - Kam vom domänenübergreifenden Nachrichtenmechanismus + - War ursprünglich von der Brücke auf L2 +5. Die L1-Brücke überträgt das entsprechende Asset (ETH oder ERC-20) an die entsprechende Adresse. + +## Layer-1-Code {#layer-1-code} + +Dies ist der Code, der auf L1, dem Ethereum Mainnet, läuft. + +### IL1ERC20Bridge {#IL1ERC20Bridge} + +[Diese Schnittstelle ist hier definiert](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/IL1ERC20Bridge.sol). +Sie enthält Funktionen und Definitionen, die für die Überbrückung von ERC-20-Tokens erforderlich sind. + +```solidity +// SPDX-License-Identifier: MIT +``` + +[Der größte Teil des Codes von Optimism wird unter der MIT-Lizenz veröffentlicht](https://help.optimism.io/hc/en-us/articles/4411908707995-What-software-license-does-Optimism-use-). + +```solidity +pragma solidity >0.5.0 <0.9.0; +``` + +Zum Zeitpunkt des Schreibens ist die neueste Version von Solidity 0.8.12. +Bis zur Veröffentlichung von Version 0.9.0 wissen wir nicht, ob dieser Code damit kompatibel ist oder nicht. + +```solidity +/** + * @title IL1ERC20Bridge + */ +interface IL1ERC20Bridge { + /********** + * Ereignisse * + **********/ + + event ERC20DepositInitiated( +``` + +In der Terminologie der Optimism-Brücke bedeutet _Einzahlung_ eine Übertragung von L1 nach L2 und _Auszahlung_ eine Übertragung von L2 nach L1. + +```solidity + address indexed _l1Token, + address indexed _l2Token, +``` + +In den meisten Fällen ist die Adresse eines ERC-20 auf L1 nicht dieselbe wie die Adresse des entsprechenden ERC-20 auf L2. +[Sie können die Liste der Token-Adressen hier einsehen](https://static.optimism.io/optimism.tokenlist.json). +Die Adresse mit `chainId` 1 befindet sich auf L1 (Mainnet) und die Adresse mit `chainId` 10 auf L2 (Optimism). +Die anderen beiden `chainId`-Werte sind für das Kovan-Testnetz (42) und das Optimistic Kovan-Testnetz (69). + +```solidity + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); +``` + +Es ist möglich, Notizen zu Übertragungen hinzuzufügen, in diesem Fall werden sie zu den Ereignissen hinzugefügt, die sie melden. + +```solidity + event ERC20WithdrawalFinalized( + address indexed _l1Token, + address indexed _l2Token, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); +``` + +Derselbe Brückenvertrag behandelt Übertragungen in beide Richtungen. +Im Fall der L1-Brücke bedeutet dies die Initiierung von Einzahlungen und die Finalisierung von Auszahlungen. + +```solidity + + /******************** + * Öffentliche Funktionen * + ********************/ + + /** + * @dev Ruft die Adresse des entsprechenden L2-Brückenvertrags ab. + * @return Adresse des entsprechenden L2-Brückenvertrags. + */ + function l2TokenBridge() external returns (address); +``` + +Diese Funktion wird nicht wirklich benötigt, da es sich auf L2 um einen vorab bereitgestellten Vertrag (predeployed contract) handelt, der sich also immer an der Adresse `0x4200000000000000000000000000000000000010` befindet. +Sie ist hier aus Symmetriegründen zur L2-Brücke, da die Adresse der L1-Brücke _nicht_ trivial zu kennen ist. + +```solidity + /** + * @dev Zahlt einen Betrag des ERC20 auf das Guthaben des Aufrufers auf L2 ein. + * @param _l1Token Adresse des L1 ERC20, das wir einzahlen. + * @param _l2Token Adresse des entsprechenden L2 ERC20. + * @param _amount Einzuzahlender Betrag des ERC20. + * @param _l2Gas Erforderliches Gaslimit, um die Einzahlung auf L2 abzuschließen. + * @param _data Optionale Daten zur Weiterleitung an L2. Diese Daten werden + * lediglich als Annehmlichkeit für externe Verträge zur Verfügung gestellt. Abgesehen von der Durchsetzung einer maximalen + * Länge geben diese Verträge keine Garantien über ihren Inhalt. + */ + function depositERC20( + address _l1Token, + address _l2Token, + uint256 _amount, + uint32 _l2Gas, + bytes calldata _data + ) external; +``` + +Der Parameter `_l2Gas` ist die Menge an L2-Gas, die die Transaktion verbrauchen darf. +[Bis zu einem bestimmten (hohen) Limit ist dies kostenlos](https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions-2), daher sollte es kein Problem sein, es sei denn, der ERC-20-Vertrag macht beim Prägen etwas wirklich Seltsames. +Diese Funktion kümmert sich um das übliche Szenario, bei dem ein Benutzer Assets an dieselbe Adresse auf einer anderen Blockchain überbrückt. + +```solidity + /** + * @dev zahlt einen Betrag von ERC20 auf das Guthaben eines Empfängers auf L2 ein. + * @param _l1Token Adresse des L1 ERC20, den wir einzahlen + * @param _l2Token Adresse des entsprechenden L2 ERC20 auf L1 + * @param _to L2-Adresse, auf die die Abhebung gutgeschrieben werden soll. + * @param _amount Einzuzahlender Betrag des ERC20. + * @param _l2Gas Gaslimit, das erforderlich ist, um die Einzahlung auf L2 abzuschließen. + * @param _data Optionale Daten zur Weiterleitung an L2. Diese Daten werden + * lediglich als Annehmlichkeit für externe Verträge zur Verfügung gestellt. Abgesehen von der Durchsetzung einer maximalen + * Länge geben diese Verträge keine Garantien über ihren Inhalt. + */ + function depositERC20To( + address _l1Token, + address _l2Token, + address _to, + uint256 _amount, + uint32 _l2Gas, + bytes calldata _data + ) external; +``` + +Diese Funktion ist fast identisch mit `depositERC20`, aber sie ermöglicht es Ihnen, den ERC-20 an eine andere Adresse zu senden. + +```solidity + /************************* + * Cross-Chain-Funktionen * + *************************/ + + /** + * @dev Schließt eine Auszahlung von L2 nach L1 ab und schreibt den Betrag dem Guthaben des + * L1-ERC20-Tokens des Empfängers gut. + * Dieser Aufruf schlägt fehl, wenn die initiierte Auszahlung von L2 noch nicht finalisiert wurde. + * + * @param _l1Token Adresse des L1-Tokens, für das finalizeWithdrawal ausgeführt wird. + * @param _l2Token Adresse des L2-Tokens, auf dem die Auszahlung eingeleitet wurde. + * @param _from L2-Adresse, die die Übertragung initiiert. + * @param _to L1-Adresse, der die Auszahlung gutgeschrieben werden soll. + * @param _amount Einzuzahlender Betrag des ERC20. + * @param _data Vom Absender auf L2 bereitgestellte Daten. Diese Daten werden + * lediglich als Annehmlichkeit für externe Verträge zur Verfügung gestellt. Abgesehen von der Durchsetzung einer maximalen + * Länge geben diese Verträge keine Garantien über ihren Inhalt. + */ + function finalizeERC20Withdrawal( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external; +} +``` + +Auszahlungen (und andere Nachrichten von L2 nach L1) in Optimism sind ein zweistufiger Prozess: + +1. Eine initiierende Transaktion auf L2. +2. Eine finalisierende oder beanspruchende Transaktion auf L1. + Diese Transaktion muss nach dem Ende des [Fehler-Anfechtungszeitraums](https://community.optimism.io/docs/how-optimism-works/#fault-proofs) für die L2-Transaktion erfolgen. + +### IL1StandardBridge {#il1standardbridge} + +[Diese Schnittstelle ist hier definiert](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/IL1StandardBridge.sol). +Diese Datei enthält Ereignis- und Funktionsdefinitionen für ETH. +Diese Definitionen sind sehr ähnlich zu denen, die oben in `IL1ERC20Bridge` für ERC-20 definiert wurden. + +Die Brückenschnittstelle ist auf zwei Dateien aufgeteilt, da einige ERC-20-Tokens eine benutzerdefinierte Verarbeitung erfordern und nicht von der Standardbrücke verarbeitet werden können. +Auf diese Weise kann die benutzerdefinierte Brücke, die einen solchen Token verarbeitet, `IL1ERC20Bridge` implementieren und muss nicht auch ETH überbrücken. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity >0.5.0 <0.9.0; + +import "./IL1ERC20Bridge.sol"; + +/** + * @title IL1StandardBridge + */ +interface IL1StandardBridge is IL1ERC20Bridge { + /********** + * Ereignisse * + **********/ + event ETHDepositInitiated( + address indexed _from, + address indexed _to, + uint256 _amount, + bytes _data + ); +``` + +Dieses Ereignis ist fast identisch mit der ERC-20-Version (`ERC20DepositInitiated`), jedoch ohne die L1- und L2-Token-Adressen. +Dasselbe gilt für die anderen Ereignisse und die Funktionen. + +```solidity + event ETHWithdrawalFinalized( + . + . + . + ); + + /******************** + * Öffentliche Funktionen * + ********************/ + + /** + * @dev Einzahlung eines ETH-Betrags in das Guthaben des Aufrufers auf L2. + . + . + . + */ + function depositETH(uint32 _l2Gas, bytes calldata _data) external payable; + + /** + * @dev Einzahlung eines ETH-Betrags in das Guthaben eines Empfängers auf L2. + . + . + . + */ + function depositETHTo( + address _to, + uint32 _l2Gas, + bytes calldata _data + ) external payable; + + /************************* + * Cross-Chain-Funktionen * + *************************/ + + /** + * @dev Schließen Sie eine Auszahlung von L2 nach L1 ab und schreiben Sie den Betrag dem Guthaben des L1-ETH-Tokens des Empfängers gut. + * Da nur der xDomainMessenger diese Funktion aufrufen kann, wird sie niemals aufgerufen + * bevor die Auszahlung finalisiert ist. + . + . + . + */ + function finalizeETHWithdrawal( + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external; +} +``` + +### CrossDomainEnabled {#crossdomainenabled} + +[Dieser Vertrag](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/bridge/CrossDomainEnabled.sol) wird von beiden Brücken ([L1](#the-l1-bridge-contract) und [L2](#the-l2-bridge-contract)) geerbt, um Nachrichten an den anderen Layer zu senden. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity >0.5.0 <0.9.0; + +/* Schnittstellen-Importe */ +import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol"; +``` + +[Diese Schnittstelle](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/bridge/ICrossDomainMessenger.sol) teilt dem Vertrag mit, wie Nachrichten an den anderen Layer gesendet werden sollen, indem der Cross-Domain-Messenger verwendet wird. +Dieser Cross-Domain-Messenger ist ein ganz anderes System und verdient einen eigenen Artikel, den ich hoffentlich in Zukunft schreiben werde. + +```solidity +/** + * @title CrossDomainEnabled + * @dev Hilfsvertrag für Verträge, die domänenübergreifende Kommunikation durchführen + * + * Verwendeter Compiler: definiert durch vererbenden Vertrag + */ +contract CrossDomainEnabled { + /************* + * Variablen * + *************/ + + // Messenger-Vertrag, der zum Senden und Empfangen von Nachrichten aus der anderen Domäne verwendet wird. + address public messenger; + + /*************** + * Konstruktor * + ***************/ + + /** + * @param _messenger Adresse des CrossDomainMessenger auf der aktuellen Ebene. + */ + constructor(address _messenger) { + messenger = _messenger; + } +``` + +Der eine Parameter, den der Vertrag kennen muss, ist die Adresse des Cross-Domain-Messengers auf diesem Layer. +Dieser Parameter wird einmal im Konstruktor gesetzt und ändert sich nie. + +```solidity + + /********************** + * Funktionsmodifikatoren * + **********************/ + + /** + * Erzwingt, dass die modifizierte Funktion nur von einem bestimmten domänenübergreifenden Konto aufgerufen werden kann. + * @param _sourceDomainAccount Das einzige Konto in der Ursprungsdomäne, das + * zur Ausführung dieser Funktion berechtigt ist. + */ + modifier onlyFromCrossDomainAccount(address _sourceDomainAccount) { +``` + +Das domänenübergreifende Messaging ist für jeden Vertrag auf der Blockchain zugänglich, auf der es läuft (entweder Ethereum Mainnet oder Optimism). +Aber wir brauchen die Brücke auf jeder Seite, um _nur_ bestimmten Nachrichten zu vertrauen, wenn sie von der Brücke auf der anderen Seite kommen. + +```solidity + require( + msg.sender == address(getCrossDomainMessenger()), + "OVM_XCHAIN: messenger contract unauthenticated" + ); +``` + +Nur Nachrichten vom entsprechenden Cross-Domain-Messenger (`messenger`, wie Sie unten sehen) können als vertrauenswürdig eingestuft werden. + +```solidity + + require( + getCrossDomainMessenger().xDomainMessageSender() == _sourceDomainAccount, + "OVM_XCHAIN: wrong sender of cross-domain message" + ); +``` + +Die Art und Weise, wie der Cross-Domain-Messenger die Adresse bereitstellt, die eine Nachricht mit dem anderen Layer gesendet hat, ist [die Funktion `.xDomainMessageSender()`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L122-L128). +Solange sie in der Transaktion aufgerufen wird, die durch die Nachricht initiiert wurde, kann sie diese Information bereitstellen. + +Wir müssen sicherstellen, dass die Nachricht, die wir erhalten haben, von der anderen Brücke kam. + +```solidity + + _; + } + + /********************** + * Interne Funktionen * + **********************/ + + /** + * Ruft den Messenger ab, normalerweise aus dem Speicher. Diese Funktion wird für den Fall verfügbar gemacht, dass ein untergeordneter Vertrag + * sie überschreiben muss. + * @return Die Adresse des Cross-Domain-Messenger-Vertrags, der verwendet werden sollte. + */ + function getCrossDomainMessenger() internal virtual returns (ICrossDomainMessenger) { + return ICrossDomainMessenger(messenger); + } +``` + +Diese Funktion gibt den Cross-Domain-Messenger zurück. +Wir verwenden eine Funktion anstelle der Variable `messenger`, um Verträgen, die von diesem erben, zu ermöglichen, einen Algorithmus zu verwenden, um anzugeben, welcher Cross-Domain-Messenger verwendet werden soll. + +```solidity + + /** + * Sendet eine Nachricht an ein Konto in einer anderen Domäne + * @param _crossDomainTarget Der beabsichtigte Empfänger in der Zieldomäne + * @param _message Die an das Ziel zu sendenden Daten (normalerweise Calldata an eine Funktion mit + * `onlyFromCrossDomainAccount()`) + * @param _gasLimit Das GasLimit für den Empfang der Nachricht in der Zieldomäne. + */ + function sendCrossDomainMessage( + address _crossDomainTarget, + uint32 _gasLimit, + bytes memory _message +``` + +Schließlich die Funktion, die eine Nachricht an den anderen Layer sendet. + +```solidity + ) internal { + // slither-disable-next-line reentrancy-events, reentrancy-benign +``` + +[Slither](https://github.com/crytic/slither) ist ein statischer Analysator, den Optimism auf jedem Vertrag ausführt, um nach Schwachstellen und anderen potenziellen Problemen zu suchen. +In diesem Fall löst die folgende Zeile zwei Schwachstellen aus: + +1. [Reentrancy-Ereignisse](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3) +2. [Gutartige Wiedereintrittsfähigkeit](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2) + +```solidity + getCrossDomainMessenger().sendMessage(_crossDomainTarget, _message, _gasLimit); + } +} +``` + +In diesem Fall machen wir uns keine Sorgen über Wiedereintrittsfähigkeit, da wir wissen, dass `getCrossDomainMessenger()` eine vertrauenswürdige Adresse zurückgibt, auch wenn Slither keine Möglichkeit hat, das zu wissen. + +### Der L1-Brückenvertrag {#the-l1-bridge-contract} + +[Der Quellcode für diesen Vertrag befindet sich hier](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol). + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; +``` + +Die Schnittstellen können Teil anderer Verträge sein, daher müssen sie eine breite Palette von Solidity-Versionen unterstützen. +Aber die Brücke selbst ist unser Vertrag, und wir können streng sein, welche Solidity-Version sie verwendet. + +```solidity +/* Schnittstellen-Importe */ +import { IL1StandardBridge } from "./IL1StandardBridge.sol"; +import { IL1ERC20Bridge } from "./IL1ERC20Bridge.sol"; +``` + +[IL1ERC20Bridge](#IL1ERC20Bridge) und [IL1StandardBridge](#IL1StandardBridge) werden oben erklärt. + +```solidity +import { IL2ERC20Bridge } from "../../L2/messaging/IL2ERC20Bridge.sol"; +``` + +[Diese Schnittstelle](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol) ermöglicht es uns, Nachrichten zu erstellen, um die Standardbrücke auf L2 zu steuern. + +```solidity +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +``` + +[Diese Schnittstelle](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) ermöglicht es uns, ERC-20-Verträge zu steuern. +[Hier können Sie mehr darüber lesen](/developers/tutorials/erc20-annotated-code/#the-interface). + +```solidity +/* Bibliotheks-Importe */ +import { CrossDomainEnabled } from "../../libraries/bridge/CrossDomainEnabled.sol"; +``` + +[Wie oben erklärt](#crossdomainenabled), wird dieser Vertrag für die Interlayer-Nachrichtenübermittlung verwendet. + +```solidity +import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol"; +``` + +`Lib_PredeployAddresses` (https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/constants/Lib_PredeployAddresses.sol) hat die Adressen für die L2-Verträge, die immer dieselbe Adresse haben. Dies schließt die Standard-Brücke auf L2 mit ein. + +```solidity +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; +``` + +[OpenZeppelins Address-Dienstprogramme](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol). Es wird verwendet, um zwischen Vertragsadressen und solchen zu unterscheiden, die zu extern besessenen Konten (EOA) gehören. + +Beachten Sie, dass dies keine perfekte Lösung ist, da es keine Möglichkeit gibt, zwischen direkten Aufrufen und Aufrufen aus dem Konstruktor eines Vertrags zu unterscheiden, aber zumindest können wir so einige häufige Benutzerfehler identifizieren und verhindern. + +```solidity +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +``` + +[Der ERC-20-Standard](https://eips.ethereum.org/EIPS/eip-20) unterstützt zwei Möglichkeiten für einen Vertrag, einen Fehler zu melden: + +1. Zurücksetzen (revert) +2. `false` zurückgeben + +Die Handhabung beider Fälle würde unseren Code komplizierter machen, daher verwenden wir stattdessen [OpenZeppelins `SafeERC20`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol), das sicherstellt, dass [alle Fehler zu einem Revert führen](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol#L96). + +```solidity +/** + * @title L1StandardBridge + * @dev Die L1-ETH- und ERC20-Brücke ist ein Vertrag, der hinterlegte L1-Mittel und Standard-Token + * speichert, die auf L2 verwendet werden. Sie synchronisiert eine entsprechende L2-Brücke, informiert sie über Einzahlungen + * und hört auf sie für neu finalisierte Auszahlungen. + * + */ +contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled { + using SafeERC20 for IERC20; +``` + +Diese Zeile gibt an, dass der `SafeERC20`-Wrapper jedes Mal verwendet werden soll, wenn wir die `IERC20`-Schnittstelle verwenden. + +```solidity + + /******************************** + * Externe Vertragsreferenzen * + ********************************/ + + address public l2TokenBridge; +``` + +Die Adresse von [L2StandardBridge](#the-l2-bridge-contract). + +```solidity + + // Ordnet L1-Token zu L2-Token zu Saldo des hinterlegten L1-Tokens zu + mapping(address => mapping(address => uint256)) public deposits; +``` + +Ein doppeltes [Mapping](https://www.tutorialspoint.com/solidity/solidity_mappings.htm) wie dieses ist die Art und Weise, wie Sie ein [zweidimensionales dünn besetztes Array](https://en.wikipedia.org/wiki/Sparse_matrix) definieren. +Werte in dieser Datenstruktur werden als `deposit[L1-Token-Adresse][L2-Token-Adresse]` identifiziert. +Der Standardwert ist Null. +Nur Zellen, die auf einen anderen Wert gesetzt sind, werden in den Speicher geschrieben. + +```solidity + + /*************** + * Konstruktor * + ***************/ + + // Dieser Vertrag lebt hinter einem Proxy, daher werden die Konstruktorparameter ungenutzt bleiben. + constructor() CrossDomainEnabled(address(0)) {} +``` + +Wir wollen diesen Vertrag aktualisieren können, ohne alle Variablen im Speicher kopieren zu müssen. +Dazu verwenden wir einen [`Proxy`](https://docs.openzeppelin.com/contracts/3.x/api/proxy), einen Vertrag, der `delegatecall` verwendet, um Aufrufe an einen separaten Vertrag zu übertragen, dessen Adresse vom Proxy-Vertrag gespeichert wird (wenn Sie ein Upgrade durchführen, weisen Sie den Proxy an, diese Adresse zu ändern). +Wenn Sie `delegatecall` verwenden, bleibt der Speicher der Speicher des _aufrufenden_ Vertrags, sodass die Werte aller Vertragszustandsvariablen unberührt bleiben. + +Ein Effekt dieses Musters ist, dass der Speicher des Vertrags, der der _Aufgerufene_ von `delegatecall` ist, nicht verwendet wird und daher die an ihn übergebenen Konstruktorwerte keine Rolle spielen. +Dies ist der Grund, warum wir dem `CrossDomainEnabled`-Konstruktor einen unsinnigen Wert übergeben können. +Dies ist auch der Grund, warum die folgende Initialisierung vom Konstruktor getrennt ist. + +```solidity + /****************** + * Initialisierung * + ******************/ + + /** + * @param _l1messenger L1 Messenger-Adresse, die für die Cross-Chain-Kommunikation verwendet wird. + * @param _l2TokenBridge L2-Standard-Brückenadresse. + */ + // slither-disable-next-line external-function +``` + +Dieser [Slither-Test](https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external) identifiziert Funktionen, die nicht aus dem Vertragscode aufgerufen werden und daher als `external` anstatt `public` deklariert werden könnten. +Die Gaskosten von `external`-Funktionen können niedriger sein, da sie mit Parametern in den Calldata versorgt werden können. +Als `public` deklarierte Funktionen müssen innerhalb des Vertrags zugänglich sein. +Verträge können ihre eigenen Calldata nicht ändern, daher müssen sich die Parameter im Speicher befinden. +Wenn eine solche Funktion extern aufgerufen wird, ist es notwendig, die Calldata in den Speicher zu kopieren, was Gas kostet. +In diesem Fall wird die Funktion nur einmal aufgerufen, sodass die Ineffizienz für uns keine Rolle spielt. + +```solidity + function initialize(address _l1messenger, address _l2TokenBridge) public { + require(messenger == address(0), "Vertrag wurde bereits initialisiert."); +``` + +Die `initialize`-Funktion sollte nur einmal aufgerufen werden. +Wenn sich die Adresse des L1-Cross-Domain-Messengers oder der L2-Token-Brücke ändert, erstellen wir einen neuen Proxy und eine neue Brücke, die ihn aufruft. +Dies wird wahrscheinlich nur bei einem Upgrade des gesamten Systems geschehen, ein sehr seltenes Ereignis. + +Beachten Sie, dass diese Funktion keinen Mechanismus hat, der einschränkt, _wer_ sie aufrufen kann. +Das bedeutet, dass ein Angreifer theoretisch warten könnte, bis wir den Proxy und die erste Version der Brücke deployen, und dann [Front-Running betreiben](https://solidity-by-example.org/hacks/front-running/) könnte, um zur `initialize`-Funktion zu gelangen, bevor der legitime Benutzer dies tut. Es gibt jedoch zwei Methoden, um dies zu verhindern: + +1. Wenn die Verträge nicht direkt von einem EOA, sondern [in einer Transaktion bereitgestellt werden, in der ein anderer Vertrag sie erstellt](https://medium.com/upstate-interactive/creating-a-contract-with-a-smart-contract-bdb67c5c8595), kann der gesamte Prozess atomar sein und abgeschlossen werden, bevor eine andere Transaktion ausgeführt wird. +2. Wenn der legitime Aufruf von `initialize` fehlschlägt, ist es immer möglich, den neu erstellten Proxy und die Brücke zu ignorieren und neue zu erstellen. + +```solidity + messenger = _l1messenger; + l2TokenBridge = _l2TokenBridge; + } +``` + +Dies sind die beiden Parameter, die die Brücke kennen muss. + +```solidity + + /************** + * Einzahlung * + **************/ + + /** @dev Modifikator, der erfordert, dass der Absender ein EOA ist. Diese Prüfung könnte von einem bösartigen + * Vertrag über initcode umgangen werden, aber sie kümmert sich um den Benutzerfehler, den wir vermeiden wollen. + */ + modifier onlyEOA() { + // Wird verwendet, um Einzahlungen von Verträgen zu stoppen (verhindert versehentlich verlorene Tokens) + require(!Address.isContract(msg.sender), "Konto nicht EOA"); + _; + } +``` + +Dies ist der Grund, warum wir die `Address`-Dienstprogramme von OpenZeppelin benötigten. + +```solidity + /** + * @dev Diese Funktion kann ohne Daten aufgerufen werden + * um einen Betrag von ETH auf das Guthaben des Aufrufers auf L2 einzuzahlen. + * Da die Empfangsfunktion keine Daten entgegennimmt, wird ein konservativer + * Standardbetrag an L2 weitergeleitet. + */ + receive() external payable onlyEOA { + _initiateETHDeposit(msg.sender, msg.sender, 200_000, bytes("")); + } +``` + +Diese Funktion existiert zu Testzwecken. +Beachten Sie, dass sie nicht in den Schnittstellendefinitionen erscheint – sie ist nicht für den normalen Gebrauch bestimmt. + +```solidity + /** + * @inheritdoc IL1StandardBridge + */ + function depositETH(uint32 _l2Gas, bytes calldata _data) external payable onlyEOA { + _initiateETHDeposit(msg.sender, msg.sender, _l2Gas, _data); + } + + /** + * @inheritdoc IL1StandardBridge + */ + function depositETHTo( + address _to, + uint32 _l2Gas, + bytes calldata _data + ) external payable { + _initiateETHDeposit(msg.sender, _to, _l2Gas, _data); + } +``` + +Diese beiden Funktionen sind Wrapper um `_initiateETHDeposit`, die Funktion, die die eigentliche ETH-Einzahlung abwickelt. + +```solidity + /** + * @dev Führt die Logik für Einzahlungen durch, indem der ETH gespeichert und das L2-ETH-Gateway + * über die Einzahlung informiert wird. + * @param _from Konto, von dem die Einzahlung auf L1 abgezogen wird. + * @param _to Konto, dem die Einzahlung auf L2 gutgeschrieben wird. + * @param _l2Gas Gaslimit, das erforderlich ist, um die Einzahlung auf L2 abzuschließen. + * @param _data Optionale Daten zur Weiterleitung an L2. Diese Daten werden + * lediglich als Annehmlichkeit für externe Verträge zur Verfügung gestellt. Abgesehen von der Durchsetzung einer maximalen + * Länge geben diese Verträge keine Garantien über ihren Inhalt. + */ + function _initiateETHDeposit( + address _from, + address _to, + uint32 _l2Gas, + bytes memory _data + ) internal { + // Konstruiere Calldata für den finalizeDeposit-Aufruf + bytes memory message = abi.encodeWithSelector( +``` + +Die Funktionsweise von Cross-Domain-Nachrichten besteht darin, dass der Zielvertrag mit der Nachricht als Calldata aufgerufen wird. +Solidity-Verträge interpretieren ihre Calldata immer gemäß +[den ABI-Spezifikationen](https://docs.soliditylang.org/en/v0.8.12/abi-spec.html). +Die Solidity-Funktion [`abi.encodeWithSelector`](https://docs.soliditylang.org/en/v0.8.12/units-and-global-variables.html#abi-encoding-and-decoding-functions) erstellt diese Calldata. + +```solidity + IL2ERC20Bridge.finalizeDeposit.selector, + address(0), + Lib_PredeployAddresses.OVM_ETH, + _from, + _to, + msg.value, + _data + ); +``` + +Die Nachricht hier ist, [die Funktion `finalizeDeposit`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol#L141-L148) mit diesen Parametern aufzurufen: + +| Parameter | Wert | Bedeutung | +| ------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| \_l1Token | Adresse(0) | Sonderwert für ETH (das kein ERC-20-Token ist) auf L1 | +| \_l2Token | Lib_PredeployAddresses.OVM_ETH | Der L2-Vertrag, der ETH auf Optimism verwaltet, `0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000` (dieser Vertrag ist nur für den internen Gebrauch von Optimism bestimmt) | +| \_from | \_from | Die Adresse auf L1, die die ETH sendet | +| \_to | \_to | Die Adresse auf L2, die die ETH empfängt | +| Betrag | msg.value | Gesendeter Betrag in Wei (der bereits an die Brücke gesendet wurde) | +| \_data | \_data | Zusätzliche Daten, die an die Einzahlung angehängt werden | + +```solidity + // Senden Sie Calldata nach L2 + // slither-disable-next-line reentrancy-events + sendCrossDomainMessage(l2TokenBridge, _l2Gas, message); +``` + +Senden Sie die Nachricht über den Cross-Domain-Messenger. + +```solidity + // slither-disable-next-line reentrancy-events + emit ETHDepositInitiated(_from, _to, msg.value, _data); + } +``` + +Ein Ereignis auslösen, um jede dezentralisierte Anwendung, die zuhört, über diese Übertragung zu informieren. + +```solidity + /** + * @inheritdoc IL1ERC20Bridge + */ + function depositERC20( + . + . + . + ) external virtual onlyEOA { + _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, msg.sender, _amount, _l2Gas, _data); + } + + /** + * @inheritdoc IL1ERC20Bridge + */ + function depositERC20To( + . + . + . + ) external virtual { + _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _l2Gas, _data); + } +``` + +Diese beiden Funktionen sind Wrapper um `_initiateERC20Deposit`, die Funktion, die die eigentliche ERC-20-Einzahlung abwickelt. + +```solidity + /** + * @dev Führt die Logik für Einzahlungen durch, indem der L2 Deposited Token + * Vertrag über die Einzahlung informiert wird und ein Handler aufgerufen wird, um die L1-Mittel zu sperren. (z. B. transferFrom) + * + * @param _l1Token Adresse des L1 ERC20, den wir einzahlen + * @param _l2Token Adresse des entsprechenden L2 ERC20 auf L1 + * @param _from Konto, von dem die Einzahlung auf L1 abgezogen wird + * @param _to Konto, dem die Einzahlung auf L2 gutgeschrieben wird + * @param _amount Einzuzahlender Betrag des ERC20. + * @param _l2Gas Gaslimit, das erforderlich ist, um die Einzahlung auf L2 abzuschließen. + * @param _data Optionale Daten zur Weiterleitung an L2. Diese Daten werden + * lediglich als Annehmlichkeit für externe Verträge zur Verfügung gestellt. Abgesehen von der Durchsetzung einer maximalen + * Länge geben diese Verträge keine Garantien über ihren Inhalt. + */ + function _initiateERC20Deposit( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + uint32 _l2Gas, + bytes calldata _data + ) internal { +``` + +Diese Funktion ist ähnlich wie `_initiateETHDeposit` oben, mit einigen wichtigen Unterschieden. +Der erste Unterschied besteht darin, dass diese Funktion die Token-Adressen und den zu übertragenden Betrag als Parameter erhält. +Im Fall von ETH enthält der Aufruf der Brücke bereits die Übertragung des Assets auf das Brückenkonto (`msg.value`). + +```solidity + // Wenn eine Einzahlung auf L1 initiiert wird, überträgt die L1-Brücke die Mittel an sich selbst für zukünftige + // Auszahlungen. safeTransferFrom prüft auch, ob der Vertrag Code hat, sodass dies fehlschlägt, wenn + // _from ein EOA oder address(0) ist. + // slither-disable-next-line reentrancy-events, reentrancy-benign + IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount); +``` + +Übertragungen von ERC-20-Token folgen einem anderen Prozess als ETH: + +1. Der Benutzer (`_from`) erteilt der Brücke eine Freigabe, um die entsprechenden Tokens zu übertragen. +2. Der Benutzer ruft die Brücke mit der Adresse des Token-Vertrags, dem Betrag usw. auf. +3. Die Brücke überträgt die Tokens (an sich selbst) als Teil des Einzahlungsprozesses. + +Der erste Schritt kann in einer separaten Transaktion von den letzten beiden erfolgen. +Front-Running ist jedoch kein Problem, da die beiden Funktionen, die `_initiateERC20Deposit` aufrufen (`depositERC20` und `depositERC20To`), diese Funktion nur mit `msg.sender` als `_from`-Parameter aufrufen. + +```solidity + // Konstruiere Calldata für _l2Token.finalizeDeposit(_to, _amount) + bytes memory message = abi.encodeWithSelector( + IL2ERC20Bridge.finalizeDeposit.selector, + _l1Token, + _l2Token, + _from, + _to, + _amount, + _data + ); + + // Sende Calldata nach L2 + // slither-disable-next-line reentrancy-events, reentrancy-benign + sendCrossDomainMessage(l2TokenBridge, _l2Gas, message); + + // slither-disable-next-line reentrancy-benign + deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] + _amount; +``` + +Fügen Sie den eingezahlten Betrag an Tokens zur `deposits`-Datenstruktur hinzu. +Es könnten mehrere Adressen auf L2 vorhanden sein, die demselben L1-ERC-20-Token entsprechen, daher ist es nicht ausreichend, das Guthaben der Brücke am L1-ERC-20-Token zu verwenden, um die Einzahlungen zu verfolgen. + +```solidity + + // slither-disable-next-line reentrancy-events + emit ERC20DepositInitiated(_l1Token, _l2Token, _from, _to, _amount, _data); + } + + /************************* + * Cross-Chain-Funktionen * + *************************/ + + /** + * @inheritdoc IL1StandardBridge + */ + function finalizeETHWithdrawal( + address _from, + address _to, + uint256 _amount, + bytes calldata _data +``` + +Die L2-Brücke sendet eine Nachricht an den L2-Cross-Domain-Messenger, der bewirkt, dass der L1-Cross-Domain-Messenger diese Funktion aufruft (natürlich sobald die [Transaktion, die die Nachricht finalisiert](https://community.optimism.io/docs/developers/bridge/messaging/#fees-for-l2-%E2%87%92-l1-transactions), auf L1 übermittelt wurde). + +```solidity + ) external onlyFromCrossDomainAccount(l2TokenBridge) { +``` + +Stellen Sie sicher, dass dies eine _legitime_ Nachricht ist, die vom Cross-Domain-Messenger kommt und von der L2-Token-Brücke stammt. +Diese Funktion wird verwendet, um ETH von der Brücke abzuheben, daher müssen wir sicherstellen, dass sie nur vom autorisierten Aufrufer aufgerufen wird. + +```solidity + // slither-disable-next-line reentrancy-events + (bool success, ) = _to.call{ value: _amount }(new bytes(0)); +``` + +Die Art und Weise, ETH zu übertragen, besteht darin, den Empfänger mit dem Betrag in Wei im `msg.value` aufzurufen. + +```solidity + require(success, "TransferHelper::safeTransferETH: ETH transfer failed"); + + // slither-disable-next-line reentrancy-events + emit ETHWithdrawalFinalized(_from, _to, _amount, _data); +``` + +Ein Ereignis über die Auszahlung auslösen. + +```solidity + } + + /** + * @inheritdoc IL1ERC20Bridge + */ + function finalizeERC20Withdrawal( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external onlyFromCrossDomainAccount(l2TokenBridge) { +``` + +Diese Funktion ist ähnlich wie `finalizeETHWithdrawal` oben, mit den notwendigen Änderungen für ERC-20-Tokens. + +```solidity + deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] - _amount; +``` + +Aktualisieren Sie die `deposits`-Datenstruktur. + +```solidity + + // Wenn eine Auszahlung auf L1 finalisiert wird, überträgt die L1-Brücke die Mittel an den Auszahlenden + // slither-disable-next-line reentrancy-events + IERC20(_l1Token).safeTransfer(_to, _amount); + + // slither-disable-next-line reentrancy-events + emit ERC20WithdrawalFinalized(_l1Token, _l2Token, _from, _to, _amount, _data); + } + + + /***************************** + * Vorübergehend - Migration von ETH * + *****************************/ + + /** + * @dev Fügt ETH-Guthaben zum Konto hinzu. Dies soll ermöglichen, dass ETH + * von einem alten Gateway zu einem neuen Gateway migriert wird. + * HINWEIS: Dies wird nur für ein Upgrade beibehalten, damit wir die migrierte ETH aus dem + * alten Vertrag empfangen können + */ + function donateETH() external payable {} +} +``` + +Es gab eine frühere Implementierung der Brücke. +Als wir von dieser Implementierung zu dieser wechselten, mussten wir alle Assets verschieben. +ERC-20-Tokens können einfach verschoben werden. +Um jedoch ETH an einen Vertrag zu übertragen, benötigen Sie die Zustimmung dieses Vertrags, was uns `donateETH` bietet. + +## ERC-20-Tokens auf L2 {#erc-20-tokens-on-l2} + +Damit ein ERC-20-Token in die Standardbrücke passt, muss es der Standardbrücke und _nur_ der Standardbrücke erlauben, Token zu prägen. +Dies ist notwendig, weil die Brücken sicherstellen müssen, dass die Anzahl der auf Optimism zirkulierenden Tokens gleich der Anzahl der im L1-Brückenvertrag gesperrten Tokens ist. +Wenn es zu viele Tokens auf L2 gibt, könnten einige Benutzer ihre Assets nicht zurück auf L1 überbrücken. +Anstelle einer vertrauenswürdigen Brücke würden wir im Wesentlichen [Mindestreservebanking](https://www.investopedia.com/terms/f/fractionalreservebanking.asp) nachbilden. +Wenn es zu viele Tokens auf L1 gibt, würden einige dieser Tokens für immer im Brückenvertrag gesperrt bleiben, weil es keine Möglichkeit gibt, sie ohne das Verbrennen von L2-Tokens freizugeben. + +### IL2StandardERC20 {#il2standarderc20} + +Jeder ERC-20-Token auf L2, der die Standardbrücke verwendet, muss [diese Schnittstelle](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/standards/IL2StandardERC20.sol) bereitstellen, die die Funktionen und Ereignisse enthält, die die Standardbrücke benötigt. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +``` + +[Die Standard-ERC-20-Schnittstelle](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) enthält nicht die Funktionen `mint` und `burn`. +Diese Methoden sind nicht durch [den ERC-20-Standard](https://eips.ethereum.org/EIPS/eip-20) vorgeschrieben, der die Mechanismen zur Erstellung und Zerstörung von Tokens nicht spezifiziert. + +```solidity +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +``` + +[Die ERC-165-Schnittstelle](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/IERC165.sol) wird verwendet, um anzugeben, welche Funktionen ein Vertrag bereitstellt. +[Sie können den Standard hier lesen](https://eips.ethereum.org/EIPS/eip-165). + +```solidity +interface IL2StandardERC20 is IERC20, IERC165 { + function l1Token() external returns (address); +``` + +Diese Funktion gibt die Adresse des L1-Tokens an, das auf diesen Vertrag überbrückt wird. +Beachten Sie, dass wir keine ähnliche Funktion in die entgegengesetzte Richtung haben. +Wir müssen in der Lage sein, jeden L1-Token zu überbrücken, unabhängig davon, ob bei seiner Implementierung eine L2-Unterstützung geplant war oder nicht. + +```solidity + + function mint(address _to, uint256 _amount) external; + + function burn(address _from, uint256 _amount) external; + + event Mint(address indexed _account, uint256 _amount); + event Burn(address indexed _account, uint256 _amount); +} +``` + +Funktionen und Ereignisse zum Prägen (Erstellen) und Verbrennen (Zerstören) von Tokens. +Die Brücke sollte die einzige Entität sein, die diese Funktionen ausführen kann, um sicherzustellen, dass die Anzahl der Tokens korrekt ist (gleich der Anzahl der auf L1 gesperrten Tokens). + +### L2StandardERC20 {#L2StandardERC20} + +[Dies ist unsere Implementierung der `IL2StandardERC20`-Schnittstelle](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/standards/L2StandardERC20.sol). +Sofern Sie keine benutzerdefinierte Logik benötigen, sollten Sie diese verwenden. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +``` + +[Der OpenZeppelin ERC-20-Vertrag](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol). +Optimism glaubt nicht daran, das Rad neu zu erfinden, besonders wenn das Rad gut geprüft ist und vertrauenswürdig genug sein muss, um Vermögenswerte zu halten. + +```solidity +import "./IL2StandardERC20.sol"; + +contract L2StandardERC20 is IL2StandardERC20, ERC20 { + address public l1Token; + address public l2Bridge; +``` + +Dies sind die beiden zusätzlichen Konfigurationsparameter, die wir benötigen und die ERC-20 normalerweise nicht hat. + +```solidity + + /** + * @param _l2Bridge Adresse der L2-Standardbrücke. + * @param _l1Token Adresse des entsprechenden L1-Tokens. + * @param _name ERC20-Name. + * @param _symbol ERC20-Symbol. + */ + constructor( + address _l2Bridge, + address _l1Token, + string memory _name, + string memory _symbol + ) ERC20(_name, _symbol) { + l1Token = _l1Token; + l2Bridge = _l2Bridge; + } +``` + +Zuerst den Konstruktor für den Vertrag aufrufen, von dem wir erben (`ERC20(_name, _symbol)`) und dann unsere eigenen Variablen setzen. + +```solidity + + modifier onlyL2Bridge() { + require(msg.sender == l2Bridge, "Nur die L2-Brücke kann prägen und verbrennen"); + _; + } + + + // slither-disable-next-line external-function + function supportsInterface(bytes4 _interfaceId) public pure returns (bool) { + bytes4 firstSupportedInterface = bytes4(keccak256("supportsInterface(bytes4)")); // ERC165 + bytes4 secondSupportedInterface = IL2StandardERC20.l1Token.selector ^ + IL2StandardERC20.mint.selector ^ + IL2StandardERC20.burn.selector; + return _interfaceId == firstSupportedInterface || _interfaceId == secondSupportedInterface; + } +``` + +So funktioniert [ERC-165](https://eips.ethereum.org/EIPS/eip-165). +Jede Schnittstelle ist eine Anzahl von unterstützten Funktionen und wird als [Exklusiv-Oder](https://en.wikipedia.org/wiki/Exclusive_or) der [ABI-Funktionsselektoren](https://docs.soliditylang.org/en/v0.8.12/abi-spec.html#function-selector) dieser Funktionen identifiziert. + +Die L2-Brücke verwendet ERC-165 als Plausibilitätsprüfung, um sicherzustellen, dass der ERC-20-Vertrag, an den sie Assets sendet, ein `IL2StandardERC20` ist. + +**Hinweis:** Es gibt nichts, was betrügerische Verträge daran hindert, falsche Antworten auf `supportsInterface` zu geben, daher ist dies ein Plausibilitätsprüfungsmechanismus, _kein_ Sicherheitsmechanismus. + +```solidity + // slither-disable-next-line external-function + function mint(address _to, uint256 _amount) public virtual onlyL2Bridge { + _mint(_to, _amount); + + emit Mint(_to, _amount); + } + + // slither-disable-next-line external-function + function burn(address _from, uint256 _amount) public virtual onlyL2Bridge { + _burn(_from, _amount); + + emit Burn(_from, _amount); + } +} +``` + +Nur die L2-Brücke darf Assets prägen und verbrennen. + +`_mint` und `_burn` sind tatsächlich im [OpenZeppelin ERC-20-Vertrag](/developers/tutorials/erc20-annotated-code/#the-_mint-and-_burn-functions-_mint-and-_burn) definiert. +Dieser Vertrag macht sie nur nicht extern zugänglich, weil die Bedingungen zum Prägen und Verbrennen von Tokens so vielfältig sind wie die Anzahl der Verwendungsmöglichkeiten von ERC-20. + +## L2-Brücken-Code {#l2-bridge-code} + +Dies ist der Code, der die Brücke auf Optimism ausführt. +[Die Quelle für diesen Vertrag befindet sich hier](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol). + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +/* Schnittstellen-Importe */ +import { IL1StandardBridge } from "../../L1/messaging/IL1StandardBridge.sol"; +import { IL1ERC20Bridge } from "../../L1/messaging/IL1ERC20Bridge.sol"; +import { IL2ERC20Bridge } from "./IL2ERC20Bridge.sol"; +``` + +Die [IL2ERC20Bridge](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol)-Schnittstelle ist dem [L1-Äquivalent](#IL1ERC20Bridge), das wir oben gesehen haben, sehr ähnlich. +Es gibt zwei wesentliche Unterschiede: + +1. Auf L1 initiieren Sie Einzahlungen und finalisieren Auszahlungen. + Hier initiieren Sie Auszahlungen und finalisieren Einzahlungen. +2. Auf L1 ist es notwendig, zwischen ETH- und ERC-20-Tokens zu unterscheiden. + Auf L2 können wir dieselben Funktionen für beide verwenden, da intern ETH-Guthaben auf Optimism als ERC-20-Token mit der Adresse [0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000](https://explorer.optimism.io/address/0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000) behandelt werden. + +```solidity +/* Bibliotheks-Importe */ +import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; +import { CrossDomainEnabled } from "../../libraries/bridge/CrossDomainEnabled.sol"; +import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol"; + +/* Vertrags-Importe */ +import { IL2StandardERC20 } from "../../standards/IL2StandardERC20.sol"; + +/** + * @title L2StandardBridge + * @dev Die L2-Standardbrücke ist ein Vertrag, der zusammen mit der L1-Standardbrücke + * ETH- und ERC20-Übergänge zwischen L1 und L2 ermöglicht. + * Dieser Vertrag fungiert als Minter für neue Tokens, wenn er von Einzahlungen in die L1-Standardbrücke + * erfährt. + * Dieser Vertrag fungiert auch als Burner der für die Auszahlung vorgesehenen Tokens und informiert die L1- + * Brücke, L1-Mittel freizugeben. + */ +contract L2StandardBridge is IL2ERC20Bridge, CrossDomainEnabled { + /******************************** + * Externe Vertragsreferenzen * + ********************************/ + + address public l1TokenBridge; +``` + +Die Adresse der L1-Brücke im Auge behalten. +Beachten Sie, dass wir im Gegensatz zum L1-Äquivalent hier diese Variable _brauchen_. +Die Adresse der L1-Brücke ist nicht im Voraus bekannt. + +```solidity + + /*************** + * Konstruktor * + ***************/ + + /** + * @param _l2CrossDomainMessenger Von diesem Vertrag verwendeter domänenübergreifender Messenger. + * @param _l1TokenBridge Adresse der auf der Hauptkette bereitgestellten L1-Brücke. + */ + constructor(address _l2CrossDomainMessenger, address _l1TokenBridge) + CrossDomainEnabled(_l2CrossDomainMessenger) + { + l1TokenBridge = _l1TokenBridge; + } + + /*************** + * Auszahlung * + ***************/ + + /** + * @inheritdoc IL2ERC20Bridge + */ + function withdraw( + address _l2Token, + uint256 _amount, + uint32 _l1Gas, + bytes calldata _data + ) external virtual { + _initiateWithdrawal(_l2Token, msg.sender, msg.sender, _amount, _l1Gas, _data); + } + + /** + * @inheritdoc IL2ERC20Bridge + */ + function withdrawTo( + address _l2Token, + address _to, + uint256 _amount, + uint32 _l1Gas, + bytes calldata _data + ) external virtual { + _initiateWithdrawal(_l2Token, msg.sender, _to, _amount, _l1Gas, _data); + } +``` + +Diese beiden Funktionen leiten Auszahlungen ein. +Beachten Sie, dass die L1-Token-Adresse nicht angegeben werden muss. +Es wird erwartet, dass L2-Tokens uns die Adresse des L1-Äquivalents mitteilen. + +```solidity + + /** + * @dev Führt die Logik für Auszahlungen durch, indem der Token verbrannt und + * das L1-Token-Gateway über die Auszahlung informiert wird. + * @param _l2Token Adresse des L2-Tokens, bei dem die Auszahlung eingeleitet wird. + * @param _from Konto, von dem die Auszahlung auf L2 abgezogen wird. + * @param _to Konto, dem die Auszahlung auf L1 gutgeschrieben wird. + * @param _amount Betrag des abzuhebenden Tokens. + * @param _l1Gas Unbenutzt, aber aus Gründen der potenziellen Vorwärtskompatibilität enthalten. + * @param _data Optionale Daten zur Weiterleitung an L1. Diese Daten werden + * lediglich als Annehmlichkeit für externe Verträge zur Verfügung gestellt. Abgesehen von der Durchsetzung einer maximalen + * Länge geben diese Verträge keine Garantien über ihren Inhalt. + */ + function _initiateWithdrawal( + address _l2Token, + address _from, + address _to, + uint256 _amount, + uint32 _l1Gas, + bytes calldata _data + ) internal { + // Wenn eine Auszahlung eingeleitet wird, verbrennen wir die Mittel des Auszahlenden, um eine nachfolgende L2- + // Nutzung zu verhindern + // slither-disable-next-line reentrancy-events + IL2StandardERC20(_l2Token).burn(msg.sender, _amount); +``` + +Beachten Sie, dass wir uns _nicht_ auf den `_from`-Parameter verlassen, sondern auf `msg.sender`, was viel schwerer zu fälschen ist (soweit ich weiß, unmöglich). + +```solidity + + // Konstruiere Calldata für l1TokenBridge.finalizeERC20Withdrawal(_to, _amount) + // slither-disable-next-line reentrancy-events + address l1Token = IL2StandardERC20(_l2Token).l1Token(); + bytes memory message; + + if (_l2Token == Lib_PredeployAddresses.OVM_ETH) { +``` + +Auf L1 ist es notwendig, zwischen ETH und ERC-20 zu unterscheiden. + +```solidity + message = abi.encodeWithSelector( + IL1StandardBridge.finalizeETHWithdrawal.selector, + _from, + _to, + _amount, + _data + ); + } else { + message = abi.encodeWithSelector( + IL1ERC20Bridge.finalizeERC20Withdrawal.selector, + l1Token, + _l2Token, + _from, + _to, + _amount, + _data + ); + } + + // Nachricht an L1-Brücke senden + // slither-disable-next-line reentrancy-events + sendCrossDomainMessage(l1TokenBridge, _l1Gas, message); + + // slither-disable-next-line reentrancy-events + emit WithdrawalInitiated(l1Token, _l2Token, msg.sender, _to, _amount, _data); + } + + /************************************ + * Cross-Chain-Funktion: Einzahlung * + ************************************/ + + /** + * @inheritdoc IL2ERC20Bridge + */ + function finalizeDeposit( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data +``` + +Diese Funktion wird von `L1StandardBridge` aufgerufen. + +```solidity + ) external virtual onlyFromCrossDomainAccount(l1TokenBridge) { +``` + +Stellen Sie sicher, dass die Quelle der Nachricht legitim ist. +Dies ist wichtig, da diese Funktion `_mint` aufruft und verwendet werden könnte, um Tokens auszugeben, die nicht durch Tokens gedeckt sind, die die Brücke auf L1 besitzt. + +```solidity + // Prüfen Sie, ob der Ziel-Token konform ist und + // überprüfen Sie, ob der eingezahlte Token auf L1 mit der L2-Darstellung des eingezahlten Tokens hier übereinstimmt + if ( + // slither-disable-next-line reentrancy-events + ERC165Checker.supportsInterface(_l2Token, 0x1d1d8b63) && + _l1Token == IL2StandardERC20(_l2Token).l1Token() +``` + +Plausibilitätsprüfungen: + +1. Die richtige Schnittstelle wird unterstützt +2. Die L1-Adresse des L2-ERC-20-Vertrags stimmt mit der L1-Quelle der Tokens überein + +```solidity + ) { + // Wenn eine Einzahlung abgeschlossen ist, schreiben wir dem Konto auf L2 den gleichen Betrag an + // Tokens gut. + // slither-disable-next-line reentrancy-events + IL2StandardERC20(_l2Token).mint(_to, _amount); + // slither-disable-next-line reentrancy-events + emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _data); +``` + +Wenn die Plausibilitätsprüfungen erfolgreich sind, schließen Sie die Einzahlung ab: + +1. Prägen Sie die Tokens +2. Das entsprechende Ereignis auslösen + +```solidity + } else { + // Entweder ist der L2-Token, in den eingezahlt wird, mit der korrekten Adresse + // seines L1-Tokens nicht einverstanden oder er unterstützt nicht die korrekte Schnittstelle. + // Dies sollte nur passieren, wenn es einen bösartigen L2-Token gibt oder wenn ein Benutzer irgendwie + // die falsche L2-Token-Adresse für die Einzahlung angegeben hat. + // In jedem Fall stoppen wir den Prozess hier und erstellen eine Auszahlungsnachricht + //, damit Benutzer in einigen Fällen ihre Gelder abheben können. + // Es gibt keine Möglichkeit, bösartige Token-Verträge vollständig zu verhindern, aber dies begrenzt + // Benutzerfehler und mildert einige Formen von bösartigem Vertragsverhalten. +``` + +Wenn ein Benutzer einen erkennbaren Fehler gemacht hat, indem er die falsche L2-Token-Adresse verwendet hat, möchten wir die Einzahlung stornieren und die Tokens auf L1 zurückgeben. +Der einzige Weg, wie wir dies von L2 aus tun können, ist das Senden einer Nachricht, die den Fehler-Anfechtungszeitraum abwarten muss, aber das ist für den Benutzer viel besser als der dauerhafte Verlust der Tokens. + +```solidity + bytes memory message = abi.encodeWithSelector( + IL1ERC20Bridge.finalizeERC20Withdrawal.selector, + _l1Token, + _l2Token, + _to, // hier _to und _from vertauscht, um die Einzahlung an den Absender zurückzuspringen + _from, + _amount, + _data + ); + + // Nachricht an die L1-Brücke senden + // slither-disable-next-line reentrancy-events + sendCrossDomainMessage(l1TokenBridge, 0, message); + // slither-disable-next-line reentrancy-events + emit DepositFailed(_l1Token, _l2Token, _from, _to, _amount, _data); + } + } +} +``` + +## Fazit {#conclusion} + +Die Standard-Brücke ist der flexibelste Mechanismus für Asset-Übertragungen. +Da er jedoch so generisch ist, ist er nicht immer der einfachste zu verwendende Mechanismus. +Insbesondere für Auszahlungen bevorzugen die meisten Benutzer [Drittanbieter-Brücken](https://optimism.io/apps#bridge), die nicht auf den Anfechtungszeitraum warten und keinen Merkle-Beweis benötigen, um die Auszahlung abzuschließen. + +Diese Brücken funktionieren typischerweise, indem sie Assets auf L1 haben, die sie sofort gegen eine geringe Gebühr (oft weniger als die Gaskosten für eine Standard-Brückenauszahlung) zur Verfügung stellen. +Wenn die Brücke (oder die Personen, die sie betreiben) erwartet, dass sie auf L1 knapp bei Kasse ist, überträgt sie ausreichend Vermögenswerte von L2. Da es sich hierbei um sehr große Auszahlungen handelt, werden die Auszahlungskosten auf einen großen Betrag verteilt und machen einen viel kleineren Prozentsatz aus. + +Hoffentlich hat Ihnen dieser Artikel geholfen, besser zu verstehen, wie Layer 2 funktioniert und wie man klaren und sicheren Solidity-Code schreibt. + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). diff --git a/public/content/translations/de/developers/tutorials/reverse-engineering-a-contract/index.md b/public/content/translations/de/developers/tutorials/reverse-engineering-a-contract/index.md new file mode 100644 index 00000000000..87a88af2535 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/reverse-engineering-a-contract/index.md @@ -0,0 +1,744 @@ +--- +title: "Reverse Engineering eines Contracts" +description: Wie Sie einen Contract verstehen, wenn Sie den Quellcode nicht haben +author: Ori Pomerantz +lang: de +tags: ["evm", "opcodes"] +skill: advanced +published: 2021-12-30 +--- + +## Einführung {#introduction} + +_Auf der Blockchain gibt es keine Geheimnisse_, alles, was geschieht, ist konsistent, nachprüfbar und öffentlich zugänglich. Idealerweise sollten [Contracts ihren Quellcode auf Etherscan veröffentlichen und verifizieren lassen](https://etherscan.io/address/0xb8901acb165ed027e32754e0ffe830802919727f#code). [Das ist jedoch nicht immer der Fall](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f#code). In diesem Artikel lernen Sie, wie Sie Contracts per Reverse Engineering analysieren, indem Sie sich einen Contract ohne Quellcode ansehen: [`0x2510c039cc3b061d79e564b38836da87e31b342f`](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f). + +Es gibt Reverse-Compiler, aber sie liefern nicht immer [brauchbare Ergebnisse](https://etherscan.io/bytecode-decompiler?a=0x2510c039cc3b061d79e564b38836da87e31b342f). In diesem Artikel lernen Sie, wie Sie einen Contract manuell per Reverse Engineering analysieren und anhand [der Opcodes](https://github.com/wolflo/evm-opcodes) verstehen können, und wie Sie die Ergebnisse eines Decompilers interpretieren. + +Um diesen Artikel zu verstehen, sollten Sie bereits die Grundlagen der EVM kennen und zumindest einigermaßen mit EVM-Assembler vertraut sein. [Hier können Sie mehr über diese Themen lesen](https://medium.com/mycrypto/the-ethereum-virtual-machine-how-does-it-work-9abac2b7c9e). + +## Vorbereitung des ausführbaren Codes {#prepare-the-executable-code} + +Sie können die Opcodes abrufen, indem Sie auf Etherscan zum Contract navigieren, auf den Tab **Contract** und dann auf **Switch to Opcodes View** klicken. Sie erhalten eine Ansicht mit einem Opcode pro Zeile. + +![Opcode-Ansicht von Etherscan](opcode-view.png) + +Um Sprünge (Jumps) zu verstehen, müssen Sie jedoch wissen, wo sich die einzelnen Opcodes im Code befinden. Eine Möglichkeit besteht darin, ein Google Spreadsheet zu öffnen und die Opcodes in Spalte C einzufügen. [Sie können die folgenden Schritte überspringen, indem Sie eine Kopie dieser bereits vorbereiteten Tabelle erstellen](https://docs.google.com/spreadsheets/d/1tKmTJiNjUwHbW64wCKOSJxHjmh0bAUapt6btUYE7kDA/edit?usp=sharing). + +Der nächste Schritt besteht darin, die korrekten Code-Positionen zu ermitteln, damit wir die Sprünge verstehen können. Wir tragen die Opcode-Größe in Spalte B und die Position (in hexadezimaler Schreibweise) in Spalte A ein. Geben Sie diese Funktion in Zelle `B1` ein und kopieren Sie sie dann für den Rest von Spalte B bis zum Ende des Codes. Danach können Sie Spalte B ausblenden. + +``` +=1+IF(REGEXMATCH(C1,"PUSH"),REGEXEXTRACT(C1,"PUSH(\d+)"),0) +``` + +Zuerst fügt diese Funktion ein Byte für den Opcode selbst hinzu und sucht dann nach `PUSH`. Push-Opcodes sind besonders, da sie zusätzliche Bytes für den Wert benötigen, der gepusht wird. Wenn der Opcode ein `PUSH` ist, extrahieren wir die Anzahl der Bytes und addieren sie. + +In `A1` setzen Sie den ersten Offset, Null. Geben Sie dann in `A2` diese Funktion ein und kopieren Sie sie wieder für den Rest der Spalte A: + +``` +=dec2hex(hex2dec(A1)+B1) +``` + +Wir benötigen diese Funktion, um den hexadezimalen Wert zu erhalten, da die Werte, die vor Sprüngen (`JUMP` und `JUMPI`) gepusht werden, in hexadezimaler Form angegeben werden. + +## Der Einstiegspunkt (0x00) {#the-entry-point-0x00} + +Contracts werden immer ab dem ersten Byte ausgeführt. Dies ist der erste Teil des Codes: + +| Offset | Opcode | Stack (nach dem Opcode) | +| -----: | ------------ | ---------------------------------------------- | +| 0 | PUSH1 0x80 | 0x80 | +| 2 | PUSH1 0x40 | 0x40, 0x80 | +| 4 | MSTORE | Leer | +| 5 | PUSH1 0x04 | 0x04 | +| 7 | CALLDATASIZE | CALLDATASIZE 0x04 | +| 8 | LT | CALLDATASIZE\<4 | +| 9 | PUSH2 0x005e | 0x5E CALLDATASIZE\<4 | +| C | JUMPI | Leer | + +Dieser Code macht zwei Dinge: + +1. Schreibt 0x80 als 32-Byte-Wert in die Speicherstellen 0x40-0x5F (0x80 wird in 0x5F gespeichert, und 0x40-0x5E sind alle Nullen). +2. Liest die Calldata-Größe. Normalerweise folgen die Calldata für einen Ethereum-Contract [dem ABI (Application Binary Interface)](https://docs.soliditylang.org/en/v0.8.10/abi-spec.html), das mindestens vier Bytes für den Funktions-Selektor erfordert. Wenn die Calldata-Größe kleiner als vier ist, springe zu 0x5E. + +![Flussdiagramm für diesen Abschnitt](flowchart-entry.png) + +### Der Handler bei 0x5E (für Nicht-ABI-Calldata) {#the-handler-at-0x5e-for-non-abi-call-data} + +| Offset | Opcode | +| -----: | ------------ | +| 5E | JUMPDEST | +| 5F | CALLDATASIZE | +| 60 | PUSH2 0x007c | +| 63 | JUMPI | + +Dieses Snippet beginnt mit einem `JUMPDEST`. EVM-Programme (Ethereum Virtual Machine) lösen eine Ausnahme aus, wenn Sie zu einem Opcode springen, der nicht `JUMPDEST` ist. Dann prüft er die CALLDATASIZE und springt, wenn sie „wahr“ ist (d. h. nicht null), zu 0x7C. Darauf kommen wir unten zu sprechen. + +| Offset | Opcode | Stack (nach Opcode) | +| -----: | ---------- | --------------------------------------------------------------------------------------------------------------------- | +| 64 | CALLVALUE | [Wei](/glossary/#wei), der durch den Aufruf bereitgestellt wird. Wird in Solidity `msg.value` genannt | +| 65 | PUSH1 0x06 | 6 CALLVALUE | +| 67 | PUSH1 0x00 | 0 6 CALLVALUE | +| 69 | DUP3 | CALLVALUE 0 6 CALLVALUE | +| 6A | DUP3 | 6 CALLVALUE 0 6 CALLVALUE | +| 6B | SLOAD | Speicher[6] CALLVALUE 0 6 CALLVALUE | + +Wenn es also keine Calldata gibt, lesen wir den Wert von Speicher[6]. Wir wissen noch nicht, was dieser Wert ist, aber wir können nach Transaktionen suchen, die der Contract ohne Calldata erhalten hat. Transaktionen, die nur ETH ohne Calldata (und damit ohne Methode) übertragen, haben in Etherscan die Methode `Transfer`. Tatsächlich ist [die allererste Transaktion, die der Contract erhalten hat](https://etherscan.io/tx/0xeec75287a583c36bcc7ca87685ab41603494516a0f5986d18de96c8e630762e7), ein Transfer. + +Wenn wir uns diese Transaktion ansehen und auf **Click to see More** klicken, sehen wir, dass die Calldata, als Input Data bezeichnet, tatsächlich leer sind (`0x`). Beachten Sie auch, dass der Wert 1,559 ETH beträgt, was später relevant sein wird. + +![Die Calldata sind leer](calldata-empty.png) + +Klicken Sie als Nächstes auf den Tab **State** und erweitern Sie den Contract, den wir per Reverse Engineering analysieren (0x2510...). Sie können sehen, dass sich `Speicher[6]` während der Transaktion geändert hat, und wenn Sie Hex auf **Number** ändern, sehen Sie, dass es 1.559.000.000.000.000.000 wurde, der in Wei übertragene Wert (ich habe die Kommas zur besseren Lesbarkeit hinzugefügt), der dem nächsten Contract-Wert entspricht. + +![Die Änderung in Speicher[6]](storage6.png) + +Wenn wir uns die Zustandsänderungen ansehen, die durch [andere `Transfer`-Transaktionen aus demselben Zeitraum](https://etherscan.io/tx/0xf708d306de39c422472f43cb975d97b66fd5d6a6863db627067167cbf93d84d1#statechange) verursacht wurden, sehen wir, dass `Speicher[6]` den Wert des Contracts eine Zeit lang nachverfolgt hat. Vorerst nennen wir es `Wert*`. Das Sternchen (`*`) erinnert uns daran, dass wir noch nicht _wissen_, was diese Variable tut, aber sie kann nicht nur dazu dienen, den Contract-Wert zu verfolgen, da es nicht nötig ist, Speicher zu verwenden, der sehr teuer ist, wenn man den Kontostand seines Accounts mit `ADDRESS BALANCE` abrufen kann. Der erste Opcode pusht die eigene Adresse des Contracts. Der zweite liest die Adresse an der Spitze des Stacks und ersetzt sie durch das Guthaben dieser Adresse. + +| Offset | Opcode | Stack | +| -----: | ------------ | ------------------------------------------ | +| 6C | PUSH2 0x0075 | 0x75 Wert\* CALLVALUE 0 6 CALLVALUE | +| 6F | SWAP2 | CALLVALUE Wert\* 0x75 0 6 CALLVALUE | +| 70 | SWAP1 | Wert\* CALLVALUE 0x75 0 6 CALLVALUE | +| 71 | PUSH2 0x01a7 | 0x01A7 Wert\* CALLVALUE 0x75 0 6 CALLVALUE | +| 74 | JUMP | | + +Wir werden diesen Code am Sprungziel weiterverfolgen. + +| Offset | Opcode | Stack | +| -----: | ---------- | ---------------------------------------------------------- | +| 1A7 | JUMPDEST | Wert\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1A8 | PUSH1 0x00 | 0x00 Wert\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AA | DUP3 | CALLVALUE 0x00 Wert\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AB | NOT | 2^256-CALLVALUE-1 0x00 Wert\* CALLVALUE 0x75 0 6 CALLVALUE | + +Das `NOT` ist bitweise, also kehrt es den Wert jedes Bits im Aufrufwert um. + +| Offset | Opcode | Stack | +| -----: | ------------ | ---------------------------------------------------------------------------------------------------- | +| 1AC | DUP3 | Wert\* 2^256-CALLVALUE-1 0x00 Wert\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AD | GT | Wert\*>2^256-CALLVALUE-1 0x00 Wert\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AE | ISZERO | Wert\*\<=2^256-CALLVALUE-1 0x00 Wert\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AF | PUSH2 0x01df | 0x01DF Wert\*\<=2^256-CALLVALUE-1 0x00 Wert\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1B2 | JUMPI | | + +Wir springen, wenn `Wert*` kleiner oder gleich 2^256-CALLVALUE-1 ist. Das sieht nach einer Logik zur Vermeidung von Überläufen aus. Und tatsächlich sehen wir, dass der Contract nach ein paar unsinnigen Operationen (z. B. das Schreiben in den Speicher, der gleich gelöscht wird) bei Offset 0x01DE zurückgesetzt wird, wenn der Überlauf erkannt wird, was ein normales Verhalten ist. + +Beachten Sie, dass ein solcher Überlauf extrem unwahrscheinlich ist, da der Aufrufwert plus `Wert*` vergleichbar mit 2^256 Wei sein müsste, was etwa 10^59 ETH entspricht. [Die gesamte ETH-Menge beträgt zum Zeitpunkt der Erstellung dieses Artikels weniger als zweihundert Millionen](https://etherscan.io/stat/supply). + +| Offset | Opcode | Stack | +| -----: | -------- | ---------------------------------------- | +| 1DF | JUMPDEST | 0x00 Wert\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1E0 | POP | Wert\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1E1 | ADD | Wert\*+CALLVALUE 0x75 0 6 CALLVALUE | +| 1E2 | SWAP1 | 0x75 Wert\*+CALLVALUE 0 6 CALLVALUE | +| 1E3 | JUMP | | + +Wenn wir hier angekommen sind, erhalten wir `Wert* + CALLVALUE` und springen zu Offset 0x75. + +| Offset | Opcode | Stack | +| -----: | -------- | ------------------------------ | +| 75 | JUMPDEST | Wert\*+CALLVALUE 0 6 CALLVALUE | +| 76 | SWAP1 | 0 Wert\*+CALLVALUE 6 CALLVALUE | +| 77 | SWAP2 | 6 Wert\*+CALLVALUE 0 CALLVALUE | +| 78 | SSTORE | 0 CALLVALUE | + +Wenn wir hier ankommen (was leere Calldata voraussetzt), addieren wir den Aufrufwert zu `Wert*`. Dies stimmt mit dem überein, was `Transfer`-Transaktionen laut unserer Aussage tun. + +| Offset | Opcode | +| -----: | ------ | +| 79 | POP | +| 7A | POP | +| 7B | STOP | + +Leeren Sie schließlich den Stack (was nicht notwendig ist) und signalisieren Sie das erfolgreiche Ende der Transaktion. + +Zusammenfassend finden Sie hier ein Flussdiagramm für den ursprünglichen Code. + +![Flussdiagramm für den Einstiegspunkt](flowchart-entry.png) + +## Der Handler bei 0x7C {#the-handler-at-0x7c} + +Ich habe absichtlich nicht in die Überschrift geschrieben, was dieser Handler tut. Der Punkt ist nicht, Ihnen beizubringen, wie dieser spezielle Contract funktioniert, sondern wie man Contracts per Reverse Engineering analysiert. Sie werden auf die gleiche Weise wie ich lernen, was er tut, indem Sie dem Code folgen. + +Wir gelangen von mehreren Stellen hierher: + +- Wenn Calldata von 1, 2 oder 3 Bytes vorhanden sind (von Offset 0x63) +- Wenn die Methodensignatur unbekannt ist (von Offsets 0x42 und 0x5D) + +| Offset | Opcode | Stack | +| -----: | ------------ | ------------------------------------------------------------------------- | +| 7C | JUMPDEST | | +| 7D | PUSH1 0x00 | 0x00 | +| 7F | PUSH2 0x009d | 0x9D 0x00 | +| 82 | PUSH1 0x03 | 0x03 0x9D 0x00 | +| 84 | SLOAD | Speicher[3] 0x9D 0x00 | + +Dies ist eine weitere Speicherzelle, die ich in keiner Transaktion finden konnte, so dass es schwieriger ist zu wissen, was sie bedeutet. Der folgende Code wird dies verdeutlichen. + +| Offset | Opcode | Stack | +| -----: | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| 85 | PUSH20 0xffffffffffffffffffffffffffffffffffffffff | 0xff....ff Speicher[3] 0x9D 0x00 | +| 9A | AND | Speicher[3]-als-Adresse 0x9D 0x00 | + +Diese Opcodes kürzen den Wert, den wir aus Speicher[3] lesen, auf 160 Bit, die Länge einer Ethereum-Adresse. + +| Offset | Opcode | Stack | +| -----: | ------ | ------------------------------------------------------------------------------------- | +| 9B | SWAP1 | 0x9D Speicher[3]-als-Adresse 0x00 | +| 9C | JUMP | Speicher[3]-als-Adresse 0x00 | + +Dieser Sprung ist überflüssig, da wir zum nächsten Opcode gehen. Dieser Code ist bei weitem nicht so gas-effizient, wie er sein könnte. + +| Offset | Opcode | Stack | +| -----: | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| 9D | JUMPDEST | Speicher[3]-als-Adresse 0x00 | +| 9E | SWAP1 | 0x00 Speicher[3]-als-Adresse | +| 9F | POP | Speicher[3]-als-Adresse | +| A0 | PUSH1 0x40 | 0x40 Speicher[3]-als-Adresse | +| A2 | MLOAD | Mem[0x40] Speicher[3]-als-Adresse | + +Ganz am Anfang des Codes setzen wir Mem[0x40] auf 0x80. Wenn wir später nach 0x40 suchen, sehen wir, dass wir es nicht ändern - wir können also davon ausgehen, dass es 0x80 ist. + +| Offset | Opcode | Stack | +| -----: | ------------ | ------------------------------------------------------------------------------------------------------- | +| A3 | CALLDATASIZE | CALLDATASIZE 0x80 Speicher[3]-als-Adresse | +| A4 | PUSH1 0x00 | 0x00 CALLDATASIZE 0x80 Speicher[3]-als-Adresse | +| A6 | DUP3 | 0x80 0x00 CALLDATASIZE 0x80 Speicher[3]-als-Adresse | +| A7 | CALLDATACOPY | 0x80 Speicher[3]-als-Adresse | + +Kopieren Sie alle Calldata in den Speicher, beginnend bei 0x80. + +| Offset | Opcode | Stack | +| -----: | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| A8 | PUSH1 0x00 | 0x00 0x80 Speicher[3]-als-Adresse | +| AA | DUP1 | 0x00 0x00 0x80 Speicher[3]-als-Adresse | +| AB | CALLDATASIZE | CALLDATASIZE 0x00 0x00 0x80 Speicher[3]-als-Adresse | +| AC | DUP4 | 0x80 CALLDATASIZE 0x00 0x00 0x80 Speicher[3]-als-Adresse | +| AD | DUP6 | Speicher[3]-als-Adresse 0x80 CALLDATASIZE 0x00 0x00 0x80 Speicher[3]-als-Adresse | +| AE | GAS | GAS Speicher[3]-als-Adresse 0x80 CALLDATASIZE 0x00 0x00 0x80 Speicher[3]-als-Adresse | +| AF | DELEGATE_CALL | | + +Jetzt sind die Dinge viel klarer. Dieser Contract kann als [Proxy](https://blog.openzeppelin.com/proxy-patterns/) fungieren und die Adresse in Speicher[3] aufrufen, um die eigentliche Arbeit zu erledigen. `DELEGATE_CALL` ruft einen separaten Contract auf, bleibt aber im selben Speicher. Das bedeutet, dass der delegierte Contract, für den wir ein Proxy sind, auf denselben Speicherplatz zugreift. Die Parameter für den Aufruf sind: + +- _Gas_: Das gesamte verbleibende Gas +- _Aufgerufene Adresse_: Speicher[3]-als-Adresse +- _Calldata_: Die CALLDATASIZE-Bytes, die bei 0x80 beginnen, wo wir die ursprünglichen Calldata platziert haben +- _Rückgabedaten_: Keine (0x00 - 0x00) Wir erhalten die Rückgabedaten auf andere Weise (siehe unten) + +| Offset | Opcode | Stack | +| -----: | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| B0 | RETURNDATASIZE | RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| B1 | DUP1 | RETURNDATASIZE RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| B2 | PUSH1 0x00 | 0x00 RETURNDATASIZE RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| B4 | DUP5 | 0x80 0x00 RETURNDATASIZE RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| B5 | RETURNDATACOPY | RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | + +Hier kopieren wir alle Rückgabedaten in den Speicherpuffer, beginnend bei 0x80. + +| Offset | Opcode | Stack | +| -----: | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| B6 | DUP2 | (((Aufruf erfolgreich/fehlgeschlagen))) RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| B7 | DUP1 | (((Aufruf erfolgreich/fehlgeschlagen))) (((Aufruf erfolgreich/fehlgeschlagen))) RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| B8 | ISZERO | (((ist der Aufruf fehlgeschlagen))) (((Aufruf erfolgreich/fehlgeschlagen))) RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| B9 | PUSH2 0x00c0 | 0xC0 (((ist der Aufruf fehlgeschlagen))) (((Aufruf erfolgreich/fehlgeschlagen))) RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| BC | JUMPI | (((Aufruf erfolgreich/fehlgeschlagen))) RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| BD | DUP2 | RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| BE | DUP5 | 0x80 RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| BF | RETURN | | + +Nach dem Aufruf kopieren wir also die Rückgabedaten in den Puffer 0x80 - 0x80+RETURNDATASIZE, und wenn der Aufruf erfolgreich ist, führen wir `RETURN` mit genau diesem Puffer aus. + +### DELEGATECALL fehlgeschlagen {#delegatecall-failed} + +Wenn wir hier, bei 0xC0, ankommen, bedeutet das, dass der aufgerufene Contract zurückgesetzt wurde. Da wir nur ein Proxy für diesen Contract sind, wollen wir dieselben Daten zurückgeben und ebenfalls zurücksetzen. + +| Offset | Opcode | Stack | +| -----: | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| C0 | JUMPDEST | (((Aufruf erfolgreich/fehlgeschlagen))) RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| C1 | DUP2 | RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| C2 | DUP5 | 0x80 RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) RETURNDATASIZE (((Aufruf erfolgreich/fehlgeschlagen))) 0x80 Speicher[3]-als-Adresse | +| C3 | REVERT | | + +Wir führen also `REVERT` mit demselben Puffer aus, den wir zuvor für `RETURN` verwendet haben: 0x80 - 0x80+RETURNDATASIZE + +![Aufruf an Proxy-Flussdiagramm](flowchart-proxy.png) + +## ABI-Aufrufe {#abi-calls} + +Wenn die Calldata-Größe vier Bytes oder mehr beträgt, könnte dies ein gültiger ABI-Aufruf sein. + +| Offset | Opcode | Stack | +| -----: | ------------ | ------------------------------------------------------------------------------------------------------------------------- | +| D | PUSH1 0x00 | 0x00 | +| F | CALLDATALOAD | (((Erstes Wort (256 Bit) der Calldata))) | +| 10 | PUSH1 0xe0 | 0xE0 (((Erstes Wort (256 Bit) der Calldata))) | +| 12 | SHR | (((erste 32 Bits (4 Bytes) der Calldata))) | + +Etherscan teilt uns mit, dass `1C` ein unbekannter Opcode ist, da [er hinzugefügt wurde, nachdem Etherscan diese Funktion geschrieben hat](https://eips.ethereum.org/EIPS/eip-145) und sie sie nicht aktualisiert haben. Eine [aktuelle Opcode-Tabelle](https://github.com/wolflo/evm-opcodes) zeigt uns, dass dies eine Rechtsverschiebung ist + +| Offset | Opcode | Stack | +| -----: | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 13 | DUP1 | (((erste 32 Bits (4 Bytes) der Calldata))) (((erste 32 Bits (4 Bytes) der Calldata))) | +| 14 | PUSH4 0x3cd8045e | 0x3CD8045E (((erste 32 Bits (4 Bytes) der Calldata))) (((erste 32 Bits (4 Bytes) der Calldata))) | +| 19 | GT | 0x3CD8045E>erste-32-Bits-der-Calldata (((erste 32 Bits (4 Bytes) der Calldata))) | +| 1A | PUSH2 0x0043 | 0x43 0x3CD8045E>erste-32-Bits-der-Calldata (((erste 32 Bits (4 Bytes) der Calldata))) | +| 1D | JUMPI | (((erste 32 Bits (4 Bytes) der Calldata))) | + +Indem die Tests zum Abgleich der Methodensignatur auf diese Weise in zwei Teile aufgeteilt werden, wird im Durchschnitt die Hälfte der Tests eingespart. Der Code, der unmittelbar darauf folgt, und der Code in 0x43 folgen demselben Muster: `DUP1` die ersten 32 Bits der Calldata, `PUSH4 (((Methodensignatur>`, `EQ` ausführen, um auf Gleichheit zu prüfen, und dann `JUMPI`, wenn die Methodensignatur übereinstimmt. Hier sind die Methodensignaturen, ihre Adressen und, falls bekannt, [die entsprechende Methodendefinition](https://www.4byte.directory/): + +| Methode | Methodensignatur | Offset, zu dem gesprungen wird | +| --------------------------------------------------------------------------------------------------------- | ---------------- | ------------------------------ | +| [splitter()](https://www.4byte.directory/signatures/?bytes4_signature=0x3cd8045e) | 0x3cd8045e | 0x0103 | +| ??? | 0x81e580d3 | 0x0138 | +| [currentWindow()](https://www.4byte.directory/signatures/?bytes4_signature=0xba0bafb4) | 0xba0bafb4 | 0x0158 | +| ??? | 0x1f135823 | 0x00C4 | +| [merkleRoot()](https://www.4byte.directory/signatures/?bytes4_signature=0x2eb4a7ab) | 0x2eb4a7ab | 0x00ED | + +Wenn keine Übereinstimmung gefunden wird, springt der Code zum [Proxy-Handler bei 0x7C](#the-handler-at-0x7c) in der Hoffnung, dass der Contract, für den wir ein Proxy sind, eine Übereinstimmung hat. + +![Flussdiagramm der ABI-Aufrufe](flowchart-abi.png) + +## splitter() {#splitter} + +| Offset | Opcode | Stack | +| -----: | ------------ | ----------------------------- | +| 103 | JUMPDEST | | +| 104 | CALLVALUE | CALLVALUE | +| 105 | DUP1 | CALLVALUE CALLVALUE | +| 106 | ISZERO | CALLVALUE==0 CALLVALUE | +| 107 | PUSH2 0x010f | 0x010F CALLVALUE==0 CALLVALUE | +| 10A | JUMPI | CALLVALUE | +| 10B | PUSH1 0x00 | 0x00 CALLVALUE | +| 10D | DUP1 | 0x00 0x00 CALLVALUE | +| 10E | REVERT | | + +Als Erstes prüft diese Funktion, ob der Aufruf keine ETH gesendet hat. Diese Funktion ist nicht [`payable`](https://solidity-by-example.org/payable/). Wenn uns jemand ETH geschickt hat, muss das ein Fehler sein und wir wollen `REVERT` ausführen, um zu vermeiden, dass diese ETH dort sind, wo sie sie nicht zurückbekommen können. + +| Offset | Opcode | Stack | +| -----: | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 10F | JUMPDEST | | +| 110 | POP | | +| 111 | PUSH1 0x03 | 0x03 | +| 113 | SLOAD | (((Speicher[3] a.k.a. der Contract, für den wir ein Proxy sind))) | +| 114 | PUSH1 0x40 | 0x40 (((Speicher[3] a.k.a. der Contract, für den wir ein Proxy sind))) | +| 116 | MLOAD | 0x80 (((Speicher[3] a.k.a. der Contract, für den wir ein Proxy sind))) | +| 117 | PUSH20 0xffffffffffffffffffffffffffffffffffffffff | 0xFF...FF 0x80 (((Speicher[3] a.k.a. der Contract, für den wir ein Proxy sind))) | +| 12C | SWAP1 | 0x80 0xFF...FF (((Speicher[3] a.k.a. der Contract, für den wir ein Proxy sind))) | +| 12D | SWAP2 | (((Speicher[3] a.k.a. der Contract, für den wir ein Proxy sind))) 0xFF...FF 0x80 | +| 12E | AND | ProxyAddr 0x80 | +| 12F | DUP2 | 0x80 ProxyAddr 0x80 | +| 130 | MSTORE | 0x80 | + +Und 0x80 enthält nun die Proxy-Adresse + +| Offset | Opcode | Stack | +| -----: | ------------ | --------- | +| 131 | PUSH1 0x20 | 0x20 0x80 | +| 133 | ADD | 0xA0 | +| 134 | PUSH2 0x00e4 | 0xE4 0xA0 | +| 137 | JUMP | 0xA0 | + +### Der E4-Code {#the-e4-code} + +Wir sehen diese Zeilen zum ersten Mal, aber sie werden mit anderen Methoden geteilt (siehe unten). Wir nennen den Wert im Stack also X und merken uns nur, dass in `splitter()` der Wert dieses X 0xA0 ist. + +| Offset | Opcode | Stack | +| -----: | ---------- | ----------- | +| E4 | JUMPDEST | X | +| E5 | PUSH1 0x40 | 0x40 X | +| E7 | MLOAD | 0x80 X | +| E8 | DUP1 | 0x80 0x80 X | +| E9 | SWAP2 | X 0x80 0x80 | +| EA | SUB | X-0x80 0x80 | +| EB | SWAP1 | 0x80 X-0x80 | +| EC | RETURN | | + +Dieser Code empfängt also einen Speicherzeiger im Stack (X) und veranlasst den Contract, mit einem Puffer, der 0x80 - X ist, `RETURN` auszuführen. + +Im Fall von `splitter()` wird die Adresse zurückgegeben, für die wir ein Proxy sind. `RETURN` gibt den Puffer in 0x80-0x9F zurück, wo wir diese Daten geschrieben haben (Offset 0x130 oben). + +## currentWindow() {#currentwindow} + +Der Code in den Offsets 0x158-0x163 ist identisch mit dem, was wir in 0x103-0x10E in `splitter()` gesehen haben (außer dem `JUMPI`-Ziel), also wissen wir, dass `currentWindow()` auch nicht `payable` ist. + +| Offset | Opcode | Stack | +| -----: | ------------ | ------------------------------------------------------------------------- | +| 164 | JUMPDEST | | +| 165 | POP | | +| 166 | PUSH2 0x00da | 0xDA | +| 169 | PUSH1 0x01 | 0x01 0xDA | +| 16B | SLOAD | Speicher[1] 0xDA | +| 16C | DUP2 | 0xDA Speicher[1] 0xDA | +| 16D | JUMP | Speicher[1] 0xDA | + +### Der DA-Code {#the-da-code} + +Dieser Code wird auch von anderen Methoden verwendet. Wir nennen den Wert im Stack also Y und merken uns nur, dass in `currentWindow()` der Wert dieses Y Speicher[1] ist. + +| Offset | Opcode | Stack | +| -----: | ---------- | ---------------- | +| DA | JUMPDEST | Y 0xDA | +| DB | PUSH1 0x40 | 0x40 Y 0xDA | +| DD | MLOAD | 0x80 Y 0xDA | +| DE | SWAP1 | Y 0x80 0xDA | +| DF | DUP2 | 0x80 Y 0x80 0xDA | +| E0 | MSTORE | 0x80 0xDA | + +Schreiben Sie Y nach 0x80-0x9F. + +| Offset | Opcode | Stack | +| -----: | ---------- | -------------- | +| E1 | PUSH1 0x20 | 0x20 0x80 0xDA | +| E3 | ADD | 0xA0 0xDA | + +Und der Rest wird bereits [oben](#the-e4-code) erklärt. Sprünge zu 0xDA schreiben also den obersten Stack-Wert (Y) in 0x80-0x9F und geben diesen Wert zurück. Im Fall von `currentWindow()` wird Speicher[1] zurückgegeben. + +## merkleRoot() {#merkleroot} + +Der Code in den Offsets 0xED-0xF8 ist identisch mit dem, was wir in 0x103-0x10E in `splitter()` gesehen haben (abgesehen vom `JUMPI`-Ziel), also wissen wir, dass `merkleRoot()` ebenfalls nicht `payable` ist. + +| Offset | Opcode | Stack | +| -----: | ------------ | ------------------------------------------------------------------------- | +| F9 | JUMPDEST | | +| FA | POP | | +| FB | PUSH2 0x00da | 0xDA | +| FE | PUSH1 0x00 | 0x00 0xDA | +| 100 | SLOAD | Speicher[0] 0xDA | +| 101 | DUP2 | 0xDA Speicher[0] 0xDA | +| 102 | JUMP | Speicher[0] 0xDA | + +Was nach dem Sprung passiert, [haben wir bereits herausgefunden](#the-da-code). `merkleRoot()` gibt also Speicher[0] zurück. + +## 0x81e580d3 {#0x81e580d3} + +Der Code in den Offsets 0x138-0x143 ist identisch mit dem, was wir in 0x103-0x10E in `splitter()` gesehen haben (abgesehen vom `JUMPI`-Ziel), also wissen wir, dass diese Funktion ebenfalls nicht `payable` ist. + +| Offset | Opcode | Stack | +| -----: | ------------ | ------------------------------------------------------------------------------- | +| 144 | JUMPDEST | | +| 145 | POP | | +| 146 | PUSH2 0x00da | 0xDA | +| 149 | PUSH2 0x0153 | 0x0153 0xDA | +| 14C | CALLDATASIZE | CALLDATASIZE 0x0153 0xDA | +| 14D | PUSH1 0x04 | 0x04 CALLDATASIZE 0x0153 0xDA | +| 14F | PUSH2 0x018f | 0x018F 0x04 CALLDATASIZE 0x0153 0xDA | +| 152 | JUMP | 0x04 CALLDATASIZE 0x0153 0xDA | +| 18F | JUMPDEST | 0x04 CALLDATASIZE 0x0153 0xDA | +| 190 | PUSH1 0x00 | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 192 | PUSH1 0x20 | 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 194 | DUP3 | 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 195 | DUP5 | CALLDATASIZE 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 196 | SUB | CALLDATASIZE-4 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 197 | SLT | CALLDATASIZE-4\<32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 198 | ISZERO | CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 199 | PUSH2 0x01a0 | 0x01A0 CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 19C | JUMPI | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | + +Es sieht so aus, als ob diese Funktion mindestens 32 Bytes (ein Wort) an Calldata benötigt. + +| Offset | Opcode | Stack | +| -----: | ------ | -------------------------------------------- | +| 19D | DUP1 | 0x00 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 19E | DUP2 | 0x00 0x00 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 19F | REVERT | | + +Wenn sie die Calldata nicht erhält, wird die Transaktion ohne Rückgabedaten zurückgesetzt. + +Sehen wir uns an, was passiert, wenn die Funktion die benötigten Calldata _doch_ erhält. + +| Offset | Opcode | Stack | +| -----: | ------------ | ----------------------------------------------------------- | +| 1A0 | JUMPDEST | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 1A1 | POP | 0x04 CALLDATASIZE 0x0153 0xDA | +| 1A2 | CALLDATALOAD | calldataload(4) CALLDATASIZE 0x0153 0xDA | + +`calldataload(4)` ist das erste Wort der Calldata _nach_ der Methodensignatur + +| Offset | Opcode | Stack | +| -----: | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1A3 | SWAP2 | 0x0153 CALLDATASIZE calldataload(4) 0xDA | +| 1A4 | SWAP1 | CALLDATASIZE 0x0153 calldataload(4) 0xDA | +| 1A5 | POP | 0x0153 calldataload(4) 0xDA | +| 1A6 | JUMP | calldataload(4) 0xDA | +| 153 | JUMPDEST | calldataload(4) 0xDA | +| 154 | PUSH2 0x016e | 0x016E calldataload(4) 0xDA | +| 157 | JUMP | calldataload(4) 0xDA | +| 16E | JUMPDEST | calldataload(4) 0xDA | +| 16F | PUSH1 0x04 | 0x04 calldataload(4) 0xDA | +| 171 | DUP2 | calldataload(4) 0x04 calldataload(4) 0xDA | +| 172 | DUP2 | 0x04 calldataload(4) 0x04 calldataload(4) 0xDA | +| 173 | SLOAD | Speicher[4] calldataload(4) 0x04 calldataload(4) 0xDA | +| 174 | DUP2 | calldataload(4) Speicher[4] calldataload(4) 0x04 calldataload(4) 0xDA | +| 175 | LT | calldataload(4)\)`, und eine andere ist `isClaimed()`, also sieht es wie ein Airdrop-Contract aus. Anstatt den Rest Opcode für Opcode durchzugehen, können wir [den Decompiler ausprobieren](https://etherscan.io/bytecode-decompiler?a=0x2f81e57ff4f4d83b40a9f719fd892d8e806e0761), der für drei Funktionen aus diesem Contract brauchbare Ergebnisse liefert. Das Reverse Engineering der anderen wird dem Leser als Übung überlassen. + +### scaleAmountByPercentage {#scaleamountbypercentage} + +Das gibt uns der Decompiler für diese Funktion aus: + +```python +def unknown8ffb5c97(uint256 _param1, uint256 _param2) payable: + require calldata.size - 4 >=′ 64 + if _param1 and _param2 > -1 / _param1: + revert with 0, 17 + return (_param1 * _param2 / 100 * 10^6) +``` + +Das erste `require` testet, ob die Calldata zusätzlich zu den vier Bytes der Funktionssignatur mindestens 64 Bytes haben, genug für die beiden Parameter. Wenn nicht, dann ist offensichtlich etwas falsch. + +Die `if`-Anweisung scheint zu prüfen, dass `_param1` nicht Null ist und dass `_param1 * _param2` nicht negativ ist. Es dient wahrscheinlich dazu, Fälle von Wrap-Around zu verhindern. + +Schließlich gibt die Funktion einen skalierten Wert zurück. + +### claim {#claim} + +Der vom Decompiler erstellte Code ist komplex und nicht alles davon ist für uns relevant. Ich werde einen Teil davon überspringen, um mich auf die Zeilen zu konzentrieren, von denen ich glaube, dass sie nützliche Informationen liefern + +```python +def unknown2e7ba6ef(uint256 _param1, uint256 _param2, uint256 _param3, array _param4) payable: + ... + require _param2 == addr(_param2) + ... + if currentWindow <= _param1: + revert with 0, 'cannot claim for a future window' +``` + +Wir sehen hier zwei wichtige Dinge: + +- `_param2` ist, obwohl als `uint256` deklariert, tatsächlich eine Adresse +- `_param1` ist das Fenster, das beansprucht wird, und muss `currentWindow` oder früher sein. + +```python + ... + if stor5[_claimWindow][addr(_claimFor)]: + revert with 0, 'Account already claimed the given window' +``` + +Jetzt wissen wir also, dass Speicher[5] ein Array von Fenstern und Adressen ist und ob die Adresse die Belohnung für dieses Fenster beansprucht hat. + +```python + ... + idx = 0 + s = 0 + while idx < _param4.length: + ... + if s + sha3(mem[(32 * _param4.length) + 328 len mem[(32 * _param4.length) + 296]]) > mem[(32 * idx) + 296]: + mem[mem[64] + 32] = mem[(32 * idx) + 296] + ... + s = sha3(mem[_62 + 32 len mem[_62]]) + continue + ... + s = sha3(mem[_66 + 32 len mem[_66]]) + continue + if unknown2eb4a7ab != s: + revert with 0, 'Invalid proof' +``` + +Wir wissen, dass `unknown2eb4a7ab` eigentlich die Funktion `merkleRoot()` ist, also sieht dieser Code so aus, als würde er einen [Merkle-Beweis](https://medium.com/crypto-0-nite/merkle-proofs-explained-6dd429623dc5) verifizieren. Das bedeutet, dass `_param4` ein Merkle-Beweis ist. + +```python + call addr(_param2) with: + value unknown81e580d3[_param1] * _param3 / 100 * 10^6 wei + gas 30000 wei +``` + +So überträgt ein Contract seine eigenen ETH an eine andere Adresse (Contract oder externer Besitz). Er ruft ihn mit einem Wert auf, der dem zu übertragenden Betrag entspricht. Es sieht also so aus, als ob dies ein Airdrop von ETH ist. + +```python + if not return_data.size: + if not ext_call.success: + require ext_code.size(stor2) + call stor2.deposit() with: + value unknown81e580d3[_param1] * _param3 / 100 * 10^6 wei +``` + +Die unteren beiden Zeilen sagen uns, dass Speicher[2] auch ein Contract ist, den wir aufrufen. Wenn wir uns [die Konstruktor-Transaktion ansehen](https://etherscan.io/tx/0xa1ea0549fb349eb7d3aff90e1d6ce7469fdfdcd59a2fd9b8d1f5e420c0d05b58#statechange), sehen wir, dass dieser Contract [0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) ist, ein Wrapped Ether-Contract, [dessen Quellcode auf Etherscan hochgeladen wurde](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code). + +Es sieht also so aus, als ob die Contracts versuchen, ETH an `_param2` zu senden. Wenn er es tun kann, großartig. Wenn nicht, versucht er, [WETH](https://weth.tkn.eth.limo/) zu senden. Wenn `_param2` ein Konto in externem Besitz (EOA) ist, kann es immer ETH empfangen, aber Contracts können den Empfang von ETH verweigern. WETH ist jedoch ERC-20 und Contracts können die Annahme nicht verweigern. + +```python + ... + log 0xdbd5389f: addr(_param2), unknown81e580d3[_param1] * _param3 / 100 * 10^6, bool(ext_call.success) +``` + +Am Ende der Funktion sehen wir, dass ein Log-Eintrag generiert wird. [Sehen Sie sich die generierten Log-Einträge an](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f#events) und filtern Sie nach dem Thema, das mit `0xdbd5...` beginnt. Wenn wir [auf eine der Transaktionen klicken, die einen solchen Eintrag generiert haben](https://etherscan.io/tx/0xe7d3b7e00f645af17dfbbd010478ef4af235896c65b6548def1fe95b3b7d2274), sehen wir, dass es tatsächlich wie ein Anspruch aussieht - das Konto hat eine Nachricht an den Contract gesendet, den wir per Reverse Engineering analysieren, und im Gegenzug ETH erhalten. + +![Eine Anspruchs-Transaktion](claim-tx.png) + +### 1e7df9d3 {#1e7df9d3} + +Diese Funktion ist sehr ähnlich zu [`claim`](#claim) oben. Es prüft auch einen Merkle-Beweis, versucht ETH auf den ersten zu übertragen und erzeugt die gleiche Art von Log-Eintrag. + +```python +def unknown1e7df9d3(uint256 _param1, uint256 _param2, array _param3) payable: + ... + idx = 0 + s = 0 + while idx < _param3.length: + if idx >= mem[96]: + revert with 0, 50 + _55 = mem[(32 * idx) + 128] + if s + sha3(mem[(32 * _param3.length) + 160 len mem[(32 * _param3.length) + 128]]) > mem[(32 * idx) + 128]: + ... + s = sha3(mem[_58 + 32 len mem[_58]]) + continue + mem[mem[64] + 32] = s + sha3(mem[(32 * _param3.length) + 160 len mem[(32 * _param3.length) + 128]]) + ... + if unknown2eb4a7ab != s: + revert with 0, 'Invalid proof' + ... + call addr(_param1) with: + value s wei + gas 30000 wei + if not return_data.size: + if not ext_call.success: + require ext_code.size(stor2) + call stor2.deposit() with: + value s wei + gas gas_remaining wei + ... + log 0xdbd5389f: addr(_param1), s, bool(ext_call.success) +``` + +Der Hauptunterschied besteht darin, dass der erste Parameter, das abzuhebende Fenster, nicht vorhanden ist. Stattdessen gibt es eine Schleife über alle Fenster, die beansprucht werden könnten. + +```python + idx = 0 + s = 0 + while idx < currentWindow: + ... + if stor5[mem[0]]: + if idx == -1: + revert with 0, 17 + idx = idx + 1 + s = s + continue + ... + stor5[idx][addr(_param1)] = 1 + if idx >= unknown81e580d3.length: + revert with 0, 50 + mem[0] = 4 + if unknown81e580d3[idx] and _param2 > -1 / unknown81e580d3[idx]: + revert with 0, 17 + if s > !(unknown81e580d3[idx] * _param2 / 100 * 10^6): + revert with 0, 17 + if idx == -1: + revert with 0, 17 + idx = idx + 1 + s = s + (unknown81e580d3[idx] * _param2 / 100 * 10^6) + continue +``` + +Es sieht also wie eine `claim`-Variante aus, die alle Fenster beansprucht. + +## Fazit {#conclusion} + +Inzwischen sollten Sie wissen, wie Sie Contracts verstehen können, deren Quellcode nicht verfügbar ist, indem Sie entweder die Opcodes oder (wenn es funktioniert) den Decompiler verwenden. Wie aus der Länge dieses Artikels ersichtlich ist, ist das Reverse Engineering eines Contracts nicht trivial, aber in einem System, in dem Sicherheit unerlässlich ist, ist es eine wichtige Fähigkeit, überprüfen zu können, ob Contracts wie versprochen funktionieren. + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). diff --git a/public/content/translations/de/developers/tutorials/run-node-raspberry-pi/index.md b/public/content/translations/de/developers/tutorials/run-node-raspberry-pi/index.md index 38d446dbd7b..bd08eba2a1b 100644 --- a/public/content/translations/de/developers/tutorials/run-node-raspberry-pi/index.md +++ b/public/content/translations/de/developers/tutorials/run-node-raspberry-pi/index.md @@ -1,267 +1,184 @@ --- -title: So verwandeln Sie Ihren Raspberry Pi 4 durch Überschreiben der MicroSD-Karte in einen Node -description: Verbinden Sie Ihren Raspberry Pi 4 mit einem Ethernetkabel, schließen Sie ihn anschließend an die SSD-Festplatte an und starten Sie das Gerät. Nun können Sie es als Ethereum-Node nutzen und eine Ausführungsebene oder Konsensebene (Beacon Chain/Validator) ausführen. +title: Betreibe einen Ethereum-Node auf einem Raspberry Pi 4 +description: "Flashe deinen Raspberry Pi 4, stecke ein Ethernet-Kabel ein, schließe die SSD-Festplatte an und schalte das Gerät ein, um den Raspberry Pi 4 in einen vollwertigen Ethereum-Node + Validator zu verwandeln" author: "EthereumOnArm" -tags: - - "Clients" - - "Ausführungsebene" - - "Konsensebene" - - "Nodes" +tags: ["clients", "execution layer", "consensus layer", "nodes"] lang: de -skill: advanced -published: 2020-05-07 -source: r/ethereum -sourceUrl: https://www.reddit.com/r/ethereum/comments/gf3nhg/ethereum_on_arm_raspberry_pi_4_images_release/ +skill: intermediate +published: 2022-06-10 +source: Ethereum on ARM +sourceUrl: https://ethereum-on-arm-documentation.readthedocs.io/en/latest/ --- -**TL;DR**: Verbinden Sie Ihren Raspberry Pi 4 mit einem Ethernetkabel, schließen Sie ihn anschließend an die SSD-Festplatte an und starten Sie das Gerät. Nun können Sie es als Ethereum-Node nutzen und eine Ausführungsebene oder Konsensebene (Beacon Chain/Validator) ausführen. +**Ethereum on Arm ist ein benutzerdefiniertes Linux-Image, das einen Raspberry Pi in einen Ethereum-Node verwandeln kann.** -[Mehr erfahren über Ethereum-Upgrades](/roadmap/) +Um Ethereum on Arm zu verwenden, um einen Raspberry Pi in einen Ethereum-Node zu verwandeln, wird die folgende Hardware empfohlen: -Zunächst ein paar Hintergrundinformationen: Wie Sie bereits wissen, gibt es einige Speicherprobleme [[1]](/developers/tutorials/run-node-raspberry-pi/#references) bei dem Raspberry Pi 4-Image, da die Betriebssoftware Raspbian OS bisher nur mit der 32-Bit-Version erhältlich ist [[2]](/developers/tutorials/run-node-raspberry-pi/#references) (das gilt jedenfalls für die Benutzeroberfläche). Obwohl wir das offizielle Betriebssystem bevorzugen würden, sind wir zu dem Entschluss gekommen, dass wir auf ein natives 64-Bit-Betriebssystem umsteigen müssen, um diese Probleme zu lösen. - -Außerdem unterstützen Konsens-Clients keine 32-Bit-Binärdateien, so dass die Verwendung von Raspbian den Raspberry Pi 4 vom Betrieb eines Node auf Konsensebene (und der Möglichkeit des Staking) ausschließen würde. - -Nach mehreren Tests veröffentlichen wir nun zwei verschiedene Images auf Basis von Ubuntu 20.04 64-Bit [[3]](/developers/tutorials/run-node-raspberry-pi/#references): Ausführungsebenen- und Konsensebenen-Editionen. - -Im Grunde genommen handelt es sich bei beiden um das gleiche Image, das die gleichen Funktionen wie die Raspbian-basierten Images enthält. Sie sind jedoch standardmäßig für die Ausführung von Software der Ausführungsebene oder der Konsensebene eingerichtet. - -**Images übernehmen alle notwendigen Schritte**, von der Einrichtung der Umgebung und der Formatierung der SSD-Platte über die Installation und Ausführung der Ethereum-Software bis hin zum Start der Blockchain-Synchronisation. - -## Hauptfunktionen {#main-features} - -- Basierend auf Ubuntu 20.04 64-Bit -- Automatische Partitionierung und Formatierung von USB-Festplatten -- Fügt Swap-Speicher hinzu (ZRAM-Kernel-Modul und eine Swap-Datei), basierend auf der Arbeit von Armbian [[7]](/developers/tutorials/run-node-raspberry-pi/#references) -- Ändert den Hostnamen anhand des MAC-Hashes in etwas wie "ethnode-e2a3e6fe" -- Führt Software als systemd-Dienst aus und beginnt mit der Synchronisierung der Blockchain -- Enthält ein APT-Repository für die Installation und Aktualisierung von Ethereum-Software -- Enthält ein auf Grafana/Prometheus basierendes Überwachungs-Dashboard - -## Enthaltene Software {#software-included} - -Beide Images enthalten die gleichen Pakete. Der einzige Unterschied besteht darin, dass die Ausführungsversion standardmäßig Geth und die Konsensversion standardmäßig die Prysm-Beacon Chain ausführt. - -### Ausführungs-Clients {#execution-clients} - -- Geth [[8]](/developers/tutorials/run-node-raspberry-pi/#references): 1.9.13 (offizielle Binärdatei) -- Parity [[9]](/developers/tutorials/run-node-raspberry-pi/#references): 2.7.2 (quer kompiliert) -- Nethermind [[10]](/developers/tutorials/run-node-raspberry-pi/#references): 1.8.28 (quer kompiliert) -- Hyperledger Besu [[11]](/developers/tutorials/run-node-raspberry-pi/#references): 1.4.4 (kompiliert) - -### Konsens-Clients {#consensus-clients} - -- Prysm [[12]](/developers/tutorials/run-node-raspberry-pi/#references): 1.0.0-alpha6 (offizielle Binärdatei) -- Lighthouse [[13]](/developers/tutorials/run-node-raspberry-pi/#references): 0.1.1 (kompiliert) - -### Ethereum-Framework {#ethereum-framework} - -- Swarm [[14]](/developers/tutorials/run-node-raspberry-pi/#references): 0.5.7 (offizielle Binärdatei) -- Raiden Network [[15]](/developers/tutorials/run-node-raspberry-pi/#references): 0.200.0~rc1 (offizielle Binärdatei) -- IPFS [[16]](/developers/tutorials/run-node-raspberry-pi/#references): 0.5.0 (offizielle Binärdatei) -- Statusd [[17]](/developers/tutorials/run-node-raspberry-pi/#references): 0.52.3 (kompiliert) -- Vipnode [[18]](/developers/tutorials/run-node-raspberry-pi/#references): 2.3.3 (offizielle Binärdatei) - -## Installationsanleitung und Anwendung {#installation-guide-and-usage} - -### Empfohlene Hardware und Einrichtung {#recommended-hardware-and-setup} - -- Raspberry 4 (Model B) – 4 GB -- MicroSD-Karte (mindestens 16 GB Klasse 10) -- SSD-USB-3.0-Festplatte (siehe Speicherabschnitt) +- Raspberry 4 (Modell B 8 GB), Odroid M1 oder Rock 5B (8 GB/16 GB RAM) Platine +- MicroSD-Karte (mindestens 16 GB, Klasse 10) +- Mindestens 2 TB große SSD als USB-3.0-Laufwerk oder eine SSD in einem USB-zu-SATA-Gehäuse. - Stromversorgung - Ethernet-Kabel -- 30303-Portweiterleitung (Ausführungseben) und 13000-Portweiterleitung (Konsensebene) [[4]](/developers/tutorials/run-node-raspberry-pi/#references) -- Ein Gehäuse mit Kühlkörper und Lüfter (optional, aber dringend empfohlen) -- USB-Tastatur, Monitor und HDMI-Kabel (micro-HDMI) (optional) - -## Speicher {#storage} - -Sie benötigen eine SSD, um die Ethereum-Clients auszuführen (ohne SSD-Laufwerk gibt es absolut keine Chance, die Ethereum-Blockchain zu synchronisieren). Es gibt zwei Optionen: - -- Verwenden Sie eine tragbare USB-SSD-Festplatte wie die Samsung T5 Portable SSD. -- Verwenden Sie ein externes USB 3.0-Festplattengehäuse mit einer SSD-Festplatte. In unserem Fall haben wir ein Inateck 2.5 Hard Drive Enclosure FE2011 verwendet. Achten Sie darauf, ein Gehäuse mit einem UAS-kompatiblen Chip zu kaufen, insbesondere einen der folgenden: JMicron (JMS567 oder JMS578) oder ASMedia (ASM1153E). +- Portweiterleitung (siehe Clients für weitere Informationen) +- Ein Gehäuse mit Kühlkörper und Lüfter +- USB-Tastatur, Monitor und HDMI-Kabel (Micro-HDMI) (optional) -In beiden Fällen sollten Sie vermeiden, minderwertige SSD-Festplatten zu kaufen, da sie eine Schlüsselkomponente Ihrer Nodes sind und die Leistung (und die Synchronisierungszeiten) drastisch beeinträchtigen können. +## Warum Ethereum auf ARM betreiben? {#why-run-ethereum-on-arm} -Beachten Sie, dass die Festplatte an einen USB 3.0-Anschluss (blau) angeschlossen sein muss. +ARM-Platinen sind sehr preisgünstige, flexible, kleine Computer. Sie sind eine gute Wahl für den Betrieb von Ethereum-Nodes, weil sie günstig zu erwerben sind, so konfiguriert werden können, dass alle ihre Ressourcen nur auf den Node ausgerichtet sind, was sie effizient macht, sie wenig Strom verbrauchen und physisch klein sind, sodass sie unauffällig in jedes Zuhause passen. Es ist auch sehr einfach, Nodes zu starten, da die MicroSD-Karte des Raspberry Pi einfach mit einem vorgefertigten Image geflasht werden kann, ohne dass Software heruntergeladen oder erstellt werden muss. -## Images herunterladen und Installieren {#image-download-and-installation} +## Wie funktioniert das? {#how-does-it-work} -### 1. Laden Sie die Images der Ausführungs- und Konsensebene herunter {#1-download-execution-or-consensus-images} +Die Speicherkarte des Raspberry Pi wird mit einem vorgefertigten Image geflasht. Dieses Image enthält alles, was zum Betreiben eines Ethereum-Nodes benötigt wird. Mit einer geflashten Karte musst du nur noch den Raspberry Pi einschalten. Alle Prozesse, die für den Betrieb des Nodes erforderlich sind, werden automatisch gestartet. Das funktioniert, weil die Speicherkarte ein Linux-basiertes Betriebssystem (OS) enthält, auf dem automatisch Prozesse auf Systemebene ausgeführt werden, die das Gerät in einen Ethereum-Node verwandeln. - - Image der Ausführungsebene herunterladen - +Ethereum kann nicht mit dem beliebten Raspberry Pi Linux-Betriebssystem „Raspbian“ betrieben werden, da Raspbian immer noch eine 32-Bit-Architektur verwendet, was dazu führt, dass Ethereum-Benutzer auf Speicherprobleme stoßen und Konsens-Clients keine 32-Bit-Binärdateien unterstützen. Um dies zu überwinden, ist das Ethereum-on-Arm-Team auf ein natives 64-Bit-Betriebssystem namens „Armbian“ umgestiegen. -sha256 7fa9370d13857dd6abcc8fde637c7a9a7e3a66b307d5c28b0c0d29a09c73c55c +**Images übernehmen alle notwendigen Schritte, von der Einrichtung der Umgebung und der Formatierung der SSD-Platte über die Installation und Ausführung der Ethereum-Software bis hin zum Start der Blockchain-Synchronisation.** - - Image der Konsensebene herunterladen - +## Hinweis zu Ausführungs- und Konsens-Clients {#note-on-execution-and-consensus-clients} -sha256 74c0c15b708720e5ae5cac324f1afded6316537fb17166109326755232cd316e +Das Ethereum on Arm-Image enthält vorgefertigte Ausführungs- und Konsens-Clients als Dienste. Ein Ethereum-Node erfordert, dass beide Clients synchronisiert sind und laufen. Du musst nur das Image herunterladen, es flashen und dann die Dienste starten. Das Image ist mit den folgenden Ausführungs-Clients vorinstalliert: -### 2. Das Image flashen {#2-flash-the-image} +- Geth +- Nethermind +- Besu -Stecken Sie die microSD-Karte in Ihren Desktop/Laptop und laden Sie die Datei herunter (z. B. die Ausführungsebene): +und die folgenden Konsens-Clients: -```bash -wget https://ethraspbian.com/downloads/ubuntu-20.04-preinstalled-server-arm64+raspi-eth1.img.zip -``` +- Lighthouse +- Nimbus +- Prysm +- Teku -Hinweis: Wenn Sie mit der Befehlszeile nicht vertraut sind oder unter Windows arbeiten, können Sie [Etcher](https://etcher.io) verwenden. +Du solltest einen von jedem zum Ausführen auswählen - alle Ausführungs-Clients sind mit allen Konsens-Clients kompatibel. Wenn du nicht explizit einen Client auswählst, greift der Node auf seine Standardeinstellungen - Geth und Lighthouse - zurück und führt sie automatisch aus, wenn die Platine eingeschaltet wird. Du musst Port 30303 auf deinem Router öffnen, damit Geth Peers finden und sich mit ihnen verbinden kann. -Öffnen Sie ein Terminal und überprüfen Sie den Namen Ihres MicroSD-Geräts: +## Herunterladen des Images {#downloading-the-image} -```bash -sudo fdisk -l -``` +Das Raspberry Pi 4 Ethereum-Image ist ein „Plug-and-Play“-Image, das automatisch sowohl die Ausführungs- als auch die Konsens-Clients installiert und einrichtet, sie so konfiguriert, dass sie miteinander kommunizieren und sich mit dem Ethereum-Netzwerk verbinden. Alles, was du tun musst, ist, deine Prozesse mit einem einfachen Befehl zu starten. -Sie sollten ein Gerät namens "mmcblk0" oder "sdd" sehen. Entpacken und flashen des Images: +Lade das Raspberry Pi-Image von [Ethereum on Arm](https://ethereumonarm-my.sharepoint.com/:u:/p/dlosada/Ec_VmUvr80VFjf3RYSU-NzkBmj2JOteDECj8Bibde929Gw?download=1) herunter und überprüfe den SHA256-Hash: -```bash -unzip ubuntu-20.04-preinstalled-server-arm64+raspi-eth1.img.zip -sudo dd bs=1M if=ubuntu-20.04-preinstalled-server-arm64+raspi-eth1.img of=/dev/mmcblk0 && sync +```sh +# Aus dem Verzeichnis, das das heruntergeladene Image enthält +shasum -a 256 ethonarm_22.04.00.img.zip +# Der Hash sollte Folgendes ausgeben: fb497e8f8a7388b62d6e1efbc406b9558bee7ef46ec7e53083630029c117444f ``` -### 3. Setzen Sie die MicroSD-Karte in den Raspberry Pi 4 ein. Schließen ein Ethernet-Kabel und die USB-SSD-Festplatte an (stellen Sie sicher, dass Sie einen blauen Anschluss verwenden). {#3-insert-the-microsd-into-the-raspberry-pi-4-connect-an-ethernet-cable-and-attach-the-usb-ssd-disk-make-sure-you-are-using-a-blue-port} - -### 4. Das Gerät einschalten {#4-power-on-the-device} - -Das Ubuntu-Betriebssystem wird in weniger als einer Minute hochgefahren, aber Sie müssen **etwa 10 Minuten warten**, damit das Skript die notwendigen Aufgaben durchführen kann, um das Gerät in einen Ethereum-Node zu verwandeln und den Raspberry neu zu starten. +Beachte, dass Images für Rock 5B- und Odroid M1-Platinen auf der Ethereum-on-Arm-[Download-Seite](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/quick-guide/download-and-install.html) verfügbar sind. -Je nach Image, wird das wie folgt ausgeführt: +## Flashen der MicroSD {#flashing-the-microsd} -- Ausführungs-Client: Geth als Standard-Client für die Synchronisierung der Blockchain -- Konsens-Client: Prysm als Standard-Client für die Synchronisierung der Beacon Chain (Goerli-Testnet) +Die MicroSD-Karte, die für den Raspberry Pi verwendet werden soll, sollte zuerst in einen Desktop oder Laptop eingelegt werden, damit sie geflasht werden kann. Mit den folgenden Terminalbefehlen wird das heruntergeladene Image auf die SD-Karte geflasht: -### 5. Anmelden {#5-log-in} - -Sie können sich über SSH oder über die Konsole anmelden (wenn Sie einen Monitor und eine Tastatur angeschlossen haben). +```shell +# Namen der MicroSD-Karte überprüfen +sudo fdisk -l -```bash -User: ethereum -Password: ethereum +>> sdxxx ``` -Bei der ersten Anmeldung werden Sie aufgefordert, das Passwort zu ändern, so dass Sie sich zweimal anmelden müssen. - -### 6. Öffnen Sie den Port 30303 für Geth und 13000, wenn Sie eine Prysm-Beacon Chain verwenden. Wenn Sie nicht wissen, wie das geht, geben Sie in einer Suchmaschine "Portweiterleitung" gefolgt von Ihrem Routermodell ein. {#6-open-30303-port-for-geth-and-13000-if-you-are-running-prysm-beacon-chain-if-you-dont-know-how-to-do-this-google-port-forwarding-followed-by-your-router-model} - -### 7. Konsolenausgabe abrufen {#7-get-console-output} - -Sie können sehen, was im Hintergrund passiert, indem Sie Folgendes eingeben: +Es ist sehr wichtig, den richtigen Namen zu verwenden, da der nächste Befehl `dd` enthält, der den gesamten Inhalt der Karte löscht, bevor das Image darauf geschrieben wird. Um fortzufahren, navigiere zu dem Verzeichnis, das das gezippte Image enthält: -```bash -sudo tail -f /var/log/syslog +```shell +# Image entpacken und flashen +unzip ethonarm_22.04.00.img.zip +sudo dd bs=1M if=ethonarm_22.04.00.img of=/dev/ conv=fdatasync status=progress ``` -**Herzlichen Glückwunsch. Sie betreiben nun einen vollständigen Ethereum-Node auf Ihrem Raspberry Pi 4.** +Die Karte ist nun geflasht und kann in den Raspberry Pi eingelegt werden. -## Synchronisierung mit der Blockchain {#syncing-the-blockchain} +## Den Node starten {#start-the-node} -Jetzt müssen Sie warten, bis die Blockchain synchronisiert ist. Im Falle der Ausführungsebene wird dieser Vorgang einige Tage dauern, da er von verschiedenen Faktoren abhängig ist. Sie können mit bis zu 5-7 Tagen rechnen. +Stecke die SD-Karte in den Raspberry Pi, schließe das Ethernet-Kabel und die SSD an und schalte das Gerät ein. Das Betriebssystem wird hochfahren und automatisch damit beginnen, die vorkonfigurierten Aufgaben auszuführen, die den Raspberry Pi in einen Ethereum-Node verwandeln, einschließlich der Installation und Erstellung der Client-Software. Dies wird wahrscheinlich 10-15 Minuten dauern. -Wenn Sie die Konsensebene des Goerli-Testnets verwenden, können Sie mit einer Synchronisationszeit von 1-2 Tagen für die Beacon Chain rechnen. Denken Sie daran, dass Sie den Validator später einrichten müssen, um den Staking-Prozess zu starten. [So führen Sie den Konsensebenen-Validator aus](/developers/tutorials/run-node-raspberry-pi/#validator). +Sobald alles installiert und konfiguriert ist, melde dich über eine SSH-Verbindung am Gerät an oder verwende das Terminal direkt, wenn ein Monitor und eine Tastatur an die Platine angeschlossen sind. Verwende das `ethereum`-Konto zum Anmelden, da dieses die erforderlichen Berechtigungen zum Starten des Nodes hat. -## Dashboards überwachen {#monitoring-dashboards} +```shell +Benutzer: ethereum +Passwort: ethereum +``` -Für diese erste Version haben wir 3 Überwachungs-Dashboards auf Grundlage von Prometheus [[5]](/developers/tutorials/run-node-raspberry-pi/#references)/Grafana [[6]](/developers/tutorials/run-node-raspberry-pi/#references) eingebaut, um den Node und die Daten der Clients (Geth und Besu) zu überwachen. Sie können über Ihren Webbrowser darauf zugreifen: +Der Standard-Ausführungs-Client, Geth, startet automatisch. Du kannst dies bestätigen, indem du die Protokolle mit dem folgenden Terminal-Befehl überprüfst: -```bash -URL: http://your_raspberrypi_IP:3000 -User: admin -Password: ethereum +```sh +sudo journalctl -u geth -f ``` -## Clients wechseln {#switching-clients} +Der Konsens-Client muss explizit gestartet werden. Öffne dazu zuerst Port 9000 auf deinem Router, damit Lighthouse Peers finden und eine Verbindung zu ihnen herstellen kann. Aktivieren und starten Sie dann den Lighthouse-Dienst: -Alle Clients laufen als Systemdienst. Das ist wichtig, denn wenn ein Problem auftritt, wird das System den Prozess automatisch neu starten. +```sh +sudo systemctl enable lighthouse-beacon +sudo systemctl start lighthouse-beacon +``` -Die Beacon Chain von Geth und Prysm läuft standardmäßig (je nachdem, wie Sie synchronisieren, Ausführungsebene oder Konsensebene). Wenn Sie also zu einem anderen Client wechseln möchten (z. B. von Geth zu Nethermind), müssen Sie zuerst Geth anhalten und deaktivieren und dann den anderen Client aktivieren und starten: +Überprüfe den Client anhand der Protokolle: -```bash -sudo systemctl stop geth && sudo systemctl disable geth +```sh +sudo journalctl -u lighthouse-beacon ``` -Befehle zum Aktivieren und Starten jedes Ausführungs-Clients: +Beachte, dass der Konsens-Client in wenigen Minuten synchronisiert wird, da er die Checkpoint-Synchronisierung verwendet. Der Ausführungs-Client wird länger brauchen – möglicherweise mehrere Stunden – und er startet erst, wenn der Konsens-Client bereits synchronisiert ist (das liegt daran, dass der Ausführungs-Client ein Ziel für die Synchronisierung benötigt, das der synchronisierte Konsens-Client bereitstellt). -```bash -sudo systemctl enable besu && sudo systemctl start besu -sudo systemctl enable nethermind && sudo systemctl start nethermind -sudo systemctl enable parity && sudo systemctl start parity -``` +Wenn die Dienste Geth und Lighthouse laufen und synchronisiert sind, ist dein Raspberry Pi jetzt ein Ethereum-Node! Am häufigsten wird mit dem Ethereum-Netzwerk über die Javascript-Konsole von Geth interagiert, die an den Geth-Client an Port 8545 angehängt werden kann. Es ist auch möglich, Befehle, die als JSON-Objekte formatiert sind, mit einem Anfrage-Tool wie Curl zu übermitteln. Mehr dazu findest du in der [Geth-Dokumentation](https://geth.ethereum.org/). -Konsens-Clients: +Geth ist so vorkonfiguriert, dass es Metriken an ein Grafana-Dashboard meldet, das im Browser angezeigt werden kann. Als fortgeschrittener Benutzer möchtest du diese Funktion möglicherweise nutzen, um den Zustand deines Nodes zu überwachen, indem du zu `ipaddress:3000` navigierst und `user: admin` sowie `passwd: ethereum` eingibst. -```bash -sudo systemctl stop prysm-beacon && sudo systemctl disable prysm-beacon -sudo systemctl start lighthouse && sudo systemctl enable lighthouse -``` +## Validatoren {#validators} -## Parameter ändern {#changing-parameters} +Optional kann auch ein Validator zum Konsens-Client hinzugefügt werden. Die Validator-Software ermöglicht es deinem Node, aktiv am Konsens teilzunehmen und versorgt das Netzwerk mit kryptoökonomischer Sicherheit. Für diese Arbeit wirst du in ETH belohnt. Um einen Validator zu betreiben, musst du zunächst 32 ETH besitzen, die in den Einzahlungsvertrag eingezahlt werden müssen. Die Einzahlung kann erfolgen, indem du der Schritt-für-Schritt-Anleitung auf dem [Launchpad](https://launchpad.ethereum.org/) folgst. Führe dies auf einem Desktop/Laptop durch, aber generiere keine Schlüssel – dies kann direkt auf dem Raspberry Pi erledigt werden. -Die Konfigurationsdateien der Clients befinden sich in dem Verzeichnis /etc/ethereum/. Sie können diese Dateien bearbeiten und den Systemdienst neu starten, damit die Änderungen wirksam werden. Die einzige Ausnahme ist Nethermind, das zusätzlich eine Mainnet-Konfigurationsdatei hat, die sich hier befindet: +Öffne ein Terminal auf dem Raspberry Pi und führe den folgenden Befehl aus, um die Einzahlungsschlüssel zu generieren: -```bash -/etc/nethermind/configs/mainnet.cfg +``` +sudo apt-get update +sudo apt-get install staking-deposit-cli +cd && deposit new-mnemonic --num_validators 1 ``` -Die Daten der Blockchain-Clients werden wie folgt auf dem Ethereum-Home-Konto gespeichert (beachten Sie den Punkt vor dem Verzeichnisnamen): - -### Ausführungsebene {#execution-layer} +(Oder lade das [staking-deposit-cli](https://github.com/ethereum/staking-deposit-cli) herunter, um es auf einer Air-Gapped-Maschine auszuführen, und führe den Befehl `deposit new-mnemnonic` aus) -```bash -/home/ethereum/.geth -/home/ethereum/.parity -/home/ethereum/.besu -/home/ethereum/.nethermind -``` +Bewahre die mnemonische Phrase sicher auf! Der obige Befehl hat zwei Dateien im Keystore des Nodes generiert: die Validator-Schlüssel und eine Einzahlungsdatendatei. Die Einzahlungsdaten müssen auf das Launchpad hochgeladen werden, daher müssen sie vom Raspberry Pi auf den Desktop/Laptop kopiert werden. Dies kann über eine SSH-Verbindung oder eine andere Kopieren-und-Einfügen-Methode erfolgen. -### Konsensebene {#consensus-layer} +Sobald die Einzahlungsdatendatei auf dem Computer verfügbar ist, auf dem das Launchpad läuft, kann sie auf das `+` auf dem Launchpad-Bildschirm gezogen und abgelegt werden. Folge den Anweisungen auf dem Bildschirm, um eine Transaktion an den Einzahlungsvertrag zu senden. -```bash -/home/ethereum/.eth2 -/home/ethereum/.eth2validators -/home/ethereum/.lighthouse -``` +Zurück auf dem Raspberry Pi kann ein Validator gestartet werden. Dies erfordert den Import der Validator-Schlüssel, das Festlegen der Adresse zum Sammeln von Belohnungen und dann das Starten des vorkonfigurierten Validator-Prozesses. Das folgende Beispiel gilt für Lighthouse – Anleitungen für andere Konsens-Clients sind in den [Ethereum-on-Arm-Dokumenten](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/) verfügbar: -## Nethermind und Hyperledger Besu {#nethermind-and-hyperledger-besu} +```shell +# Validator-Schlüssel importieren +lighthouse account validator import --directory=/home/ethereum/validator_keys -Diese beiden großartigen Ausführungs-Clients sind eine sehr gute Alternative zu Geth und Parity geworden. Je größer die Vielfalt im Netz, desto besser. Also probieren Sie sie aus und leisten Sie damit einen Beitrag zur Gesundheit des Netzwerks. +# Belohnungsadresse festlegen +sudo sed -i 's/' /etc/ethereum/lighthouse-validator.conf -Beide Clients müssen noch weiter getestet werden. Experimentieren Sie also gerne damit und geben Sie uns Feedback dazu. +# Validator starten +sudo systemctl start lighthouse-validator +``` -## So führen Sie den Konsensvalidator aus (Staking) {#validator} +Herzlichen Glückwunsch, du hast jetzt einen vollständigen Ethereum-Node und Validator, der auf einem Raspberry Pi läuft! -Sobald die Görli-Testnet-Beacon-Chain synchronisiert ist, können Sie einen Validator in demselben Gerät ausführen. Sie müssen [diese Teilnahmeschritte](https://prylabs.net/participate) befolgen. +## Weitere Details {#more-details} -Beim ersten Mal ist es erforderlich, manuell ein Konto zu erstellen. Führen Sie dazu das Binärprogramm "validator" aus und legen Sie ein Passwort fest. Sobald Sie diesen Schritt abgeschlossen haben, können Sie das Passwort zu `/etc/ethereum/prysm-validator.conf` hinzufügen und den Validator als Systemdienst starten. +Diese Seite gab einen Überblick darüber, wie man einen Geth-Lighthouse-Node und -Validator mit einem Raspberry Pi einrichtet. Detailliertere Anweisungen sind auf der [Ethereum-on-Arm-Webseite](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/index.html) verfügbar. ## Feedback erwünscht {#feedback-appreciated} -Wir haben viel Arbeit investiert, um den Raspberry Pi 4 als vollwertigen Ethereum-Node einzurichten, da wir wissen, dass die große Nutzerbasis dieses Geräts einen sehr positiven Einfluss auf das Netzwerk haben kann. - -Beachten Sie, dass dies das erste Image auf Basis von Ubuntu 20.04 ist und es daher noch einige Fehler enthalten kann. Wenn Sie das feststellen, eröffnen Sie ein Thema auf [GitHub](https://github.com/diglos/ethereumonarm) oder kontaktieren Sie uns auf [Twitter](https://twitter.com/EthereumOnARM). +Wir wissen, dass der Raspberry Pi eine riesige Nutzerbasis hat, die einen sehr positiven Einfluss auf die Gesundheit des Ethereum-Netzwerks haben könnte. +Bitte vertiefe dich in die Details dieses Tutorials, probiere es auf Testnets aus, sieh dir das Ethereum on Arm GitHub an, gib Feedback, melde Probleme und erstelle Pull-Requests und hilf mit, die Technologie und Dokumentation voranzubringen! ## Referenzen {#references} -1. [geth stürzt wiederholt mit SIGSEGV ab](https://github.com/ethereum/go-ethereum/issues/20190) -2. [https://github.com/diglos/ethereumonarm](https://github.com/diglos/ethereumonarm) -3. https://ubuntu.com/download/raspberry-pi -4. https://wikipedia.org/wiki/Port_forwarding -5. https://prometheus.io -6. https://grafana.com -7. https://forum.armbian.com/topic/5565-zram-vs-swap/ -8. https://geth.ethereum.org -9. https://github.com/openethereum/openethereum \* **Beachten Sie, dass OpenEthereum [veraltet](https://medium.com/openethereum/gnosis-joins-erigon-formerly-turbo-geth-to-release-next-gen-ethereum-client-c6708dd06dd) ist und nicht mehr gepflegt wird.** Verwenden Sie es mit Vorsicht und wechseln Sie lieber zu einer anderen Client-Implementierung. -10. https://nethermind.io -11. https://www.hyperledger.org/projects/besu -12. https://github.com/prysmaticlabs/prysm -13. https://lighthouse.sigmaprime.io -14. https://ethersphere.github.io/swarm-home -15. https://raiden.network -16. https://ipfs.io -17. https://status.im -18. https://vipnode.org +1. https://ubuntu.com/download/raspberry-pi +2. https://wikipedia.org/wiki/Port_forwarding +3. https://prometheus.io +4. https://grafana.com +5. https://forum.armbian.com/topic/5565-zram-vs-swap/ +6. https://geth.ethereum.org +7. https://nethermind.io +8. https://www.hyperledger.org/projects/besu +9. https://github.com/prysmaticlabs/prysm +10. https://lighthouse.sigmaprime.io +11. https://ethersphere.github.io/swarm-home +12. https://raiden.network +13. https://ipfs.io +14. https://status.im +15. https://vipnode.org diff --git a/public/content/translations/de/developers/tutorials/scam-token-tricks/index.md b/public/content/translations/de/developers/tutorials/scam-token-tricks/index.md new file mode 100644 index 00000000000..5f1fa214ef4 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/scam-token-tricks/index.md @@ -0,0 +1,463 @@ +--- +title: "Einige Tricks, die von Betrugs-Tokens verwendet werden, und wie man sie erkennt" +description: "In diesem Tutorial analysieren wir einen Betrugs-Token, um einige der Tricks zu sehen, die Betrüger anwenden, wie sie sie implementieren und wie wir sie erkennen können." +author: Ori Pomerantz +tags: ["scam", "solidity", "erc-20", "javascript", "typescript"] +skill: intermediate +published: 2023-09-15 +lang: de +--- + +In diesem Tutorial analysieren wir [einen Betrugs-Token](https://etherscan.io/token/0xb047c8032b99841713b8e3872f06cf32beb27b82#code), um einige der Tricks zu sehen, die Betrüger anwenden und wie sie sie implementieren. Am Ende des Tutorials werden Sie einen umfassenderen Überblick über ERC-20-Token-Verträge, ihre Fähigkeiten und warum Skepsis notwendig ist, haben. Dann schauen wir uns die von diesem Betrugs-Token ausgegebenen Ereignisse an und sehen, wie wir automatisch erkennen können, dass er nicht legitim ist. + +## Betrugs-Tokens – was sind sie, warum machen Leute sie und wie kann man sie vermeiden {#scam-tokens} + +Eine der häufigsten Anwendungen von Ethereum ist die Schaffung eines handelbaren Tokens durch eine Gruppe, der gewissermaßen ihre eigene Währung darstellt. Jedoch gibt es überall, wo es legitime wertschöpfende Anwendungsmöglichkeiten gibt, auch Kriminelle, die diese Werte stehlen möchten. + +Sie können mehr über dieses Thema [an anderer Stelle auf ethereum.org](/guides/how-to-id-scam-tokens/) aus der Perspektive eines Benutzers lesen. Dieses Tutorial konzentriert sich auf die Analyse eines Betrugs-Tokens, um zu sehen, wie es gemacht wird und wie es erkannt werden kann. + +### Woher weiß ich, dass wARB ein Betrug ist? {#warb-scam} + +Der Token, den wir analysieren, ist [wARB](https://etherscan.io/token/0xb047c8032b99841713b8e3872f06cf32beb27b82#code), der vorgibt, dem legitimen [ARB-Token](https://etherscan.io/token/0xb50721bcf8d664c30412cfbc6cf7a15145234ad1) gleichwertig zu sein. + +Der einfachste Weg, um zu wissen, welcher der legitime Token ist, ist ein Blick auf die Herkunftsorganisation, [Arbitrum](https://arbitrum.foundation/). Die legitimen Adressen sind [in ihrer Dokumentation](https://docs.arbitrum.foundation/deployment-addresses#token) angegeben. + +### Warum ist der Quellcode verfügbar? {#why-source} + +Normalerweise würden wir erwarten, dass Leute, die versuchen, andere zu betrügen, geheimnisvoll sind, und tatsächlich haben viele Betrugs-Tokens ihren Code nicht verfügbar (zum Beispiel [dieser](https://optimistic.etherscan.io/token/0x15992f382d8c46d667b10dc8456dc36651af1452#code) und [dieser](https://optimistic.etherscan.io/token/0x026b623eb4aada7de37ef25256854f9235207178#code)). + +Allerdings veröffentlichen legitime Tokens normalerweise ihren Quellcode, sodass die Autoren von Betrugs-Tokens manchmal dasselbe tun, um legitim zu erscheinen. [wARB](https://etherscan.io/token/0xb047c8032b99841713b8e3872f06cf32beb27b82#code) ist einer dieser Tokens mit verfügbarem Quellcode, was das Verständnis erleichtert. + +Während Vertrags-Deployer wählen können, ob sie den Quellcode veröffentlichen oder nicht, können sie _nicht_ den falschen Quellcode veröffentlichen. Der Block-Explorer kompiliert den bereitgestellten Quellcode unabhängig, und wenn er nicht genau denselben Bytecode erhält, lehnt er diesen Quellcode ab. [Sie können mehr darüber auf der Etherscan-Seite lesen](https://etherscan.io/verifyContract). + +## Vergleich mit legitimen ERC-20-Tokens {#compare-legit-erc20} + +Wir werden diesen Token mit legitimen ERC-20-Tokens vergleichen. Wenn Sie nicht damit vertraut sind, wie legitime ERC-20-Tokens typischerweise geschrieben werden, [sehen Sie sich dieses Tutorial an](/developers/tutorials/erc20-annotated-code/). + +### Konstanten für privilegierte Adressen {#constants-for-privileged-addresses} + +Verträge benötigen manchmal privilegierte Adressen. Verträge, die für den langfristigen Gebrauch konzipiert sind, erlauben es einigen privilegierten Adressen, diese Adressen zu ändern, zum Beispiel um die Verwendung eines neuen Multisig-Vertrags zu ermöglichen. Es gibt mehrere Möglichkeiten, dies zu tun. + +Der [`HOP`-Token-Vertrag](https://etherscan.io/address/0xc5102fe9359fd9a28f877a67e36b0f050d81a3cc#code) verwendet das [`Ownable`](https://docs.openzeppelin.com/contracts/2.x/access-control#ownership-and-ownable)-Muster. Die privilegierte Adresse wird im Speicher in einem Feld namens `_owner` gehalten (siehe die dritte Datei, `Ownable.sol`). + +```solidity +abstract contract Ownable is Context { + address private _owner; + . + . + . +} +``` + +Der [`ARB`-Token-Vertrag](https://etherscan.io/address/0xad0c361ef902a7d9851ca7dcc85535da2d3c6fc7#code) hat nicht direkt eine privilegierte Adresse. Er braucht jedoch auch keine. Er befindet sich hinter einem [`Proxy`](https://docs.openzeppelin.com/contracts/5.x/api/proxy) an der [Adresse `0xb50721bcf8d664c30412cfbc6cf7a15145234ad1`](https://etherscan.io/address/0xb50721bcf8d664c30412cfbc6cf7a15145234ad1#code). Dieser Vertrag hat eine privilegierte Adresse (siehe die vierte Datei, `ERC1967Upgrade.sol`), die für Upgrades verwendet werden kann. + +```solidity + /** + * @dev Speichert eine neue Adresse im EIP1967-Admin-Slot. + */ + function _setAdmin(address newAdmin) private { + require(newAdmin != address(0), "ERC1967: new admin is the zero address"); + StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; + } +``` + +Im Gegensatz dazu hat der `wARB`-Vertrag einen fest codierten `contract_owner`. + +```solidity +contract WrappedArbitrum is Context, IERC20 { + . + . + . + address deployer = 0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1; + address public contract_owner = 0xb40dE7b1beE84Ff2dc22B70a049A07A13a411A33; + . + . + . +} +``` + +[Dieser Vertragsinhaber](https://etherscan.io/address/0xb40dE7b1beE84Ff2dc22B70a049A07A13a411A33) ist kein Vertrag, der zu verschiedenen Zeiten von verschiedenen Konten kontrolliert werden könnte, sondern ein [externes Konto](/developers/docs/accounts/#externally-owned-accounts-and-key-pairs). Das bedeutet, dass es wahrscheinlich für den kurzfristigen Gebrauch durch eine Einzelperson konzipiert ist, anstatt als langfristige Lösung zur Kontrolle eines ERC-20, das wertvoll bleiben wird. + +Und tatsächlich, wenn wir in Etherscan nachsehen, sehen wir, dass der Betrüger diesen Vertrag nur für 12 Stunden (von der [ersten Transaktion](https://etherscan.io/tx/0xf49136198c3f925fcb401870a669d43cecb537bde36eb8b41df77f06d5f6fbc2) bis zur [letzten Transaktion](https://etherscan.io/tx/0xdfd6e717157354e64bbd5d6adf16761e5a5b3f914b1948d3545d39633244d47b)) am 19. Mai 2023 verwendet hat. + +### Die gefälschte `_transfer`-Funktion {#the-fake-transfer-function} + +Es ist Standard, dass tatsächliche Transfers über eine [interne `_transfer`-Funktion](/developers/tutorials/erc20-annotated-code/#the-_transfer-function-_transfer) stattfinden. + +In `wARB` sieht diese Funktion fast legitim aus: + +```solidity + function _transfer(address sender, address recipient, uint256 amount) internal virtual{ + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); + if (sender == contract_owner){ + sender = deployer; + } + emit Transfer(sender, recipient, amount); + } +``` + +Der verdächtige Teil ist: + +```solidity + if (sender == contract_owner){ + sender = deployer; + } + emit Transfer(sender, recipient, amount); +``` + +Wenn der Vertragsinhaber Tokens sendet, warum zeigt das `Transfer`-Ereignis, dass sie von `deployer` kommen? + +Es gibt jedoch ein wichtigeres Problem. Wer ruft diese `_transfer`-Funktion auf? Sie kann nicht von außen aufgerufen werden, sie ist als `internal` markiert. Und der Code, den wir haben, enthält keine Aufrufe an `_transfer`. Offensichtlich ist sie hier als Köder. + +```solidity + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _f_(_msgSender(), recipient, amount); + return true; + } + + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _f_(sender, recipient, amount); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); + return true; + } +``` + +Wenn wir uns die Funktionen ansehen, die zum Übertragen von Tokens aufgerufen werden, `transfer` und `transferFrom`, sehen wir, dass sie eine völlig andere Funktion aufrufen, `_f_`. + +### Die echte `_f_`-Funktion {#the-real-f-function} + +```solidity + function _f_(address sender, address recipient, uint256 amount) internal _mod_(sender,recipient,amount) virtual { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); + if (sender == contract_owner){ + + sender = deployer; + } + emit Transfer(sender, recipient, amount); + } +``` + +Es gibt zwei potenzielle Warnsignale in dieser Funktion. + +- Die Verwendung des [Funktionsmodifikators](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm) `_mod_`. Wenn wir uns jedoch den Quellcode ansehen, sehen wir, dass `_mod_` tatsächlich harmlos ist. + + ```solidity + modifier _mod_(address sender, address recipient, uint256 amount){ + _; + } + ``` + +- Das gleiche Problem, das wir bei `_transfer` gesehen haben: Wenn `contract_owner` Tokens sendet, scheinen sie von `deployer` zu kommen. + +### Die gefälschte Ereignisfunktion `dropNewTokens` {#the-fake-events-function-dropNewTokens} + +Jetzt kommen wir zu etwas, das wie ein tatsächlicher Betrug aussieht. Ich habe die Funktion zur besseren Lesbarkeit etwas bearbeitet, aber sie ist funktional gleichwertig. + +```solidity +function dropNewTokens(address uPool, + address[] memory eReceiver, + uint256[] memory eAmounts) public auth() +``` + +Diese Funktion hat den `auth()`-Modifikator, was bedeutet, dass sie nur vom Vertragsinhaber aufgerufen werden kann. + +```solidity +modifier auth() { + require(msg.sender == contract_owner, "Not allowed to interact"); + _; +} +``` + +Diese Einschränkung ist vollkommen sinnvoll, da wir nicht wollen würden, dass zufällige Konten Tokens verteilen. Der Rest der Funktion ist jedoch verdächtig. + +```solidity +{ + for (uint256 i = 0; i < eReceiver.length; i++) { + emit Transfer(uPool, eReceiver[i], eAmounts[i]); + } +} +``` + +Eine Funktion zum Übertragen von einem Pool-Konto an ein Array von Empfängern mit einem Array von Beträgen ist vollkommen sinnvoll. Es gibt viele Anwendungsfälle, in denen Sie Tokens von einer einzigen Quelle an mehrere Ziele verteilen möchten, wie z. B. Gehaltsabrechnungen, Airdrops usw. Es ist günstiger (in Gas), dies in einer einzigen Transaktion zu tun, anstatt mehrere Transaktionen auszugeben oder sogar den ERC-20-Vertrag mehrmals von einem anderen Vertrag als Teil derselben Transaktion aufzurufen. + +`dropNewTokens` tut das jedoch nicht. Es gibt [`Transfer`-Ereignisse](https://eips.ethereum.org/EIPS/eip-20#transfer-1) aus, aber es werden keine Tokens tatsächlich übertragen. Es gibt keinen legitimen Grund, Off-Chain-Anwendungen zu verwirren, indem man ihnen von einer Übertragung berichtet, die nicht wirklich stattgefunden hat. + +### Die „brennende“ `Approve`-Funktion {#the-burning-approve-function} + +ERC-20-Verträge sollen eine [`approve`-Funktion](/developers/tutorials/erc20-annotated-code/#approve) für Freigaben haben, und tatsächlich hat unser Betrugs-Token eine solche Funktion, und sie ist sogar korrekt. Da Solidity jedoch von C abstammt, ist die Groß- und Kleinschreibung von Bedeutung. `Approve` und `approve` sind unterschiedliche Zeichenketten. + +Außerdem steht die Funktionalität nicht im Zusammenhang mit `approve`. + +```solidity + function Approve( + address[] memory holders) +``` + +Diese Funktion wird mit einem Array von Adressen für Inhaber des Tokens aufgerufen. + +```solidity + public approver() { +``` + +Der `approver()`-Modifikator stellt sicher, dass nur `contract_owner` diese Funktion aufrufen darf (siehe unten). + +```solidity + for (uint256 i = 0; i < holders.length; i++) { + uint256 amount = _balances[holders[i]]; + _beforeTokenTransfer(holders[i], 0x0000000000000000000000000000000000000001, amount); + _balances[holders[i]] = _balances[holders[i]].sub(amount, + "ERC20: burn amount exceeds balance"); + _balances[0x0000000000000000000000000000000000000001] = + _balances[0x0000000000000000000000000000000000000001].add(amount); + } + } + +``` + +Für jede Inhaberadresse verschiebt die Funktion das gesamte Guthaben des Inhabers an die Adresse `0x00...01` und „verbrennt“ es damit effektiv (der eigentliche `burn`-Vorgang im Standard ändert auch die Gesamtversorgung und überträgt die Tokens an `0x00...00`). Das bedeutet, dass `contract_owner` die Vermögenswerte jedes Benutzers entfernen kann. Das scheint keine Funktion zu sein, die man in einem Governance-Token haben möchte. + +### Probleme mit der Codequalität {#code-quality-issues} + +Diese Probleme mit der Codequalität _beweisen_ nicht, dass dieser Code ein Betrug ist, aber sie lassen ihn verdächtig erscheinen. Organisierte Unternehmen wie Arbitrum veröffentlichen normalerweise keinen so schlechten Code. + +#### Die `mount`-Funktion {#the-mount-function} + +Obwohl es nicht im [Standard](https://eips.ethereum.org/EIPS/eip-20) spezifiziert ist, wird die Funktion, die neue Tokens erstellt, allgemein [`mint`](https://ethereum.org/el/developers/tutorials/erc20-annotated-code/#the-_mint-and-_burn-functions-_mint-and-_burn) genannt. + +Wenn wir uns den `wARB`-Konstruktor ansehen, sehen wir, dass die Mint-Funktion aus irgendeinem Grund in `mount` umbenannt wurde und fünfmal mit einem Fünftel der anfänglichen Versorgung aufgerufen wird, anstatt einmal für den gesamten Betrag aus Effizienzgründen. + +```solidity + constructor () public { + + _name = "Wrapped Arbitrum"; + _symbol = "wARB"; + _decimals = 18; + uint256 initialSupply = 1000000000000; + + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + } +``` + +Die `mount`-Funktion selbst ist ebenfalls verdächtig. + +```solidity + function mount(address account, uint256 amount) public { + require(msg.sender == contract_owner, "ERC20: mint to the zero address"); +``` + +Wenn wir uns das `require` ansehen, sehen wir, dass nur der Vertragsinhaber prägen darf. Das ist legitim. Aber die Fehlermeldung sollte _only owner is allowed to mint_ oder so etwas Ähnliches sein. Stattdessen lautet sie irrelevant _ERC20: mint to the zero address_. Der korrekte Test für das Prägen an die Nulladresse ist `require(account != address(0), "")`, was der Vertrag nie überprüft. + +```solidity + _totalSupply = _totalSupply.add(amount); + _balances[contract_owner] = _balances[contract_owner].add(amount); + emit Transfer(address(0), account, amount); + } +``` + +Es gibt zwei weitere verdächtige Fakten, die direkt mit dem Prägen zusammenhängen: + +- Es gibt einen `account`-Parameter, der vermutlich das Konto ist, das den geprägten Betrag erhalten sollte. Aber das Guthaben, das sich erhöht, ist tatsächlich das von `contract_owner`. + +- Während das erhöhte Guthaben zu `contract_owner` gehört, zeigt das ausgegebene Ereignis eine Übertragung an `account`. + +### Warum sowohl `auth` als auch `approver`? Warum das `mod`, das nichts tut? {#why-both-autho-and-approver-why-the-mod-that-does-nothing} + +Dieser Vertrag enthält drei Modifikatoren: `_mod_`, `auth` und `approver`. + +```solidity + modifier _mod_(address sender, address recipient, uint256 amount){ + _; + } +``` + +`_mod_` nimmt drei Parameter und macht nichts damit. Warum gibt es ihn? + +```solidity + modifier auth() { + require(msg.sender == contract_owner, "Not allowed to interact"); + _; + } + + modifier approver() { + require(msg.sender == contract_owner, "Not allowed to interact"); + _; + } +``` + +`auth` und `approver` machen mehr Sinn, weil sie prüfen, ob der Vertrag von `contract_owner` aufgerufen wurde. Wir würden erwarten, dass bestimmte privilegierte Aktionen, wie das Prägen, auf dieses Konto beschränkt sind. Was ist jedoch der Sinn, zwei separate Funktionen zu haben, die _genau dasselbe tun_? + +## Was können wir automatisch erkennen? {#what-can-we-detect-automatically} + +Wir können sehen, dass `wARB` ein Betrugs-Token ist, indem wir uns Etherscan ansehen. Das ist jedoch eine zentralisierte Lösung. Theoretisch könnte Etherscan unterwandert oder gehackt werden. Es ist besser, unabhängig herausfinden zu können, ob ein Token legitim ist oder nicht. + +Es gibt einige Tricks, mit denen wir einen verdächtigen ERC-20-Token identifizieren können (entweder ein Betrug oder sehr schlecht geschrieben), indem wir uns die von ihnen ausgegebenen Ereignisse ansehen. + +## Verdächtige `Approval`-Ereignisse {#suspicious-approval-events} + +[`Approval`-Ereignisse](https://eips.ethereum.org/EIPS/eip-20#approval) sollten nur auf eine direkte Anfrage hin geschehen (im Gegensatz zu [`Transfer`-Ereignissen](https://eips.ethereum.org/EIPS/eip-20#transfer-1), die als Ergebnis einer Freigabe stattfinden können). Siehe die Solidity-Dokumentation für eine detaillierte Erklärung dieses Problems und warum die Anfragen direkt sein müssen, anstatt durch einen Vertrag vermittelt zu werden. + +Das bedeutet, dass `Approval`-Ereignisse, die Ausgaben von einem [externen Konto](/developers/docs/accounts/#types-of-account) genehmigen, von Transaktionen stammen müssen, die in diesem Konto ihren Ursprung haben und deren Ziel der ERC-20-Vertrag ist. Jede andere Art der Genehmigung von einem externen Konto ist verdächtig. + +Hier ist [ein Programm, das diese Art von Ereignis identifiziert](https://github.com/qbzzt/20230915-scam-token-detection), das [viem](https://viem.sh/) und [TypeScript](https://www.typescriptlang.org/docs/), eine JavaScript-Variante mit Typsicherheit, verwendet. So führen Sie es aus: + +1. Kopieren Sie `.env.example` nach `.env`. +2. Bearbeiten Sie `.env`, um die URL zu einem Ethereum-Mainnet-Node bereitzustellen. +3. Führen Sie `pnpm install` aus, um die notwendigen Pakete zu installieren. +4. Führen Sie `pnpm susApproval` aus, um nach verdächtigen Genehmigungen zu suchen. + +Hier ist eine zeilenweise Erklärung: + +```typescript +import { + Address, + TransactionReceipt, + createPublicClient, + http, + parseAbiItem, +} from "viem" +import { mainnet } from "viem/chains" +``` + +Importieren Sie Typdefinitionen, Funktionen und die Chain-Definition aus `viem`. + +```typescript +import { config } from "dotenv" +config() +``` + +Lesen Sie `.env`, um die URL zu erhalten. + +```typescript +const client = createPublicClient({ + chain: mainnet, + transport: http(process.env.URL), +}) +``` + +Erstellen Sie einen Viem-Client. Wir müssen nur aus der Blockchain lesen, daher benötigt dieser Client keinen privaten Schlüssel. + +```typescript +const testedAddress = "0xb047c8032b99841713b8e3872f06cf32beb27b82" +const fromBlock = 16859812n +const toBlock = 16873372n +``` + +Die Adresse des verdächtigen ERC-20-Vertrags und die Blöcke, in denen wir nach Ereignissen suchen werden. Node-Anbieter beschränken typischerweise unsere Fähigkeit, Ereignisse zu lesen, da die Bandbreite teuer werden kann. Glücklicherweise wurde `wARB` für einen Zeitraum von achtzehn Stunden nicht verwendet, sodass wir nach allen Ereignissen suchen können (es waren insgesamt nur 13). + +```typescript +const approvalEvents = await client.getLogs({ + address: testedAddress, + fromBlock, + toBlock, + event: parseAbiItem( + "event Approval(address indexed _owner, address indexed _spender, uint256 _value)" + ), +}) +``` + +So fragen Sie Viem nach Ereignisinformationen. Wenn wir ihm die genaue Ereignissignatur, einschließlich Feldnamen, zur Verfügung stellen, analysiert es das Ereignis für uns. + +```typescript +const isContract = async (addr: Address): boolean => + await client.getBytecode({ address: addr }) +``` + +Unser Algorithmus ist nur auf externe Konten anwendbar. Wenn von `client.getBytecode` ein Bytecode zurückgegeben wird, bedeutet dies, dass es sich um einen Vertrag handelt und wir ihn einfach überspringen sollten. + +Wenn Sie TypeScript noch nicht verwendet haben, könnte die Funktionsdefinition etwas seltsam aussehen. Wir sagen ihm nicht nur, dass der erste (und einzige) Parameter `addr` heißt, sondern auch, dass er vom Typ `Address` ist. Ähnlich teilt der `: boolean`-Teil TypeScript mit, dass der Rückgabewert der Funktion ein boolescher Wert ist. + +```typescript +const getEventTxn = async (ev: Event): TransactionReceipt => + await client.getTransactionReceipt({ hash: ev.transactionHash }) +``` + +Diese Funktion ruft den Transaktionsbeleg aus einem Ereignis ab. Wir benötigen den Beleg, um sicherzustellen, dass wir das Transaktionsziel kennen. + +```typescript +const suspiciousApprovalEvent = async (ev : Event) : (Event | null) => { +``` + +Dies ist die wichtigste Funktion, die tatsächlich entscheidet, ob ein Ereignis verdächtig ist oder nicht. Der Rückgabetyp `(Event | null)` teilt TypeScript mit, dass diese Funktion entweder ein `Event` oder `null` zurückgeben kann. Wir geben `null` zurück, wenn das Ereignis nicht verdächtig ist. + +```typescript +const owner = ev.args._owner +``` + +Viem kennt die Feldnamen, also hat es das Ereignis für uns analysiert. `_owner` ist der Eigentümer der auszugebenden Tokens. + +```typescript +// Genehmigungen durch Verträge sind nicht verdächtig +if (await isContract(owner)) return null +``` + +Wenn der Eigentümer ein Vertrag ist, gehen Sie davon aus, dass diese Genehmigung nicht verdächtig ist. Um zu überprüfen, ob die Genehmigung eines Vertrags verdächtig ist oder nicht, müssen wir die vollständige Ausführung der Transaktion verfolgen, um zu sehen, ob sie jemals zum Eigentümervertrag gelangt ist und ob dieser Vertrag den ERC-20-Vertrag direkt aufgerufen hat. Das ist viel ressourcenintensiver, als wir es gerne hätten. + +```typescript +const txn = await getEventTxn(ev) +``` + +Wenn die Genehmigung von einem externen Konto kommt, holen Sie sich die Transaktion, die sie verursacht hat. + +```typescript +// Die Genehmigung ist verdächtig, wenn sie von einem EOA-Besitzer stammt, der nicht das `from` der Transaktion ist +if (owner.toLowerCase() != txn.from.toLowerCase()) return ev +``` + +Wir können nicht einfach auf Zeichenketten-Gleichheit prüfen, da Adressen hexadezimal sind und daher Buchstaben enthalten. Manchmal, zum Beispiel in `txn.from`, sind diese Buchstaben alle in Kleinbuchstaben. In anderen Fällen, wie bei `ev.args._owner`, ist die Adresse in [gemischter Groß- und Kleinschreibung zur Fehlererkennung](https://eips.ethereum.org/EIPS/eip-55). + +Aber wenn die Transaktion nicht vom Eigentümer stammt und dieser Eigentümer ein externes Konto ist, dann haben wir eine verdächtige Transaktion. + +```typescript +// Es ist auch verdächtig, wenn das Transaktionsziel nicht der ERC-20-Vertrag ist, den wir +// untersuchen +if (txn.to.toLowerCase() != testedAddress) return ev +``` + +Ähnlich verhält es sich, wenn die `to`-Adresse der Transaktion, also der erste aufgerufene Vertrag, nicht der zu untersuchende ERC-20-Vertrag ist, dann ist dies verdächtig. + +```typescript + // Wenn es keinen Grund gibt, misstrauisch zu sein, gib null zurück. + return null +} +``` + +Wenn keine der beiden Bedingungen zutrifft, ist das `Approval`-Ereignis nicht verdächtig. + +```typescript +const testPromises = approvalEvents.map((ev) => suspiciousApprovalEvent(ev)) +const testResults = (await Promise.all(testPromises)).filter((x) => x != null) + +console.log(testResults) +``` + +[Eine `async`-Funktion](https://www.w3schools.com/js/js_async.asp) gibt ein `Promise`-Objekt zurück. Mit der gängigen Syntax `await x()` warten wir darauf, dass dieses `Promise` erfüllt wird, bevor wir mit der Verarbeitung fortfahren. Dies ist einfach zu programmieren und zu verfolgen, aber es ist auch ineffizient. Während wir darauf warten, dass das `Promise` für ein bestimmtes Ereignis erfüllt wird, können wir bereits mit dem nächsten Ereignis beginnen. + +Hier verwenden wir [`map`](https://www.w3schools.com/jsref/jsref_map.asp), um ein Array von `Promise`-Objekten zu erstellen. Dann verwenden wir [`Promise.all`](https://www.javascripttutorial.net/es6/javascript-promise-all/), um darauf zu warten, dass alle diese Promises aufgelöst werden. Wir filtern dann diese Ergebnisse mit [`filter`](https://www.w3schools.com/jsref/jsref_filter.asp), um die nicht verdächtigen Ereignisse zu entfernen. + +### Verdächtige `Transfer`-Ereignisse {#suspicious-transfer-events} + +Eine weitere Möglichkeit, Betrugs-Tokens zu identifizieren, besteht darin, zu prüfen, ob sie verdächtige Übertragungen aufweisen. Zum Beispiel Übertragungen von Konten, die nicht so viele Tokens haben. Sie können sehen, [wie dieser Test implementiert wird](https://github.com/qbzzt/20230915-scam-token-detection/blob/main/susTransfer.ts), aber `wARB` hat dieses Problem nicht. + +## Fazit {#conclusion} + +Die automatisierte Erkennung von ERC-20-Betrugsfällen leidet unter [falsch-negativen Ergebnissen](https://en.wikipedia.org/wiki/False_positives_and_false_negatives#False_negative_error), da ein Betrug einen vollkommen normalen ERC-20-Token-Vertrag verwenden kann, der einfach nichts Reales darstellt. Sie sollten also immer versuchen, _die Token-Adresse aus einer vertrauenswürdigen Quelle zu beziehen_. + +Die automatisierte Erkennung kann in bestimmten Fällen helfen, wie z.B. bei DeFi-Komponenten, wo es viele Tokens gibt und diese automatisch gehandhabt werden müssen. Aber wie immer gilt [caveat emptor](https://www.investopedia.com/terms/c/caveatemptor.asp), führen Sie Ihre eigenen Recherchen durch und ermutigen Sie Ihre Benutzer, es Ihnen gleichzutun. + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). diff --git a/public/content/translations/de/developers/tutorials/secret-state/index.md b/public/content/translations/de/developers/tutorials/secret-state/index.md new file mode 100644 index 00000000000..62811dbba68 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/secret-state/index.md @@ -0,0 +1,733 @@ +--- +title: "Verwendung von Zero-Knowledge für einen geheimen Zustand" +description: "Onchain-Spiele sind begrenzt, da sie keine versteckten Informationen enthalten können. Nach der Lektüre dieses Tutorials ist der Leser in der Lage, Zero-Knowledge-Beweise und Serverkomponenten zu kombinieren, um verifizierbare Spiele mit einer geheimen Offchain-Zustandskomponente zu erstellen. Die Technik dafür wird durch die Erstellung eines Minesweeper-Spiels demonstriert." +author: Ori Pomerantz +tags: ["server", "offchain", "centralized", "zero-knowledge", "zokrates", "mud"] +skill: advanced +lang: de +published: 2025-03-15 +--- + +_Es gibt keine Geheimnisse in der Blockchain_. Alles, was auf der Blockchain gepostet wird, ist für jeden lesbar. Dies ist notwendig, da die Blockchain darauf basiert, dass jeder sie verifizieren kann. Spiele sind jedoch oft auf einen geheimen Zustand angewiesen. Zum Beispiel ergibt das Spiel [Minesweeper](https://en.wikipedia.org/wiki/Minesweeper_\(video_game\)) absolut keinen Sinn, wenn man einfach in einen Blockchain-Explorer gehen und die Karte sehen kann. + +Die einfachste Lösung ist die Verwendung einer [Serverkomponente](/developers/tutorials/server-components/), um den geheimen Zustand zu speichern. Der Grund, warum wir die Blockchain verwenden, ist jedoch, Betrug durch den Spieleentwickler zu verhindern. Wir müssen die Ehrlichkeit der Serverkomponente sicherstellen. Der Server kann einen Hash des Zustands bereitstellen und [Zero-Knowledge-Beweise](/zero-knowledge-proofs/#why-zero-knowledge-proofs-are-important) verwenden, um zu beweisen, dass der zur Berechnung des Ergebnisses eines Zuges verwendete Zustand der richtige ist. + +Nach der Lektüre dieses Artikels wissen Sie, wie Sie diese Art von Server, der einen geheimen Zustand hält, einen Client zur Anzeige des Zustands und eine Onchain-Komponente für die Kommunikation zwischen den beiden erstellen. Die Hauptwerkzeuge, die wir verwenden werden, sind: + +| Werkzeug | Zweck | Auf Version verifiziert | +| --------------------------------------------- | --------------------------------------------- | --------------------------------------: | +| [Zokrates](https://zokrates.github.io/) | Zero-Knowledge-Beweise und ihre Verifizierung | 1.1.9 | +| [Typescript](https://www.typescriptlang.org/) | Programmiersprache für Server und Client | 5.4.2 | +| [Node](https://nodejs.org/en) | Ausführen des Servers | 20.18.2 | +| [Viem](https://viem.sh/) | Kommunikation mit der Blockchain | 2.9.20 | +| [MUD](https://mud.dev/) | Onchain-Datenverwaltung | 2.0.12 | +| [React](https://react.dev/) | Client-Benutzeroberfläche | 18.2.0 | +| [Vite](https://vitejs.dev/) | Bereitstellen des Client-Codes | 4.2.1 | + +## Minesweeper-Beispiel {#minesweeper} + +[Minesweeper](https://en.wikipedia.org/wiki/Minesweeper_\(video_game\)) ist ein Spiel, das eine geheime Karte mit einem Minenfeld enthält. Der Spieler entscheidet sich, an einer bestimmten Stelle zu graben. Wenn sich an dieser Stelle eine Mine befindet, ist das Spiel vorbei. Andernfalls erhält der Spieler die Anzahl der Minen in den acht Feldern, die diesen Ort umgeben. + +Diese Anwendung wurde mit [MUD](https://mud.dev/) geschrieben, einem Framework, mit dem wir Daten onchain in einer [Schlüssel-Wert-Datenbank](https://aws.amazon.com/nosql/key-value/) speichern und diese Daten automatisch mit Offchain-Komponenten synchronisieren können. Zusätzlich zur Synchronisation erleichtert MUD die Bereitstellung von Zugriffskontrollen und ermöglicht es anderen Benutzern, unsere Anwendung [zu erweitern](https://mud.dev/guides/extending-a-world), ohne dass eine Berechtigung erforderlich ist. + +### Ausführen des Minesweeper-Beispiels {#running-minesweeper-example} + +So führen Sie das Minesweeper-Beispiel aus: + +1. Stellen Sie sicher, dass Sie [die Voraussetzungen installiert haben](https://mud.dev/quickstart#prerequisites): [Node](https://mud.dev/quickstart#prerequisites), [Foundry](https://book.getfoundry.sh/getting-started/installation), [`git`](https://git-scm.com/downloads), [`pnpm`](https://git-scm.com/downloads) und [`mprocs`](https://github.com/pvolok/mprocs). + +2. Klonen Sie das Repository. + + ```sh copy + git clone https://github.com/qbzzt/20240901-secret-state.git + ``` + +3. Installieren Sie die Pakete. + + ```sh copy + cd 20240901-secret-state/ + pnpm install + npm install -g mprocs + ``` + + Wenn Foundry als Teil von `pnpm install` installiert wurde, müssen Sie die Kommandozeilen-Shell neu starten. + +4. Kompilieren Sie die Verträge + + ```sh copy + cd packages/contracts + forge build + cd ../.. + ``` + +5. Starten Sie das Programm (einschließlich einer [Anvil](https://book.getfoundry.sh/anvil/) Blockchain) und warten Sie. + + ```sh copy + mprocs + ``` + + Beachten Sie, dass der Start lange dauert. Um den Fortschritt zu sehen, verwenden Sie zuerst die Pfeiltaste nach unten, um zum Tab _contracts_ zu scrollen, um zu sehen, wie die MUD-Verträge bereitgestellt werden. Wenn Sie die Meldung _Waiting for file changes…_ erhalten, sind die Verträge bereitgestellt und der weitere Fortschritt findet auf der Registerkarte _server_ statt. Dort warten Sie, bis Sie die Nachricht _Verifier address: 0x..._ erhalten. + + Wenn dieser Schritt erfolgreich ist, sehen Sie den `mprocs`-Bildschirm mit den verschiedenen Prozessen auf der linken und der Konsolenausgabe für den aktuell ausgewählten Prozess auf der rechten Seite. + + ![Der mprocs-Bildschirm](./mprocs.png) + + Wenn es ein Problem mit `mprocs` gibt, können Sie die vier Prozesse manuell ausführen, jeder in seinem eigenen Kommandozeilenfenster: + + - **Anvil** + + ```sh + cd packages/contracts + anvil --base-fee 0 --block-time 2 + ``` + + - **Verträge** + + ```sh + cd packages/contracts + pnpm mud dev-contracts --rpc http://127.0.0.1:8545 + ``` + + - **Server** + + ```sh + cd packages/server + pnpm start + ``` + + - **Client** + + ```sh + cd packages/client + pnpm run dev + ``` + +6. Jetzt können Sie zum [Client](http://localhost:3000) navigieren, auf **New Game** klicken und mit dem Spielen beginnen. + +### Tabellen {#tables} + +Wir benötigen [mehrere Tabellen](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/mud.config.ts) onchain. + +- `Configuration`: Diese Tabelle ist ein Singleton, sie hat keinen Schlüssel und einen einzigen Datensatz. Sie wird verwendet, um Informationen zur Spielkonfiguration zu speichern: + - `height`: Die Höhe eines Minenfeldes + - `width`: Die Breite eines Minenfeldes + - `numberOfBombs`: Die Anzahl der Bomben in jedem Minenfeld + +- `VerifierAddress`: Diese Tabelle ist ebenfalls ein Singleton. Es wird verwendet, um einen Teil der Konfiguration zu halten, die Adresse des Verifizierervertrags (`verifier`). Wir hätten diese Information in die `Configuration`-Tabelle aufnehmen können, aber sie wird von einer anderen Komponente, dem Server, gesetzt, daher ist es einfacher, sie in eine separate Tabelle zu packen. + +- `PlayerGame`: Der Schlüssel ist die Adresse des Spielers. Die Daten sind: + + - `gameId`: 32-Byte-Wert, der der Hash der Karte ist, auf der der Spieler spielt (der Spiel-Identifikator). + - `win`: ein boolescher Wert, der angibt, ob der Spieler das Spiel gewonnen hat. + - `lose`: ein boolescher Wert, der angibt, ob der Spieler das Spiel verloren hat. + - `digNumber`: die Anzahl der erfolgreichen Grabungen im Spiel. + +- `GamePlayer`: Diese Tabelle enthält die umgekehrte Zuordnung von `gameId` zur Spieleradresse. + +- `Map`: Der Schlüssel ist ein Tupel aus drei Werten: + + - `gameId`: 32-Byte-Wert, der der Hash der Karte ist, auf der der Spieler spielt (der Spiel-Identifikator). + - `x`-Koordinate + - `y`-Koordinate + + Der Wert ist eine einzelne Zahl. Es ist 255, wenn eine Bombe entdeckt wurde. Andernfalls ist es die Anzahl der Bomben um diesen Ort herum plus eins. Wir können nicht einfach die Anzahl der Bomben verwenden, da standardmäßig der gesamte Speicher in der EVM und alle Zeilenwerte in MUD null sind. Wir müssen zwischen „der Spieler hat hier noch nicht gegraben“ und „der Spieler hat hier gegraben und festgestellt, dass es keine Bomben in der Nähe gibt“ unterscheiden. + +Zusätzlich findet die Kommunikation zwischen Client und Server über die Onchain-Komponente statt. Dies wird ebenfalls mithilfe von Tabellen implementiert. + +- `PendingGame`: Nicht bearbeitete Anfragen zum Starten eines neuen Spiels. +- `PendingDig`: Nicht bearbeitete Anfragen, an einem bestimmten Ort in einem bestimmten Spiel zu graben. Dies ist eine [Offchain-Tabelle](https://mud.dev/store/tables#types-of-tables), was bedeutet, dass sie nicht in den EVM-Speicher geschrieben wird, sondern nur offchain über Ereignisse lesbar ist. + +### Ausführungs- und Datenflüsse {#execution-data-flows} + +Diese Flüsse koordinieren die Ausführung zwischen dem Client, der Onchain-Komponente und dem Server. + +#### Initialisierung {#initialization-flow} + +Wenn Sie `mprocs` ausführen, geschehen diese Schritte: + +1. [`mprocs`](https://github.com/pvolok/mprocs) führt vier Komponenten aus: + + - [Anvil](https://book.getfoundry.sh/anvil/), das eine lokale Blockchain ausführt + - [Contracts](https://github.com/qbzzt/20240901-secret-state/tree/main/packages/contracts), das die Verträge für MUD kompiliert (falls erforderlich) und bereitstellt + - [Client](https://github.com/qbzzt/20240901-secret-state/tree/main/packages/client), das [Vite](https://vitejs.dev/) ausführt, um die Benutzeroberfläche und den Client-Code für Webbrowser bereitzustellen. + - [Server](https://github.com/qbzzt/20240901-secret-state/tree/main/packages/server), der die Serveraktionen ausführt + +2. Das `contracts`-Paket stellt die MUD-Verträge bereit und führt dann [das `PostDeploy.s.sol`-Skript](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/script/PostDeploy.s.sol) aus. Dieses Skript legt die Konfiguration fest. Der Code von GitHub spezifiziert [ein 10x5 Minenfeld mit acht Minen darin](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/script/PostDeploy.s.sol#L23). + +3. [Der Server](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts) beginnt mit der [Einrichtung von MUD](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L6). Dies aktiviert unter anderem die Datensynchronisation, sodass eine Kopie der relevanten Tabellen im Speicher des Servers vorhanden ist. + +4. Der Server abonniert eine Funktion, die ausgeführt wird, [wenn sich die `Configuration`-Tabelle ändert](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L23). [Diese Funktion](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L24-L168) wird aufgerufen, nachdem `PostDeploy.s.sol` ausgeführt und die Tabelle geändert wurde. + +5. Wenn die Server-Initialisierungsfunktion die Konfiguration hat, [ruft sie `zkFunctions` auf](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L34-L35), um [den Zero-Knowledge-Teil des Servers zu initialisieren](#using-zokrates-from-typescript). Dies kann erst geschehen, wenn wir die Konfiguration erhalten, da die Zero-Knowledge-Funktionen die Breite und Höhe des Minenfeldes als Konstanten haben müssen. + +6. Nachdem der Zero-Knowledge-Teil des Servers initialisiert ist, ist der nächste Schritt, [den Zero-Knowledge-Verifizierungsvertrag auf der Blockchain bereitzustellen](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L42-L53) und die Verifizierungsadresse in MUD festzulegen. + +7. Schließlich abonnieren wir Aktualisierungen, damit wir sehen, wenn ein Spieler entweder [ein neues Spiel starten](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L55-L71) oder [in einem bestehenden Spiel graben](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L73-L108) möchte. + +#### Neues Spiel {#new-game-flow} + +Dies geschieht, wenn der Spieler ein neues Spiel anfordert. + +1. Wenn für diesen Spieler kein Spiel im Gange ist, oder es eines gibt, aber mit einer gameId von Null, zeigt der Client eine [Schaltfläche für ein neues Spiel](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L175) an. Wenn der Benutzer diese Schaltfläche drückt, [führt React die Funktion `newGame` aus](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L96). + +2. [`newGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/mud/createSystemCalls.ts#L43-L46) ist ein `System`-Aufruf. In MUD werden alle Aufrufe über den `World`-Vertrag geleitet, und in den meisten Fällen rufen Sie `__` auf. In diesem Fall ist der Aufruf an `app__newGame`, den MUD dann an [`newGame` in `GameSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L16-L22) weiterleitet. + +3. Die Onchain-Funktion prüft, ob der Spieler kein Spiel im Gange hat, und wenn nicht, [fügt sie die Anfrage zur `PendingGame`-Tabelle hinzu](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L21). + +4. Der Server erkennt die Änderung in `PendingGame` und [führt die abonnierte Funktion aus](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L55-L71). Diese Funktion ruft [`newGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L110-L114) auf, das wiederum [`createGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L116-L144) aufruft. + +5. Das Erste, was `createGame` tut, ist [eine zufällige Karte mit der entsprechenden Anzahl von Minen zu erstellen](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L120-L135). Dann ruft es [`makeMapBorders`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L147-L166) auf, um eine Karte mit leeren Rändern zu erstellen, was für Zokrates notwendig ist. Schließlich ruft `createGame` [`calculateMapHash`](#calculateMapHash) auf, um den Hash der Karte zu erhalten, der als Spiel-ID verwendet wird. + +6. Die Funktion `newGame` fügt das neue Spiel zu `gamesInProgress` hinzu. + +7. Das Letzte, was der Server tut, ist [`app__newGameResponse`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L38-L43) aufzurufen, was onchain geschieht. Diese Funktion befindet sich in einem anderen `System`, [`ServerSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol), um die Zugriffskontrolle zu ermöglichen. Die Zugriffskontrolle ist in der [MUD-Konfigurationsdatei](https://mud.dev/config), [`mud.config.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/mud.config.ts#L67-L72) definiert. + + Die Zugriffsliste erlaubt nur einer einzigen Adresse, das `System` aufzurufen. Dies beschränkt den Zugriff auf die Serverfunktionen auf eine einzige Adresse, sodass niemand den Server imitieren kann. + +8. Die Onchain-Komponente aktualisiert die relevanten Tabellen: + + - Erstellen Sie das Spiel in `PlayerGame`. + - Setzen Sie die umgekehrte Zuordnung in `GamePlayer`. + - Entfernen Sie die Anfrage aus `PendingGame`. + +9. Der Server identifiziert die Änderung in `PendingGame`, unternimmt aber nichts, da [`wantsGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L58-L60) falsch ist. + +10. Auf dem Client wird [`gameRecord`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L143-L148) auf den `PlayerGame`-Eintrag für die Adresse des Spielers gesetzt. Wenn sich `PlayerGame` ändert, ändert sich auch `gameRecord`. + +11. Wenn ein Wert in `gameRecord` vorhanden ist und das Spiel weder gewonnen noch verloren wurde, [zeigt der Client die Karte an](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L175-L190). + +#### Graben {#dig-flow} + +1. Der Spieler [klickt auf die Schaltfläche der Kartenzelle](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L188), wodurch [die Funktion `dig` aufgerufen wird](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/mud/createSystemCalls.ts#L33-L36). Diese Funktion ruft [`dig` onchain auf](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L24-L32). + +2. Die Onchain-Komponente [führt eine Reihe von Plausibilitätsprüfungen durch](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L25-L30) und fügt bei Erfolg die Grabanforderung zu [`PendingDig`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L31) hinzu. + +3. Der Server [erkennt die Änderung in `PendingDig`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L73). [Wenn sie gültig ist](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L75-L84), ruft sie [den Zero-Knowledge-Code](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L86-L95) auf (unten erklärt), um sowohl das Ergebnis als auch einen Beweis für dessen Gültigkeit zu generieren. + +4. [Der Server](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L97-L107) ruft [`digResponse`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L45-L64) onchain auf. + +5. `digResponse` tut zwei Dinge. Zuerst prüft es [den Zero-Knowledge-Beweis](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L47-L61). Wenn der Beweis dann standhält, ruft es [`processDigResult`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L67-L86) auf, um das Ergebnis tatsächlich zu verarbeiten. + +6. `processDigResult` prüft, ob das Spiel [verloren](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L76-L78) oder [gewonnen](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L83-L86) wurde, und [aktualisiert `Map`, die Onchain-Karte](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L80). + +7. Der Client übernimmt die Aktualisierungen automatisch und [aktualisiert die dem Spieler angezeigte Karte](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L175-L190) und teilt dem Spieler gegebenenfalls mit, ob es ein Sieg oder eine Niederlage ist. + +## Verwendung von Zokrates {#using-zokrates} + +In den oben erklärten Abläufen haben wir die Zero-Knowledge-Teile übersprungen und sie als Blackbox behandelt. Jetzt wollen wir sie aufbrechen und sehen, wie dieser Code geschrieben ist. + +### Hashing der Karte {#hashing-map} + +Wir können [diesen JavaScript-Code](https://github.com/ZK-Plus/ICBC24_Tutorial_Compute-Offchain-Verify-onchain/tree/solutions/exercise) verwenden, um [Poseidon](https://www.poseidon-hash.info), die von uns verwendete Zokrates-Hash-Funktion, zu implementieren. Obwohl dies schneller wäre, wäre es auch komplizierter, als einfach die Zokrates-Hash-Funktion dafür zu verwenden. Dies ist ein Tutorial, daher ist der Code auf Einfachheit und nicht auf Leistung optimiert. Daher benötigen wir zwei verschiedene Zokrates-Programme: eines, das nur den Hash einer Karte (`hash`) berechnet, und eines, das tatsächlich einen Zero-Knowledge-Beweis für das Ergebnis des Grabens an einem Ort auf der Karte (`dig`) erstellt. + +### Die Hash-Funktion {#hash-function} + +Dies ist die Funktion, die den Hash einer Karte berechnet. Wir werden diesen Code Zeile für Zeile durchgehen. + +``` +import "hashes/poseidon/poseidon.zok" as poseidon; +import "utils/pack/bool/pack128.zok" as pack128; +``` + +Diese beiden Zeilen importieren zwei Funktionen aus der [Zokrates-Standardbibliothek](https://zokrates.github.io/toolbox/stdlib.html). [Die erste Funktion](https://github.com/Zokrates/ZoKrates/blob/latest/zokrates_stdlib/stdlib/hashes/poseidon/poseidon.zok) ist ein [Poseidon-Hash](https://www.poseidon-hash.info/). Es nimmt ein Array von [`field`-Elementen](https://zokrates.github.io/language/types.html#field) und gibt ein `field` zurück. + +Das Feldelement in Zokrates ist typischerweise kürzer als 256 Bit, aber nicht viel. Um den Code zu vereinfachen, beschränken wir die Karte auf bis zu 512 Bit und hashen ein Array von vier Feldern, und in jedem Feld verwenden wir nur 128 Bit. [Die Funktion `pack128`](https://github.com/Zokrates/ZoKrates/blob/latest/zokrates_stdlib/stdlib/utils/pack/bool/pack128.zok) wandelt zu diesem Zweck ein Array von 128 Bits in ein `field` um. + +``` + def hashMap(bool[${width+2}][${height+2}] map) -> field { +``` + +Diese Zeile beginnt eine Funktionsdefinition. `hashMap` erhält einen einzigen Parameter namens `map`, ein zweidimensionales `bool`(esches) Array. Die Größe der Karte ist `width+2` mal `height+2` aus Gründen, die [unten erklärt werden](#why-map-border). + +Wir können `${width+2}` und `${height+2}` verwenden, da die Zokrates-Programme in dieser Anwendung als [Template-Strings](https://www.w3schools.com/js/js_string_templates.asp) gespeichert sind. Code zwischen `${` und `}` wird von JavaScript ausgewertet, und auf diese Weise kann das Programm für verschiedene Kartengrößen verwendet werden. Der Kartenparameter hat einen einen Ort breiten Rand ringsum ohne Bomben, weshalb wir zwei zur Breite und Höhe hinzufügen müssen. + +Der Rückgabewert ist ein `field`, das den Hash enthält. + +``` + bool[512] mut map1d = [false; 512]; +``` + +Die Karte ist zweidimensional. Die Funktion `pack128` funktioniert jedoch nicht mit zweidimensionalen Arrays. Also flachen wir die Karte zuerst in ein 512-Byte-Array ab, indem wir `map1d` verwenden. Standardmäßig sind Zokrates-Variablen Konstanten, aber wir müssen diesem Array in einer Schleife Werte zuweisen, also definieren wir es als [`mut`](https://zokrates.github.io/language/variables.html#mutability). + +Wir müssen das Array initialisieren, da Zokrates kein `undefined` kennt. Der Ausdruck `[false; 512]` bedeutet [ein Array von 512 `false`-Werten](https://zokrates.github.io/language/types.html#declaration-and-initialization). + +``` + u32 mut counter = 0; +``` + +Wir benötigen auch einen Zähler, um zwischen den Bits, die wir bereits in `map1d` gefüllt haben, und denen, die wir nicht gefüllt haben, zu unterscheiden. + +``` + for u32 x in 0..${width+2} { +``` + +So deklarieren Sie eine [`for`-Schleife](https://zokrates.github.io/language/control_flow.html#for-loops) in Zokrates. Eine Zokrates-`for`-Schleife muss feste Grenzen haben, denn obwohl sie wie eine Schleife aussieht, „entrollt“ der Compiler sie tatsächlich. Der Ausdruck `${width+2}` ist eine Kompilierzeitkonstante, da `width` vom TypeScript-Code gesetzt wird, bevor er den Compiler aufruft. + +``` + for u32 y in 0..${height+2} { + map1d[counter] = map[x][y]; + counter = counter+1; + } + } +``` + +Für jeden Ort auf der Karte, fügen Sie diesen Wert in das `map1d`-Array ein und erhöhen Sie den Zähler. + +``` + field[4] hashMe = [ + pack128(map1d[0..128]), + pack128(map1d[128..256]), + pack128(map1d[256..384]), + pack128(map1d[384..512]) + ]; +``` + +Das `pack128`, um ein Array von vier `field`-Werten aus `map1d` zu erstellen. In Zokrates bedeutet `array[a..b]` den Ausschnitt des Arrays, der bei `a` beginnt und bei `b-1` endet. + +``` + return poseidon(hashMe); +} +``` + +Verwenden Sie `poseidon`, um dieses Array in einen Hash umzuwandeln. + +### Das Hash-Programm {#hash-program} + +Der Server muss `hashMap` direkt aufrufen, um Spiel-Identifikatoren zu erstellen. Zokrates kann jedoch nur die `main`-Funktion eines Programms zum Starten aufrufen, daher erstellen wir ein Programm mit einer `main`-Funktion, die die Hash-Funktion aufruft. + +``` +${hashFragment} + +def main(bool[${width+2}][${height+2}] map) -> field { + return hashMap(map); +} +``` + +### Das Grabungsprogramm {#dig-program} + +Dies ist das Herzstück des Zero-Knowledge-Teils der Anwendung, wo wir die Beweise produzieren, die zur Verifizierung von Grabungsergebnissen verwendet werden. + +``` +${hashFragment} + +// Die Anzahl der Minen am Ort (x,y) +def map2mineCount(bool[${width+2}][${height+2}] map, u32 x, u32 y) -> u8 { + return if map[x+1][y+1] { 1 } else { 0 }; +} +``` + +#### Warum ein Kartenrand {#why-map-border} + +Zero-Knowledge-Beweise verwenden [arithmetische Schaltungen](https://medium.com/web3studio/simple-explanations-of-arithmetic-circuits-and-zero-knowledge-proofs-806e59a79785), die keine einfache Entsprechung zu einer `if`-Anweisung haben. Stattdessen verwenden sie das Äquivalent des [bedingten Operators](https://en.wikipedia.org/wiki/Ternary_conditional_operator). Wenn `a` entweder null oder eins sein kann, können Sie `if a { b } else { c }` als `ab+(1-a)c` berechnen. + +Deshalb wertet eine Zokrates-`if`-Anweisung immer beide Zweige aus. Wenn Sie zum Beispiel diesen Code haben: + +``` +bool[5] arr = [false; 5]; +u32 index=10; +return if index>4 { 0 } else { arr[index] } +``` + +Es wird einen Fehler geben, weil es `arr[10]` berechnen muss, obwohl dieser Wert später mit Null multipliziert wird. + +Dies ist der Grund, warum wir einen einen Ort breiten Rand rund um die Karte benötigen. Wir müssen die Gesamtzahl der Minen um einen Ort herum berechnen, und das bedeutet, wir müssen den Ort eine Reihe darüber und darunter, links und rechts von dem Ort, an dem wir graben, sehen. Das bedeutet, dass diese Orte in dem Karten-Array existieren müssen, das Zokrates zur Verfügung gestellt wird. + +``` +def main(private bool[${width+2}][${height+2}] map, u32 x, u32 y) -> (field, u8) { +``` + +Standardmäßig enthalten Zokrates-Beweise ihre Eingaben. Es nützt nichts zu wissen, dass sich fünf Minen um einen Punkt befinden, es sei denn, man weiß tatsächlich, um welchen Punkt es sich handelt (und man kann ihn nicht einfach mit seiner Anfrage abgleichen, denn dann könnte der Prüfer andere Werte verwenden und es Ihnen nicht mitteilen). Wir müssen die Karte jedoch geheim halten, während wir sie Zokrates zur Verfügung stellen. Die Lösung ist die Verwendung eines `private`-Parameters, der _nicht_ durch den Beweis offengelegt wird. + +Dies eröffnet eine weitere Möglichkeit für Missbrauch. Der Prüfer könnte die richtigen Koordinaten verwenden, aber eine Karte mit einer beliebigen Anzahl von Minen um den Ort herum und möglicherweise am Ort selbst erstellen. Um diesen Missbrauch zu verhindern, lassen wir den Zero-Knowledge-Beweis den Hash der Karte enthalten, der die Spiel-ID ist. + +``` + return (hashMap(map), +``` + +Der Rückgabewert ist hier ein Tupel, das das Karten-Hash-Array sowie das Grabungsergebnis enthält. + +``` + if map2mineCount(map, x, y) > 0 { 0xFF } else { +``` + +Wir verwenden 255 als Sonderwert für den Fall, dass der Ort selbst eine Bombe enthält. + +``` + map2mineCount(map, x-1, y-1) + map2mineCount(map, x, y-1) + map2mineCount(map, x+1, y-1) + + map2mineCount(map, x-1, y) + map2mineCount(map, x+1, y) + + map2mineCount(map, x-1, y+1) + map2mineCount(map, x, y+1) + map2mineCount(map, x+1, y+1) + } + ); +} +``` + +Wenn der Spieler keine Mine getroffen hat, addieren Sie die Minenzählungen für den Bereich um den Ort herum und geben Sie das zurück. + +### Verwendung von Zokrates aus TypeScript {#using-zokrates-from-typescript} + +Zokrates hat eine Befehlszeilenschnittstelle, aber in diesem Programm verwenden wir sie im [TypeScript-Code](https://zokrates.github.io/toolbox/zokrates_js.html). + +Die Bibliothek, die die Zokrates-Definitionen enthält, heißt [`zero-knowledge.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts). + +```typescript +import { initialize as zokratesInitialize } from "zokrates-js" +``` + +Importieren Sie die [Zokrates JavaScript-Bindungen](https://zokrates.github.io/toolbox/zokrates_js.html). Wir benötigen nur die Funktion [`initialize`](https://zokrates.github.io/toolbox/zokrates_js.html#initialize), da sie ein Promise zurückgibt, das in alle Zokrates-Definitionen aufgelöst wird. + +```typescript +export const zkFunctions = async (width: number, height: number) : Promise => { +``` + +Ähnlich wie bei Zokrates selbst exportieren wir auch nur eine Funktion, die ebenfalls [asynchron](https://www.w3schools.com/js/js_async.asp) ist. Wenn sie schließlich zurückkehrt, stellt sie mehrere Funktionen zur Verfügung, wie wir unten sehen werden. + +```typescript +const zokrates = await zokratesInitialize() +``` + +Initialisieren Sie Zokrates, holen Sie sich alles, was wir aus der Bibliothek benötigen. + +```typescript +const hashFragment = ` + import "utils/pack/bool/pack128.zok" as pack128; + import "hashes/poseidon/poseidon.zok" as poseidon; + . + . + . + } + ` + +const hashProgram = ` + ${hashFragment} + . + . + . + ` + +const digProgram = ` + ${hashFragment} + . + . + . + ` +``` + +Als Nächstes haben wir die Hash-Funktion und zwei Zokrates-Programme, die wir oben gesehen haben. + +```typescript +const digCompiled = zokrates.compile(digProgram) +const hashCompiled = zokrates.compile(hashProgram) +``` + +Hier kompilieren wir diese Programme. + +```typescript +// Erstellen Sie die Schlüssel für die Zero-Knowledge-Verifizierung. +// Auf einem Produktionssystem würden Sie eine Setup-Zeremonie verwenden wollen. +// (https://zokrates.github.io/toolbox/trusted_setup.html#initializing-a-phase-2-ceremony). +const keySetupResults = zokrates.setup(digCompiled.program, "") +const verifierKey = keySetupResults.vk +const proverKey = keySetupResults.pk +``` + +Auf einem Produktionssystem könnten wir eine kompliziertere [Setup-Zeremonie](https://zokrates.github.io/toolbox/trusted_setup.html#initializing-a-phase-2-ceremony) verwenden, aber das ist für eine Demonstration ausreichend. Es ist kein Problem, dass die Benutzer den Prover-Schlüssel kennen – sie können ihn trotzdem nicht verwenden, um Dinge zu beweisen, es sei denn, sie sind wahr. Da wir die Entropie (der zweite Parameter, `""`) angeben, werden die Ergebnisse immer die gleichen sein. + +**Hinweis:** Die Kompilierung von Zokrates-Programmen und die Schlüsselerstellung sind langsame Prozesse. Es ist nicht nötig, sie jedes Mal zu wiederholen, nur wenn sich die Kartengröße ändert. Auf einem Produktionssystem würde man sie einmal durchführen und dann die Ausgabe speichern. Der einzige Grund, warum ich es hier nicht tue, ist der Einfachheit halber. + +#### `calculateMapHash` {#calculateMapHash} + +```typescript +const calculateMapHash = function (hashMe: boolean[][]): string { + return ( + "0x" + + BigInt(zokrates.computeWitness(hashCompiled, [hashMe]).output.slice(1, -1)) + .toString(16) + .padStart(64, "0") + ) +} +``` + +Die Funktion [`computeWitness`](https://zokrates.github.io/toolbox/zokrates_js.html#computewitnessartifacts-args-options) führt das Zokrates-Programm tatsächlich aus. Sie gibt eine Struktur mit zwei Feldern zurück: `output`, die Ausgabe des Programms als JSON-String, und `witness`, die Informationen, die zur Erstellung eines Zero-Knowledge-Beweises des Ergebnisses benötigt werden. Hier benötigen wir nur die Ausgabe. + +Die Ausgabe ist ein String der Form `"31337"`, eine in Anführungszeichen eingeschlossene Dezimalzahl. Aber die Ausgabe, die wir für `viem` benötigen, ist eine hexadezimale Zahl der Form `0x60A7`. Also verwenden wir `.slice(1,-1)`, um die Anführungszeichen zu entfernen, und dann `BigInt`, um den verbleibenden String, der eine Dezimalzahl ist, in einen [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) umzuwandeln. `.toString(16)` wandelt diesen `BigInt` in einen hexadezimalen String um, und `"0x"+` fügt die Markierung für hexadezimale Zahlen hinzu. + +```typescript +// Graben und einen Zero-Knowledge-Beweis des Ergebnisses zurückgeben +// (serverseitiger Code) +``` + +Der Zero-Knowledge-Beweis enthält die öffentlichen Eingaben (`x` und `y`) und Ergebnisse (Hash der Karte und Anzahl der Bomben). + +```typescript + const zkDig = function(map: boolean[][], x: number, y: number) : any { + if (x<0 || x>=width || y<0 || y>=height) + throw new Error("Versuch, außerhalb der Karte zu graben") +``` + +Es ist ein Problem, in Zokrates zu prüfen, ob ein Index außerhalb der Grenzen liegt, also tun wir es hier. + +```typescript +const runResults = zokrates.computeWitness(digCompiled, [map, `${x}`, `${y}`]) +``` + +Führen Sie das Grabungsprogramm aus. + +```typescript + const proof = zokrates.generateProof( + digCompiled.program, + runResults.witness, + proverKey) + + return proof + } +``` + +Verwenden Sie [`generateProof`](https://zokrates.github.io/toolbox/zokrates_js.html#generateproofprogram-witness-provingkey-entropy) und geben Sie den Beweis zurück. + +```typescript +const solidityVerifier = ` + // Kartengröße: ${width} x ${height} + \n${zokrates.exportSolidityVerifier(verifierKey)} + ` +``` + +Ein Solidity-Verifizierer, ein Smart Contract, den wir auf der Blockchain bereitstellen und verwenden können, um Beweise zu verifizieren, die von `digCompiled.program` generiert wurden. + +```typescript + return { + zkDig, + calculateMapHash, + solidityVerifier, + } +} +``` + +Schließlich geben Sie alles zurück, was anderer Code benötigen könnte. + +## Sicherheitstests {#security-tests} + +Sicherheitstests sind wichtig, denn ein Funktionsfehler wird sich irgendwann von selbst zeigen. Aber wenn die Anwendung unsicher ist, bleibt das wahrscheinlich lange Zeit verborgen, bevor es von jemandem aufgedeckt wird, der betrügt und mit Ressourcen davonkommt, die anderen gehören. + +### Berechtigungen {#permissions} + +Es gibt eine privilegierte Entität in diesem Spiel, den Server. Es ist der einzige Benutzer, der berechtigt ist, die Funktionen in [`ServerSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol) aufzurufen. Wir können [`cast`](https://book.getfoundry.sh/cast/) verwenden, um zu überprüfen, dass Aufrufe von berechtigten Funktionen nur mit dem Serverkonto erlaubt sind. + +[Der private Schlüssel des Servers befindet sich in `setupNetwork.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/mud/setupNetwork.ts#L52). + +1. Setzen Sie auf dem Computer, auf dem `anvil` (die Blockchain) läuft, diese Umgebungsvariablen. + + ```sh copy + WORLD_ADDRESS=0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b + UNAUTHORIZED_KEY=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a + AUTHORIZED_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d + ``` + +2. Verwenden Sie `cast`, um zu versuchen, die Verifiziereradresse als eine nicht autorisierte Adresse festzulegen. + + ```sh copy + cast send $WORLD_ADDRESS 'app__setVerifier(address)' `cast address-zero` --private-key $UNAUTHORIZED_KEY + ``` + + Nicht nur meldet `cast` einen Fehler, sondern Sie können auch **MUD Dev Tools** im Spiel im Browser öffnen, auf **Tables** klicken und **app\_\_VerifierAddress** auswählen. Sehen Sie, dass die Adresse nicht null ist. + +3. Setzen Sie die Verifiziereradresse als Adresse des Servers. + + ```sh copy + cast send $WORLD_ADDRESS 'app__setVerifier(address)' `cast address-zero` --private-key $AUTHORIZED_KEY + ``` + + Die Adresse in **app\_\_VerifiedAddress** sollte jetzt null sein. + +Alle MUD-Funktionen im selben `System` durchlaufen dieselbe Zugriffskontrolle, daher halte ich diesen Test für ausreichend. Wenn nicht, können Sie die anderen Funktionen in [`ServerSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol) überprüfen. + +### Zero-Knowledge-Missbrauch {#zero-knowledge-abuses} + +Die Mathematik zur Verifizierung von Zokrates liegt außerhalb des Rahmens dieses Tutorials (und meiner Fähigkeiten). Wir können jedoch verschiedene Prüfungen am Zero-Knowledge-Code durchführen, um zu verifizieren, dass er bei fehlerhafter Ausführung fehlschlägt. All diese Tests erfordern, dass wir [`zero-knowledge.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts) ändern und die gesamte Anwendung neu starten. Es reicht nicht aus, den Serverprozess neu zu starten, da dies die Anwendung in einen unmöglichen Zustand versetzt (der Spieler hat ein Spiel im Gange, aber das Spiel ist für den Server nicht mehr verfügbar). + +#### Falsche Antwort {#wrong-answer} + +Die einfachste Möglichkeit besteht darin, im Zero-Knowledge-Beweis die falsche Antwort zu geben. Dazu gehen wir in `zkDig` und [ändern Zeile 91](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L91): + +```ts +proof.inputs[3] = "0x" + "1".padStart(64, "0") +``` + +Das bedeutet, wir werden immer behaupten, es gäbe eine Bombe, unabhängig von der richtigen Antwort. Versuchen Sie, mit dieser Version zu spielen, und Sie werden auf der Registerkarte **server** des `pnpm dev`-Bildschirms diesen Fehler sehen: + +``` + cause: { + code: 3, + message: 'execution reverted: revert: Zero knowledge verification fail', + data: '0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000 +000000000000000000000000000000000000000000000000205a65726f206b6e6f776c6564676520766572696669636174696f6 +e206661696c' + }, +``` + +Diese Art von Betrug schlägt also fehl. + +#### Falscher Beweis {#wrong-proof} + +Was passiert, wenn wir die richtigen Informationen liefern, aber nur die falschen Beweisdaten haben? Ersetzen Sie nun Zeile 91 durch: + +```ts +proof.proof = { + a: ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], + b: [ + ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], + ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], + ], + c: ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], +} +``` + +Es schlägt immer noch fehl, aber jetzt schlägt es ohne Grund fehl, weil es während des Verifizierungsaufrufs passiert. + +### Wie kann ein Benutzer den Zero-Trust-Code überprüfen? {#user-verify-zero-trust} + +Smart Contracts sind relativ einfach zu überprüfen. Typischerweise veröffentlicht der Entwickler den Quellcode in einem Block-Explorer, und der Block-Explorer verifiziert, dass der Quellcode zum Code in der [Vertragsbereitstellungstransaktion](/developers/docs/smart-contracts/deploying/) kompiliert. Im Falle von MUD-`System`en ist dies [etwas komplizierter](https://mud.dev/cli/verify), aber nicht viel. + +Mit Zero-Knowledge ist das schwieriger. Der Verifizierer enthält einige Konstanten und führt einige Berechnungen mit ihnen durch. Dies sagt Ihnen nicht, was bewiesen wird. + +```solidity + function verifyingKey() pure internal returns (VerifyingKey memory vk) { + vk.alpha = Pairing.G1Point(uint256(0x0f43f4fe7b5c2326fed4ac6ed2f4003ab9ab4ea6f667c2bdd77afb068617ee16), uint256(0x25a77832283f9726935219b5f4678842cda465631e72dbb24708a97ba5d0ce6f)); + vk.beta = Pairing.G2Point([uint256(0x2cebd0fbd21aca01910581537b21ae4fed46bc0e524c055059aa164ba0a6b62b), uint256(0x18fd4a7bc386cf03a95af7163d5359165acc4e7961cb46519e6d9ee4a1e2b7e9)], [uint256(0x11449dee0199ef6d8eebfe43b548e875c69e7ce37705ee9a00c81fe52f11a009), uint256(0x066d0c83b32800d3f335bb9e8ed5e2924cf00e77e6ec28178592eac9898e1a00)]); +``` + +Die Lösung besteht, zumindest bis Block-Explorer dazu übergehen, Zokrates-Verifizierung zu ihren Benutzeroberflächen hinzuzufügen, darin, dass die Anwendungsentwickler die Zokrates-Programme zur Verfügung stellen und dass zumindest einige Benutzer sie selbst mit dem entsprechenden Verifizierungsschlüssel kompilieren. + +Dazu gehen Sie wie folgt vor: + +1. [Installieren Sie Zokrates](https://zokrates.github.io/gettingstarted.html). + +2. Erstellen Sie eine Datei `dig.zok` mit dem Zokrates-Programm. Der nachstehende Code geht davon aus, dass Sie die ursprüngliche Kartengröße von 10x5 beibehalten haben. + + ```zokrates + import "utils/pack/bool/pack128.zok" as pack128; + import "hashes/poseidon/poseidon.zok" as poseidon; + + def hashMap(bool[12][7] map) -> field { + bool[512] mut map1d = [false; 512]; + u32 mut counter = 0; + + for u32 x in 0..12 { + for u32 y in 0..7 { + map1d[counter] = map[x][y]; + counter = counter+1; + } + } + + field[4] hashMe = [ + pack128(map1d[0..128]), + pack128(map1d[128..256]), + pack128(map1d[256..384]), + pack128(map1d[384..512]) + ]; + + return poseidon(hashMe); + } + + + // Die Anzahl der Minen am Ort (x,y) + def map2mineCount(bool[12][7] map, u32 x, u32 y) -> u8 { + return if map[x+1][y+1] { 1 } else { 0 }; + } + + def main(private bool[12][7] map, u32 x, u32 y) -> (field, u8) { + return (hashMap(map) , + if map2mineCount(map, x, y) > 0 { 0xFF } else { + map2mineCount(map, x-1, y-1) + map2mineCount(map, x, y-1) + map2mineCount(map, x+1, y-1) + + map2mineCount(map, x-1, y) + map2mineCount(map, x+1, y) + + map2mineCount(map, x-1, y+1) + map2mineCount(map, x, y+1) + map2mineCount(map, x+1, y+1) + } + ); + } + ``` + +3. Kompilieren Sie den Zokrates-Code und erstellen Sie den Verifizierungsschlüssel. Der Verifizierungsschlüssel muss mit der gleichen Entropie erstellt werden, die im ursprünglichen Server verwendet wurde, [in diesem Fall ein leerer String](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L67). + + ```sh copy + zokrates compile --input dig.zok + zokrates setup -e "" + ``` + +4. Erstellen Sie den Solidity-Verifizierer selbst und überprüfen Sie, ob er funktionell mit dem auf der Blockchain identisch ist (der Server fügt einen Kommentar hinzu, aber das ist nicht wichtig). + + ```sh copy + zokrates export-verifier + diff verifier.sol ~/20240901-secret-state/packages/contracts/src/verifier.sol + ``` + +## Designentscheidungen {#design} + +In jeder ausreichend komplexen Anwendung gibt es konkurrierende Designziele, die Kompromisse erfordern. Schauen wir uns einige der Kompromisse an und warum die aktuelle Lösung anderen Optionen vorzuziehen ist. + +### Warum Zero-Knowledge {#why-zero-knowledge} + +Für Minesweeper braucht man nicht wirklich Zero-Knowledge. Der Server kann die Karte immer behalten und sie dann einfach aufdecken, wenn das Spiel vorbei ist. Dann kann der Smart Contract am Ende des Spiels den Karten-Hash berechnen, überprüfen, ob er übereinstimmt, und wenn nicht, den Server bestrafen oder das Spiel vollständig ignorieren. + +Ich habe diese einfachere Lösung nicht verwendet, da sie nur für kurze Spiele mit einem genau definierten Endzustand funktioniert. Wenn ein Spiel potenziell unendlich ist (wie im Fall von [autonomen Welten](https://0xparc.org/blog/autonomous-worlds)), benötigen Sie eine Lösung, die den Zustand beweist, _ohne_ ihn preiszugeben. + +Als Tutorial benötigte dieser Artikel ein kurzes, leicht verständliches Spiel, aber diese Technik ist am nützlichsten für längere Spiele. + +### Warum Zokrates? {#why-zokrates} + +[Zokrates](https://zokrates.github.io/) ist nicht die einzige verfügbare Zero-Knowledge-Bibliothek, aber sie ähnelt einer normalen, [imperativen](https://en.wikipedia.org/wiki/Imperative_programming) Programmiersprache und unterstützt boolesche Variablen. + +Für Ihre Anwendung, mit unterschiedlichen Anforderungen, bevorzugen Sie möglicherweise die Verwendung von [Circum](https://docs.circom.io/getting-started/installation/) oder [Cairo](https://www.cairo-lang.org/tutorials/getting-started-with-cairo/). + +### Wann Zokrates kompilieren {#when-compile-zokrates} + +In diesem Programm kompilieren wir die Zokrates-Programme [jedes Mal, wenn der Server startet](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L60-L61). Das ist eindeutig eine Verschwendung von Ressourcen, aber dies ist ein Tutorial, das auf Einfachheit optimiert ist. + +Wenn ich eine produktionsreife Anwendung schreiben würde, würde ich prüfen, ob ich eine Datei mit den kompilierten Zokrates-Programmen für diese Minenfeldgröße habe, und wenn ja, diese verwenden. Dasselbe gilt für die Bereitstellung eines Verifizierervertrags onchain. + +### Erstellen der Verifizierer- und Prüferschlüssel {#key-creation} + +Die [Schlüsselerstellung](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L63-L69) ist eine weitere reine Berechnung, die für eine gegebene Minenfeldgröße nicht mehr als einmal durchgeführt werden muss. Auch hier wird es aus Gründen der Einfachheit nur einmal gemacht. + +Zusätzlich könnten wir eine [Setup-Zeremonie](https://zokrates.github.io/toolbox/trusted_setup.html#initializing-a-phase-2-ceremony) verwenden. Der Vorteil einer Setup-Zeremonie besteht darin, dass man entweder die Entropie oder ein Zwischenergebnis von jedem Teilnehmer benötigt, um beim Zero-Knowledge-Beweis zu betrügen. Wenn mindestens ein Teilnehmer der Zeremonie ehrlich ist und diese Informationen löscht, sind die Zero-Knowledge-Beweise vor bestimmten Angriffen sicher. Es gibt jedoch _keinen Mechanismus_, um zu überprüfen, ob Informationen von überall gelöscht wurden. Wenn Zero-Knowledge-Beweise von entscheidender Bedeutung sind, sollten Sie an der Setup-Zeremonie teilnehmen. + +Hier verlassen wir uns auf [perpetual powers of tau](https://github.com/privacy-scaling-explorations/perpetualpowersoftau), an denen Dutzende von Teilnehmern beteiligt waren. Es ist wahrscheinlich sicher genug und viel einfacher. Wir fügen auch keine Entropie während der Schlüsselerstellung hinzu, was es den Benutzern erleichtert, die [Zero-Knowledge-Konfiguration zu überprüfen](#user-verify-zero-trust). + +### Wo verifizieren {#where-verification} + +Wir können die Zero-Knowledge-Beweise entweder onchain (was Gas kostet) oder im Client (mithilfe von [`verify`](https://zokrates.github.io/toolbox/zokrates_js.html#verifyverificationkey-proof)) verifizieren. Ich habe die erste gewählt, da Sie damit [den Verifizierer einmal überprüfen](#user-verify-zero-trust) und dann darauf vertrauen können, dass er sich nicht ändert, solange die Vertragsadresse dafür gleich bleibt. Wenn die Verifizierung auf dem Client durchgeführt würde, müssten Sie den Code jedes Mal überprüfen, wenn Sie den Client herunterladen. + +Auch wenn dieses Spiel Einzelspieler ist, sind viele Blockchain-Spiele Mehrspieler. Onchain-Verifizierung bedeutet, dass Sie den Zero-Knowledge-Beweis nur einmal verifizieren. Wenn man es im Client macht, müsste jeder Client unabhängig verifizieren. + +### Die Karte in TypeScript oder Zokrates abflachen? {#where-flatten} + +Im Allgemeinen ist es besser, die Verarbeitung in TypeScript durchzuführen, wenn sie entweder in TypeScript oder Zokrates erfolgen kann, da TypeScript viel schneller ist und keine Zero-Knowledge-Beweise erfordert. Dies ist zum Beispiel der Grund, warum wir Zokrates nicht den Hash zur Verfügung stellen und ihn überprüfen lassen, ob er korrekt ist. Das Hashing muss innerhalb von Zokrates erfolgen, aber der Abgleich zwischen dem zurückgegebenen Hash und dem Hash onchain kann außerhalb davon stattfinden. + +Allerdings [flachen wir die Karte immer noch in Zokrates ab](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L15-L20), obwohl wir es auch in TypeScript hätten tun können. Der Grund ist, dass die anderen Optionen meiner Meinung nach schlechter sind. + +- Stellen Sie dem Zokrates-Code ein eindimensionales Array von Booleschen Werten zur Verfügung und verwenden Sie einen Ausdruck wie `x*(height+2) + +y`, um die zweidimensionale Karte zu erhalten. Dies würde [den Code](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L44-L47) etwas komplizierter machen, also habe ich entschieden, dass der Leistungsgewinn für ein Tutorial nicht wert ist. + +- Senden Sie Zokrates sowohl das eindimensionale als auch das zweidimensionale Array. Diese Lösung bringt uns jedoch nichts. Der Zokrates-Code müsste überprüfen, ob das bereitgestellte eindimensionale Array wirklich die korrekte Darstellung des zweidimensionalen Arrays ist. Es gäbe also keinen Leistungsgewinn. + +- Flachen Sie das zweidimensionale Array in Zokrates ab. Dies ist die einfachste Option, also habe ich sie gewählt. + +### Wo Karten speichern {#where-store-maps} + +In dieser Anwendung ist [`gamesInProgress`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L20) einfach eine Variable im Speicher. Das bedeutet, dass, wenn Ihr Server ausfällt und neu gestartet werden muss, alle gespeicherten Informationen verloren gehen. Spieler können nicht nur ihr Spiel nicht fortsetzen, sie können nicht einmal ein neues Spiel starten, weil die Onchain-Komponente denkt, dass sie noch ein Spiel im Gange haben. + +Dies ist eindeutig ein schlechtes Design für ein Produktionssystem, in dem Sie diese Informationen in einer Datenbank speichern würden. Der einzige Grund, warum ich hier eine Variable verwendet habe, ist, dass dies ein Tutorial ist und Einfachheit die Hauptüberlegung ist. + +## Fazit: Unter welchen Bedingungen ist dies die geeignete Technik? {#conclusion} + +So, jetzt wissen Sie, wie man ein Spiel mit einem Server schreibt, der geheime Zustände speichert, die nicht onchain gehören. Aber in welchen Fällen sollten Sie es tun? Es gibt zwei Hauptüberlegungen. + +- _Langlaufendes Spiel_: [Wie oben erwähnt](#why-zero-knowledge), können Sie in einem kurzen Spiel den Zustand einfach veröffentlichen, sobald das Spiel vorbei ist, und dann alles verifizieren lassen. Aber das ist keine Option, wenn das Spiel eine lange oder unbestimmte Zeit dauert und der Zustand geheim bleiben muss. + +- _Etwas Zentralisierung akzeptabel_: Zero-Knowledge-Beweise können die Integrität überprüfen, dass eine Entität die Ergebnisse nicht fälscht. Was sie nicht tun können, ist sicherzustellen, dass die Entität weiterhin verfügbar ist und auf Nachrichten antwortet. In Situationen, in denen die Verfügbarkeit auch dezentralisiert sein muss, sind Zero-Knowledge-Beweise keine ausreichende Lösung, und Sie benötigen eine [Mehrparteienberechnung](https://en.wikipedia.org/wiki/Secure_multi-party_computation). + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). + +### Anerkennungen {#acknowledgements} + +- Alvaro Alonso las einen Entwurf dieses Artikels und klärte einige meiner Missverständnisse über Zokrates auf. + +Alle verbleibenden Fehler liegen in meiner Verantwortung. diff --git a/public/content/translations/de/developers/tutorials/secure-development-workflow/index.md b/public/content/translations/de/developers/tutorials/secure-development-workflow/index.md new file mode 100644 index 00000000000..0c07f84ac80 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/secure-development-workflow/index.md @@ -0,0 +1,52 @@ +--- +title: Smart-Contract-Sicherheitscheckliste +description: Ein empfohlener Workflow zum Schreiben sicherer Smart Contracts +author: "Trailofbits" +tags: ["smart contracts", "security", "solidity"] +skill: intermediate +lang: de +published: 2020-09-07 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/blob/master/development-guidelines/workflow.md +--- + +## Smart-Contract-Entwicklungscheckliste {#smart-contract-development-checklist} + +Nachfolgend finden Sie einen allgemeinen Prozess, dessen Befolgung wir beim Schreiben Ihrer Smart Contracts empfehlen. + +Auf bekannte Sicherheitsprobleme prüfen: + +- Überprüfen Sie Ihre Verträge mit [Slither](https://github.com/crytic/slither). Es verfügt über mehr als 40 integrierte Detektoren für häufige Schwachstellen. Führen Sie es bei jedem Check-in mit neuem Code aus und stellen Sie sicher, dass Sie einen fehlerfreien Bericht erhalten (oder verwenden Sie den Triage-Modus, um bestimmte Probleme auszublenden). +- Überprüfen Sie Ihre Verträge mit [Crytic](https://crytic.io/). Es prüft auf 50 Probleme, die von Slither nicht geprüft werden. Crytic kann Ihrem Team auch dabei helfen, den Überblick zu behalten, indem es Sicherheitsprobleme in Pull-Requests auf GitHub leicht aufdeckt. + +Berücksichtigen Sie die besonderen Merkmale Ihres Vertrags: + +- Sind Ihre Verträge upgradefähig? Überprüfen Sie Ihren Upgradefähigkeits-Code auf Fehler mit [`slither-check-upgradeability`](https://github.com/crytic/slither/wiki/Upgradeability-Checks) oder [Crytic](https://blog.trailofbits.com/2020/06/12/upgradeable-contracts-made-safer-with-crytic/). Wir haben 17 Möglichkeiten dokumentiert, wie Upgrades fehlschlagen können. +- Erheben Ihre Verträge den Anspruch, ERC-konform zu sein? Überprüfen Sie sie mit [`slither-check-erc`](https://github.com/crytic/slither/wiki/ERC-Conformance). Dieses Tool erkennt sofort Abweichungen von sechs gängigen Spezifikationen. +- Integrieren Sie Token von Drittanbietern? Lesen Sie unsere [Checkliste für die Token-Integration](/developers/tutorials/token-integration-checklist/), bevor Sie sich auf externe Verträge verlassen. + +Überprüfen Sie die kritischen Sicherheitsmerkmale Ihres Codes visuell: + +- Überprüfen Sie den [inheritance-graph](https://github.com/trailofbits/slither/wiki/Printer-documentation#inheritance-graph)-Printer von Slither. Vermeiden Sie unbeabsichtigtes Shadowing und Probleme mit der C3-Linearisierung. +- Überprüfen Sie den [function-summary](https://github.com/trailofbits/slither/wiki/Printer-documentation#function-summary)-Printer von Slither. Er meldet die Sichtbarkeit von Funktionen und die Zugriffskontrollen. +- Überprüfen Sie den [vars-and-auth](https://github.com/trailofbits/slither/wiki/Printer-documentation#variables-written-and-authorization)-Printer von Slither. Er meldet die Zugriffskontrollen für Zustandsvariablen. + +Dokumentieren Sie kritische Sicherheitseigenschaften und verwenden Sie automatisierte Testgeneratoren, um sie zu bewerten: + +- Lernen Sie, [wie Sie Sicherheitseigenschaften für Ihren Code dokumentieren](/developers/tutorials/guide-to-smart-contract-security-tools/). Anfangs ist es schwierig, aber es ist die wichtigste Tätigkeit, um ein gutes Ergebnis zu erzielen. Es ist auch eine Voraussetzung für die Anwendung der fortgeschrittenen Techniken in diesem Tutorial. +- Definieren Sie Sicherheitseigenschaften in Solidity, zur Verwendung mit [Echidna](https://github.com/crytic/echidna) und [Manticore](https://manticore.readthedocs.io/en/latest/verifier.html). Konzentrieren Sie sich auf Ihre Statusmaschine, Zugriffskontrollen, arithmetische Operationen, externe Interaktionen und die Einhaltung von Standards. +- Definieren Sie Sicherheitseigenschaften mit der [Python-API von Slither](/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/). Konzentrieren Sie sich auf Vererbung, Variablenabhängigkeiten, Zugriffskontrollen und andere strukturelle Probleme. +- Führen Sie Ihre Eigenschaftstests bei jedem Commit mit [Crytic](https://crytic.io) aus. Crytic kann Tests von Sicherheitseigenschaften verarbeiten und auswerten, sodass jeder in Ihrem Team auf GitHub leicht sehen kann, dass sie erfolgreich sind. Fehlgeschlagene Tests können Commits blockieren. + +Achten Sie schließlich auf Probleme, die automatisierte Werkzeuge nicht leicht finden können: + +- Mangelnde Privatsphäre: Alle anderen können Ihre Transaktionen sehen, während sie sich im Pool in der Warteschlange befinden. +- Front-Running von Transaktionen +- Kryptografische Operationen +- Riskante Interaktionen mit externen DeFi-Komponenten + +## Bitten Sie um Hilfe {#ask-for-help} + +Die [Ethereum-Sprechstunden](https://calendly.com/dan-trailofbits/office-hours) finden jeden Dienstagnachmittag statt. Diese einstündigen Einzelsitzungen sind eine Gelegenheit, uns alle Ihre Fragen zur Sicherheit zu stellen, Probleme bei der Verwendung unserer Werkzeuge zu beheben und Feedback von Experten zu Ihrem aktuellen Ansatz zu erhalten. Wir helfen Ihnen, diesen Leitfaden durchzuarbeiten. + +Treten Sie unserem Slack bei: [Empire Hacking](https://join.slack.com/t/empirehacking/shared_invite/zt-h97bbrj8-1jwuiU33nnzg67JcvIciUw). Wir sind immer in den Kanälen #crytic und #ethereum erreichbar, wenn Sie Fragen haben. diff --git a/public/content/translations/de/developers/tutorials/send-token-ethersjs/index.md b/public/content/translations/de/developers/tutorials/send-token-ethersjs/index.md new file mode 100644 index 00000000000..60bfab22670 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/send-token-ethersjs/index.md @@ -0,0 +1,210 @@ +--- +title: Senden von Token mit ethers.js +description: Einsteigerfreundliche Anleitung zum Senden von Token mit ethers.js. +author: Kim YongJun +tags: [ "ETHERS.JS", "ERC-20", "TOKENS" ] +skill: beginner +lang: de +published: 2021-04-06 +--- + +## Token senden mit ethers.js (5.0) {#send-token} + +### In diesem Tutorial erfahren Sie, wie Sie {#you-learn-about} + +- Ethers.js importieren +- Token transferieren +- Gas-Preis entsprechend der Netzwerkauslastung festlegen + +### Erste Schritte {#to-get-started} + +Zu Beginn müssen wir zunächst die ethers.js-Bibliothek in unser Javascript importieren +Binden Sie ethers.js (5.0) ein + +### Installation {#install-ethersjs} + +```shell +/home/ricmoo> npm install --save ethers +``` + +ES6 im Browser + +```html + +``` + +ES3(UMD) im Browser + +```html + +``` + +### Parameter {#param} + +1. **`contract_address`**: Token-Vertragsadresse (die Vertragsadresse wird benötigt, wenn der Token, den Sie übertragen möchten, nicht Ether ist) +2. **`send_token_amount`**: Der Betrag, den Sie an den Empfänger senden möchten +3. **`to_address`**: Die Adresse des Empfängers +4. **`send_account`**: Die Adresse des Absenders +5. **`private_key`**: Privater Schlüssel des Absenders, um die Transaktion zu signieren und die Token tatsächlich zu übertragen + +## Hinweis {#notice} + +`signTransaction(tx)` wird entfernt, da `sendTransaction()` dies intern erledigt. + +## Sendeablauf {#procedure} + +### 1. Mit dem Netzwerk (Testnet) verbinden {#connect-to-network} + +#### Provider festlegen (Infura) {#set-provider} + +Mit dem Ropsten-Testnet verbinden + +```javascript +window.ethersProvider = new ethers.providers.InfuraProvider("ropsten") +``` + +### 2. Wallet erstellen {#create-wallet} + +```javascript +let wallet = new ethers.Wallet(private_key) +``` + +### 3. Wallet mit dem Netzwerk verbinden {#connect-wallet-to-net} + +```javascript +let walletSigner = wallet.connect(window.ethersProvider) +``` + +### 4. Aktuellen Gas-Preis abrufen {#get-gas} + +```javascript +window.ethersProvider.getGasPrice() // gasPrice +``` + +### 5. Transaktion definieren {#define-transaction} + +Die unten definierten Variablen sind von `send_token()` abhängig + +### Transaktionsparameter {#transaction-params} + +1. **`send_account`**: Adresse des Token-Absenders +2. **`to_address`**: Adresse des Token-Empfängers +3. **`send_token_amount`**: die Anzahl der zu sendenden Token +4. **`gas_limit`**: Gaslimit +5. **`gas_price`**: Gas-Preis + +[Siehe unten zur Verwendung](#how-to-use) + +```javascript +const tx = { + from: send_account, + to: to_address, + value: ethers.utils.parseEther(send_token_amount), + nonce: window.ethersProvider.getTransactionCount(send_account, "latest"), + gasLimit: ethers.utils.hexlify(gas_limit), // 100000 + gasPrice: gas_price, +} +``` + +### 6. Übertragung {#transfer} + +```javascript +walletSigner.sendTransaction(tx).then((transaction) => { + console.dir(transaction) + alert("Senden abgeschlossen!") +}) +``` + +## Verwendung {#how-to-use} + +```javascript +let private_key = + "41559d28e936dc92104ff30691519693fc753ffbee6251a611b9aa1878f12a4d" +let send_token_amount = "1" +let to_address = "0x4c10D2734Fb76D3236E522509181CC3Ba8DE0e80" +let send_address = "0xda27a282B5B6c5229699891CfA6b900A716539E6" +let gas_limit = "0x100000" +let wallet = new ethers.Wallet(private_key) +let walletSigner = wallet.connect(window.ethersProvider) +let contract_address = "" +window.ethersProvider = new ethers.providers.InfuraProvider("ropsten") + +send_token( + contract_address, + send_token_amount, + to_address, + send_address, + private_key +) +``` + +### Erfolg! {#success} + +![Bild einer erfolgreich durchgeführten Transaktion](./successful-transaction.png) + +## send_token() {#send-token-method} + +```javascript +function send_token( + contract_address, + send_token_amount, + to_address, + send_account, + private_key +) { + let wallet = new ethers.Wallet(private_key) + let walletSigner = wallet.connect(window.ethersProvider) + + window.ethersProvider.getGasPrice().then((currentGasPrice) => { + let gas_price = ethers.utils.hexlify(parseInt(currentGasPrice)) + console.log(`gas_price: ${gas_price}`) + + if (contract_address) { + // allgemeiner Token-Versand + let contract = new ethers.Contract( + contract_address, + send_abi, + walletSigner + ) + + // Wie viele Token? + let numberOfTokens = ethers.utils.parseUnits(send_token_amount, 18) + console.log(`numberOfTokens: ${numberOfTokens}`) + + // Token senden + contract.transfer(to_address, numberOfTokens).then((transferResult) => { + console.dir(transferResult) + alert("Token gesendet") + }) + } // Ether senden + else { + const tx = { + from: send_account, + to: to_address, + value: ethers.utils.parseEther(send_token_amount), + nonce: window.ethersProvider.getTransactionCount( + send_account, + "latest" + ), + gasLimit: ethers.utils.hexlify(gas_limit), // 100000 + gasPrice: gas_price, + } + console.dir(tx) + try { + walletSigner.sendTransaction(tx).then((transaction) => { + console.dir(transaction) + alert("Senden abgeschlossen!") + }) + } catch (error) { + alert("Senden fehlgeschlagen!!") + } + } + }) +} +```