diff --git a/docs/solutions/integration-issues/crowdin-import-review-agent-calibration.md b/docs/solutions/integration-issues/crowdin-import-review-agent-calibration.md new file mode 100644 index 00000000000..f6e9048bb08 --- /dev/null +++ b/docs/solutions/integration-issues/crowdin-import-review-agent-calibration.md @@ -0,0 +1,217 @@ +--- +title: "Crowdin Import Review Agent Calibration: Czech Part 07 (PR #17553)" +date: 2026-02-21 +category: integration-issues +tags: + - translation-review + - i18n + - crowdin + - review-agent + - worktree + - false-positive-correction + - code-block-policy + - sanitizer +problem_type: process/integration +components: + - review-translations-local.md + - known-patterns.md + - post_import_sanitize.ts + - gh-cli + - next-build (scoped locale) +severity: medium +status: resolved +related: + - docs/solutions/translation-review/crowdin-import-review-turkish-pr-17182.md +--- + +# Crowdin Import Review Agent Calibration + +## Problem Summary + +During the Czech (cs) part-7 translation import for ethereum.org (PR #17553), the AI-powered translation review workflow produced **27 false positives out of 29 findings** on its first run. The review agent incorrectly flagged all translated concept tags in tutorial frontmatter as needing reversion to English, and applied a blanket "no translation in code blocks" rule that penalized translated code comments (which are beneficial for readers). + +A second calibrated run found **4 genuine critical issues and 5 warnings** — all in code blocks where functional code had been translated. + +## Symptoms + +1. Review agent flagged 27 translated tutorial tags (e.g., `"smart kontrakt ucty"`, `"testovani"`) as critical issues requiring English reversion +2. Review agent flagged translated Solidity comments inside code blocks as warnings +3. 16 incorrect tag reverts + 2 incorrect JSON fixes were applied before the user caught them +4. Scoring summary was missing from workflow output +5. `gh` CLI failed with TLS errors in sandbox; `git diff` returned empty results in worktree + +## Root Cause Analysis + +### 1. Blanket Tag Rule (27 false positives) + +`known-patterns.md` Pattern #10 stated "Tags should remain in English" with no distinction between brand-name tags and concept tags. Tutorial frontmatter `tags` arrays contain both: + +- **Brand-name tags** (must stay English): `"solidity"`, `"hardhat"`, `"web3"`, `"remix"` +- **Concept tags** (intentionally translated): `"smart contracts"`, `"testing"`, `"security"`, `"deploying"` + +The review command (`review-translations-local.md`) had no guidance about this distinction, so the agent treated every non-English tag as an error. + +### 2. Blanket Code Block Rule (false warnings on comments) + +Line 231 of `review-translations-local.md` said "Code blocks are intact and not translated." This made no distinction between: + +- **Functional code** (identifiers, strings, config keys) — must stay English +- **Code comments** (`//`, `/* */`, `#`) — may be translated to aid reader comprehension + +### 3. Genuine Issues Masked by Noise + +The 27 false positives buried the 2 real issues in the first run. Only after calibrating the rules and re-running did the agent surface actual problems: translated struct field names, TOML config keys, string literals, and console output in code blocks. + +## Solution + +### Fix 1: Nuanced Tag Policy + +**File:** `known-patterns.md (local: `~/.claude/translation-review/`)` (Pattern #10) + +Rewrote from blanket "Tags should remain in English" to: + +> **Frontmatter Tag Translation -- Brand Names Only (MEDIUM)** +> +> Only **brand-name tags** must remain in English; concept tags are intentionally translated by Crowdin. +> +> - Brand-name tag example (MUST fix): `tags: ["solidity", ...]` translated to local equivalent +> - Concept tag example (CORRECT): `tags: ["smart contracts", ...]` translated to `["smart kontrakt ucty", ...]` +> +> Rule: Only flag translated tags that are proper nouns or brand names. + +**File:** `review-translations-local command (local: `~/.claude/commands/`)` (terminology checklist) + +Added explicit block: + +``` +IMPORTANT -- Tutorial frontmatter `tags` arrays: +- Brand-name tags (e.g., "solidity", "hardhat", "alchemy", "JavaScript") MUST stay English +- Concept/category tags (e.g., "smart contracts", "testing", "security") are intentionally translated +- Rule of thumb: proper noun or product name = English; generic descriptive term = translated is correct +``` + +### Fix 2: Nuanced Code Block Policy + +**File:** `review-translations-local command (local: `~/.claude/commands/`)` (structural checklist, line 231) + +Replaced: +``` +- Code blocks are intact and not translated +``` + +With: +``` +- Code blocks: functional code (identifiers, strings, config keys, variable names, + console/error output) must stay in English. Code comments (//, /* */, #) may be + translated -- these aid reader comprehension and don't affect execution. +``` + +**File:** `known-patterns.md (local: `~/.claude/translation-review/`)` (new Pattern #11) + +Added "Code Block Translation Policy" with examples: + +| Category | Rule | Example | +|----------|------|---------| +| Variable/function names | Must stay English | `uint256 balance` not `uint256 zustatek` | +| String literals | Must stay English | `bytes("entry already written")` | +| Config keys (TOML/JSON) | Must stay English | `[[accounts]]` not `[[ucty]]` | +| Console/error output | Must stay English | `Listening on port 3000` | +| Code comments | May be translated | `// Zkontrolujte zustatek uctu` is fine | + +### Fix 3: Genuine Translation Issues (second run) + +After calibration, the review agent correctly identified and we fixed: + +| File | Issue | Fix | +|------|-------|-----| +| `all-you-can-cache/index.md` | Misplaced backtick causing MDX build failure | Fixed backtick placement | +| `all-you-can-cache/index.md` | 3 translated Solidity string literals | Restored English strings | +| `app-plasma/index.md` | Noir struct field names translated | Restored `balance`, `address`, `from`, `to`, `amount` | +| `app-plasma/index.md` | TOML config keys/values translated | Restored `message`, `[[accounts]]`, `address`, `balance` | +| `app-plasma/index.md` | Console + error output translated | Restored English output | + +### Fix 4: Mandatory Scoring Summary + +**File:** `review-translations-local command (local: `~/.claude/commands/`)` (Phase 2 and Phase 3) + +Added explicit instructions requiring the agent to display the scoring summary before concluding. The summary is now a mandatory deliverable of every review run. + +## Infrastructure Notes + +### Worktree Setup Pattern + +The established pattern for translation review in worktrees: + +1. Worktree at `.worktrees/{lang}-part-{N}/` (typically created by sanitizer) +2. All file reads/edits use the worktree absolute path +3. English source comparison at worktree's `public/content/` (not translations dir) +4. Scoped build: `NEXT_PUBLIC_BUILD_LOCALES=en,{lang} pnpm build` +5. PR file list via `gh api repos/{owner}/{repo}/pulls/{N}/files --paginate` (not `git diff` in worktree) + +### Sandbox Permissions + +- `gh` CLI requires `dangerouslyDisableSandbox: true` due to TLS certificate verification failures in sandbox +- `api.github.com` is in the network allowlist but socket-level TLS handshake still fails in sandboxed mode +- Scoped builds avoid network-dependent data fetches that can cause hangs + +## Prevention Strategies + +### Before Applying Any Review Agent Fixes + +1. **Spot-check the first 5 findings manually.** If more than 1 is a false positive, stop and calibrate before continuing. +2. **Never bulk-apply fixes** from a first-run agent on a new language or updated ruleset. Always validate. +3. **Separate staged (sanitizer) from unstaged (review) changes.** The sanitizer's deterministic fixes are pre-validated; review agent fixes need human confirmation. + +### Iterative Calibration Loop + +``` +Run review agent on sample files + | + v +Examine output for false positives / false negatives + | + v +Classify each error: + - Knowledge base gap --> update rules + - Prompt ambiguity --> rewrite instruction + - Infrastructure issue --> fix config + | + v +Re-run on SAME files (regression test) + | + v +Confirm error count decreased, no new false positives +``` + +### New Language Onboarding Checklist + +- [ ] Extract glossary terms for the language from `fetch-translation-glossary.json` +- [ ] Check if `per-language/{code}.md` exists with prior findings +- [ ] Run review agent on 5-10 calibration files covering diverse content types +- [ ] Validate: zero false positives on translated concept tags +- [ ] Validate: zero false positives on translated code comments +- [ ] Validate: scoring summary is produced +- [ ] Run scoped build to verify no MDX errors + +## Quality Scores (PR #17553, Czech) + +| Category | Score | +|----------|-------| +| Brand Name Preservation | 9/10 | +| Technical Accuracy | 7/10 | +| Semantic Fidelity | 9/10 | +| Terminology Consistency | 9/10 | +| Tone/Register | 8/10 | +| **Overall** | **8.4/10** | + +## Cross-References + +- [Turkish PR #17182 Review](../translation-review/crowdin-import-review-turkish-pr-17182.md) -- first translation review case study, established the 5-agent architecture + +### Local Tooling (not yet committed to repo) + +The following files are part of a local Claude Code workflow currently being iterated. They will be committed to the repo once the review process stabilizes: + +- **known-patterns.md** (`~/.claude/translation-review/`) -- living document of all discovered patterns (11 categories) +- **review-translations-local** (`~/.claude/commands/`) -- full review workflow definition with sub-agent architecture +- **fetch-translation-glossary.json** (`~/.claude/translation-review/`) -- community-voted term translations diff --git a/public/content/translations/cs/developers/docs/transactions/index.md b/public/content/translations/cs/developers/docs/transactions/index.md index a90640619fa..34a30e537da 100644 --- a/public/content/translations/cs/developers/docs/transactions/index.md +++ b/public/content/translations/cs/developers/docs/transactions/index.md @@ -1,6 +1,6 @@ --- title: Transakce -description: Přehled transakcí v síti Ethereum – jak fungují, jaká je jejich datová struktura a jak je odeslat prostřednictvím aplikace. +description: "Přehled transakcí v síti Ethereum – jak fungují, jaká je jejich datová struktura a jak je odeslat prostřednictvím aplikace." lang: cs --- @@ -8,13 +8,14 @@ Transakce jsou kryptograficky podepsané instrukce poslané z účtů. Účet za ## Předpoklady {#prerequisites} -Abyste této stránce lépe porozuměli, doporučujeme vám si nejprve přečíst sekci [Účty](/developers/docs/accounts/) a náš [úvod do Etherea](/developers/docs/intro-to-ethereum/). +Abyste této stránce lépe porozuměli, doporučujeme vám si nejprve přečíst [Účty](/developers/docs/accounts/) a náš [úvod do Etherea](/developers/docs/intro-to-ethereum/). ## Co je to transakce? {#whats-a-transaction} Transakce na Ethereu odkazuje na akci zahájenou externě vlastněným účtem, jinými slovy účtem spravovaným člověkem, nikoliv kontraktem. Např. pokud Bob pošle Alici 1 ETH, zůstatek na Bobově účtu musí být snížen a Alicin musí být navýšen. Tato akce mění stav sítě a probíhá v rámci transakce. -![Diagram ukazující, jak transakce způsobí změnu stavu](./tx.png) _Diagram převzat z [ilustrace Ethereum EVM](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ +![Diagram znázorňující změnu stavu způsobenou transakcí](./tx.png) +_Diagram převzat z [Ethereum EVM illustrated](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ Transakce, které mění stav EVM, musí být posílány do celé sítě. Jakýkoliv uzel může vyslat požadavek na provedení transakce na EVM. Poté validátor provede transakci a výslednou změnu stavu sdílí do zbytku sítě. @@ -23,16 +24,16 @@ Transakce vyžadují poplatek a musí být zahrnuty do validovaného bloku. Abyc Odeslaná transakce obsahuje následující informace: - `from` – adresa odesílatele, který transakci podepíše. Toto bude externě vlastněný účet, protože kontraktové účty nemohou odesílat transakce -- `to` – adresa příjemce. Pokud jde o externě vlastněný účet, transakce převede hodnotu. Pokud jde o kontraktový účet, transakce vykoná kód kontraktu. -- `signature` – podpis, který je identifikátorem odesílatele. Tento identifikátor je vygenerován, když odesílatelův privátní klíč podepíše transakci a potvrdí, že odesílatel tuto transakci autorizoval. -- `nonce` – postupně se zvyšující čítač, který označuje číslo transakce z účtu. -- `value` – množství ETH, které má být převedeno od odesílatele k příjemci (denominováno ve WEI, kde 1 ETH odpovídá 1e+18 wei). -- `input data` – volitelné pole pro přidání libovolných dat. -- `gasLimit` – maximální množství jednotek paliva, které může být transakcí spotřebováno. [EVM](/developers/docs/evm/opcodes) specifikuje jednotky paliva potřebné pro každý výpočetní krok. -- `maxPriorityFeePerGas` – maximální cena spotřebovaného paliva, která bude zahrnuta jako spropitné pro validátora. -- `maxFeePerGas` – maximální poplatek za jednotku paliva, který je uživatel ochoten zaplatit za transakci (včetně `baseFeePerGas` a `maxPriorityFeePerGas`). +- `to` – adresa příjemce (pokud se jedná o externě vlastněný účet, transakce převede hodnotu. Pokud jde o kontraktový účet, transakce vykoná kód kontraktu. +- `signature` – identifikátor odesílatele. Tento identifikátor je vygenerován, když odesílatelův privátní klíč podepíše transakci a potvrdí, že odesílatel tuto transakci autorizoval. +- `nonce` – sekvenčně se zvyšující čítač, který udává číslo transakce z účtu +- `value` – množství ETH, které se má převést od odesílatele k příjemci (vyjádřeno ve WEI, kde 1 ETH se rovná 1e+18 wei) +- `input data` – volitelné pole pro zahrnutí libovolných dat +- `gasLimit` – maximální množství jednotek paliva, které může transakce spotřebovat. [EVM](/developers/docs/evm/opcodes) specifikuje jednotky paliva potřebné pro každý výpočetní krok +- `maxPriorityFeePerGas` – maximální cena spotřebovaného paliva, která bude zahrnuta jako spropitné pro validátora +- `maxFeePerGas` – maximální poplatek za jednotku paliva, který je uživatel ochoten zaplatit za transakci (včetně `baseFeePerGas` a `maxPriorityFeePerGas`) -Palivo reprezentuje výpočetní výkon potřebný k provedení transakce validátorem. Uživatelé musí za tento výpočet zaplatit poplatek. `gasLimit` a `maxPriorityFeePerGas` určují maximální transakční poplatek zaplacený validátorovi. [Další informace o palivu](/developers/docs/gas/). +Palivo reprezentuje výpočetní výkon potřebný k provedení transakce validátorem. Uživatelé musí za tento výpočet zaplatit poplatek. `gasLimit` a `maxPriorityFeePerGas` určují maximální transakční poplatek zaplacený validátorovi. [Více o palivu](/developers/docs/gas/). Objekt transakce bude vypadat zhruba takto: @@ -99,22 +100,26 @@ Ukázková odpověď: } ``` -- `raw` je podepsaná transakce v kódovaném formátu [Recursive Length Prefix (RLP)](/developers/docs/data-structures-and-encoding/rlp). -- `tx` je podepsaná transakce ve formátu JSON. +- `raw` je podepsaná transakce v kódovaném formátu [Recursive Length Prefix (RLP)](/developers/docs/data-structures-and-encoding/rlp) +- `tx` je podepsaná transakce ve formátu JSON S hashem podpisu může být transakce kryptograficky ověřena jako pocházející od odesílatele a odeslána do sítě. ### Datové pole {#the-data-field} -Většina transakcí putuje do kontraktu z externě vlastněného účtu. Většina kontraktů je napsána v jazyce Solidity a interpretuje své datové pole v souladu se specifikací [aplikačního binárního rozhraní (ABI)](/glossary/#abi). +Většina transakcí putuje do kontraktu z externě vlastněného účtu. +Většina kontraktů je napsána v Solidity a interpretuje své datové pole v souladu s [aplikačním binárním rozhraním (ABI)](/glossary/#abi). -První čtyři bajty specifikují, která funkce se má zavolat, pomocí hashe názvu funkce a jejích argumentů. Někdy můžete identifikovat funkci pomocí selektoru s využitím [této databáze](https://www.4byte.directory/signatures/). +První čtyři bajty specifikují, která funkce se má zavolat, pomocí hashe názvu funkce a jejích argumentů. +Někdy můžete funkci identifikovat ze selektoru pomocí [této databáze](https://www.4byte.directory/signatures/). -Zbytek calldat jsou argumenty, [zakódované podle specifikace ABI](https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding). +Zbytek calldat jsou argumenty, [zakódované podle specifikací ABI](https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding). -Např. se podívejme na [tuto transakci](https://etherscan.io/tx/0xd0dcbe007569fcfa1902dae0ab8b4e078efe42e231786312289b1eee5590f6a1). K zobrazení calldat použijte možnost **„Click to see More“**. +Podívejme se například na [tuto transakci](https://etherscan.io/tx/0xd0dcbe007569fcfa1902dae0ab8b4e078efe42e231786312289b1eee5590f6a1). +Použijte **Click to see More** k zobrazení calldat. -Funkční selektor je `0xa9059cbb`. Existuje několik [známých funkcí s tímto podpisem](https://www.4byte.directory/signatures/?bytes4_signature=0xa9059cbb). V tomto případě byl [zdrojový kód kontraktu](https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#code) nahrán na Etherscan, takže víme, že funkce je `transfer(address,uint256)`. +Funkční selektor je `0xa9059cbb`. Existuje několik [známých funkcí s tímto podpisem](https://www.4byte.directory/signatures/?bytes4_signature=0xa9059cbb). +V tomto případě byl [zdrojový kód kontraktu](https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#code) nahrán na Etherscan, takže víme, že funkce je `transfer(address,uint256)`. Zbytek dat je: @@ -123,7 +128,9 @@ Zbytek dat je: 000000000000000000000000000000000000000000000000000000003b0559f4 ``` -Podle specifikací ABI se celočíselné hodnoty (jako jsou adresy, což jsou 20bajtová celá čísla) zobrazují v ABI jako 32bajtová slova, doplněná nulami na začátku. Takže víme, že adresa `to` je [`4f6742badb049791cd9a37ea913f2bac38d01279`](https://etherscan.io/address/0x4f6742badb049791cd9a37ea913f2bac38d01279). `value` je 0x3b0559f4 = 990206452. +Podle specifikací ABI se celočíselné hodnoty (jako jsou adresy, což jsou 20bajtová celá čísla) zobrazují v ABI jako 32bajtová slova, doplněná nulami na začátku. +Takže víme, že adresa `to` je [`4f6742badb049791cd9a37ea913f2bac38d01279`](https://etherscan.io/address/0x4f6742badb049791cd9a37ea913f2bac38d01279). +`value` je 0x3b0559f4 = 990206452. ## Typy transakcí {#types-of-transactions} @@ -135,9 +142,9 @@ Na Ethereu existuje několik různých typů transakcí: ### O palivu {#on-gas} -Jak bylo zmíněno, vykonání transakce stojí [palivo](/developers/docs/gas/). Jednoduché převodní transakce vyžadují 21 000 jednotek paliva. +Jak již bylo zmíněno, vykonání transakcí stojí [palivo](/developers/docs/gas/). Jednoduché převodní transakce vyžadují 21 000 jednotek paliva. -Takže pokud Bob chce poslat Alici 1 ETH s `baseFeePerGas` 190 gwei a `maxPriorityFeePerGas` 10 gwei, bude muset zaplatit následující poplatek: +Aby Bob poslal Alici 1 ETH při `baseFeePerGas` 190 gwei a `maxPriorityFeePerGas` 10 gwei, bude muset zaplatit následující poplatek: ``` (190 + 10) * 21 000 = 4 200 000 gwei @@ -145,16 +152,16 @@ Takže pokud Bob chce poslat Alici 1 ETH s `baseFeePerGas` 190 gwei a `maxPriori 0,0042 ETH ``` -Zůstatek Bobova účtu bude snížen **o 1,0042 ETH** (1 ETH pro Alici + 0,0042 ETH na poplatky za palivo). +Z Bobova účtu se odečte **-1,0042 ETH** (1 ETH pro Alici + 0,0042 ETH na poplatcích za palivo) -Zůstatek na Alicině účtu bude navýšen **o 1,0 ETH**. +Na Alicin účet bude připsáno **+1,0 ETH** -Základní poplatek **0,00399 ETH** bude spálen. +Základní poplatek bude spálen **-0,00399 ETH** -Validátor si ponechá spropitné ve výši **0,000210 ETH**. +Validátor si ponechá spropitné **+0,000210 ETH** - -![Diagram ukazující vrácení nepoužitého paliva](./gas-tx.png) _Schéma převzato z [ilustrace Ethereum EVM](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ +![Diagram znázorňující, jak se vrací nespotřebované palivo](./gas-tx.png) +_Diagram převzat z [Ethereum EVM illustrated](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ Jakékoliv palivo, které nebude v transakci použito, bude vráceno na účet odesílatele. @@ -162,18 +169,21 @@ Jakékoliv palivo, které nebude v transakci použito, bude vráceno na účet o Palivo je potřeba pro jakoukoliv transakci, která zahrnuje chytrý kontrakt. -Chytré kontrakty mohou také obsahovat funkce známé jako [`view`](https://docs.soliditylang.org/en/latest/contracts.html#view-functions) nebo [`pure`](https://docs.soliditylang.org/en/latest/contracts.html#pure-functions), které nemění stav kontraktu. Volání těchto funkcí z externě vlastněného účtu tedy nevyžaduje žádné palivo. Základní RPC volání pro tento scénář je [`eth_call`](/developers/docs/apis/json-rpc#eth_call). +Chytré kontrakty mohou také obsahovat funkce známé jako [`view`](https://docs.soliditylang.org/en/latest/contracts.html#view-functions) nebo [`pure`](https://docs.soliditylang.org/en/latest/contracts.html#pure-functions), které nemění stav kontraktu. Volání těchto funkcí z externě vlastněného účtu tedy nevyžaduje žádné palivo. Podkladové volání RPC pro tento scénář je [`eth_call`](/developers/docs/apis/json-rpc#eth_call). -Na rozdíl od volání pomocí `eth_call` jsou tyto funkce `view` nebo `pure` často volány interně (tj. z kontraktu samotného nebo z jiného kontraktu), což stojí palivo. +Na rozdíl od přístupu pomocí `eth_call` se tyto funkce `view` nebo `pure` také běžně volají interně (tj. ze samotného kontraktu nebo z jiného kontraktu), což stojí palivo. ## Životní cyklus transakce {#transaction-lifecycle} Jakmile je transakce odeslána, následuje tento proces: -1. Kryptograficky je vygenerován hash transakce: `0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017`. +1. Haš transakce je kryptograficky vygenerován: + `0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017` 2. Transakce je poté poslána do sítě a přidána do transakčního poolu obsahujícího všechny ostatní čekající transakce v síti. 3. Validátor musí vaši transakci vybrat a zahrnout ji do bloku, aby bylo možné ji ověřit a považovat za „úspěšnou“. -4. S postupem času bude blok obsahující vaši transakci označen jako „oprávněný“ a poté „finalizovaný“. Tato vylepšení znamenají mnohem větší jistotu, že vaše transakce byla úspěšná a nikdy nebude změněna. Jakmile je blok „finalizován“, mohl by být změněn pouze útokem na úrovni sítě, který by stál několik miliard dolarů. +4. S postupem času bude blok obsahující vaši transakci označen jako „oprávněný“ a poté „finalizovaný“. Tato vylepšení poskytují mnohem větší jistotu, + že vaše transakce byla úspěšná a nikdy nebude změněna. Jakmile je blok „finalizován“, mohl by být změněn pouze + útokem na úrovni sítě, který by stál mnoho miliard dolarů. ## Vizuální ukázka {#a-visual-demo} @@ -181,13 +191,13 @@ Na tomto videu vás Austin provede transakcemi, palivem a těžbou. -## Typizovaná transakční obálka {#typed-transaction-envelope} +## Typovaná obálka transakce {#typed-transaction-envelope} -Ethereum původně mělo pro transakce jen jeden formát. Každá transakce obsahovala pole nonce, gas price, gas limit, adresu to, value, data, v, r a s. Tato pole jsou [RLP-kódovaná](/developers/docs/data-structures-and-encoding/rlp/) a vypadají zhruba takto: +Ethereum původně mělo pro transakce jen jeden formát. Každá transakce obsahovala pole nonce, gas price, gas limit, adresu to, value, data, v, r a s. Tato pole jsou [kódována pomocí RLP](/developers/docs/data-structures-and-encoding/rlp/) a vypadají přibližně takto: `RLP([nonce, gasPrice, gasLimit, to, value, data, v, r, s])` -Ethereum se vyvinulo tak, aby podporovalo více typů transakcí a umožnilo implementaci nových funkcí, jako jsou seznamy přístupu a [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), aniž by to ovlivnilo starší formáty transakcí. +Ethereum se vyvinulo, aby podporovalo více typů transakcí a umožnilo implementaci nových funkcí, jako jsou přístupové seznamy a [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), aniž by to ovlivnilo starší formáty transakcí. [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) toto chování umožňuje. Transakce jsou interpretovány takto: @@ -198,24 +208,26 @@ Kde jsou pole definována jako: - `TransactionType` – číslo mezi 0 a 0x7f, což umožňuje celkem 128 možných typů transakcí. - `TransactionPayload` – libovolné pole bajtů definované typem transakce. -Na základě hodnoty `TransactionType` může být transakce klasifikována jako: +Na základě hodnoty `TransactionType` lze transakci klasifikovat jako: -1. **Transakce typu 0 (Legacy):** Původní formát transakce používaný od spuštění Etherea. Neobsahuje funkce z [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), jako je dynamický výpočet poplatků za palivo nebo seznamy přístupu pro chytré kontrakty. Legacy transakce nemají specifický prefix označující jejich typ ve svém serializovaném formátu, začínají bajtem `0xf8` při použití kódování [Recursive Length Prefix (RLP)](/developers/docs/data-structures-and-encoding/rlp). Hodnota TransactionType pro tyto transakce je `0x0`. +1. **Transakce typu 0 (staršího typu):** Původní formát transakce používaný od spuštění Etherea. Nezahrnují funkce z [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), jako jsou dynamické výpočty poplatků za palivo nebo přístupové seznamy pro chytré kontrakty. Starší transakce nemají ve svém serializovaném tvaru specifický prefix označující jejich typ, začínají bajtem `0xf8` při použití kódování [Recursive Length Prefix (RLP)](/developers/docs/data-structures-and-encoding/rlp). Hodnota TransactionType pro tyto transakce je `0x0`. -2. **Transakce typu 1**: Ty byly zavedeny v [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) jako součást [vylepšení Berlín](/ethereum-forks/#berlin) a zahrnují parametr `accessList`. Tento seznam specifikuje adresy a klíče úložiště, které by měla transakce kontaktovat, což může potenciálně snížit náklady na [palivo](/developers/docs/gas/) pro složité transakce zahrnující chytré kontrakty. Změny v tržním mechanismu poplatků podle EIP-1559 nejsou v transakcích typu 1 zahrnuty. Transakce typu 1 také obsahují parametr `yParity`, který může být buď `0x0` nebo `0x1`, což označuje paritu hodnoty y v podpisu secp256k1. Tyto transakce jsou identifikovány začátkem bajtu `0x01` a jejich hodnota TransactionType je `0x1`. +2. **Transakce typu 1:** Byly zavedeny v [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) jako součást [vylepšení Berlin](/ethereum-forks/#berlin) sítě Ethereum a zahrnují parametr `accessList`. Tento seznam uvádí adresy a klíče úložiště, ke kterým má transakce přistupovat, což pomáhá potenciálně snížit náklady na [palivo](/developers/docs/gas/) u složitých transakcí zahrnujících chytré kontrakty. Změny v tržním mechanismu poplatků podle EIP-1559 nejsou v transakcích typu 1 zahrnuty. Transakce typu 1 také obsahují parametr `yParity`, který může být buď `0x0`, nebo `0x1`, a označuje paritu y-hodnoty podpisu secp256k1. Jsou identifikovány začátečním bajtem `0x01` a jejich hodnota TransactionType je `0x1`. -3. **Transakce typu 2**, běžně označovány jako transakce podle EIP-1559, byly zavedeny v [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) v rámci [vylepšení Londýn](/ethereum-forks/#london). Staly se standardním typem transakcí na Ethereu. Tyto transakce zavádějí nový mechanismus trhu s poplatky, který zlepšuje předvídatelnost rozdělením poplatku za transakci na základní poplatek a prioritní poplatek. Začínají bajtem `0x02` a zahrnují pole, jako je `maxPriorityFeePerGas` a `maxFeePerGas`. Transakce typu 2 jsou nyní výchozí díky své flexibilitě a efektivitě, zejména v obdobích vysokého zatížení sítě, protože uživatelům umožňují lépe plánovat poplatky za transakce. Hodnota TransactionType pro tyto transakce je `0x2`. +3. **Transakce typu 2**, běžně označované jako transakce EIP-1559, jsou transakce zavedené v [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), v rámci [vylepšení London](/ethereum-forks/#london) sítě Ethereum. Staly se standardním typem transakcí na Ethereu. Tyto transakce zavádějí nový mechanismus trhu s poplatky, který zlepšuje předvídatelnost rozdělením poplatku za transakci na základní poplatek a prioritní poplatek. Začínají bajtem `0x02` a obsahují pole jako `maxPriorityFeePerGas` a `maxFeePerGas`. Transakce typu 2 jsou nyní výchozí díky své flexibilitě a efektivitě, zejména v obdobích vysokého zatížení sítě, protože uživatelům umožňují lépe plánovat poplatky za transakce. Hodnota TransactionType pro tyto transakce je `0x2`. +4. **Transakce typu 3 (Blob)** byly zavedeny v [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) jako součást [vylepšení Dencun](/ethereum-forks/#dencun) sítě Ethereum. Tyto transakce jsou navrženy pro efektivnější zpracování dat typu „blob“ (Binary Large Objects), což je výhodné zejména pro rollupy druhé vrstvy, protože poskytují způsob, jak odesílat data do sítě Ethereum s nižšími náklady. Blob transakce obsahují další pole jako `blobVersionedHashes`, `maxFeePerBlobGas` a `blobGasPrice`. Začínají bajtem `0x03` a jejich hodnota TransactionType je `0x3`. Blob transakce představují významné zlepšení v dostupnosti dat a možnostech škálování Etherea. +5. **Transakce typu 4** byly zavedeny v [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) jako součást [vylepšení Pectra](/roadmap/pectra/) sítě Ethereum. Tyto transakce jsou navrženy tak, aby byly dopředně kompatibilní s abstrakcí účtu. Umožňují EOA, aby se dočasně chovaly jako účty chytrých kontraktů, aniž by byla ohrožena jejich původní funkčnost. Obsahují parametr `authorization_list`, který určuje chytrý kontrakt, na který EOA deleguje svou pravomoc. Po transakci bude pole kódu EOA obsahovat adresu delegovaného chytrého kontraktu. -## Další informace {#further-reading} +## Další čtení {#further-reading} -- [EIP-2718: Typizovaná transakční obálka](https://eips.ethereum.org/EIPS/eip-2718) +- [EIP-2718: Typovaná obálka transakce](https://eips.ethereum.org/EIPS/eip-2718) _Víte o komunitním zdroji, který vám pomohl? Upravte tuto stránku a přidejte ho!_ ## Související témata {#related-topics} - [Účty](/developers/docs/accounts/) -- [Virtuální stroj Ethereum (EVM)](/developers/docs/evm/) +- [Ethereum Virtual Machine (EVM)](/developers/docs/evm/) - [Palivo](/developers/docs/gas/) diff --git a/public/content/translations/cs/developers/docs/web2-vs-web3/index.md b/public/content/translations/cs/developers/docs/web2-vs-web3/index.md index feb89f5bc28..b175a7936fc 100644 --- a/public/content/translations/cs/developers/docs/web2-vs-web3/index.md +++ b/public/content/translations/cs/developers/docs/web2-vs-web3/index.md @@ -1,6 +1,6 @@ --- title: Web2 vs. Web3 -description: +description: "Porovnejte centralizované služby Web2 s decentralizovanými aplikacemi Web3 postavenými na technologii blockchainu Ethereum." lang: cs --- @@ -17,10 +17,10 @@ Většina vývojářů Web3 se rozhodlo vytvářet dappky z důvodu inherentní - Platby jsou integrované prostřednictvím nativního tokenu, etheru (ETH). - Ethereum je Turingovsky úplné, což znamená, že na něm můžete naprogramovat prakticky cokoli. -## Praktické srovnání {#practical-comparisons} +## Praktická srovnání {#practical-comparisons} -| Web2 | Web3 | -| ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| Web2 | Web3 | +| --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | Twitter může cenzurovat jakýkoliv účet nebo tweet. | Tweety ve Web3 by byly necenzurovatelné, protože je taková síť decentralizovaná. | | Platební služba se může rozhodnout, že uživatelům neumožní platby za určitý typ práce. | Platební aplikace na Web3 nevyžadují osobní údaje a nemohou zabránit žádným platbám. | | Servery pro aplikace založené na tvorbě obsahu by mohly přestat fungovat a ovlivnit příjmy uživatelů. | Servery Web3 nemohou přestat fungovat – jako svůj backend používají Ethereum, decentralizovanou síť tisíců počítačů. | @@ -36,27 +36,27 @@ Web3 má v současnosti několik omezení: - Přístupnost – nedostatek integrace v moderních webových prohlížečích činí Web3 pro většinu uživatelů méně přístupným. - Náklady – většina úspěšných dappek umísťuje na blockchain jen malé části svého kódu, protože je to drahé. -## Centralizace vs decentralizace {#centralization-vs-decentralization} +## Centralizace vs. decentralizace {#centralization-vs-decentralization} V následující tabulce jsou uvedeny některé obecné výhody a nevýhody centralizovaných a decentralizovaných digitálních sítí. -| Centralizované systémy | Decentralizované systémy | -| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Nízký síťový průměr (všichni účastníci jsou připojeni k centrální autoritě) – informace se šíří rychle, protože šíření zajišťuje centrální autorita s velkými výpočetními zdroji. | Účastníci na opačných stranách sítě mohou být vzdáleni několik spojení. Což znamená, že šíření informací z jedné strany sítě na druhou může trvat dlouho. | -| Obvykle vyšší výkon (vyšší propustnost, spotřebovává méně celkových výpočetních zdrojů) a jednodušší implementace. | Obvykle nižší výkon (nižší propustnost, spotřebovává více celkových výpočetních zdrojů) a složitější implementace. | -| V případě datového konfliktu je řešení jasné a snadné – konečným zdrojem pravdy je centrální autorita. | Při řešení sporů je třeba spoléhat se na protokol (často složitý), pokud účastníci poskytují rozdílné informace o stavu dat, který má být synchronizován. | +| Centralizované systémy | Decentralizované systémy | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Nízký síťový průměr (všichni účastníci jsou připojeni k centrální autoritě) – informace se šíří rychle, protože šíření zajišťuje centrální autorita s velkými výpočetními zdroji. | Účastníci na opačných stranách sítě mohou být vzdáleni několik spojení. Což znamená, že šíření informací z jedné strany sítě na druhou může trvat dlouho. | +| Obvykle vyšší výkon (vyšší propustnost, spotřebovává méně celkových výpočetních zdrojů) a jednodušší implementace. | Obvykle nižší výkon (nižší propustnost, spotřebovává více celkových výpočetních zdrojů) a složitější implementace. | +| V případě datového konfliktu je řešení jasné a snadné – konečným zdrojem pravdy je centrální autorita. | Při řešení sporů je třeba spoléhat se na protokol (často složitý), pokud účastníci poskytují rozdílné informace o stavu dat, který má být synchronizován. | | Jediný bod selhání: Nečestní aktéři mohou potenciálně zničit síť tím, že zacílí na centrální autoritu. | Jediný bod selhání neexistuje: Síť může fungovat, i když je velká část účastníků napadena nebo vyřazena. | | Koordinace mezi účastníky sítě je mnohem jednodušší a je řízena centrální autoritou. Centrální autorita může s velmi malým odporem přinutit účastníky sítě přijmout vylepšení, aktualizace protokolu atd. | Koordinace je často obtížná, protože žádný jeden agent nemá poslední slovo v rozhodnutích na úrovni sítě, vylepšení protokolu atd. V nejhorším případě je síť náchylná k tříštění, pokud dojde k neshodám ohledně změn protokolu. | -| Centrální autorita může cenzurovat data, což může potenciálně odříznout části sítě od interakce se zbytkem sítě. | Cenzura je mnohem těžší, protože existuje mnoho způsobů, jak po síti sířit informace. | -| Účast v síti je řízena centrální autoritou. | Kdokoli se může do sítě připojit, nejsou zde žádní „hlídači vstupu“. V ideálním případě jsou náklady na vstup do sítě velmi nízké. | +| Centrální autorita může cenzurovat data, což může potenciálně odříznout části sítě od interakce se zbytkem sítě. | Cenzura je mnohem těžší, protože existuje mnoho způsobů, jak po síti sířit informace. | +| Účast v síti je řízena centrální autoritou. | Kdokoli se může do sítě připojit, nejsou zde žádní „hlídači vstupu“. V ideálním případě jsou náklady na vstup do sítě velmi nízké. | Poznámka: Tyto vzorce jsou obecné a nemusí platit pro každou síť. Navíc v reálném světě leží míra centralizace/decentralizace na spektru. Žádná síť není zcela centralizovaná nebo zcela decentralizovaná. -## Další informace {#further-reading} +## Další čtení {#further-reading} - [Co je Web3?](/web3/) – _ethereum.org_ -- [The Architecture of a Web 3.0 application](https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application) – _Preethi Kasireddy_ -- [Význam decentralizace](https://medium.com/@VitalikButerin/the-meaning-of-decentralization-a0c92b76a274) – _6. února 2017, Vitalik Buterin_ -- [Proč na decentralizaci záleží](https://medium.com/s/story/why-decentralization-matters-5e3f79f7638e) – _18. února 2018, Chris Dixon_ -- [Co je Web 3.0 a proč na něm záleží](https://medium.com/fabric-ventures/what-is-web-3-0-why-it-matters-934eb07f3d2b) – _31. prosince 2019, Max Mersch a Richard Muirhead_ -- [Proč potřebujeme Web 3.0](https://medium.com/@gavofyork/why-we-need-web-3-0-5da4f2bf95ab) – _12. září 2018, Gavin Wood_ +- [Architektura aplikace Web 3.0](https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application) – _Preethi Kasireddy_ +- [Význam decentralizace](https://medium.com/@VitalikButerin/the-meaning-of-decentralization-a0c92b76a274) _6. února 2017 – Vitalik Buterin_ +- [Proč na decentralizaci záleží](https://onezero.medium.com/why-decentralization-matters-5e3f79f7638e) _18. února 2018 – Chris Dixon_ +- [Co je Web 3.0 a proč na něm záleží](https://medium.com/fabric-ventures/what-is-web-3-0-why-it-matters-934eb07f3d2b) _31. prosince 2019 – Max Mersch and Richard Muirhead_ +- [Proč potřebujeme Web 3.0](https://gavofyork.medium.com/why-we-need-web-3-0-5da4f2bf95ab) _12. září 2018 – Gavin Wood_ diff --git a/public/content/translations/cs/developers/docs/wrapped-eth/index.md b/public/content/translations/cs/developers/docs/wrapped-eth/index.md deleted file mode 100644 index 85b8dd4debc..00000000000 --- a/public/content/translations/cs/developers/docs/wrapped-eth/index.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Co je Zabalený ether (WETH) -description: Úvod do Zabaleného etheru (WETH), ERC20-kompatibilního wrapperu pro ether (ETH). -lang: cs ---- - -# Zabalený ether (WETH) {#intro-to-weth} - -Ether (ETH) je hlavní měna Etherea. Používá se pro různé účely, jako je uzamčení, platby a placení poplatků za palivo potřebné pro výpočetní operace. **WETH je v podstatě vylepšená forma ETH s přidanými funkcemi, které vyžaduje řada aplikací a [ERC-20 tokeny(/glossary/#erc-20)**, což jsou další typy digitálních aktiv na Ethereu. Aby ETH mohlo pracovat s těmito tokeny, musí dodržovat stejná pravidla, která jsou stanovena standardem ERC-20. - -Zabalené ETH (WETH) byl vytvořeno za účelem zaplnění této mezery. Zabalené ETH je chytrý kontrakt, který vám umožní vložit libovolné množství ETH do kontraktu a obdržet stejné množství ve vyraženém WETH, které odpovídá standardu ERC-20 tokenů. WETH je reprezentací ETH, která vám umožňuje s ním zacházet jako s ERC-20 tokenem, nikoliv jako s nativním ETH. Stále však budete potřebovat nativní ETH k placení poplatků za palivo, takže si při směně nezapomeňte nechat potřebnou část ETH. - -Pomocí WETH chytrého kontraktu můžete WETH zpětně vyměnit za ETH. Pomocí WETH chytrého kontraktu si můžete i zpětně vyměnit libovolné množství WETH a obdržíte stejné množství v ETH. Takto vložené WETH je následně spáleno a odebráno z oběhu. - -**V kontraktu WETH tokenu jsou uzamčena asi ~3 % oběžné zásoby ETH**, což z něj činí jeden z nejpoužívanějších [chytrých kontraktů](/glossary/#smart-contract). WETH je obzvláště důležité pro uživatele, kteří interagují s aplikacemi v decentralizovaných financích (DeFi). - -## Proč potřebujeme ETH zabalit jako ERC-20? {#why-do-we-need-to-wrap-eth} - -[ERC-20](/developers/docs/standards/tokens/erc-20/) definuje standardní rozhraní pro transferovatelné tokeny, takže kdokoli může vytvářet tokeny, které spolupracují s aplikacemi bez jakýchkoliv problémů, a tokeny, které používají tento standard v ekosystému Etherea. Protože ETH existovalo dříve než standard ERC-20, nesplňuje tuto specifikaci. To znamená, že **nemůžete snadno** směnit ETH za jiné ERC-20 tokeny nebo **používat ETH v aplikacích, které používají standard ERC-20**. Zabalení ETH vám umožňuje následující: - -- **Směna ETH za ERC-20 tokeny**: Nemůžete přímo vyměnit ETH za jiné ERC-20 tokeny. WETH je reprezentací etheru, která splňuje standard ERC-20 a může být směňováno za jiné ERC-20 tokeny. - -- **Použití ETH v dappkách**: Protože ETH není kompatibilní s ERC-20, vývojáři by museli vytvořit samostatná rozhraní (jedno pro ETH a další pro ERC-20 tokeny) v dappkách. Zabalení ETH tento problém odstraňuje a umožňuje vývojářům pracovat s ETH a dalšími tokeny v rámci stejné aplikace. Spousta aplikací v decentralizovaných financích používá tento standard a vytváří trhy pro směnu těchto tokenů. - -## Zabalený ether (WETH) vs. ether (ETH): Čím se liší? {#weth-vs-eth-differences} - -| | **Ether (ETH)** | **Zabalený ether (WETH)** | -| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Zásoba | Zásoba ETH je spravována protokolem Ethereum. [Vydávání](/roadmap/merge/issuance) ETH je zajišťováno validátory Etherea během zpracování transakcí a vytváření bloků. | WETH je ERC-20 token, jehož zásoba je spravována chytrým kontraktem. Nové jednotky WETH jsou vydávány kontraktem po obdržení vkladů ETH od uživatelů nebo jsou jednotky WETH spáleny, když si uživatel přeje zpětně vyměnit WETH za ETH. | -| Vlastnictví | Vlastnictví je spravováno protokolem Ethereum prostřednictvím vašeho zůstatku na účtu. | Vlastnictví WETH je spravováno chytrým kontraktem WETH, který je zabezpečen protokolem Ethereum. | -| Palivo | Ether (ETH) je akceptovaná jednotka platby za výpočetní operace v síti Ethereum. Poplatky za palivo jsou denominovány v gwei (jednotka etheru). | Placení poplatků za palivo pomocí WETH tokenů není nativně podporováno. | - -## Často kladené dotazy {#faq} - - - -Platíte poplatky za palivo při balení nebo rozbalování ETH pomocí WETH kontraktu. - - - - - -WETH je obecně považováno za bezpečné, protože je založeno na jednoduchém, osvědčeném chytrém kontraktu. WETH kontrakt byl také formálně ověřen, což je nejvyšší bezpečnostní standard pro chytré kontrakty na Ethereu. - - - - - -Kromě [kanonické implementace WETH](https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) popsané na této stránce existují i jiné varianty. Tyto varianty mohou být tokeny, které vytvořili vývojáři aplikací pro vlastní účely, nebo verze vydané na jiných blockchainech a mohou se chovat odlišně nebo mít odlišné bezpečnostní vlastnosti. **Vždy si ověřte informace o tokenu, abyste věděli, s jakou implementací WETH pracujete.** - - - - - -- [Hlavní síť Ethereum](https://etherscan.io/token/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) -- [Arbitrum](https://arbiscan.io/token/0x82af49447d8a07e3bd95bd0d56f35241523fbab1) -- [Optimism](https://optimistic.etherscan.io/token/0x4200000000000000000000000000000000000006) - - - -## Further reading {#further-reading} - -- [Co je WETH?](https://weth.tkn.eth.limo/) -- [Informace o tokenu WETH na Etherscanu](https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) -- [Formální verifikace WETH](https://zellic.io/blog/formal-verification-weth) diff --git a/public/content/translations/cs/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md b/public/content/translations/cs/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md new file mode 100644 index 00000000000..526446edaf7 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md @@ -0,0 +1,300 @@ +--- +title: "Úvod do Etherea pro vývojáře v Pythonu, 1. část" +description: "Úvod do vývoje na Ethereu, zvláště užitečný pro ty, kdo znají programovací jazyk Python." +author: Marc Garreau +lang: cs +tags: [ "python", "web3.py" ] +skill: beginner +published: 2020-09-08 +source: Snake charmers +sourceUrl: https://snakecharmers.ethereum.org/a-developers-guide-to-ethereum-pt-1/ +--- + +Takže jsi slyšel o tomhle Ethereu a jsi připravený vydat se do králičí nory? V tomto příspěvku si rychle projdeme základy blockchainu a pak tě necháme interagovat se simulovaným uzlem sítě Ethereum – číst data z bloků, kontrolovat zůstatky na účtech a odesílat transakce. Cestou si také ukážeme rozdíly mezi tradičními způsoby tvorby aplikací a tímto novým decentralizovaným paradigmatem. + +## (Nepovinné) předpoklady {#soft-prerequisites} + +Tento příspěvek se snaží být přístupný širokému okruhu vývojářů. [Nástroje pro Python](/developers/docs/programming-languages/python/) sice použijeme, ale jsou jen prostředkem k předání myšlenek – nevadí, pokud nejsi vývojář v Pythonu. Budu však předpokládat, že už pár věcí znáš, abychom se mohli rychle přesunout k částem specifickým pro Ethereum. + +Předpoklady: + +- Vyznáš se v terminálu, +- Napsal jsi pár řádků kódu v Pythonu, +- na svém počítači máš nainstalovaný Python verze 3.6 nebo vyšší (důrazně doporučujeme použít [virtuální prostředí](https://realpython.com/effective-python-environment/#virtual-environments)) a +- používal jsi `pip`, instalátor balíčků pro Python. + I když něco z toho nesplňuješ nebo si neplánuješ kód z tohoto článku zkoušet, nejspíš i tak všechno bez problémů pochopíš. + +## Stručně o blockchainech {#blockchains-briefly} + +Ethereum lze popsat mnoha způsoby, ale v jeho jádru je blockchain. Blockchainy se skládají z řady bloků, takže začněme u nich. Zjednodušeně řečeno, každý blok na blockchainu Etherea je jen několik metadat a seznam transakcí. Ve formátu JSON to vypadá nějak takto: + +```json +{ + "number": 1234567, + "hash": "0xabc123...", + "parentHash": "0xdef456...", + ..., + "transactions": [...] +} +``` + +Každý [blok](/developers/docs/blocks/) má odkaz na blok, který mu předcházel; `parentHash` je jednoduše haš předchozího bloku. + +Poznámka: Ethereum pravidelně používá hašovací funkce k vytváření hodnot pevné velikosti („hašů“). Haše hrají v Ethereu důležitou roli, ale prozatím si je můžeš bezpečně představit jako jedinečné identifikátory. + +![Diagram znázorňující blockchain včetně dat uvnitř každého bloku](./blockchain-diagram.png) + +_Blockchain je v podstatě spojový seznam; každý blok má odkaz na předchozí blok._ + +Tato datová struktura není nijak nová, ale pravidla (tj. protokoly peer-to-peer), která řídí síť, ano. Neexistuje žádná centrální autorita; síť peerů musí spolupracovat na udržení sítě a soutěžit o to, které transakce zahrnout do dalšího bloku. Takže když chceš poslat nějaké peníze kamarádovi, musíš tuto transakci odvysílat do sítě a pak počkat, až bude zahrnuta do některého z nadcházejících bloků. + +Jediný způsob, jak může blockchain ověřit, že peníze byly skutečně poslány od jednoho uživatele k druhému, je použití měny, která je pro daný blockchain nativní (tj. vytvořená a řízená jím). V Ethereu se tato měna nazývá ether a blockchain Etherea obsahuje jediný oficiální záznam o zůstatcích na účtech. + +## Nové paradigma {#a-new-paradigm} + +Tento nový decentralizovaný technologický stack dal vzniknout novým vývojářským nástrojům. Takové nástroje existují v mnoha programovacích jazycích, ale my se na ně podíváme z pohledu Pythonu. Znovu opakuji: i když Python není tvůj preferovaný jazyk, neměl by být velký problém vše sledovat. + +Vývojáři v Pythonu, kteří chtějí interagovat s Ethereem, pravděpodobně sáhnou po [Web3.py](https://web3py.readthedocs.io/). Web3.py je knihovna, která výrazně zjednodušuje způsob, jakým se připojíš k uzlu Ethereum a poté z něj odesíláš a přijímáš data. + +Poznámka: „Uzel sítě Ethereum“ a „klient sítě Ethereum“ se používají zaměnitelně. V obou případech se jedná o software, který spouští účastník sítě Ethereum. Tento software umí číst data bloků, přijímat aktualizace, když jsou do řetězce přidány nové bloky, vysílat nové transakce a další. Technicky vzato je klient software a uzel je počítač, na kterém software běží. + +[Klienti sítě Ethereum](/developers/docs/nodes-and-clients/) mohou být nakonfigurováni tak, aby byli dosažitelní pomocí [IPC](https://wikipedia.org/wiki/Inter-process_communication), HTTP nebo Websockets, takže Web3.py bude muset tuto konfiguraci zrcadlit. Web3.py označuje tyto možnosti připojení jako **providery**. Budeš si chtít vybrat jednoho ze tří providerů, abys propojil instanci Web3.py se svým uzlem. + +![Diagram znázorňující, jak web3.py používá IPC k připojení tvé aplikace k uzlu Ethereum](./web3py-and-nodes.png) + +_Nakonfiguruj uzel Ethereum a Web3.py tak, aby komunikovaly pomocí stejného protokolu, např. IPC v tomto diagramu._ + +Jakmile je Web3.py správně nakonfigurováno, můžeš začít interagovat s blockchainem. Zde je několik příkladů použití Web3.py jako ukázka toho, co přijde: + +```python +# čtení dat bloku: +w3.eth.get_block('latest') + +# odeslání transakce: +w3.eth.send_transaction({'from': ..., 'to': ..., 'value': ...}) +``` + +## Instalace {#installation} + +V tomto návodu budeme pracovat pouze v interpretu Pythonu. Nebudeme vytvářet žádné adresáře, soubory, třídy ani funkce. + +Poznámka: V níže uvedených příkladech jsou příkazy začínající na `$` určeny ke spuštění v terminálu. (Znak `$` nepiš, označuje pouze začátek řádku.) + +Nejprve si nainstaluj [IPython](https://ipython.org/) pro uživatelsky přívětivé prostředí k prozkoumávání. IPython nabízí mimo jiné doplňování pomocí tabulátoru, což výrazně usnadňuje zjištění, co všechno je v Web3.py možné. + +```bash +pip install ipython +``` + +Web3.py je publikován pod názvem `web3`. Nainstaluj ho takto: + +```bash +pip install web3 +``` + +Ještě jedna věc – později budeme simulovat blockchain, což vyžaduje několik dalších závislostí. Můžeš si je nainstalovat pomocí: + +```bash +pip install 'web3[tester]' +``` + +Máš všechno připravené a můžeme začít! + +Poznámka: Balíček `web3[tester]` funguje až do Pythonu 3.10.xx + +## Spuštění sandboxu {#spin-up-a-sandbox} + +Otevři si nové prostředí Python spuštěním `ipython` v terminálu. Je to srovnatelné se spuštěním `python`, ale s více vychytávkami. + +```bash +ipython +``` + +Tím se vypíší informace o verzích Pythonu a IPythonu, které používáš, a pak bys měl vidět výzvu čekající na zadání: + +```python +In [1]: +``` + +Právě se díváš na interaktivní Python shell. V podstatě je to sandbox, ve kterém si můžeš hrát. Pokud ses dostal až sem, je čas importovat Web3.py: + +```python +In [1]: from web3 import Web3 +``` + +## Představení modulu Web3 {#introducing-the-web3-module} + +Kromě toho, že je modul [Web3](https://web3py.readthedocs.io/en/stable/overview.html#base-api) bránou do Etherea, nabízí i několik pomocných funkcí. Pojďme si jich pár prozkoumat. + +V aplikaci pro Ethereum budeš běžně potřebovat převádět nominální hodnoty měn. Modul Web3 poskytuje pro tento účel několik pomocných metod: [from_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.from_wei) a [to_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.to_wei). + + +Poznámka: Počítače jsou proslulé tím, že špatně zvládají desetinnou matematiku. Aby se tomu předešlo, vývojáři často ukládají dolarové částky v centech. Například položka s cenou 5,99 $ může být v databázi uložena jako 599. + +Podobný vzorec se používá při zpracování transakcí v etheru. Avšak místo dvou desetinných míst má ether 18! Nejmenší nominální hodnota etheru se nazývá wei, takže to je hodnota, která se zadává při odesílání transakcí. + +1 ether = 1000000000000000000 wei + +1 wei = 0,000000000000000001 etheru + + + +Zkus si převést některé hodnoty do a z wei. Všimni si, že [existují názvy pro mnoho nominálních hodnot](https://web3py.readthedocs.io/en/stable/troubleshooting.html#how-do-i-convert-currency-denominations) mezi etherem a wei. Jednou z nejznámějších mezi nimi je **gwei**, protože se v ní často uvádějí transakční poplatky. + +```python +In [2]: Web3.to_wei(1, 'ether') +Out[2]: 1000000000000000000 + +In [3]: Web3.from_wei(500000000, 'gwei') +Out[3]: Decimal('0.5') +``` + +Další pomocné metody v modulu Web3 zahrnují převodníky datových formátů (např. [`toHex`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.toHex)), pomocné funkce pro adresy (např. [`isAddress`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.isAddress)) a hašovací funkce (např. [`keccak`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.keccak)). Mnohým z nich se budeme věnovat později v této sérii. Chceš-li zobrazit všechny dostupné metody a vlastnosti, využij automatické doplňování v IPythonu zadáním `Web3`. a dvojitým stisknutím klávesy Tab za tečkou. + +## Komunikace s chainem {#talk-to-the-chain} + +Pomocné metody jsou fajn, ale pojďme se přesunout k blockchainu. Dalším krokem je nakonfigurovat Web3.py pro komunikaci s uzlem Etherea. Zde máme možnost použít providery IPC, HTTP nebo Websocket. + +Tudy se nevydáme, ale příklad kompletního pracovního postupu s použitím HTTP Provideru by mohl vypadat asi takto: + +- Stáhni si uzel Etherea, např. [Geth](https://geth.ethereum.org/). +- Spusť Geth v jednom okně terminálu a počkej, až se synchronizuje se sítí. Výchozí port HTTP je `8545`, ale je konfigurovatelný. +- Řekni Web3.py, aby se připojil k uzlu přes HTTP na `localhost:8545`. + `w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))` +- Použij instanci `w3` k interakci s uzlem. + +I když je to jeden ze „skutečných“ způsobů, jak to udělat, proces synchronizace trvá hodiny a je zbytečný, pokud chceš jen vývojové prostředí. Web3.py pro tento účel zpřístupňuje čtvrtého providera, **EthereumTesterProvider**. Tento tester provider se připojuje k simulovanému uzlu Etherea s uvolněnými oprávněními a falešnou měnou na hraní. + +![Diagram znázorňující EthereumTesterProvider propojující tvou aplikaci web3.py se simulovaným uzlem Ethereum](./ethereumtesterprovider.png) + +_EthereumTesterProvider se připojuje k simulovanému uzlu a je praktický pro rychlé vytvoření vývojového prostředí._ + +Tento simulovaný uzel se nazývá [eth-tester](https://github.com/ethereum/eth-tester) a nainstalovali jsme si ho jako součást příkazu `pip install web3[tester]`. Konfigurace Web3.py pro použití tohoto tester providera je tak jednoduchá: + +```python +In [4]: w3 = Web3(Web3.EthereumTesterProvider()) +``` + +Teď jsi připravený surfovat na chainu! Takhle se to neříká. To jsem si právě vymyslel. Pojďme na rychlou prohlídku. + +## Rychlá prohlídka {#the-quick-tour} + +Nejdříve ze všeho, kontrola funkčnosti: + +```python +In [5]: w3.is_connected() +Out[5]: True +``` + +Jelikož používáme tester providera, není to příliš cenný test, ale pokud selže, je pravděpodobné, že jsi při vytváření instance proměnné `w3` něco špatně napsal. Zkontroluj si, že jsi zahrnul vnitřní závorky, tj. `Web3.EthereumTesterProvider()`. + +## Zastávka č. 1: [účty](/developers/docs/accounts/) {#tour-stop-1-accounts} + +Pro usnadnění tester provider vytvořil několik účtů a předem je nabil testovacím etherem. + +Nejprve se podívejme na seznam těchto účtů: + +```python +In [6]: w3.eth.accounts +Out[6]: ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', + '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', + '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69', ...] +``` + +Pokud tento příkaz spustíš, měl bys vidět seznam deseti řetězců, které začínají `0x`. Každý z nich je **veřejná adresa** a je v některých ohledech analogický číslu běžného účtu. Tuto adresu bys poskytl někomu, kdo by ti chtěl poslat ether. + +Jak již bylo zmíněno, tester provider předem nabil každý z těchto účtů testovacím etherem. Pojďme zjistit, kolik je na prvním účtu: + +```python +In [7]: w3.eth.get_balance(w3.eth.accounts[0]) +Out[7]: 1000000000000000000000000 +``` + +To je hodně nul! Než se s úsměvem vydáš do falešné banky, vzpomeň si na dřívější lekci o nominálních hodnotách měn. Hodnoty etheru jsou vyjádřeny v nejmenší nominální hodnotě, wei. Převeďme to na ether: + +```python +In [8]: w3.from_wei(1000000000000000000000000, 'ether') +Out[8]: Decimal('1000000') +``` + +Jeden milion testovacích etherů – to pořád není špatné. + +## Zastávka č. 2: data bloku {#tour-stop-2-block-data} + +Pojďme se podívat na stav tohoto simulovaného blockchainu: + +```python +In [9]: w3.eth.get_block('latest') +Out[9]: AttributeDict({ + 'number': 0, + 'hash': HexBytes('0x9469878...'), + 'parentHash': HexBytes('0x0000000...'), + ... + 'transactions': [] +}) +``` + +O bloku se vrací spousta informací, ale zde je třeba upozornit jen na pár věcí: + +- Číslo bloku je nula – bez ohledu na to, jak dávno jsi nakonfiguroval tester providera. Na rozdíl od skutečné sítě Ethereum, která přidává nový blok každých 12 sekund, tato simulace počká, dokud jí nedáš nějakou práci. +- `transactions` je prázdný seznam ze stejného důvodu: ještě jsme nic neudělali. Tento první blok je **prázdný blok**, jen aby se spustil chain. +- Všimni si, že `parentHash` je jen spousta prázdných bytů. To znamená, že se jedná o první blok v řetězci, známý také jako **genesis blok**. + +## Zastávka č. 3: [transakce](/developers/docs/transactions/) {#tour-stop-3-transactions} + +Zasekli jsme se na bloku nula, dokud nebude čekající transakce, tak mu ji dejme. Pošli pár testovacích etherů z jednoho účtu na druhý: + +```python +In [10]: tx_hash = w3.eth.send_transaction({ + 'from': w3.eth.accounts[0], + 'to': w3.eth.accounts[1], + 'value': w3.to_wei(3, 'ether'), + 'gas': 21000 +}) +``` + +To je obvykle bod, kde bys čekal několik sekund, než se tvá transakce zahrne do nového bloku. Celý proces probíhá nějak takhle: + +1. Odešli transakci a uschovej si haš transakce. Dokud není vytvořen a odvysílán blok obsahující transakci, transakce je „čekající“. + `tx_hash = w3.eth.send_transaction({ …` `})` +2. Počkej, až bude transakce zahrnuta do bloku: + `w3.eth.wait_for_transaction_receipt(tx_hash)` +3. Pokračuj v logice aplikace. Pro zobrazení úspěšné transakce: + `w3.eth.get_transaction(tx_hash)` + +Naše simulované prostředí přidá transakci do nového bloku okamžitě, takže si transakci můžeme hned prohlédnout: + +```python +In [11]: w3.eth.get_transaction(tx_hash) +Out[11]: AttributeDict({ + 'hash': HexBytes('0x15e9fb95dc39...'), + 'blockNumber': 1, + 'transactionIndex': 0, + 'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', + 'to': '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', + 'value': 3000000000000000000, + ... +}) +``` + +Zde uvidíš některé známé detaily: pole `from`, `to` a `value` by měla odpovídat vstupům našeho volání `send_transaction`. Další uklidňující věcí je, že tato transakce byla zahrnuta jako první transakce (`'transactionIndex': 0`) v bloku číslo 1. + +Úspěšnost této transakce můžeme také snadno ověřit kontrolou zůstatků na obou zúčastněných účtech. Tři ethery se měly přesunout z jednoho na druhý. + +```python +In [12]: w3.eth.get_balance(w3.eth.accounts[0]) +Out[12]: 999996999979000000000000 + +In [13]: w3.eth.get_balance(w3.eth.accounts[1]) +Out[13]: 1000003000000000000000000 +``` + +To druhé vypadá dobře! Zůstatek se změnil z 1 000 000 na 1 000 003 etherů. Ale co se stalo s prvním účtem? Zdá se, že ztratil o něco více než tři ethery. Bohužel, nic v životě není zadarmo, a používání veřejné sítě Ethereum vyžaduje, abys odměnil ostatní účastníky sítě za jejich podporu. Z účtu, který transakci odeslal, byl odečten malý transakční poplatek – tento poplatek je množství spáleného paliva (21 000 jednotek paliva za převod ETH) vynásobené základním poplatkem, který se liší podle aktivity sítě, plus spropitné, které jde validátorovi, který transakci zahrne do bloku. + +Více o [palivu](/developers/docs/gas/#post-london) + +Poznámka: Ve veřejné síti jsou transakční poplatky proměnlivé v závislosti na poptávce v síti a na tom, jak rychle chceš, aby byla transakce zpracována. Pokud tě zajímá, jak se poplatky počítají, podívej se na můj dřívější příspěvek o tom, jak se transakce zahrnují do bloku. + +## A teď výdech {#and-breathe} + +Už jsme u toho nějakou dobu, takže se zdá, že je to dobré místo na přestávku. Králičí nora pokračuje a my budeme pokračovat v průzkumu v druhé části této série. Některé koncepty, které přijdou: připojení ke skutečnému uzlu, chytré kontrakty a tokeny. Máš další otázky? Dej mi vědět! Tvá zpětná vazba ovlivní, kam se odtud vydáme. Žádosti jsou vítány přes [Twitter](https://twitter.com/wolovim). diff --git a/public/content/translations/cs/developers/tutorials/all-you-can-cache/index.md b/public/content/translations/cs/developers/tutorials/all-you-can-cache/index.md new file mode 100644 index 00000000000..d2785fa834c --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/all-you-can-cache/index.md @@ -0,0 +1,867 @@ +--- +title: "Vše, co můžete cachovat" +description: "Naučte se, jak vytvořit a používat cachovací kontrakt pro levnější rollupové transakce" +author: Ori Pomerantz +tags: [ "vrstva 2", "cachování", "úložiště" ] +skill: intermediate +published: 2022-09-15 +lang: cs +--- + +Při používání rollupů je cena jednoho bajtu v transakci o mnoho vyšší než cena slotu v úložišti. Proto dává smysl cachovat co nejvíce informací na blockchainu. + +V tomto článku se naučíte, jak vytvořit a používat cachovací kontrakt tak, aby se každá hodnota parametru, která se pravděpodobně použije vícekrát, uložila do cache a byla (po prvním použití) dostupná za použití mnohem menšího počtu bajtů, a jak napsat off-chain kód, který tuto cache využívá. + +Pokud chcete článek přeskočit a podívat se rovnou na zdrojový kód, [najdete ho zde](https://github.com/qbzzt/20220915-all-you-can-cache). Vývojový stack je [Foundry](https://getfoundry.sh/introduction/installation/). + +## Celkový návrh {#overall-design} + +Pro zjednodušení budeme předpokládat, že všechny parametry transakce jsou typu `uint256` o délce 32 bajtů. Když obdržíme transakci, zpracujeme každý parametr následujícím způsobem: + +1. Pokud je první bajt `0xFF`, vezměte následujících 32 bajtů jako hodnotu parametru a zapište ji do cache. + +2. Pokud je první bajt `0xFE`, vezměte následujících 32 bajtů jako hodnotu parametru, ale _nezapisujte_ ji do cache. + +3. Pro jakoukoliv jinou hodnotu vezměte horní čtyři bity jako počet dalších bajtů a spodní čtyři bity jako nejvýznamnější bity klíče cache. Zde je několik příkladů: + + | Bajty v calldata | Klíč cache | + | :--------------- | ---------: | + | 0x0F | 0x0F | + | 0x10,0x10 | 0x10 | + | 0x12,0xAC | 0x02AC | + | 0x2D,0xEA, 0xD6 | 0x0DEAD6 | + +## Manipulace s cache {#cache-manipulation} + +Cache je implementována v souboru [`Cache.sol`](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol). Pojďme si ho projít řádek po řádku. + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + + +contract Cache { + + bytes1 public constant INTO_CACHE = 0xFF; + bytes1 public constant DONT_CACHE = 0xFE; +``` + +Tyto konstanty se používají k interpretaci speciálních případů, kdy poskytujeme všechny informace a chceme je buď zapsat do cache, nebo ne. Zápis do cache vyžaduje dvě operace [`SSTORE`](https://www.evm.codes/#55) do dříve nepoužitých slotů v úložišti s cenou 22 100 gasu za každou, takže je to volitelné. + +```solidity + + mapping(uint => uint) public val2key; +``` + +[Mapování](https://www.geeksforgeeks.org/solidity/solidity-mappings/) mezi hodnotami a jejich klíči. Tato informace je nezbytná k zakódování hodnot před odesláním transakce. + +```solidity + // Umístění n má hodnotu pro klíč n+1, protože potřebujeme zachovat + // nulu jako „není v cache“. + uint[] public key2val; +``` + +Pro mapování z klíčů na hodnoty můžeme použít pole, protože klíče přiřazujeme my a pro zjednodušení to děláme sekvenčně. + +```solidity + function cacheRead(uint _key) public view returns (uint) { + require(_key <= key2val.length, "Reading uninitialize cache entry"); + return key2val[_key-1]; + } // cacheRead +``` + +Přečte hodnotu z cache. + +```solidity + // Zapíše hodnotu do cache, pokud tam ještě není + // Veřejné jen proto,aby fungoval test + function cacheWrite(uint _value) public returns (uint) { + // Pokud je hodnota již v cache, vrátí aktuální klíč + if (val2key[_value] != 0) { + return val2key[_value]; + } +``` + +Nemá smysl vkládat stejnou hodnotu do cache více než jednou. Pokud tam hodnota již je, stačí vrátit stávající klíč. + +```solidity + // Jelikož 0xFE je speciální případ, největší klíč, který může cache + // obsahovat, je 0x0D následovaný 15x 0xFF. Pokud už je délka cache tak + // velká, selže. + // 1 2 3 4 5 6 7 8 9 A B C D E F + require(key2val.length+1 < 0x0DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + "přetečení cache"); +``` + +Nemyslím si, že se někdy dočkáme tak velké cache (přibližně 1,8\*1037 položek, což by vyžadovalo asi 1027 TB k uložení). Jsem však dost starý na to, abych si pamatoval [„640 kB bude vždy stačit“](https://quoteinvestigator.com/2011/09/08/640k-enough/). Tento test je velmi levný. + +```solidity + // Zapíše hodnotu pomocí dalšího klíče + val2key[_value] = key2val.length+1; +``` + +Přidá zpětné vyhledávání (od hodnoty ke klíči). + +```solidity + key2val.push(_value); +``` + +Přidá dopředné vyhledávání (od klíče k hodnotě). Protože přiřazujeme hodnoty sekvenčně, můžeme ji jednoduše přidat za poslední hodnotu v poli. + +```solidity + return key2val.length; + } // cacheWrite +``` + +Vrátí novou délku `key2val`, což je buňka, kde je uložena nová hodnota. + +```solidity + function _calldataVal(uint startByte, uint length) + private pure returns (uint) +``` + +Tato funkce čte hodnotu z calldata libovolné délky (až 32 bajtů, což je velikost slova). + +```solidity + { + uint _retVal; + + require(length < 0x21, + "limit délky _calldataVal je 32 bajtů"); + require(length + startByte <= msg.data.length, + "_calldataVal se snaží číst za calldatasize"); +``` + +Tato funkce je interní, takže pokud je zbytek kódu napsán správně, tyto testy nejsou nutné. Nestojí však mnoho, takže je můžeme klidně použít. + +```solidity + assembly { + _retVal := calldataload(startByte) + } +``` + +Tento kód je v jazyce [Yul](https://docs.soliditylang.org/en/v0.8.16/yul.html). Čte 32bajtovou hodnotu z calldata. Funguje to i v případě, že calldata končí před `startByte+32`, protože neinicializovaný prostor v EVM je považován za nulový. + +```solidity + _retVal = _retVal >> (256-length*8); +``` + +Nemusíme nutně chtít 32bajtovou hodnotu. Tím se zbavíme přebytečných bajtů. + +```solidity + return _retVal; + } // _calldataVal + + + // Načte jeden parametr z calldata, počínaje od _fromByte + function _readParam(uint _fromByte) internal + returns (uint _nextByte, uint _parameterValue) + { +``` + +Načte jeden parametr z calldata. Všimněte si, že musíme vrátit nejen hodnotu, kterou jsme načetli, ale také umístění dalšího bajtu, protože parametry mohou mít délku od 1 do 33 bajtů. + +```solidity + // První bajt nám říká, jak interpretovat zbytek + uint8 _firstByte; + + _firstByte = uint8(_calldataVal(_fromByte, 1)); +``` + +Solidity se snaží snížit počet chyb tím, že zakazuje potenciálně nebezpečné [implicitní převody typů](https://docs.soliditylang.org/en/v0.8.16/types.html#implicit-conversions). Downgrade, například z 256 bitů na 8 bitů, musí být explicitní. + +```solidity + + // Přečte hodnotu, ale nezapíše ji do cache + if (_firstByte == uint8(DONT_CACHE)) + return(_fromByte+33, _calldataVal(_fromByte+1, 32)); + + // Přečte hodnotu a zapíše ji do cache + if (_firstByte == uint8(INTO_CACHE)) { + uint _param = _calldataVal(_fromByte+1, 32); + cacheWrite(_param); + return(_fromByte+33, _param); + } + + // Pokud jsme se dostali sem, znamená to, že musíme číst z cache + + // Počet bajtů navíc ke čtení + uint8 _extraBytes = _firstByte / 16; +``` + +Vezměte nižší [půlbajt](https://en.wikipedia.org/wiki/Nibble) a zkombinujte ho s ostatními bajty, abyste načetli hodnotu z cache. + +```solidity + uint _key = (uint256(_firstByte & 0x0F) << (8*_extraBytes)) + + _calldataVal(_fromByte+1, _extraBytes); + + return (_fromByte+_extraBytes+1, cacheRead(_key)); + + } // _readParam + + + // Načte n parametrů (funkce vědí, kolik parametrů očekávají) + function _readParams(uint _paramNum) internal returns (uint[] memory) { +``` + +Počet parametrů, které máme, bychom mohli získat ze samotné calldata, ale funkce, které nás volají, vědí, kolik parametrů očekávají. Je jednodušší, nechat si to od nich říct. + +```solidity + // Parametry, které čteme + uint[] memory params = new uint[](_paramNum); + + // Parametry začínají na 4. bajtu, předtím je podpis funkce + uint _atByte = 4; + + for(uint i=0; i<_paramNum; i++) { + (_atByte, params[i]) = _readParam(_atByte); + } +``` + +Čtěte parametry, dokud nebudete mít požadovaný počet. Pokud překročíme konec calldata, `_readParams` vrátí volání zpět. + +```solidity + + return(params); + } // readParams + + // Pro testování _readParams, testování čtení čtyř parametrů + function fourParam() public + returns (uint256,uint256,uint256,uint256) + { + uint[] memory params; + params = _readParams(4); + return (params[0], params[1], params[2], params[3]); + } // fourParam +``` + +Jednou z velkých výhod Foundry je, že umožňuje psát testy v Solidity ([viz Testování cache níže](#testing-the-cache)). To značně usnadňuje jednotkové testy. Jedná se o funkci, která přečte čtyři parametry a vrátí je, aby test mohl ověřit, že byly správné. + +```solidity + // Získá hodnotu, vrátí bajty, které ji zakódují (pokud možno s použitím cache) + function encodeVal(uint _val) public view returns(bytes memory) { +``` + +`encodeVal` je funkce, kterou volá off-chain kód, aby pomohla vytvořit calldata, která používá cache. Přijímá jednu hodnotu a vrací bajty, které ji kódují. Tato funkce je `view`, takže nevyžaduje transakci a při externím volání nestojí žádný gas. + +```solidity + uint _key = val2key[_val]; + + // Hodnota ještě není v cache, přidejte ji + if (_key == 0) + return bytes.concat(INTO_CACHE, bytes32(_val)); +``` + +V [EVM](/developers/docs/evm/) se předpokládá, že všechna neinicializovaná úložiště jsou nulová. Takže pokud hledáme klíč pro hodnotu, která tam není, dostaneme nulu. V takovém případě jsou bajty, které ji kódují, `INTO_CACHE` (takže bude při příštím použití cachována), následované skutečnou hodnotou. + +```solidity + // Pokud je klíč <0x10, vraťte ho jako jeden bajt + if (_key < 0x10) + return bytes.concat(bytes1(uint8(_key))); +``` + +Jednotlivé bajty jsou nejjednodušší. Použijeme jen [`bytes.concat`](https://docs.soliditylang.org/en/v0.8.16/types.html#the-functions-bytes-concat-and-string-concat) pro převod typu `bytes` na pole bajtů, které může mít libovolnou délku. Navzdory svému názvu funguje dobře i při zadání pouze jednoho argumentu. + +```solidity + // Dvoubajtová hodnota, zakódovaná jako 0x1vvv + if (_key < 0x1000) + return bytes.concat(bytes2(uint16(_key) | 0x1000)); +``` + +Pokud máme klíč, který je menší než 163, můžeme ho vyjádřit ve dvou bajtech. Nejprve převedeme `_key`, což je 256bitová hodnota, na 16bitovou hodnotu a pomocí logického součtu přidáme počet bajtů navíc k prvnímu bajtu. Poté ji převedeme na hodnotu `bytes2`, kterou lze převést na `bytes`. + +```solidity + // Pravděpodobně existuje chytrý způsob, jak provést následující řádky jako smyčku, + // ale je to funkce typu view, takže optimalizuji na čas programátora a + // jednoduchost. + + if (_key < 16*256**2) + return bytes.concat(bytes3(uint24(_key) | (0x2 * 16 * 256**2))); + if (_key < 16*256**3) + return bytes.concat(bytes4(uint32(_key) | (0x3 * 16 * 256**3))); + . + . + . + if (_key < 16*256**14) + return bytes.concat(bytes15(uint120(_key) | (0xE * 16 * 256**14))); + if (_key < 16*256**15) + return bytes.concat(bytes16(uint128(_key) | (0xF * 16 * 256**15))); +``` + +Ostatní hodnoty (3 bajty, 4 bajty atd.) jsou zpracovávány stejným způsobem, jen s jinými velikostmi polí. + +```solidity + // Pokud se dostaneme sem, něco je špatně. + revert("Error in encodeVal, should not happen"); +``` + +Pokud se dostaneme sem, znamená to, že jsme dostali klíč, který je větší než 16\*25615. Ale `cacheWrite` omezuje klíče, takže se nemůžeme dostat ani na 14\*25616 (což by mělo první bajt 0xFE, takže by to vypadalo jako `DONT_CACHE`). Ale přidání testu pro případ, že budoucí programátor zavede chybu, nás moc nestojí. + +```solidity + } // encodeVal + +} // Cache +``` + +### Testování cache {#testing-the-cache} + +Jednou z výhod Foundry je, že [vám umožňuje psát testy v Solidity](https://getfoundry.sh/forge/tests/overview/), což usnadňuje psaní jednotkových testů. Testy pro třídu `Cache` jsou [zde](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/Cache.t.sol). Protože testovací kód je repetitivní, jak už to u testů bývá, tento článek vysvětluje pouze zajímavé části. + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; + + +// Je potřeba spustit `forge test -vv` pro konzoli. +import "forge-std/console.sol"; +``` + +Toto je pouze boilerplate, který je nezbytný pro použití testovacího balíčku a `console.log`. + +```solidity +import "src/Cache.sol"; +``` + +Potřebujeme znát kontrakt, který testujeme. + +```solidity +contract CacheTest is Test { + Cache cache; + + function setUp() public { + cache = new Cache(); + } +``` + +Funkce `setUp` se volá před každým testem. V tomto případě pouze vytvoříme novou cache, aby se naše testy navzájem neovlivňovaly. + +```solidity + function testCaching() public { +``` + +Testy jsou funkce, jejichž názvy začínají na `test`. Tato funkce kontroluje základní funkčnost cache, zapisuje hodnoty a znovu je čte. + +```solidity + for(uint i=1; i<5000; i++) { + cache.cacheWrite(i*i); + } + + for(uint i=1; i<5000; i++) { + assertEq(cache.cacheRead(i), i*i); +``` + +Takto se provádí skutečné testování pomocí [funkcí `assert...`](https://getfoundry.sh/reference/forge-std/std-assertions/). V tomto případě kontrolujeme, že hodnota, kterou jsme zapsali, je ta, kterou jsme přečetli. Výsledek `cache.cacheWrite` můžeme zahodit, protože víme, že klíče cache jsou přiřazovány lineárně. + +```solidity + } + } // testCaching + + + // Cachovat stejnou hodnotu vícekrát, zajistit, aby klíč zůstal + // stejný + function testRepeatCaching() public { + for(uint i=1; i<100; i++) { + uint _key1 = cache.cacheWrite(i); + uint _key2 = cache.cacheWrite(i); + assertEq(_key1, _key2); + } +``` + +Nejprve zapíšeme každou hodnotu dvakrát do cache a ujistíme se, že klíče jsou stejné (což znamená, že druhý zápis se ve skutečnosti neuskutečnil). + +```solidity + for(uint i=1; i<100; i+=3) { + uint _key = cache.cacheWrite(i); + assertEq(_key, i); + } + } // testRepeatCaching +``` + +Teoreticky by mohla existovat chyba, která neovlivní po sobě jdoucí zápisy do cache. Takže zde provedeme několik zápisů, které nejsou po sobě jdoucí, a uvidíme, že hodnoty se stále nepřepisují. + +```solidity + // Přečte uint z bufferu v paměti (abychom se ujistili, že dostaneme zpět parametry, + // které jsme odeslali) + function toUint256(bytes memory _bytes, uint256 _start) internal pure + returns (uint256) +``` + +Přečte 256bitové slovo z bufferu `bytes memory`. Tato pomocná funkce nám umožňuje ověřit, že při spuštění volání funkce, která používá cache, obdržíme správné výsledky. + +```solidity + { + require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); + uint256 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x20), _start)) + } +``` + +Yul nepodporuje datové struktury nad rámec `uint256`, takže když odkazujete na sofistikovanější datovou strukturu, jako je paměťový buffer `_bytes`, získáte adresu této struktury. Solidity ukládá hodnoty `bytes memory` jako 32bajtové slovo, které obsahuje délku, následované skutečnými bajty, takže pro získání bajtu číslo `_start` musíme vypočítat `_bytes+32+_start`. + +```solidity + + return tempUint; + } // toUint256 + + // Podpis funkce pro fourParams(), s laskavým svolením + // https://www.4byte.directory/signatures/?bytes4_signature=0x3edc1e6d + bytes4 constant FOUR_PARAMS = 0x3edc1e6d; + + // Jen několik konstantních hodnot, abychom viděli, že dostáváme zpět správné hodnoty + uint256 constant VAL_A = 0xDEAD60A7; + uint256 constant VAL_B = 0xBEEF; + uint256 constant VAL_C = 0x600D; + uint256 constant VAL_D = 0x600D60A7; +``` + +Některé konstanty, které potřebujeme pro testování. + +```solidity + function testReadParam() public { +``` + +Zavoláním `fourParams()`, funkce, která používá `readParams`, otestujeme, zda umíme správně číst parametry. + +```solidity + address _cacheAddr = address(cache); + bool _success; + bytes memory _callInput; + bytes memory _callOutput; +``` + +Nemůžeme použít normální mechanismus ABI pro volání funkce pomocí cache, takže musíme použít nízkoúrovňový mechanismus [`
.call()`](https://docs.soliditylang.org/en/v0.8.16/types.html#members-of-addresses). Tento mechanismus přijímá jako vstup `bytes memory` a vrací je (stejně jako booleovskou hodnotu) jako výstup. + +```solidity + // První volání, cache je prázdná + _callInput = bytes.concat( + FOUR_PARAMS, +``` + +Je užitečné, aby stejný kontrakt podporoval jak cachované funkce (pro volání přímo z transakcí), tak i necachované funkce (pro volání z jiných chytrých kontraktů). Abychom toho dosáhli, musíme se nadále spoléhat na mechanismus Solidity pro volání správné funkce, namísto toho, abychom vše vkládali do [funkce `fallback`](https://docs.soliditylang.org/en/v0.8.16/contracts.html#fallback-function). Tímto způsobem je kompozitnost mnohem snazší. Jeden bajt by ve většině případů stačil k identifikaci funkce, takže plýtváme třemi bajty (16\*3=48 gasu). Nicméně, v době psaní tohoto článku stojí těchto 48 gasů 0,07 centů, což je rozumná cena za jednodušší a méně chybový kód. + +```solidity + // První hodnota, přidejte ji do cache + cache.INTO_CACHE(), + bytes32(VAL_A), +``` + +První hodnota: Příznak, který říká, že je to plná hodnota, která se musí zapsat do cache, následovaný 32 bajty hodnoty. Ostatní tři hodnoty jsou podobné, s výjimkou toho, že `VAL_B` se do cache nezapisuje a `VAL_C` je jak třetím, tak čtvrtým parametrem. + +```solidity + . + . + . + ); + (_success, _callOutput) = _cacheAddr.call(_callInput); +``` + +Zde skutečně voláme kontrakt `Cache`. + +```solidity + assertEq(_success, true); +``` + +Očekáváme, že volání bude úspěšné. + +```solidity + assertEq(cache.cacheRead(1), VAL_A); + assertEq(cache.cacheRead(2), VAL_C); +``` + +Začínáme s prázdnou cache a poté přidáme `VAL_A` následované `VAL_C`. Očekávali bychom, že první bude mít klíč 1 a druhý klíč 2. + +``` + assertEq(toUint256(_callOutput,0), VAL_A); + assertEq(toUint256(_callOutput,32), VAL_B); + assertEq(toUint256(_callOutput,64), VAL_C); + assertEq(toUint256(_callOutput,96), VAL_C); +``` + +Výstupem jsou čtyři parametry. Zde ověřujeme, že je správný. + +```solidity + // Druhé volání, můžeme použít cache + _callInput = bytes.concat( + FOUR_PARAMS, + + // První hodnota v cache + bytes1(0x01), +``` + +Klíče cache pod 16 jsou pouze jeden bajt. + +```solidity + // Druhá hodnota, nepřidávejte ji do cache + cache.DONT_CACHE(), + bytes32(VAL_B), + + // Třetí a čtvrtá hodnota, stejná hodnota + bytes1(0x02), + bytes1(0x02) + ); + . + . + . + } // testReadParam +``` + +Testy po volání jsou totožné s testy po prvním volání. + +```solidity + function testEncodeVal() public { +``` + +Tato funkce je podobná funkci `testReadParam`, s výjimkou toho, že místo explicitního psaní parametrů používáme `encodeVal()`. + +```solidity + . + . + . + _callInput = bytes.concat( + FOUR_PARAMS, + cache.encodeVal(VAL_A), + cache.encodeVal(VAL_B), + cache.encodeVal(VAL_C), + cache.encodeVal(VAL_D) + ); + . + . + . + assertEq(_callInput.length, 4+1*4); + } // testEncodeVal +``` + +Jediný dodatečný test v `testEncodeVal()` je ověření, že délka `_callInput` je správná. Pro první volání je to 4+33\*4. Pro druhé, kde je každá hodnota již v cache, je to 4+1\*4. + +```solidity + // Testujte encodeVal, když je klíč delší než jeden bajt + // Maximálně tři bajty, protože plnění cache na čtyři bajty trvá + // příliš dlouho. + function testEncodeValBig() public { + // Vložte několik hodnot do cache. + // Pro zjednodušení použijte klíč n pro hodnotu n. + for(uint i=1; i<0x1FFF; i++) { + cache.cacheWrite(i); + } +``` + +Výše uvedená funkce `testEncodeVal` zapisuje do cache pouze čtyři hodnoty, takže [část funkce, která se zabývá vícebajtovými hodnotami](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol#L144-L171) se nekontroluje. Ale tento kód je složitý a náchylný k chybám. + +První část této funkce je smyčka, která zapisuje všechny hodnoty od 1 do 0x1FFF do cache v pořadí, takže budeme moci tyto hodnoty zakódovat a vědět, kam jdou. + +```solidity + . + . + . + + _callInput = bytes.concat( + FOUR_PARAMS, + cache.encodeVal(0x000F), // Jeden bajt 0x0F + cache.encodeVal(0x0010), // Dva bajty 0x1010 + cache.encodeVal(0x0100), // Dva bajty 0x1100 + cache.encodeVal(0x1000) // Tři bajty 0x201000 + ); +``` + +Otestujte jednobajtové, dvoubajtové a tříbajtové hodnoty. Dále netestujeme, protože by trvalo příliš dlouho zapsat dostatek položek zásobníku (alespoň 0x10000000, přibližně čtvrt miliardy). + +```solidity + . + . + . + . + } // testEncodeValBig + + + // Otestujte, co se stane s příliš malým bufferem, dostaneme revert + function testShortCalldata() public { +``` + +Otestujte, co se stane v abnormálním případě, kdy není dostatek parametrů. + +```solidity + . + . + . + (_success, _callOutput) = _cacheAddr.call(_callInput); + assertEq(_success, false); + } // testShortCalldata +``` + +Jelikož se to vrací, výsledek, který bychom měli dostat, je `false`. + +``` + // Volání s klíči cache, které tam nejsou + function testNoCacheKey() public { + . + . + . + _callInput = bytes.concat( + FOUR_PARAMS, + + // První hodnota, přidejte ji do cache + cache.INTO_CACHE(), + bytes32(VAL_A), + + // Druhá hodnota + bytes1(0x0F), + bytes2(0x1234), + bytes11(0xA10102030405060708090A) + ); +``` + +Tato funkce dostane čtyři naprosto legitimní parametry, s výjimkou toho, že cache je prázdná, takže tam nejsou žádné hodnoty ke čtení. + +```solidity + . + . + . + // Otestujte, co s příliš dlouhým bufferem, vše funguje + function testLongCalldata() public { + address _cacheAddr = address(cache); + bool _success; + bytes memory _callInput; + bytes memory _callOutput; + + // První volání, cache je prázdná + _callInput = bytes.concat( + FOUR_PARAMS, + + // První hodnota, přidejte ji do cache + cache.INTO_CACHE(), bytes32(VAL_A), + + // Druhá hodnota, přidejte ji do cache + cache.INTO_CACHE(), bytes32(VAL_B), + + // Třetí hodnota, přidejte ji do cache + cache.INTO_CACHE(), bytes32(VAL_C), + + // Čtvrtá hodnota, přidejte ji do cache + cache.INTO_CACHE(), bytes32(VAL_D), + + // A další hodnota pro „štěstí“ + bytes4(0x31112233) + ); +``` + +Tato funkce posílá pět hodnot. Víme, že pátá hodnota je ignorována, protože se nejedná o platný záznam cache, což by způsobilo vrácení, kdyby nebyla zahrnuta. + +```solidity + (_success, _callOutput) = _cacheAddr.call(_callInput); + assertEq(_success, true); + . + . + . + } // testLongCalldata + +} // CacheTest + +``` + +## Ukázková aplikace {#a-sample-app} + +Psaní testů v Solidity je sice skvělé, ale aby byla dapp užitečná, musí být schopna zpracovávat požadavky i mimo blockchain. Tento článek ukazuje, jak používat cachování v dapp s `WORM`, což znamená „Write Once, Read Many“ (Zapiš jednou, čti mnohokrát). Pokud klíč ještě není zapsán, můžete do něj zapsat hodnotu. Pokud je klíč již zapsán, dostanete revert. + +### Kontrakt {#the-contract} + +[Zde je kontrakt](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/WORM.sol). Většinou se opakuje to, co jsme již udělali s `Cache` a `CacheTest`, takže se budeme zabývat pouze zajímavými částmi. + +```solidity +import "./Cache.sol"; + +contract WORM is Cache { +``` + +Nejjednodušší způsob, jak použít `Cache`, je zdědit ho ve vlastním kontraktu. + +```solidity + function writeEntryCached() external { + uint[] memory params = _readParams(2); + writeEntry(params[0], params[1]); + } // writeEntryCached +``` + +Tato funkce je podobná `fourParam` ve výše uvedeném `CacheTest`. Protože se nedržíme specifikací ABI, je nejlepší do funkce nedeklarovat žádné parametry. + +```solidity + // Usnadněte si volání + // Podpis funkce pro writeEntryCached(), s laskavým svolením + // https://www.4byte.directory/signatures/?bytes4_signature=0xe4e4f2d3 + bytes4 constant public WRITE_ENTRY_CACHED = 0xe4e4f2d3; +``` + +Externí kód, který volá `writeEntryCached`, bude muset manuálně sestavit calldata, namísto použití `worm.writeEntryCached`, protože nedodržujeme specifikace ABI. Tato konstantní hodnota pouze usnadňuje její zápis. + +Všimněte si, že i když definujeme `WRITE_ENTRY_CACHED` jako stavovou proměnnou, pro její externí čtení je nutné použít getter funkci, `worm.WRITE_ENTRY_CACHED()`. + +```solidity + function readEntry(uint key) public view + returns (uint _value, address _writtenBy, uint _writtenAtBlock) +``` + +Funkce čtení je `view`, takže nevyžaduje transakci a nestojí žádný gas. V důsledku toho nemá použití cache pro parametr žádný přínos. U funkcí typu view je nejlepší používat standardní mechanismus, který je jednodušší. + +### Testovací kód {#the-testing-code} + +[Zde je testovací kód pro kontrakt](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/WORM.t.sol). Opět se podívejme pouze na to, co je zajímavé. + +```solidity + function testWReadWrite() public { + worm.writeEntry(0xDEAD, 0x60A7); + + vm.expectRevert(bytes("entry already written")); + worm.writeEntry(0xDEAD, 0xBEEF); +``` + +[Tímto (`vm.expectRevert`)](https://book.getfoundry.sh/cheatcodes/expect-revert#expectrevert) v testu Foundry specifikujeme, že další volání by mělo selhat, a uvádíme důvod selhání. To platí, když používáme syntaxi `.()` spíše než vytváření calldata a volání kontraktu pomocí nízkoúrovňového rozhraní (`.call()` atd.). + +```solidity + function testReadWriteCached() public { + uint cacheGoat = worm.cacheWrite(0x60A7); +``` + +Zde využíváme toho, že `cacheWrite` vrací klíč cache. To není něco, co bychom očekávali v produkci, protože `cacheWrite` mění stav, a proto může být volána pouze během transakce. Transakce nemají návratové hodnoty, pokud mají nějaké výsledky, mají být emitovány jako události. Návratová hodnota `cacheWrite` je tedy přístupná pouze z on-chain kódu a on-chain kód nepotřebuje cachování parametrů. + +```solidity + (_success,) = address(worm).call(_callInput); +``` + +Takto říkáme Solidity, že ačkoli `.call()` má dvě návratové hodnoty, zajímá nás pouze ta první. + +```solidity + (_success,) = address(worm).call(_callInput); + assertEq(_success, false); +``` + +Protože používáme nízkoúrovňovou funkci `
.call()`, nemůžeme použít `vm.expectRevert()` a musíme se podívat na booleovskou hodnotu úspěchu, kterou získáme z volání. + +```solidity + event EntryWritten(uint indexed key, uint indexed value); + + . + . + . + + _callInput = bytes.concat( + worm.WRITE_ENTRY_CACHED(), worm.encodeVal(a), worm.encodeVal(b)); + vm.expectEmit(true, true, false, false); + emit EntryWritten(a, b); + (_success,) = address(worm).call(_callInput); +``` + +Toto je způsob, jak ve Foundry ověřit, že kód [správně emituje událost](https://getfoundry.sh/reference/cheatcodes/expect-emit/). + +### Klient {#the-client} + +Jedna věc, kterou se testy v Solidity nezískáte, je JavaScriptový kód, který můžete zkopírovat a vložit do své vlastní aplikace. Abych mohl napsat tento kód, nasadil jsem WORM na [Optimism Goerli](https://community.optimism.io/docs/useful-tools/networks/#optimism-goerli), nový testnet [Optimismu](https://www.optimism.io/). Je na adrese [`0xd34335b1d818cee54e3323d3246bd31d94e6a78a`](https://goerli-optimism.etherscan.io/address/0xd34335b1d818cee54e3323d3246bd31d94e6a78a). + +[Zde si můžete prohlédnout JavaScriptový kód pro klienta](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/javascript/index.js). Použití: + +1. Klonujte git repozitář: + + ```sh + git clone https://github.com/qbzzt/20220915-all-you-can-cache.git + ``` + +2. Nainstalujte potřebné balíčky: + + ```sh + cd javascript + yarn + ``` + +3. Zkopírujte konfigurační soubor: + + ```sh + cp .env.example .env + ``` + +4. Upravte `.env` pro vaši konfiguraci: + + | Parametr | Hodnota | + | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | MNEMONIC | Mnemotechnická pomůcka pro účet, který má dostatek ETH na zaplacení transakce. [Zde můžete získat ETH zdarma pro síť Optimism Goerli](https://optimismfaucet.xyz/). | + | OPTIMISM_GOERLI_URL | URL k Optimism Goerli. Veřejný koncový bod `https://goerli.optimism.io` má omezenou rychlost, ale pro naše potřeby je dostačující. | + +5. Spusťte `index.js`. + + ```sh + node index.js + ``` + + Tato ukázková aplikace nejprve zapíše položku do WORM, zobrazí calldata a odkaz na transakci na Etherscanu. Poté přečte zpět tuto položku a zobrazí klíč, který používá, a hodnoty v položce (hodnota, číslo bloku a autor). + +Většina klienta je normální Dapp JavaScript. Takže opět projdeme jen zajímavé části. + +```javascript +. +. +. +const main = async () => { + const func = await worm.WRITE_ENTRY_CACHED() + + // Pokaždé je potřeba nový klíč + const key = await worm.encodeVal(Number(new Date())) +``` + +Do daného slotu lze zapsat pouze jednou, takže použijeme časové razítko, abychom se ujistili, že sloty nepoužíváme opakovaně. + +```javascript +const val = await worm.encodeVal("0x600D") + +// Zapište položku +const calldata = func + key.slice(2) + val.slice(2) +``` + +Ethers očekává, že data volání budou hexadecimální řetězec, `0x` následovaný sudým počtem hexadecimálních číslic. Protože `key` i `val` začínají na `0x`, musíme tyto hlavičky odstranit. + +```javascript +const tx = await worm.populateTransaction.writeEntryCached() +tx.data = calldata + +sentTx = await wallet.sendTransaction(tx) +``` + +Stejně jako u testovacího kódu Solidity nemůžeme cachovanou funkci volat normálně. Místo toho musíme použít nízkoúrovňový mechanismus. + +```javascript + . + . + . + // Přečtěte právě zapsanou položku + const realKey = '0x' + key.slice(4) // odstraňte příznak FF + const entryRead = await worm.readEntry(realKey) + . + . + . +``` + +Pro čtení položek můžeme použít normální mechanismus. U funkcí `view` není třeba používat cachování parametrů. + +## Závěr {#conclusion} + +Kód v tomto článku je proof of concept, jehož účelem je usnadnit pochopení myšlenky. Pro produkční systém byste mohli chtít implementovat některé další funkce: + +- Zpracování hodnot, které nejsou `uint256`. Například řetězce. +- Místo globální cache možná mít mapování mezi uživateli a cachemi. Různí uživatelé používají různé hodnoty. +- Hodnoty používané pro adresy se liší od hodnot používaných pro jiné účely. Mohlo by mít smysl mít samostatnou cache pouze pro adresy. +- V současné době jsou klíče cache založeny na algoritmu „kdo dřív přijde, ten má nejmenší klíč“. Prvních šestnáct hodnot lze odeslat jako jeden bajt. Dalších 4080 hodnot lze odeslat jako dva bajty. Další přibližně milion hodnot jsou tři bajty atd. Produkční systém by měl vést počítadla použití záznamů cache a reorganizovat je tak, aby šestnáct _nejběžnějších_ hodnot bylo jednobajtových, dalších 4080 nejběžnějších hodnot dvoubajtových atd. + + To je však potenciálně nebezpečná operace. Představte si následující sled událostí: + + 1. Noam Naive zavolá `encodeVal` k zakódování adresy, na kterou chce poslat tokeny. Tato adresa je jedna z prvních použitých v aplikaci, takže zakódovaná hodnota je 0x06. Toto je funkce `view`, ne transakce, takže je to mezi Noamem a uzlem, který používá, a nikdo jiný o tom neví. + + 2. Owen Owner spustí operaci přeuspořádání cache. Velmi málo lidí skutečně používá tuto adresu, takže je nyní zakódována jako 0x201122. Jiná hodnota, 1018, je přiřazena 0x06. + + 3. Noam Naive posílá své tokeny na 0x06. Dostanou se na adresu `0x0000000000000000000000000de0b6b3a7640000`, a protože nikdo nezná soukromý klíč k této adrese, jsou tam prostě zaseknuté. Noam _není spokojený_. + + Existují způsoby, jak tento problém vyřešit, a související problém transakcí, které jsou v mempoolu během přeuspořádání cache, ale musíte si toho být vědomi. + +Cachování jsem zde demonstroval na Optimismu, protože jsem zaměstnancem Optimismu a je to rollup, který znám nejlépe. Mělo by to ale fungovat s jakýmkoli rollupem, který si účtuje minimální náklady na interní zpracování, takže v porovnání s tím je zápis transakčních dat na L1 hlavním nákladem. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). + diff --git a/public/content/translations/cs/developers/tutorials/app-plasma/index.md b/public/content/translations/cs/developers/tutorials/app-plasma/index.md new file mode 100644 index 00000000000..872d0145221 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/app-plasma/index.md @@ -0,0 +1,1255 @@ +--- +title: "Napište plazmu specifickou pro aplikaci, která zachovává soukromí" +description: "V tomto návodu vytvoříme polotajnou banku pro vklady. Banka je centralizovanou součástí; zná zůstatek každého uživatele. Tato informace se však neukládá na blockchainu. Místo toho banka zveřejňuje hash stavu. Pokaždé, když dojde k transakci, banka zveřejní nový hash spolu s důkazem s nulovou znalostí, že má podepsanou transakci, která mění stav hashe na nový. Po přečtení tohoto návodu nejenže pochopíte, jak používat důkazy s nulovou znalostí, ale také proč je používat a jak to dělat bezpečně." +author: Ori Pomerantz +tags: [ "nulová znalost", "server", "offchain", "soukromí" ] +skill: advanced +lang: cs +published: 2025-10-15 +--- + +## Úvod {#introduction} + +Na rozdíl od [rollupů](/developers/docs/scaling/zk-rollups/) používají [plazmy](/developers/docs/scaling/plasma) hlavní síť Ethereum k zajištění integrity, nikoli však dostupnosti. V tomto článku napíšeme aplikaci, která se chová jako plazma, přičemž Ethereum zaručuje integritu (žádné neoprávněné změny), ale nikoli dostupnost (centralizovaná součást může selhat a vyřadit celý systém). + +Aplikace, kterou zde píšeme, je banka zachovávající soukromí. Různé adresy mají účty se zůstatky a mohou posílat peníze (ETH) na jiné účty. Banka zveřejňuje hashe stavu (účty a jejich zůstatky) a transakce, ale skutečné zůstatky drží mimo blockchain, kde mohou zůstat soukromé. + +## Návrh {#design} + +Nejedná se o systém připravený pro produkční nasazení, ale o výukový nástroj. Jako takový je napsán s několika zjednodušujícími předpoklady. + +- Pevně daný fond účtů. Existuje určitý počet účtů a každý účet patří na předem určenou adresu. Díky tomu je systém mnohem jednodušší, protože v důkazech s nulovou znalostí je obtížné pracovat s datovými strukturami s proměnlivou velikostí. Pro systém připravený pro produkční nasazení můžeme použít [Merkle kořen](/developers/tutorials/merkle-proofs-for-offline-data-integrity/) jako hash stavu a poskytnout Merkle důkazy pro požadované zůstatky. + +- Ukládání do paměti. V produkčním systému je třeba zapisovat všechny zůstatky na účtech na disk, aby se zachovaly pro případ restartu. Zde je v pořádku, pokud se informace jednoduše ztratí. + +- Pouze převody. Produkční systém by vyžadoval způsob, jak vkládat prostředky do banky a jak je vybírat. Cílem je zde však pouze ilustrovat koncept, takže tato banka je omezena na převody. + +### Důkazy s nulovou znalostí {#zero-knowledge-proofs} + +Na základní úrovni důkaz s nulovou znalostí ukazuje, že dokazující zná nějaká data, _Datasoukromá_, taková, že existuje vztah _Vztah_ mezi nějakými veřejnými daty, _Dataveřejná_, a _Datasoukromá_. Ověřovatel zná _Vztah_ a _Dataveřejná_. + +Abychom zachovali soukromí, je třeba, aby stavy a transakce byly soukromé. Abychom však zajistili integritu, potřebujeme, aby [kryptografický hash](https://en.wikipedia.org/wiki/Cryptographic_hash_function) stavů byl veřejný. Abychom lidem, kteří odesílají transakce, dokázali, že se tyto transakce skutečně uskutečnily, musíme také zveřejňovat hashe transakcí. + +Ve většině případů je _Datasoukromá_ vstupem do programu důkazu s nulovou znalostí a _Dataveřejná_ je výstupem. + +Tato pole v _Datasoukromá_: + +- _Stavn_, starý stav +- _Stavn+1_, nový stav +- _Transakce_, transakce, která mění starý stav na nový. Tato transakce musí obsahovat tato pole: + - _Cílová adresa_, která přijímá převod + - _Částka_, která se převádí + - _Nonce_, aby se zajistilo, že každá transakce může být zpracována pouze jednou. + Zdrojová adresa nemusí být v transakci, protože ji lze obnovit z podpisu. +- _Podpis_, podpis, který je oprávněn provést transakci. V našem případě je jedinou adresou oprávněnou k provedení transakce zdrojová adresa. Protože náš systém s nulovou znalostí funguje tak, jak funguje, potřebujeme kromě podpisu Ethereum také veřejný klíč účtu. + +Toto jsou pole v _Dataveřejná_: + +- _Hash(Stavn)_ hash starého stavu +- _Hash(Stavn+1)_ hash nového stavu +- _Hash(Transakce)_ hash transakce, která mění stav ze _Stavun_ na _Stavn+1_. + +Vztah kontroluje několik podmínek: + +- Veřejné hashe jsou skutečně správnými hashi pro soukromá pole. +- Transakce, když se aplikuje na starý stav, má za následek nový stav. +- Podpis pochází ze zdrojové adresy transakce. + +Vzhledem k vlastnostem kryptografických hashovacích funkcí stačí prokázat tyto podmínky k zajištění integrity. + +### Datové struktury {#data-structures} + +Primární datovou strukturou je stav, který uchovává server. Pro každý účet server sleduje zůstatek na účtu a [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce), které se používá k zabránění [opakovacím útokům](https://en.wikipedia.org/wiki/Replay_attack). + +### Komponenty {#components} + +Tento systém vyžaduje dvě součásti: + +- _Server_, který přijímá transakce, zpracovává je a zveřejňuje hashe na řetězci spolu s důkazy s nulovou znalostí. +- _Chytrý kontrakt_, který ukládá hashe a ověřuje důkazy s nulovou znalostí, aby se zajistilo, že přechody stavů jsou legitimní. + +### Datový a řídicí tok {#flows} + +Toto jsou způsoby, jakými jednotlivé součásti komunikují při převodu z jednoho účtu na druhý. + +1. Webový prohlížeč odešle podepsanou transakci s žádostí o převod z účtu podepisujícího na jiný účet. + +2. Server ověří, že transakce je platná: + + - Podepisující má v bance účet s dostatečným zůstatkem. + - Příjemce má v bance účet. + +3. Server vypočítá nový stav odečtením převedené částky od zůstatku podepisujícího a jejím přičtením k zůstatku příjemce. + +4. Server vypočítá důkaz s nulovou znalostí, že změna stavu je platná. + +5. Server odešle na Ethereum transakci, která obsahuje: + + - Nový hash stavu + - Hash transakce (aby odesílatel transakce věděl, že byla zpracována) + - Důkaz s nulovou znalostí, který dokazuje, že přechod do nového stavu je platný + +6. Chytrý kontrakt ověří důkaz s nulovou znalostí. + +7. Pokud se důkaz s nulovou znalostí ověří, chytrý kontrakt provede tyto akce: + - Aktualizace současného hashe stavu na nový hash stavu + - Vydá záznam do protokolu s novým hashem stavu a hashem transakce + +### Nástroje {#tools} + +Pro kód na straně klienta použijeme [Vite](https://vite.dev/), [React](https://react.dev/), [Viem](https://viem.sh/) a [Wagmi](https://wagmi.sh/). Jedná se o standardní nástroje v oboru; pokud je neznáte, můžete použít [tento tutoriál](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/). + +Většina serveru je napsána v JavaScriptu pomocí [Node](https://nodejs.org/en). Část s nulovou znalostí je napsána v jazyce [Noir](https://noir-lang.org/). Potřebujeme verzi `1.0.0-beta.10`, takže po [instalaci Noir podle pokynů](https://noir-lang.org/docs/getting_started/quick_start) spusťte: + +``` +noirup -v 1.0.0-beta.10 +``` + +Blockchain, který používáme, je `anvil`, lokální testovací blockchain, který je součástí [Foundry](https://getfoundry.sh/introduction/installation). + +## Implementace {#implementation} + +Protože se jedná o složitý systém, budeme ho implementovat postupně. + +### Fáze 1 – Ruční nulová znalost {#stage-1} + +V první fázi podepíšeme transakci v prohlížeči a poté ručně poskytneme informace do důkazu s nulovou znalostí. Kód nulové znalosti očekává, že tyto informace získá v souboru `server/noir/Prover.toml` (zdokumentováno [zde](https://noir-lang.org/docs/getting_started/project_breakdown#provertoml-1)). + +Chcete-li to vidět v akci: + +1. Ujistěte se, že máte nainstalovaný [Node](https://nodejs.org/en/download) a [Noir](https://noir-lang.org/install). Nejlépe je nainstalujte na systém UNIX, jako je macOS, Linux nebo [WSL](https://learn.microsoft.com/en-us/windows/wsl/install). + +2. Stáhněte si kód 1. fáze a spusťte webový server, který bude obsluhovat kód klienta. + + ```sh + git clone https://github.com/qbzzt/250911-zk-bank.git -b 01-manual-zk + cd 250911-zk-bank + cd client + npm install + npm run dev + ``` + + Důvod, proč zde potřebujete webový server, je ten, že aby se předešlo určitým typům podvodů, mnoho peněženek (například MetaMask) nepřijímá soubory obsluhované přímo z disku. + +3. Otevřete prohlížeč s peněženkou. + +4. V peněžence zadejte novou heslovou frázi. Upozorňujeme, že tímto smažete stávající heslovou frázi, takže _se ujistěte, že máte zálohu_. + + Heslová fráze je `test test test test test test test test test test test junk`, výchozí testovací heslová fráze pro anvil. + +5. Přejděte na [kód na straně klienta](http://localhost:5173/). + +6. Připojte se k peněžence a vyberte cílový účet a částku. + +7. Klikněte na **Podepsat** a podepište transakci. + +8. Pod nadpisem **Prover.toml** najdete text. Nahraďte soubor `server/noir/Prover.toml` tímto textem. + +9. Spusťte důkaz s nulovou znalostí. + + ```sh + cd ../server/noir + nargo execute + ``` + + Výstup by měl být podobný tomuto + + ``` + ori@CryptoDocGuy:~/noir/250911-zk-bank/server/noir$ nargo execute + + [zkBank] Circuit witness successfully solved + [zkBank] Witness saved to target/zkBank.gz + [zkBank] Circuit output: (0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b, 0x0cfc0a67cb7308e4e9b254026b54204e34f6c8b041be207e64c5db77d95dd82d, 0x450cf9da6e180d6159290554ae3d8787, 0x6d8bc5a15b9037e52fb59b6b98722a85) + ``` + +10. Porovnejte poslední dvě hodnoty s hashem, který vidíte ve webovém prohlížeči, abyste zjistili, zda je zpráva správně zahashována. + +#### `server/noir/Prover.toml` {#server-noir-prover-toml} + +[Tento soubor](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml) ukazuje formát informací, který očekává Noir. + +```toml +message="send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 500 finney (milliEth) 0 " +``` + +Zpráva je v textovém formátu, což usnadňuje její pochopení uživatelem (což je nutné při podepisování) a její zpracování kódem Noir. Částka je uvedena ve finney, aby bylo možné na jedné straně provádět zlomkové převody a na druhé straně byla snadno čitelná. Poslední číslo je [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce). + +Řetězec je dlouhý 100 znaků. Důkazy s nulovou znalostí si dobře neporadí s daty s proměnlivou velikostí, proto je často nutné data doplňovat. + +```toml +pubKeyX=["0x83",...,"0x75"] +pubKeyY=["0x35",...,"0xa5"] +signature=["0xb1",...,"0x0d"] +``` + +Tyto tři parametry jsou bajtová pole s pevnou velikostí. + +```toml +[[accounts]] +address="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +balance=100_000 +nonce=0 + +[[accounts]] +address="0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +balance=100_000 +nonce=0 +``` + +Tímto způsobem se specifikuje pole struktur. Pro každou položku zadáme adresu, zůstatek (v milliETH, známé také jako [finney](https://cryptovalleyjournal.com/glossary/finney/)) a další hodnotu nonce. + +#### `client/src/Transfer.tsx` {#client-src-transfer-tsx} + +[Tento soubor](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/client/src/Transfer.tsx) implementuje zpracování na straně klienta a generuje soubor `server/noir/Prover.toml` (ten, který obsahuje parametry nulové znalosti). + +Zde je vysvětlení zajímavějších částí. + +```tsx +export default attrs => { +``` + +Tato funkce vytváří komponentu `Transfer` Reactu, kterou mohou importovat další soubory. + +```tsx + const accounts = [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + "0x90F79bf6EB2c4f870365E785982E1f101E93b906", + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + ] +``` + +Toto jsou adresy účtů, adresy vytvořené pomocí `test ...` heslové fráze `test junk`. Pokud chcete použít vlastní adresy, stačí upravit tuto definici. + +```tsx + const account = useAccount() + const wallet = createWalletClient({ + transport: custom(window.ethereum!) + }) +``` + +Tyto [Wagmi hooky](https://wagmi.sh/react/api/hooks) nám umožňují přístup ke knihovně [viem](https://viem.sh/) a k peněžence. + +```tsx + const message = `send ${toAccount} ${ethAmount*1000} finney (milliEth) ${nonce}`.padEnd(100, " ") +``` + +Toto je zpráva, doplněná mezerami. Pokaždé, když se změní jedna z proměnných [`useState`](https://react.dev/reference/react/useState), komponenta se překreslí a `zpráva` se aktualizuje. + +```tsx + const sign = async () => { +``` + +Tato funkce je volána, když uživatel klikne na tlačítko **Podepsat**. Zpráva se automaticky aktualizuje, ale podpis vyžaduje schválení uživatelem v peněžence a my o něj nechceme žádat, pokud to není nutné. + +```tsx + const signature = await wallet.signMessage({ + account: fromAccount, + message, + }) +``` + +Požádejte peněženku o [podepsání zprávy](https://viem.sh/docs/accounts/local/signMessage). + +```tsx + const hash = hashMessage(message) +``` + +Získejte hash zprávy. Je užitečné poskytnout ho uživateli pro ladění (kódu Noir). + +```tsx + const pubKey = await recoverPublicKey({ + hash, + signature + }) +``` + +[Získejte veřejný klíč](https://viem.sh/docs/utilities/recoverPublicKey). To je nutné pro funkci [Noir `ecrecover`](https://github.com/colinnielsen/ecrecover-noir). + +```tsx + setSignature(signature) + setHash(hash) + setPubKey(pubKey) +``` + +Nastavte stavové proměnné. Tímto se komponenta překreslí (po ukončení funkce `sign`) a uživateli se zobrazí aktualizované hodnoty. + +```tsx + let proverToml = ` +``` + +Text pro `Prover.toml`. + +```tsx +message="${message}" + +pubKeyX=${hexToArray(pubKey.slice(4,4+2*32))} +pubKeyY=${hexToArray(pubKey.slice(4+2*32))} +``` + +Viem nám poskytuje veřejný klíč jako 65bajtový hexadecimální řetězec. První bajt je `0x04`, označení verze. Následuje 32 bajtů pro `x` veřejného klíče a poté 32 bajtů pro `y` veřejného klíče. + +Noir však očekává, že tyto informace získá jako dvě bajtová pole, jedno pro `x` a jedno pro `y`. Je snazší ho analyzovat zde na straně klienta než v rámci důkazu s nulovou znalostí. + +Všimněte si, že se obecně jedná o dobrou praxi v oblasti nulové znalosti. Kód uvnitř důkazu s nulovou znalostí je nákladný, takže jakékoli zpracování, které lze provést mimo důkaz s nulovou znalostí, _by se mělo_ provádět mimo důkaz s nulovou znalostí. + +```tsx +signature=${hexToArray(signature.slice(2,-2))} +``` + +Podpis je také poskytován jako 65bajtový hexadecimální řetězec. Poslední bajt je však nutný pouze k obnovení veřejného klíče. Protože veřejný klíč bude již poskytnut kódu Noir, nepotřebujeme ho k ověření podpisu a kód Noir ho nevyžaduje. + +```tsx +${accounts.map(accountInProverToml).reduce((a,b) => a+b, "")} +` +``` + +Poskytněte účty. + +```tsx + setProverToml(proverToml) + } + + return ( + <> +

Převod

+``` + +Toto je formát HTML (přesněji [JSX](https://react.dev/learn/writing-markup-with-jsx)) komponenty. + +#### `server/noir/src/main.nr` {#server-noir-src-main-nr} + +[Tento soubor](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/src/main.nr) je skutečný kód nulové znalosti. + +``` +use std::hash::pedersen_hash; +``` + +[Pedersen hash](https://rya-sge.github.io/access-denied/2024/05/07/pedersen-hash-function/) je poskytován se [standardní knihovnou Noir](https://noir-lang.org/docs/noir/standard_library/cryptographic_primitives/hashes#pedersen_hash). Důkazy s nulovou znalostí běžně používají tuto hashovací funkci. V [aritmetických obvodech](https://rareskills.io/post/arithmetic-circuit) se vypočítává mnohem snadněji než standardní hashovací funkce. + +``` +use keccak256::keccak256; +use dep::ecrecover; +``` + +Tyto dvě funkce jsou externí knihovny definované v souboru [`Nargo.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Nargo.toml). Jsou přesně tím, po čem jsou pojmenovány: funkcí, která vypočítá [hash keccak256](https://emn178.github.io/online-tools/keccak_256.html), a funkcí, která ověřuje podpisy Ethereum a obnovuje adresu Ethereum podepisujícího. + +``` +global ACCOUNT_NUMBER : u32 = 5; +``` + +Noir je inspirován jazykem [Rust](https://www.rust-lang.org/). Proměnné jsou ve výchozím nastavení konstanty. Takto definujeme globální konfigurační konstanty. Konkrétně `ACCOUNT_NUMBER` je počet účtů, které ukládáme. + +Datové typy s názvem `u<číslo>` mají daný počet bitů a jsou bez znaménka. Jediné podporované typy jsou `u8`, `u16`, `u32`, `u64` a `u128`. + +``` +global FLAT_ACCOUNT_FIELDS : u32 = 2; +``` + +Tato proměnná se používá pro Pedersen hash účtů, jak je vysvětleno níže. + +``` +global MESSAGE_LENGTH : u32 = 100; +``` + +Jak bylo vysvětleno výše, délka zprávy je pevná. Je zde specifikována. + +``` +global ASCII_MESSAGE_LENGTH : [u8; 3] = [0x31, 0x30, 0x30]; +global HASH_BUFFER_SIZE : u32 = 26+3+MESSAGE_LENGTH; +``` + +[Podpisy EIP-191](https://eips.ethereum.org/EIPS/eip-191) vyžadují vyrovnávací paměť s 26bajtovou předponou, za níž následuje délka zprávy v ASCII a nakonec samotná zpráva. + +``` +struct Account { + balance: u128, + address: Field, + nonce: u32, +} +``` + +Informace, které ukládáme o účtu. [`Field`](https://noir-lang.org/docs/noir/concepts/data_types/fields) je číslo, typicky až 253 bitů, které lze použít přímo v [aritmetickém obvodu](https://rareskills.io/post/arithmetic-circuit), který implementuje důkaz s nulovou znalostí. Zde používáme `Field` k uložení 160bitové adresy Ethereum. + +``` +struct TransferTxn { + from: Field, + to: Field, + amount: u128, + nonce: u32 +} +``` + +Informace, které ukládáme pro převodní transakci. + +``` +fn flatten_account(account: Account) -> [Field; FLAT_ACCOUNT_FIELDS] { +``` + +Definice funkce. Parametrem jsou informace o `účtu`. Výsledkem je pole proměnných `Field`, jejichž délka je `FLAT_ACCOUNT_FIELDS`. + +``` + let flat = [ + account.address, + ((account.balance << 32) + account.nonce.into()).into(), + ]; +``` + +První hodnota v poli je adresa účtu. Druhá zahrnuje jak zůstatek, tak nonce. Volání `.into()` změní číslo na datový typ, kterým má být. `account.nonce` je hodnota `u32`, ale aby ji bylo možné přičíst k hodnotě `account.balance << 32`, která je `u128`, musí být `u128`. To je první `.into()`. Druhý převádí výsledek `u128` na `Field`, aby se vešel do pole. + +``` + flat +} +``` + +V jazyce Noir mohou funkce vracet hodnotu pouze na konci (neexistuje předčasné vrácení). Chcete-li zadat návratovou hodnotu, vyhodnotíte ji těsně před uzavírací závorkou funkce. + +``` +fn flatten_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] { +``` + +Tato funkce převede pole účtů na pole `Field`, které lze použít jako vstup do Petersen Hash. + +``` + let mut flat: [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] = [0; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER]; +``` + +Takto se určuje měnitelná proměnná, tj. _ne_ konstanta. Proměnné v Noir musí mít vždy hodnotu, proto tuto proměnnou inicializujeme na samé nuly. + +``` + for i in 0..ACCOUNT_NUMBER { +``` + +Toto je smyčka `for`. Všimněte si, že hranice jsou konstanty. Smyčky Noir musí mít své hranice známé v době kompilace. Důvodem je, že aritmetické obvody nepodporují řízení toku. Při zpracování smyčky `for` kompilátor jednoduše vloží kód dovnitř několikrát, jednou pro každou iteraci. + +``` + let fields = flatten_account(accounts[i]); + for j in 0..FLAT_ACCOUNT_FIELDS { + flat[i*FLAT_ACCOUNT_FIELDS + j] = fields[j]; + } + } + + flat +} + +fn hash_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> Field { + pedersen_hash(flatten_accounts(accounts)) +} +``` + +Nakonec jsme se dostali k funkci, která hashuje pole účtů. + +``` +fn find_account(accounts: [Account; ACCOUNT_NUMBER], address: Field) -> u32 { + let mut account : u32 = ACCOUNT_NUMBER; + + for i in 0..ACCOUNT_NUMBER { + if accounts[i].address == address { + account = i; + } + } +``` + +Tato funkce najde účet se specifickou adresou. Tato funkce by byla ve standardním kódu strašně neefektivní, protože iteruje přes všechny účty, i když už našla adresu. + +V důkazech s nulovou znalostí však neexistuje žádné řízení toku. Pokud někdy potřebujeme zkontrolovat podmínku, musíme ji zkontrolovat pokaždé. + +Podobná věc se děje s příkazy `if`. Příkaz `if` ve smyčce výše je přeložen do těchto matematických příkazů. + +_výsledekpodmínky = účty[i].adresa == adresa_ // jedna, pokud se rovnají, jinak nula + +_účetnový = výsledekpodmínky\*i + (1-výsledekpodmínky)\*účetstarý_ + +```rust + assert (account < ACCOUNT_NUMBER, f"{address} nemá účet"); + + account +} +``` + +Funkce [`assert`](https://noir-lang.org/docs/dev/noir/concepts/assert) způsobí pád důkazu s nulovou znalostí, pokud je tvrzení nepravdivé. V tomto případě, pokud nemůžeme najít účet s příslušnou adresou. K nahlášení adresy použijeme [formátovací řetězec](https://noir-lang.org/docs/noir/concepts/data_types/strings#format-strings). + +```rust +fn apply_transfer_txn(accounts: [Account; ACCOUNT_NUMBER], txn: TransferTxn) -> [Account; ACCOUNT_NUMBER] { +``` + +Tato funkce aplikuje převodní transakci a vrací nové pole účtů. + +```rust + let from = find_account(accounts, txn.from); + let to = find_account(accounts, txn.to); + + let (txnFrom, txnAmount, txnNonce, accountNonce) = + (txn.from, txn.amount, txn.nonce, accounts[from].nonce); +``` + +V Noir nemůžeme přistupovat k prvkům struktury uvnitř formátovacího řetězce, proto si vytvoříme použitelnou kopii. + +```rust + assert (accounts[from].balance >= txn.amount, + f"{txnFrom} nemá {txnAmount} finney"); + + assert (accounts[from].nonce == txn.nonce, + f"Transakce má nonce {txnNonce}, ale očekává se, že účet použije {accountNonce}"); +``` + +Toto jsou dvě podmínky, které by mohly způsobit neplatnost transakce. + +```rust + let mut newAccounts = accounts; + + newAccounts[from].balance -= txn.amount; + newAccounts[from].nonce += 1; + newAccounts[to].balance += txn.amount; + + newAccounts +} +``` + +Vytvořte nové pole účtů a poté ho vraťte. + +```rust +fn readAddress(messageBytes: [u8; MESSAGE_LENGTH]) -> Field +``` + +Tato funkce čte adresu ze zprávy. + +```rust +{ + let mut result : Field = 0; + + for i in 7..47 { +``` + +Adresa má vždy 20 bajtů (tj. 40 hexadecimálních číslic) a začíná na znaku #7. + +```rust + result *= 0x10; + if messageBytes[i] >= 48 & messageBytes[i] <= 57 { // 0-9 + result += (messageBytes[i]-48).into(); + } + if messageBytes[i] >= 65 & messageBytes[i] <= 70 { // A-F + result += (messageBytes[i]-65+10).into() + } + if messageBytes[i] >= 97 & messageBytes[i] <= 102 { // a-f + result += (messageBytes[i]-97+10).into() + } + } + + result +} + +fn readAmountAndNonce(messageBytes: [u8; MESSAGE_LENGTH]) -> (u128, u32) +``` + +Přečtěte částku a nonce ze zprávy. + +```rust +{ + let mut amount : u128 = 0; + let mut nonce: u32 = 0; + let mut stillReadingAmount: bool = true; + let mut lookingForNonce: bool = false; + let mut stillReadingNonce: bool = false; +``` + +Ve zprávě je první číslo za adresou částka finney (tj. tisícina ETH) k převodu. Druhé číslo je nonce. Jakýkoli text mezi nimi je ignorován. + +```rust + for i in 48..MESSAGE_LENGTH { + if messageBytes[i] >= 48 & messageBytes[i] <= 57 { // 0-9 + let digit = (messageBytes[i]-48); + + if stillReadingAmount { + amount = amount*10 + digit.into(); + } + + if lookingForNonce { // Právě jsme to našli + stillReadingNonce = true; + lookingForNonce = false; + } + + if stillReadingNonce { + nonce = nonce*10 + digit.into(); + } + } else { + if stillReadingAmount { + stillReadingAmount = false; + lookingForNonce = true; + } + if stillReadingNonce { + stillReadingNonce = false; + } + } + } + + (amount, nonce) +} +``` + +Vrácení [n-tice](https://noir-lang.org/docs/noir/concepts/data_types/tuples) je v Noir způsob, jak vrátit více hodnot z funkce. + +```rust +fn readTransferTxn(message: str) -> TransferTxn +{ + let mut txn: TransferTxn = TransferTxn { from: 0, to: 0, amount:0, nonce:0 }; + let messageBytes = message.as_bytes(); + + txn.to = readAddress(messageBytes); + let (amount, nonce) = readAmountAndNonce(messageBytes); + txn.amount = amount; + txn.nonce = nonce; + + txn +} +``` + +Tato funkce převede zprávu na bajty a poté převede částky na `TransferTxn`. + +```rust +// Ekvivalent hashMessage od Viem +// https://viem.sh/docs/utilities/hashMessage#hashmessage +fn hashMessage(message: str) -> [u8;32] { +``` + +Pedersen Hash jsme mohli použít pro účty, protože se hashují pouze v rámci důkazu s nulovou znalostí. V tomto kódu však musíme zkontrolovat podpis zprávy, který je generován prohlížečem. K tomu je třeba dodržet formát podepisování Ethereum v [EIP 191](https://eips.ethereum.org/EIPS/eip-191). To znamená, že musíme vytvořit kombinovanou vyrovnávací paměť se standardní předponou, délkou zprávy v ASCII a samotnou zprávou a k jejímu hashování použít standardní keccak256 z Etherea. + +```rust + // Předpona ASCII + let prefix_bytes = [ + 0x19, // \x19 + 0x45, // 'E' + 0x74, // 't' + 0x68, // 'h' + 0x65, // 'e' + 0x72, // 'r' + 0x65, // 'e' + 0x75, // 'u' + 0x6D, // 'm' + 0x20, // ' ' + 0x53, // 'S' + 0x69, // 'i' + 0x67, // 'g' + 0x6E, // 'n' + 0x65, // 'e' + 0x64, // 'd' + 0x20, // ' ' + 0x4D, // 'M' + 0x65, // 'e' + 0x73, // 's' + 0x73, // 's' + 0x61, // 'a' + 0x67, // 'g' + 0x65, // 'e' + 0x3A, // ':' + 0x0A // '\n' + ]; +``` + +Aby se předešlo případům, kdy aplikace požádá uživatele o podepsání zprávy, kterou lze použít jako transakci nebo pro jiný účel, EIP 191 stanoví, že všechny podepsané zprávy začínají znakem 0x19 (není to platný znak ASCII), za nímž následuje `Ethereum Signed Message:` a nový řádek. + +```rust + let mut buffer: [u8; HASH_BUFFER_SIZE] = [0u8; HASH_BUFFER_SIZE]; + for i in 0..26 { + buffer[i] = prefix_bytes[i]; + } + + let messageBytes : [u8; MESSAGE_LENGTH] = message.as_bytes(); + + if MESSAGE_LENGTH <= 9 { + for i in 0..1 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+1] = messageBytes[i]; + } + } + + if MESSAGE_LENGTH >= 10 & MESSAGE_LENGTH <= 99 { + for i in 0..2 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+2] = messageBytes[i]; + } + } + + if MESSAGE_LENGTH >= 100 { + for i in 0..3 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+3] = messageBytes[i]; + } + } + + assert(MESSAGE_LENGTH < 1000, "Zprávy s délkou přes tři číslice nejsou podporovány"); +``` + +Zpracujte délky zpráv až do 999 a selžete, pokud je větší. Tento kód jsem přidal, i když délka zprávy je konstanta, protože to usnadňuje její změnu. V produkčním systému byste pravděpodobně předpokládali, že se `MESSAGE_LENGTH` nemění kvůli lepšímu výkonu. + +```rust + keccak256::keccak256(buffer, HASH_BUFFER_SIZE) +} +``` + +Použijte standardní funkci Ethereum `keccak256`. + +```rust +fn signatureToAddressAndHash( + message: str, + pubKeyX: [u8; 32], + pubKeyY: [u8; 32], + signature: [u8; 64] + ) -> (Field, Field, Field) // adresa, prvních 16 bajtů hashe, posledních 16 bajtů hashe +{ +``` + +Tato funkce ověřuje podpis, což vyžaduje hash zprávy. Poté nám poskytne adresu, která jej podepsala, a hash zprávy. Hash zprávy je dodáván ve dvou hodnotách `Field`, protože se s nimi ve zbytku programu snadněji pracuje než s bajtovým polem. + +Musíme použít dvě hodnoty `Field`, protože výpočty pole se provádějí [modulo](https://en.wikipedia.org/wiki/Modulo) velkého čísla, ale toto číslo je obvykle menší než 256 bitů (jinak by bylo obtížné provádět tyto výpočty v EVM). + +```rust + let hash = hashMessage(message); + + let mut (hash1, hash2) = (0,0); + + for i in 0..16 { + hash1 = hash1*256 + hash[31-i].into(); + hash2 = hash2*256 + hash[15-i].into(); + } +``` + +Určete `hash1` a `hash2` jako měnitelné proměnné a zapište do nich hash bajt po bajtu. + +```rust + ( + ecrecover::ecrecover(pubKeyX, pubKeyY, signature, hash), +``` + +Je to podobné jako u [`ecrecover` v Solidity](https://docs.soliditylang.org/en/v0.8.30/cheatsheet.html#mathematical-and-cryptographic-functions), se dvěma důležitými rozdíly: + +- Pokud podpis není platný, volání selže s `assert` a program se přeruší. +- Zatímco veřejný klíč lze obnovit z podpisu a hashe, jedná se o zpracování, které lze provést externě, a proto se nevyplatí ho provádět v rámci důkazu s nulovou znalostí. Pokud se nás zde někdo pokusí podvést, ověření podpisu se nezdaří. + +```rust + hash1, + hash2 + ) +} + +fn main( + accounts: [Account; ACCOUNT_NUMBER], + message: str, + pubKeyX: [u8; 32], + pubKeyY: [u8; 32], + signature: [u8; 64], + ) -> pub ( + Field, // Hash pole starých účtů + Field, // Hash pole nových účtů + Field, // Prvních 16 bajtů hashe zprávy + Field, // Posledních 16 bajtů hashe zprávy + ) +``` + +Nakonec se dostáváme k funkci `main`. Musíme dokázat, že máme transakci, která platně mění hash účtů ze staré hodnoty na novou. Také musíme dokázat, že má tento specifický hash transakce, aby osoba, která ji odeslala, věděla, že její transakce byla zpracována. + +```rust +{ + let mut txn = readTransferTxn(message); +``` + +Potřebujeme, aby `txn` byla měnitelná, protože adresu odesílatele nečteme ze zprávy, ale z podpisu. + +```rust + let (fromAddress, txnHash1, txnHash2) = signatureToAddressAndHash( + message, + pubKeyX, + pubKeyY, + signature); + + txn.from = fromAddress; + + let newAccounts = apply_transfer_txn(accounts, txn); + + ( + hash_accounts(accounts), + hash_accounts(newAccounts), + txnHash1, + txnHash2 + ) +} +``` + +### Fáze 2 – Přidání serveru {#stage-2} + +Ve druhé fázi přidáme server, který přijímá a implementuje převodní transakce z prohlížeče. + +Chcete-li to vidět v akci: + +1. Zastavte Vite, pokud běží. + +2. Stáhněte si větev, která obsahuje server, a ujistěte se, že máte všechny potřebné moduly. + + ```sh + git checkout 02-add-server + cd client + npm install + cd ../server + npm install + ``` + + Není třeba kompilovat kód Noir, je to stejný kód, který jste použili pro 1. fázi. + +3. Spusťte server. + + ```sh + npm run start + ``` + +4. V samostatném okně příkazového řádku spusťte Vite, abyste mohli obsluhovat kód prohlížeče. + + ```sh + cd client + npm run dev + ``` + +5. Přejděte na klientský kód na adrese [http://localhost:5173](http://localhost:5173) + +6. Než budete moci vydat transakci, musíte znát nonce a také částku, kterou můžete odeslat. Chcete-li získat tyto informace, klikněte na **Aktualizovat údaje o účtu** a podepište zprávu. + + Máme zde dilema. Na jedné straně nechceme podepisovat zprávu, kterou lze znovu použít ([opakovací útok](https://en.wikipedia.org/wiki/Replay_attack)), což je důvod, proč chceme mít nonce. Nicméně ještě nemáme nonce. Řešením je zvolit nonce, které lze použít pouze jednou a které již máme na obou stranách, například aktuální čas. + + Problém s tímto řešením je, že čas nemusí být dokonale synchronizován. Takže místo toho podepíšeme hodnotu, která se mění každou minutu. To znamená, že naše okno zranitelnosti vůči opakovacím útokům je maximálně jedna minuta. Vzhledem k tomu, že v produkci bude podepsaný požadavek chráněn protokolem TLS a že druhá strana tunelu – server – již může sdělit zůstatek a nonce (musí je znát, aby mohl fungovat), jedná se o přijatelné riziko. + +7. Jakmile prohlížeč získá zpět zůstatek a nonce, zobrazí formulář pro převod. Vyberte cílovou adresu a částku a klikněte na **Převod**. Podepište tento požadavek. + +8. Chcete-li zobrazit převod, buď **Aktualizujte údaje o účtu**, nebo se podívejte do okna, kde spouštíte server. Server protokoluje stav při každé změně. + + ``` + ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start + + > server@1.0.0 start + > node --experimental-json-modules index.mjs + + Listening on port 3000 + Txn send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 36000 finney (milliEth) 0 processed + New state: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 64000 (1) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 100000 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 136000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0) + Txn send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 7200 finney (milliEth) 1 processed + New state: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 56800 (2) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 107200 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 136000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0) + Txn send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 3000 finney (milliEth) 2 processed + New state: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 53800 (3) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 107200 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 139000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0) + ``` + +#### `server/index.mjs` {#server-index-mjs-1} + +[Tento soubor](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/index.mjs) obsahuje proces serveru a interaguje s kódem Noir na [`main.nr`](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/noir/src/main.nr). Zde je vysvětlení zajímavých částí. + +```js +import { Noir } from '@noir-lang/noir_js' +``` + +Knihovna [noir.js](https://www.npmjs.com/package/@noir-lang/noir_js) propojuje kód JavaScriptu a kód Noir. + +```js +const circuit = JSON.parse(await fs.readFile("./noir/target/zkBank.json")) +const noir = new Noir(circuit) +``` + +Načtěte aritmetický obvod – kompilovaný program Noir, který jsme vytvořili v předchozí fázi – a připravte se na jeho spuštění. + +```js +// Informace o účtu poskytujeme pouze v odpovědi na podepsaný požadavek +const accountInformation = async signature => { + const fromAddress = await recoverAddress({ + hash: hashMessage("Získat data účtu " + Math.floor((new Date().getTime())/60000)), + signature + }) +``` + +Pro poskytnutí informací o účtu potřebujeme pouze podpis. Důvodem je, že již víme, jaká bude zpráva, a tedy i hash zprávy. + +```js +const processMessage = async (message, signature) => { +``` + +Zpracujte zprávu a proveďte transakci, kterou kóduje. + +```js + // Získat veřejný klíč + const pubKey = await recoverPublicKey({ + hash, + signature + }) +``` + +Nyní, když spouštíme JavaScript na serveru, můžeme získat veřejný klíč tam, spíše než na klientovi. + +```js + let noirResult + try { + noirResult = await noir.execute({ + message, + signature: signature.slice(2,-2).match(/.{2}/g).map(x => `0x${x}`), + pubKeyX, + pubKeyY, + accounts: Accounts + }) +``` + +`noir.execute` spouští program Noir. Parametry jsou ekvivalentní těm, které jsou uvedeny v souboru [`Prover.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml). Všimněte si, že dlouhé hodnoty jsou poskytovány jako pole hexadecimálních řetězců (`["0x60", "0xA7"]`), nikoli jako jediná hexadecimální hodnota (`0x60A7`), jak to dělá Viem. + +```js + } catch (err) { + console.log(`Chyba Noir: ${err}`) + throw Error("Neplatná transakce, nebyla zpracována") + } +``` + +Pokud dojde k chybě, zachyťte ji a poté předejte zjednodušenou verzi klientovi. + +```js + Accounts[fromAccountNumber].nonce++ + Accounts[fromAccountNumber].balance -= amount + Accounts[toAccountNumber].balance += amount +``` + +Proveďte transakci. Už jsme to udělali v kódu Noir, ale je snazší to udělat znovu zde, než extrahovat výsledek odtamtud. + +```js +let Accounts = [ + { + address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + balance: 5000, + nonce: 0, + }, +``` + +Počáteční struktura `účtů`. + +### Fáze 3 – Chytré kontrakty Ethereum {#stage-3} + +1. Zastavte procesy serveru a klienta. + +2. Stáhněte si větev s chytrými kontrakty a ujistěte se, že máte všechny potřebné moduly. + + ```sh + git checkout 03-smart-contracts + cd client + npm install + cd ../server + npm install + ``` + +3. Spusťte `anvil` v samostatném okně příkazového řádku. + +4. Vygenerujte ověřovací klíč a ověřovač Solidity, poté zkopírujte kód ověřovače do projektu Solidity. + + ```sh + cd noir + bb write_vk -b ./target/zkBank.json -o ./target --oracle_hash keccak + bb write_solidity_verifier -k ./target/vk -o ./target/Verifier.sol + cp target/Verifier.sol ../../smart-contracts/src + ``` + +5. Přejděte na chytré kontrakty a nastavte proměnné prostředí pro použití blockchainu `anvil`. + + ```sh + cd ../../smart-contracts + export ETH_RPC_URL=http://localhost:8545 + ETH_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + ``` + +6. Nasaďte `Verifier.sol` a uložte adresu do proměnné prostředí. + + ```sh + VERIFIER_ADDRESS=`forge create src/Verifier.sol:HonkVerifier --private-key $ETH_PRIVATE_KEY --optimize --broadcast | awk '/Deployed to:/ {print $3}'` + echo $VERIFIER_ADDRESS + ``` + +7. Nasaďte kontrakt `ZkBank`. + + ```sh + ZKBANK_ADDRESS=`forge create ZkBank --private-key $ETH_PRIVATE_KEY --broadcast --constructor-args $VERIFIER_ADDRESS 0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b | awk '/Deployed to:/ {print $3}'` + echo $ZKBANK_ADDRESS + ``` + + Hodnota `0x199..67b` je Pederson hash počátečního stavu `Účtů`. Pokud tento počáteční stav v `server/index.mjs` upravíte, můžete spustit transakci a zobrazit počáteční hash hlášený důkazem s nulovou znalostí. + +8. Spusťte server. + + ```sh + cd ../server + npm run start + ``` + +9. Spusťte klienta v jiném okně příkazového řádku. + + ```sh + cd client + npm run dev + ``` + +10. Spusťte nějaké transakce. + +11. Chcete-li ověřit, že se stav změnil na blockchainu, restartujte proces serveru. Podívejte se, že `ZkBank` již nepřijímá transakce, protože původní hodnota hashe v transakcích se liší od hodnoty hashe uložené na blockchainu. + + Toto je typ očekávané chyby. + + ``` + ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start + + > server@1.0.0 start + > node --experimental-json-modules index.mjs + + Listening on port 3000 + Verification error: ContractFunctionExecutionError: The contract function "processTransaction" reverted with the following reason: + Wrong old state hash + + Contract Call: + address: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + function: processTransaction(bytes _proof, bytes32[] _publicInputs) + args: (0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf00000000000000000000000000000000000000000000000b75c020998797da7800000000000000000000000000000000000000000000000) + ``` + +#### `server/index.mjs` {#server-index-mjs-2} + +Změny v tomto souboru se týkají především vytvoření skutečného důkazu a jeho odeslání na blockchain. + +```js +import { exec } from 'child_process' +import util from 'util' + +const execPromise = util.promisify(exec) +``` + +Musíme použít [balíček Barretenberg](https://github.com/AztecProtocol/aztec-packages/tree/next/barretenberg) k vytvoření skutečného důkazu k odeslání na blockchain. Tento balíček můžeme použít buď spuštěním rozhraní příkazového řádku (`bb`), nebo použitím [knihovny JavaScript, `bb.js`](https://www.npmjs.com/package/@aztec/bb.js). Knihovna JavaScript je mnohem pomalejší než nativní spouštění kódu, takže zde používáme [`exec`](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback) pro použití příkazového řádku. + +Všimněte si, že pokud se rozhodnete použít `bb.js`, musíte použít verzi, která je kompatibilní s verzí Noir, kterou používáte. V době psaní tohoto článku aktuální verze Noir (1.0.0-beta.11) používá `bb.js` verze 0.87. + +```js +const zkBankAddress = process.env.ZKBANK_ADDRESS || "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" +``` + +Zde uvedená adresa je ta, kterou získáte, když začnete s čistým `anvilem` a budete postupovat podle výše uvedených pokynů. + +```js +const walletClient = createWalletClient({ + chain: anvil, + transport: http(), + account: privateKeyToAccount("0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6") +}) +``` + +Tento privátní klíč je jedním z výchozích předem financovaných účtů v `anvil`. + +```js +const generateProof = async (witness, fileID) => { +``` + +Vygenerujte důkaz pomocí spustitelného souboru `bb`. + +```js + const fname = `witness-${fileID}.gz` + await fs.writeFile(fname, witness) +``` + +Zapište svědka do souboru. + +```js + await execPromise(`bb prove -b ./noir/target/zkBank.json -w ${fname} -o ${fileID} --oracle_hash keccak --output_format fields`) +``` + +Vytvořte důkaz. Tento krok také vytvoří soubor s veřejnými proměnnými, ale ten nepotřebujeme. Tyto proměnné jsme již získali z `noir.execute`. + +```js + const proof = "0x" + JSON.parse(await fs.readFile(`./${fileID}/proof_fields.json`)).reduce((a,b) => a+b, "").replace(/0x/g, "") +``` + +Důkaz je pole JSON hodnot `Field`, z nichž každá je reprezentována jako hexadecimální hodnota. Musíme ho však odeslat v transakci jako jedinou hodnotu `bytes`, kterou Viem reprezentuje velkým hexadecimálním řetězcem. Zde měníme formát zřetězením všech hodnot, odstraněním všech `0x` a následným přidáním jednoho na konec. + +```js + await execPromise(`rm -r ${fname} ${fileID}`) + + return proof +} +``` + +Vyčistěte a vraťte důkaz. + +```js +const processMessage = async (message, signature) => { + . + . + . + + const publicFields = noirResult.returnValue.map(x=>'0x' + x.slice(2).padStart(64, "0")) +``` + +Veřejná pole musí být pole 32bajtových hodnot. Jelikož jsme však potřebovali rozdělit hash transakce mezi dvě hodnoty `Field`, zobrazuje se jako 16bajtová hodnota. Zde přidáváme nuly, aby Viem pochopil, že se jedná o 32 bajtů. + +```js + const proof = await generateProof(noirResult.witness, `${fromAddress}-${nonce}`) +``` + +Každá adresa používá každou nonce pouze jednou, takže můžeme použít kombinaci `fromAddress` a `nonce` jako jedinečný identifikátor pro soubor svědka a výstupní adresář. + +```js + try { + await zkBank.write.processTransaction([ + proof, publicFields]) + } catch (err) { + console.log(`Chyba ověření: ${err}`) + throw Error("Transakci nelze ověřit na blockchainu") + } + . + . + . +} +``` + +Odešlete transakci do řetězce. + +#### `smart-contracts/src/ZkBank.sol` {#smart-contracts-src-zkbank-sol} + +Toto je onchain kód, který přijímá transakci. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.21; + +import {HonkVerifier} from "./Verifier.sol"; + +contract ZkBank { + HonkVerifier immutable myVerifier; + bytes32 currentStateHash; + + constructor(address _verifierAddress, bytes32 _initialStateHash) { + currentStateHash = _initialStateHash; + myVerifier = HonkVerifier(_verifierAddress); + } +``` + +Kód na blockchainu musí sledovat dvě proměnné: ověřovač (samostatný kontrakt vytvořený `nargem`) a aktuální hash stavu. + +```solidity + event TransactionProcessed( + bytes32 indexed transactionHash, + bytes32 oldStateHash, + bytes32 newStateHash + ); +``` + +Pokaždé, když se stav změní, vydáme událost `TransactionProcessed`. + +```solidity + function processTransaction( + bytes calldata _proof, + bytes32[] calldata _publicFields + ) public { +``` + +Tato funkce zpracovává transakce. Získá důkaz (jako `bajty`) a veřejné vstupy (jako pole `bytes32`) ve formátu, který ověřovatel vyžaduje (aby se minimalizovalo zpracování na blockchainu a tím i náklady na gas). + +```solidity + require(_publicInputs[0] == currentStateHash, + "Špatný starý hash stavu"); +``` + +Důkaz s nulovou znalostí musí být o tom, že transakce se mění z našeho současného hashe na nový. + +```solidity + myVerifier.verify(_proof, _publicFields); +``` + +Zavolejte kontrakt ověřovače, abyste ověřili důkaz s nulovou znalostí. Tento krok vrátí transakci, pokud je důkaz s nulovou znalostí nesprávný. + +```solidity + currentStateHash = _publicFields[1]; + + emit TransactionProcessed( + _publicFields[2]<<128 | _publicFields[3], + _publicFields[0], + _publicFields[1] + ); + } +} +``` + +Pokud je vše v pořádku, aktualizujte hash stavu na novou hodnotu a vydejte událost `TransactionProcessed`. + +## Zneužití centralizovanou součástí {#abuses} + +Informační bezpečnost se skládá ze tří atributů: + +- _Důvěrnost_, uživatelé nemohou číst informace, ke kterým nejsou oprávněni. +- _Integrita_, informace nemohou být měněny jinak než oprávněnými uživateli oprávněným způsobem. +- _Dostupnost_, oprávnění uživatelé mohou systém používat. + +V tomto systému je integrita zajištěna prostřednictvím důkazů s nulovou znalostí. Dostupnost je mnohem obtížnější zaručit a důvěrnost je nemožná, protože banka musí znát zůstatek každého účtu a všechny transakce. Neexistuje způsob, jak zabránit entitě, která má informace, v jejich sdílení. + +Možná by bylo možné vytvořit skutečně důvěrnou banku pomocí [neviditelných adres](https://vitalik.eth.limo/general/2023/01/20/stealth.html), ale to je nad rámec tohoto článku. + +### Nepravdivé informace {#false-info} + +Jedním ze způsobů, jak může server porušit integritu, je poskytnutí nepravdivých informací, když [jsou požadována data](https://github.com/qbzzt/250911-zk-bank/blob/03-smart-contracts/server/index.mjs#L278-L291). + +K vyřešení tohoto problému můžeme napsat druhý program Noir, který přijímá účty jako soukromý vstup a adresu, pro kterou jsou informace požadovány, jako veřejný vstup. Výstupem je zůstatek a nonce této adresy a hash účtů. + +Tento důkaz samozřejmě nelze ověřit na blockchainu, protože nechceme zveřejňovat nonce a zůstatky na blockchainu. Může však být ověřen klientským kódem spuštěným v prohlížeči. + +### Vynucené transakce {#forced-txns} + +Obvyklým mechanismem pro zajištění dostupnosti a prevenci cenzury na L2 jsou [vynucené transakce](https://docs.optimism.io/stack/transactions/forced-transaction). Ale vynucené transakce se nekombinují s důkazy s nulovou znalostí. Server je jedinou entitou, která může ověřovat transakce. + +Můžeme upravit `smart-contracts/src/ZkBank.sol` tak, aby přijímal vynucené transakce a zabránil serveru měnit stav, dokud nebudou zpracovány. To nás však vystavuje jednoduchému útoku typu denial-of-service. Co když je vynucená transakce neplatná, a proto ji nelze zpracovat? + +Řešením je mít důkaz s nulovou znalostí, že vynucená transakce je neplatná. To dává serveru tři možnosti: + +- Zpracovat vynucenou transakci a poskytnout důkaz s nulovou znalostí, že byla zpracována, a nový hash stavu. +- Odmítnout vynucenou transakci a poskytnout kontraktu důkaz s nulovou znalostí, že transakce je neplatná (neznámá adresa, špatné nonce nebo nedostatečný zůstatek). +- Ignorovat vynucenou transakci. Neexistuje způsob, jak donutit server, aby transakci skutečně zpracoval, ale znamená to, že celý systém je nedostupný. + +#### Dluhopisy dostupnosti {#avail-bonds} + +V reálné implementaci by pravděpodobně existoval nějaký druh motivace k zisku pro udržení serveru v provozu. Tuto pobídku můžeme posílit tím, že server zveřejní dluhopis dostupnosti, který může kdokoli spálit, pokud vynucená transakce není zpracována v určitém období. + +### Špatný kód Noir {#bad-noir-code} + +Normálně, aby lidé důvěřovali chytrému kontraktu, nahrajeme zdrojový kód do [prohlížeče bloků](https://eth.blockscout.com/address/0x7D16d2c4e96BCFC8f815E15b771aC847EcbDB48b?tab=contract). V případě důkazů s nulovou znalostí to však nestačí. + +`Verifier.sol` obsahuje ověřovací klíč, který je funkcí programu Noir. Tento klíč nám však neříká, jaký byl program Noir. Chcete-li mít skutečně důvěryhodné řešení, musíte nahrát program Noir (a verzi, která ho vytvořila). V opačném případě by důkazy s nulovou znalostí mohly odrážet jiný program, program se zadními vrátky. + +Dokud nám prohlížeče bloků neumožní nahrávat a ověřovat programy Noir, měli byste to dělat sami (nejlépe na [IPFS](/developers/tutorials/ipfs-decentralized-ui/)). Poté budou moci zkušení uživatelé stáhnout zdrojový kód, sami ho zkompilovat, vytvořit `Verifier.sol` a ověřit, že je identický s tím na blockchainu. + +## Závěr {#conclusion} + +Aplikace typu Plasma vyžadují centralizovanou komponentu jako úložiště informací. To otevírá potenciální zranitelnosti, ale na oplátku nám to umožňuje zachovat soukromí způsoby, které na samotném blockchainu nejsou dostupné. S důkazy s nulovou znalostí můžeme zajistit integritu a případně učinit ekonomicky výhodným, aby kdokoli, kdo provozuje centralizovanou komponentu, udržoval dostupnost. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). + +## Poděkování {#acknowledgements} + +- Josh Crites si přečetl návrh tohoto článku a pomohl mi s ošemetným problémem Noir. + +Za zbývající chyby jsem zodpovědný já. diff --git a/public/content/translations/cs/developers/tutorials/calling-a-smart-contract-from-javascript/index.md b/public/content/translations/cs/developers/tutorials/calling-a-smart-contract-from-javascript/index.md new file mode 100644 index 00000000000..3aaf6cbc2da --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/calling-a-smart-contract-from-javascript/index.md @@ -0,0 +1,131 @@ +--- +title: "Volání chytrého kontraktu z JavaScriptu" +description: "Jak volat funkci chytrého kontraktu z JavaScriptu na příkladu tokenu Dai" +author: jdourlens +tags: [ "transakce", "frontend", "JavaScript", "web3.js" ] +skill: beginner +lang: cs +published: 2020-04-19 +source: EthereumDev +sourceUrl: https://ethereumdev.io/calling-a-smart-contract-from-javascript/ +address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +--- + +V tomto návodu si ukážeme, jak zavolat funkci [chytrého kontraktu](/developers/docs/smart-contracts/) z JavaScriptu. Nejprve si přečteme stav chytrého kontraktu (např. zůstatek držitele ERC20), poté změníme stav blockchainu provedením převodu tokenu. Měli byste již být obeznámeni s [nastavením prostředí JS pro interakci s blockchainem](/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/). + +V tomto příkladu budeme pracovat s tokenem DAI. Pro účely testování vytvoříme větev blockchainu pomocí ganache-cli a odemkneme adresu, která již má hodně DAI: + +```bash +ganache-cli -f https://mainnet.infura.io/v3/[VÁŠ INFURA KLÍČ] -d -i 66 1 --unlock 0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81 +``` + +Pro interakci s chytrým kontraktem budeme potřebovat jeho adresu a ABI: + +```js +const ERC20TransferABI = [ + { + constant: false, + inputs: [ + { + name: "_to", + type: "address", + }, + { + name: "_value", + type: "uint256", + }, + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool", + }, + ], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address", + }, + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, +] + +const DAI_ADDRESS = "0x6b175474e89094c44da98b954eedeac495271d0f" +``` + +Pro tento projekt jsme z kompletního ERC20 ABI ponechali pouze funkce `balanceOf` a `transfer`, ale [celé ERC20 ABI naleznete zde](https://ethereumdev.io/abi-for-erc20-contract-on-ethereum/). + +Poté musíme náš chytrý kontrakt instanciovat: + +```js +const web3 = new Web3("http://localhost:8545") + +const daiToken = new web3.eth.Contract(ERC20TransferABI, DAI_ADDRESS) +``` + +Také si nastavíme dvě adresy: + +- tu, která obdrží převod, a +- tu, kterou jsme již odemkli a která ho odešle: + +```js +const senderAddress = "0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81" +const receiverAddress = "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +``` + +V další části zavoláme funkci `balanceOf`, abychom zjistili aktuální množství tokenů, které obě adresy drží. + +## Volání: Čtení hodnoty z chytrého kontraktu {#call-reading-value-from-a-smart-contract} + +První příklad zavolá „konstantní“ metodu a spustí metodu chytrého kontraktu v EVM bez odeslání jakékoli transakce. Za tímto účelem si přečteme zůstatek ERC20 na adrese. [Přečtěte si náš článek o ERC20 tokenech](/developers/tutorials/understand-the-erc-20-token-smart-contract/). + +K metodám instanciovaného chytrého kontraktu, pro který jste poskytli ABI, můžete přistupovat následovně: `yourContract.methods.methodname`. Použitím funkce `call` obdržíte výsledek jejího spuštění. + +```js +daiToken.methods.balanceOf(senderAddress).call(function (err, res) { + if (err) { + console.log("Došlo k chybě", err) + return + } + console.log("Zůstatek je: ", res) +}) +``` + +Pamatujte, že DAI ERC20 má 18 desetinných míst, což znamená, že pro získání správné částky musíte odstranit 18 nul. Hodnoty uint256 se vrací jako řetězce, protože JavaScript nezvládá velká číselné hodnoty. Pokud si nejste jisti, [jak zacházet s velkými čísly v JS, podívejte se na náš návod o bignumber.js](https://ethereumdev.io/how-to-deal-with-big-numbers-in-javascript/). + +## Odeslání: Odeslání transakce do funkce chytrého kontraktu {#send-sending-a-transaction-to-a-smart-contract-function} + +Ve druhém příkladu zavoláme funkci `transfer` chytrého kontraktu DAI, abychom odeslali 10 DAI na naši druhou adresu. Funkce `transfer` přijímá dva parametry: adresu příjemce a množství tokenů k převodu: + +```js +daiToken.methods + .transfer(receiverAddress, "100000000000000000000") + .send({ from: senderAddress }, function (err, res) { + if (err) { + console.log("Došlo k chybě", err) + return + } + console.log("Haš transakce: " + res) + }) +``` + +Volání funkce vrátí haš transakce, která bude vytěžena v blockchainu. Na Ethereu jsou haše transakcí předvídatelné - tak můžeme získat haš transakce ještě před jejím spuštěním ([zde se dozvíte, jak se haše počítají](https://ethereum.stackexchange.com/questions/45648/how-to-calculate-the-assigned-txhash-of-a-transaction)). + +Protože funkce pouze odešle transakci do blockchainu, neuvidíme výsledek, dokud nebudeme vědět, kdy bude vytěžena a zahrnuta do blockchainu. V příštím návodu se dozvíme, [jak počkat na provedení transakce na blockchainu, když známe její haš](https://ethereumdev.io/waiting-for-a-transaction-to-be-mined-on-ethereum-with-js/). diff --git a/public/content/translations/cs/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md b/public/content/translations/cs/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md new file mode 100644 index 00000000000..626b2703fbd --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md @@ -0,0 +1,585 @@ +--- +title: "Vytvoření uživatelského rozhraní pro váš kontrakt" +description: "Pomocí moderních komponent, jako jsou TypeScript, React, Vite a Wagmi, si projdeme moderní, ale minimální, uživatelské rozhraní a naučíme se, jak k němu připojit peněženku, jak volat chytrý kontrakt pro čtení informací, jak poslat transakci na chytrý kontrakt a jak sledovat události z chytrého kontraktu a identifikovat změny." +author: Ori Pomerantz +tags: [ "typescript", "react", "vite", "wagmi", "frontend" ] +skill: beginner +published: 2023-11-01 +lang: cs +sidebarDepth: 3 +--- + +Našli jste funkci, kterou v ekosystému Etherea potřebujeme. Napsali jste chytré kontrakty k jeho implementaci a možná i nějaký související kód, který běží offchain. To je skvělé! Bohužel bez uživatelského rozhraní nebudete mít žádné uživatele a naposledy, když jste psali webové stránky, lidé používali dial-up modemy a JavaScript byl novinkou. + +Tento článek je pro vás. Předpokládám, že umíte programovat a možná trochu JavaScript a HTML, ale vaše dovednosti v oblasti uživatelského rozhraní jsou zrezivělé a zastaralé. Společně si projdeme jednoduchou moderní aplikaci, abyste viděli, jak se to dnes dělá. + +## Proč je to důležité {#why-important} + +Teoreticky byste mohli nechat lidi používat [Etherscan](https://holesky.etherscan.io/address/0x432d810484add7454ddb3b5311f0ac2e95cecea8#writeContract) nebo [Blockscout](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=write_contract) k interakci s vašimi kontrakty. To bude skvělé pro zkušené příznivce Etherea. My se ale snažíme sloužit [další miliardě lidí](https://blog.ethereum.org/2021/05/07/ethereum-for-the-next-billion). To se nestane bez skvělého uživatelského zážitku a přátelské uživatelské rozhraní je jeho velkou součástí. + +## Aplikace Greeter {#greeter-app} + +Za moderním uživatelským rozhraním je spousta teorie a [mnoho dobrých stránek](https://react.dev/learn/thinking-in-react), [které to vysvětlují](https://wagmi.sh/core/getting-started). Místo opakování skvělé práce, kterou odvedly tyto stránky, budu předpokládat, že se raději učíte praxí a začnete s aplikací, se kterou si můžete hrát. Stále potřebujete teorii, abyste mohli věci dotáhnout do konce, a k tomu se dostaneme - projdeme si zdrojový soubor po zdrojovém souboru a probereme věci, jakmile na ně narazíme. + +### Instalace {#installation} + +1. V případě potřeby si přidejte [blockchain Holesky](https://chainlist.org/?search=holesky&testnets=true) do své peněženky a [získejte testovací ETH](https://www.holeskyfaucet.io/). + +2. Klonujte repozitář z githubu. + + ```sh + git clone https://github.com/qbzzt/20230801-modern-ui.git + ``` + +3. Nainstalujte potřebné balíčky. + + ```sh + cd 20230801-modern-ui + pnpm install + ``` + +4. Spusťte aplikaci. + + ```sh + pnpm dev + ``` + +5. Přejděte na adresu URL zobrazenou aplikací. Ve většině případů je to [http://localhost:5173/](http://localhost:5173/). + +6. Zdrojový kód kontraktu, mírně upravenou verzi Greeter od Hardhat, si můžete prohlédnout [v průzkumníku blockchainu](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=contract). + +### Procházení souborů {#file-walk-through} + +#### `index.html` {#index-html} + +Tento soubor je standardní HTML šablona s výjimkou tohoto řádku, který importuje soubor se skriptem. + +```html + +``` + +#### `src/main.tsx` {#main-tsx} + +Přípona souboru nám říká, že tento soubor je [komponenta React](https://www.w3schools.com/react/react_components.asp) napsaná v [TypeScriptu](https://www.typescriptlang.org/), rozšíření JavaScriptu, které podporuje [kontrolu typů](https://en.wikipedia.org/wiki/Type_system#Type_checking). TypeScript je kompilován do JavaScriptu, takže ho můžeme použít pro spuštění na straně klienta. + +```tsx +import '@rainbow-me/rainbowkit/styles.css' +import { RainbowKitProvider } from '@rainbow-me/rainbowkit' +import * as React from 'react' +import * as ReactDOM from 'react-dom/client' +import { WagmiConfig } from 'wagmi' +import { chains, config } from './wagmi' +``` + +Importujte kód knihovny, který potřebujeme. + +```tsx +import { App } from './App' +``` + +Importujte komponentu React, která implementuje aplikaci (viz níže). + +```tsx +ReactDOM.createRoot(document.getElementById('root')!).render( +``` + +Vytvořte kořenovou komponentu React. Parametrem `render` je [JSX](https://www.w3schools.com/react/react_jsx.asp), jazykové rozšíření, které používá jak HTML, tak JavaScript/TypeScript. Vykřičník zde říká komponentě TypeScript: "nevíte, že `document.getElementById('root')` bude platný parametr pro `ReactDOM.createRoot`, ale nebojte se – já jsem vývojář a říkám vám, že bude". + +```tsx + +``` + +Aplikace se nachází uvnitř [komponenty `React.StrictMode`](https://react.dev/reference/react/StrictMode). Tato komponenta říká knihovně React, aby vložila další kontrolu ladění, což je užitečné během vývoje. + +```tsx + +``` + +Aplikace je také uvnitř [komponenty `WagmiConfig`](https://wagmi.sh/react/api/WagmiProvider). [Knihovna wagmi (we are going to make it)](https://wagmi.sh/) propojuje definice UI v Reactu s [knihovnou viem](https://viem.sh/) pro psaní decentralizovaných aplikací na Ethereu. + +```tsx + +``` + +A nakonec [komponenta `RainbowKitProvider`](https://www.rainbowkit.com/). Tato komponenta zpracovává přihlašování a komunikaci mezi peněženkou a aplikací. + +```tsx + +``` + +Nyní můžeme mít komponentu pro aplikaci, která skutečně implementuje UI. Znak `/>` na konci komponenty říká Reactu, že tato komponenta v sobě nemá žádné definice, podle standardu XML. + +```tsx + + + , +) +``` + +Samozřejmě musíme uzavřít i ostatní komponenty. + +#### `src/App.tsx` {#app-tsx} + +```tsx +import { ConnectButton } from '@rainbow-me/rainbowkit' +import { useAccount } from 'wagmi' +import { Greeter } from './components/Greeter' + +export function App() { +``` + +Toto je standardní způsob, jak vytvořit komponentu React – definovat funkci, která se volá pokaždé, když je třeba ji vykreslit. Tato funkce má obvykle nahoře nějaký kód v TypeScriptu nebo JavaScriptu, za nímž následuje příkaz `return`, který vrací kód JSX. + +```tsx + const { isConnected } = useAccount() +``` + +Zde používáme [`useAccount`](https://wagmi.sh/react/api/hooks/useAccount) ke kontrole, zda jsme připojeni k blockchainu prostřednictvím peněženky. + +Podle konvence jsou v Reactu funkce nazvané `use...` [hooky](https://www.w3schools.com/react/react_hooks.asp), které vracejí nějaký druh dat. Když použijete takové hooky, vaše komponenta nejenže získá data, ale když se tato data změní, komponenta se znovu vykreslí s aktualizovanými informacemi. + +```tsx + return ( + <> +``` + +JSX komponenty React _musí_ vracet jednu komponentu. Když máme více komponent a nemáme nic, co by je "přirozeně" zabalilo, použijeme prázdnou komponentu (`<> ... `), abychom z nich vytvořili jedinou komponentu. + +```tsx +

Greeter

+ +``` + +Komponentu [`ConnectButton`](https://www.rainbowkit.com/docs/connect-button) získáváme z RainbowKit. Když nejsme připojeni, zobrazí se nám tlačítko `Připojit peněženku`, které otevře modální okno, které vysvětluje peněženky a umožňuje vám vybrat, kterou používáte. Když jsme připojeni, zobrazí se používaný blockchain, adresa našeho účtu a náš zůstatek ETH. Tato zobrazení můžeme použít k přepnutí sítě nebo k odpojení. + +```tsx + {isConnected && ( +``` + +Když potřebujeme vložit skutečný JavaScript (nebo TypeScript, který bude zkompilován do JavaScriptu) do JSX, použijeme závorky (`{}`). + +Syntaxe `a && b` je zkratka pro [`a ?` b : a`](https://www.w3schools.com/react/react_es6_ternary.asp). To znamená, že pokud je `a`pravda, vyhodnotí se jako`b`a jinak se vyhodnotí jako`a`(což může být`false`, `0` atd.). To je snadný způsob, jak sdělit Reactu, že se komponenta má zobrazit pouze v případě, že je splněna určitá podmínka. + +V tomto případě chceme uživateli zobrazit `Greeter` pouze v případě, že je uživatel připojen k blockchainu. + +```tsx + + )} + + ) +} +``` + +#### `src/components/Greeter.tsx` {#greeter-tsx} + +Tento soubor obsahuje většinu funkcí uživatelského rozhraní. Obsahuje definice, které by se normálně nacházely ve více souborech, ale jelikož se jedná o tutoriál, program je optimalizován pro snadné pochopení na první pokus, spíše než pro výkon nebo snadnou údržbu. + +```tsx +import { useState, ChangeEventHandler } from 'react' +import { useNetwork, + useReadContract, + usePrepareContractWrite, + useContractWrite, + useContractEvent + } from 'wagmi' +``` + +Používáme tyto funkce knihovny. Opět jsou vysvětleny níže, kde jsou použity. + +```tsx +import { AddressType } from 'abitype' +``` + +[Knihovna `abitype`](https://abitype.dev/) nám poskytuje definice TypeScriptu pro různé datové typy Etherea, jako je [`AddressType`](https://abitype.dev/config#addresstype). + +```tsx +let greeterABI = [ + . + . + . +] as const // greeterABI +``` + +ABI pro kontrakt `Greeter`. +Pokud vyvíjíte kontrakty a UI zároveň, obvykle je umístíte do stejného repozitáře a ABI vygenerované kompilátorem Solidity použijete jako soubor ve vaší aplikaci. To však zde není nutné, protože kontrakt je již vyvinut a nebude se měnit. + +```tsx +type AddressPerBlockchainType = { + [key: number]: AddressType +} +``` + +TypeScript je silně typovaný. Tuto definici používáme k určení adresy, na které je kontrakt `Greeter` nasazen na různých řetězcích. Klíčem je číslo (ID řetězce) a hodnotou je `AddressType` (adresa). + +```tsx +const contractAddrs: AddressPerBlockchainType = { + // Holesky + 17000: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8', + + // Sepolia + 11155111: '0x7143d5c190F048C8d19fe325b748b081903E3BF0' +} +``` + +Adresa kontraktu na dvou podporovaných sítích: [Holesky](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=contact_code) a [Sepolia](https://eth-sepolia.blockscout.com/address/0x7143d5c190F048C8d19fe325b748b081903E3BF0?tab=contact_code). + +Poznámka: Ve skutečnosti existuje ještě třetí definice pro Redstone Holesky, která bude vysvětlena níže. + +```tsx +type ShowObjectAttrsType = { + name: string, + object: any +} +``` + +Tento typ se používá jako parametr pro komponentu `ShowObject` (vysvětleno později). Obsahuje název objektu a jeho hodnotu, které se zobrazují pro účely ladění. + +```tsx +type ShowGreetingAttrsType = { + greeting: string | undefined +} +``` + +V každém okamžiku můžeme buď vědět, jaký je pozdrav (protože jsme ho přečetli z blockchainu), nebo nevědět (protože jsme ho ještě neobdrželi). Je tedy užitečné mít typ, který může být buď řetězec, nebo nic. + +##### Komponenta `Greeter` {#greeter-component} + +```tsx +const Greeter = () => { +``` + +Konečně se dostáváme k definici komponenty. + +```tsx + const { chain } = useNetwork() +``` + +Informace o řetězci, který používáme, s laskavým svolením [wagmi](https://wagmi.sh/react/hooks/useNetwork). +Protože se jedná o hook (`use...`), při každé změně této informace se komponenta překreslí. + +```tsx + const greeterAddr = chain && contractAddrs[chain.id] +``` + +Adresa kontraktu Greeter, která se liší podle řetězce (a která je `undefined`, pokud nemáme informace o řetězci nebo jsme na řetězci bez tohoto kontraktu). + +```tsx + const readResults = useReadContract({ + address: greeterAddr, + abi: greeterABI, + functionName: "greet" , // No arguments + watch: true + }) +``` + +[Hook `useReadContract`](https://wagmi.sh/react/api/hooks/useReadContract) čte informace z kontraktu. Přesné informace, které vrací, uvidíte, když v UI rozbalíte `readResults`. V tomto případě chceme, aby stále hledal, takže budeme informováni, když se pozdrav změní. + +**Poznámka:** Mohli bychom naslouchat [událostem `setGreeting`](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=logs), abychom věděli, kdy se pozdrav změní, a tímto způsobem ho aktualizovali. Ačkoli to může být efektivnější, nebude to platit ve všech případech. Když uživatel přepne na jiný řetězec, pozdrav se také změní, ale tato změna není doprovázena událostí. Mohli bychom mít jednu část kódu, která naslouchá událostem, a druhou, která identifikuje změny řetězce, ale to by bylo složitější než jen nastavit [parametr `watch`](https://wagmi.sh/react/api/hooks/useReadContract#watch-optional). + +```tsx + const [ newGreeting, setNewGreeting ] = useState("") +``` + +Hook [`useState` v Reactu](https://www.w3schools.com/react/react_usestate.asp) nám umožňuje specifikovat proměnnou stavu, jejíž hodnota přetrvává z jednoho vykreslení komponenty na druhé. Počáteční hodnota je parametr, v tomto případě prázdný řetězec. + +Hook `useState` vrací seznam se dvěma hodnotami: + +1. Aktuální hodnota proměnné stavu. +2. Funkce pro úpravu proměnné stavu v případě potřeby. Protože se jedná o hook, pokaždé, když je volán, komponenta se znovu vykreslí. + +V tomto případě používáme proměnnou stavu pro nový pozdrav, který chce uživatel nastavit. + +```tsx + const greetingChange : ChangeEventHandler = (evt) => + setNewGreeting(evt.target.value) +``` + +Toto je obsluha události, která se spustí při změně vstupního pole pro nový pozdrav. Typ [`ChangeEventHandler`](https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forms_and_events/) specifikuje, že se jedná o obsluhu pro změnu hodnoty vstupního prvku HTML. Část `` se používá, protože se jedná o [generický typ](https://www.w3schools.com/typescript/typescript_basic_generics.php). + +```tsx + const preparedTx = usePrepareContractWrite({ + address: greeterAddr, + abi: greeterABI, + functionName: 'setGreeting', + args: [ newGreeting ] + }) + const workingTx = useContractWrite(preparedTx.config) +``` + +Toto je proces odeslání blockchainové transakce z pohledu klienta: + +1. Odešlete transakci uzlu v blockchainu pomocí [`eth_estimateGas`](https://docs.alchemy.com/reference/eth-estimategas). +2. Počkejte na odpověď z uzlu. +3. Po obdržení odpovědi požádejte uživatele o podepsání transakce prostřednictvím peněženky. Tento krok se _musí_ provést až po obdržení odpovědi z uzlu, protože uživateli se před podpisem zobrazí náklady na transakci za palivo. +4. Počkejte na schválení uživatele. +5. Odešlete transakci znovu, tentokrát pomocí [`eth_sendRawTransaction`](https://docs.alchemy.com/reference/eth-sendrawtransaction). + +Krok 2 pravděpodobně zabere znatelné množství času, během kterého by se uživatelé divili, zda byl jejich příkaz skutečně přijat uživatelským rozhraním a proč ještě nejsou požádáni o podepsání transakce. To vede ke špatnému uživatelskému zážitku (UX). + +Řešením je použití [přípravných hooků](https://wagmi.sh/react/prepare-hooks). Pokaždé, když se změní parametr, okamžitě odešlete uzlu požadavek `eth_estimateGas`. Poté, když uživatel skutečně chce odeslat transakci (v tomto případě stisknutím **Aktualizovat pozdrav**), náklady na palivo jsou známé a uživatel může okamžitě vidět stránku peněženky. + +```tsx + return ( +``` + +Nyní můžeme konečně vytvořit skutečné HTML, které se má vrátit. + +```tsx + <> +

Greeter

+ { + !readResults.isError && !readResults.isLoading && + + } +
+``` + +Vytvořte komponentu `ShowGreeting` (vysvětleno níže), ale pouze pokud byl pozdrav úspěšně přečten z blockchainu. + +```tsx + +``` + +Toto je vstupní textové pole, kde si uživatel může nastavit nový pozdrav. Pokaždé, když uživatel stiskne klávesu, zavoláme `greetingChange`, které zavolá `setNewGreeting`. Protože `setNewGreeting` pochází z hooku `useState`, způsobí to opětovné vykreslení komponenty `Greeter`. To znamená, že: + +- Musíme specifikovat `value`, abychom zachovali hodnotu nového pozdravu, protože jinak by se vrátila zpět na výchozí hodnotu, prázdný řetězec. +- `usePrepareContractWrite` se volá pokaždé, když se `newGreeting` změní, což znamená, že v připravené transakci bude vždy nejnovější `newGreeting`. + +```tsx + +``` + +Pokud `workingTx.write` neexistuje, stále čekáme na informace potřebné k odeslání aktualizace pozdravu, takže je tlačítko zakázáno. Pokud hodnota `workingTx.write` existuje, je to funkce, která se má volat k odeslání transakce. + +```tsx +
+ + + + + ) +} +``` + +Nakonec, abychom vám pomohli vidět, co děláme, ukážeme tři objekty, které používáme: + +- `readResults` +- `preparedTx` +- `workingTx` + +##### Komponenta `ShowGreeting` {#showgreeting-component} + +Tato komponenta zobrazuje + +```tsx +const ShowGreeting = (attrs : ShowGreetingAttrsType) => { +``` + +Funkce komponenty přijímá parametr se všemi atributy komponenty. + +```tsx + return {attrs.greeting} +} +``` + +##### Komponenta `ShowObject` {#showobject-component} + +Pro informační účely používáme komponentu `ShowObject` k zobrazení důležitých objektů (`readResults` pro čtení pozdravu a `preparedTx` a `workingTx` pro transakce, které vytváříme). + +```tsx +const ShowObject = (attrs: ShowObjectAttrsType ) => { + const keys = Object.keys(attrs.object) + const funs = keys.filter(k => typeof attrs.object[k] == "function") + return <> +
+``` + +Nechceme zaplnit UI všemi informacemi, takže abychom je mohli zobrazit nebo zavřít, používáme značku [`details`](https://www.w3schools.com/tags/tag_details.asp). + +```tsx + {attrs.name} +
+        {JSON.stringify(attrs.object, null, 2)}
+```
+
+Většina polí se zobrazuje pomocí [`JSON.stringify`](https://www.w3schools.com/js/js_json_stringify.asp).
+
+```tsx
+      
+ { funs.length > 0 && + <> + Functions: +
    +``` + +Výjimkou jsou funkce, které nejsou součástí [standardu JSON](https://www.json.org/json-en.html), takže se musí zobrazovat samostatně. + +```tsx + {funs.map((f, i) => +``` + +V rámci JSX je kód uvnitř `{` složených závorek `}` interpretován jako JavaScript. Poté je kód uvnitř `(` kulatých závorek `)` interpretován znovu jako JSX. + +```tsx + (
  • {f}
  • ) + )} +``` + +React vyžaduje, aby značky ve [stromu DOM](https://www.w3schools.com/js/js_htmldom.asp) měly jedinečné identifikátory. To znamená, že potomci stejné značky (v tomto případě [neuspořádaný seznam](https://www.w3schools.com/tags/tag_ul.asp)) potřebují různé atributy `key`. + +```tsx +
+ + } +
+ +} +``` + +Ukončete různé značky HTML. + +##### Konečný `export` {#the-final-export} + +```tsx +export { Greeter } +``` + +Komponenta `Greeter` je ta, kterou potřebujeme exportovat pro aplikaci. + +#### `src/wagmi.ts` {#wagmi-ts} + +Nakonec jsou různé definice související s WAGMI v `src/wagmi.ts`. Nebudu zde vše vysvětlovat, protože většina z toho je šablona, kterou pravděpodobně nebudete muset měnit. + +Kód zde není úplně stejný jako [na githubu](https://github.com/qbzzt/20230801-modern-ui/blob/main/src/wagmi.ts), protože později v článku přidáme další řetězec ([Redstone Holesky](https://redstone.xyz/docs/network-info)). + +```ts +import { getDefaultWallets } from '@rainbow-me/rainbowkit' +import { configureChains, createConfig } from 'wagmi' +import { holesky, sepolia } from 'wagmi/chains' +``` + +Importujte blockchainy, které aplikace podporuje. Seznam podporovaných řetězců si můžete prohlédnout [v githubu viem](https://github.com/wagmi-dev/viem/tree/main/src/chains/definitions). + +```ts +import { publicProvider } from 'wagmi/providers/public' + +const walletConnectProjectId = 'c96e690bb92b6311e8e9b2a6a22df575' +``` + +Abyste mohli používat [WalletConnect](https://walletconnect.com/), potřebujete ID projektu pro vaši aplikaci. Můžete jej získat na [cloud.walletconnect.com](https://cloud.walletconnect.com/sign-in). + +```ts +const { chains, publicClient, webSocketPublicClient } = configureChains( + [ holesky, sepolia ], + [ + publicProvider(), + ], +) + +const { connectors } = getDefaultWallets({ + appName: 'My wagmi + RainbowKit App', + chains, + projectId: walletConnectProjectId, +}) + +export const config = createConfig({ + autoConnect: true, + connectors, + publicClient, + webSocketPublicClient, +}) + +export { chains } +``` + +### Přidání dalšího blockchainu {#add-blockchain} + +V dnešní době existuje mnoho [řešení pro škálování L2](/layer-2/) a možná budete chtít podporovat některá, která viem ještě nepodporuje. K tomu upravte `src/wagmi.ts`. Tyto pokyny vysvětlují, jak přidat [Redstone Holesky](https://redstone.xyz/docs/network-info). + +1. Importujte typ `defineChain` z viem. + + ```ts + import { defineChain } from 'viem' + ``` + +2. Přidejte definici sítě. + + ```ts + const redstoneHolesky = defineChain({ + id: 17_001, + name: 'Redstone Holesky', + network: 'redstone-holesky', + nativeCurrency: { + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + rpcUrls: { + default: { + http: ['https://rpc.holesky.redstone.xyz'], + webSocket: ['wss://rpc.holesky.redstone.xyz/ws'], + }, + public: { + http: ['https://rpc.holesky.redstone.xyz'], + webSocket: ['wss://rpc.holesky.redstone.xyz/ws'], + }, + }, + blockExplorers: { + default: { name: 'Explorer', url: 'https://explorer.holesky.redstone.xyz' }, + }, + }) + ``` + +3. Přidejte nový řetězec do volání `configureChains`. + + ```ts + const { chains, publicClient, webSocketPublicClient } = configureChains( + [ holesky, sepolia, redstoneHolesky ], + [ publicProvider(), ], + ) + ``` + +4. Zajistěte, aby aplikace znala adresu vašich kontraktů na nové síti. V tomto případě upravíme `src/components/Greeter.tsx`: + + ```ts + const contractAddrs : AddressPerBlockchainType = { + // Holesky + 17000: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8', + + // Redstone Holesky + 17001: '0x4919517f82a1B89a32392E1BF72ec827ba9986D3', + + // Sepolia + 11155111: '0x7143d5c190F048C8d19fe325b748b081903E3BF0' + } + ``` + +## Závěr {#conclusion} + +Samozřejmě vás nezajímá poskytování uživatelského rozhraní pro `Greeter`. Chcete vytvořit uživatelské rozhraní pro své vlastní kontrakty. Chcete-li vytvořit vlastní aplikaci, proveďte tyto kroky: + +1. Určete, že chcete vytvořit aplikaci wagmi. + + ```sh copy + pnpm create wagmi + ``` + +2. Pojmenujte aplikaci. + +3. Vyberte framework **React**. + +4. Vyberte variantu **Vite**. + +5. Můžete [přidat Rainbow kit](https://www.rainbowkit.com/docs/installation#manual-setup). + +Nyní jděte a zpřístupněte své kontrakty širokému světu. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). + diff --git a/public/content/translations/cs/developers/tutorials/deploying-your-first-smart-contract/index.md b/public/content/translations/cs/developers/tutorials/deploying-your-first-smart-contract/index.md new file mode 100644 index 00000000000..98121a07c59 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/deploying-your-first-smart-contract/index.md @@ -0,0 +1,101 @@ +--- +title: "Nasazení vašeho prvního chytrého kontraktu" +description: "Úvod do nasazení vašeho prvního chytrého kontraktu v testovací síti Etherea" +author: "jdourlens" +tags: + [ + "smart kontrakt účty", + "remix", + "solidity", + "nasazování" + ] +skill: beginner +lang: cs +published: 2020-04-03 +source: EthereumDev +sourceUrl: https://ethereumdev.io/deploying-your-first-smart-contract/ +address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +--- + +Předpokládáme, že se stejně jako my těšíte, až [nasadíte](/developers/docs/smart-contracts/deploying/) a budete interagovat s vaším prvním [chytrým kontraktem](/developers/docs/smart-contracts/) na blockchainu Etherea. + +Nemějte obavy, jelikož se jedná o náš první chytrý kontrakt, nasadíme ho na [lokální testovací síti](/developers/docs/networks/), takže vás jeho nasazení nebude nic stát a budete si s ním moci hrát, jak se vám zlíbí. + +## Napsání našeho kontraktu {#writing-our-contract} + +Prvním krokem je [navštívit Remix](https://remix.ethereum.org/) a vytvořit nový soubor. V levé horní části rozhraní Remix přidejte nový soubor a zadejte požadovaný název souboru. + +![Přidání nového souboru v rozhraní Remix](./remix.png) + +Do nového souboru vložíme následující kód. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.17; + +contract Counter { + + // Veřejná proměnná typu unsigned int pro uchování počtu + uint256 public count = 0; + + // Funkce, která navyšuje náš čítač + function increment() public { + count += 1; + } + + // Nepovinný getter pro získání hodnoty počítadla + function getCount() public view returns (uint256) { + return count; + } + +} +``` + +Pokud jste zvyklí programovat, snadno uhodnete, co tento program dělá. Zde je vysvětlení řádek po řádku: + +- Řádek 4: Definujeme kontrakt s názvem `Counter`. +- Řádek 7: Náš kontrakt ukládá jedno celé číslo bez znaménka s názvem `count` začínající na 0. +- Řádek 10: První funkce změní stav kontraktu a navýší (`increment()`) naši proměnnou `count`. +- Řádek 15: Druhá funkce je pouze getter pro čtení hodnoty proměnné `count` mimo chytrý kontrakt. Všimněte si, že jelikož jsme naši proměnnou `count` definovali jako veřejnou (public), není to nutné, ale je to uvedeno jako příklad. + +To je k našemu prvnímu jednoduchému chytrému kontraktu vše. Jak možná víte, vypadá to jako třída z jazyků OOP (objektově orientovaného programování), jako je Java nebo C++. Nyní je čas si s naším kontraktem pohrát. + +## Nasazení našeho kontraktu {#deploying-our-contract} + +Jelikož jsme napsali náš první chytrý kontrakt, nyní ho nasadíme na blockchain, abychom si s ním mohli hrát. + +[Nasazení chytrého kontraktu na blockchain](/developers/docs/smart-contracts/deploying/) je vlastně jen odeslání transakce obsahující kód zkompilovaného chytrého kontraktu bez určení jakýchkoli příjemců. + +Nejprve [zkopilujeme kontrakt](/developers/docs/smart-contracts/compiling/) kliknutím na ikonu kompilace na levé straně: + +![Ikona kompilace v nástrojové liště Remix](./remix-compile-button.png) + +Poté klikněte na tlačítko kompilace: + +![Tlačítko kompilace v kompilátoru Solidity v Remixu](./remix-compile.png) + +Můžete si vybrat možnost „Automatická kompilace“, takže kontrakt bude vždy zkompilován, když uložíte obsah v textovém editoru. + +Poté přejděte na obrazovku "nasazení a spouštění transakcí": + +![Ikona nasazení v nástrojové liště Remix](./remix-deploy.png) + +Jakmile se ocitnete na obrazovce "nasazení a spouštění transakcí", dvakrát zkontrolujte, zda se zobrazuje název vašeho kontraktu, a klikněte na tlačítko Nasadit. Jak vidíte v horní části stránky, aktuální prostředí je „JavaScript VM“, to znamená, že nasadíme náš chytrý kontrakt a budeme s ním interagovat na lokálním testovacím blockchainu, abychom mohli testovat rychleji a bez poplatků. + +![Tlačítko nasazení v kompilátoru Solidity v Remixu](./remix-deploy-button.png) + +Jakmile kliknete na tlačítko „Nasadit“, uvidíte, že se váš kontrakt objeví ve spodní části. Kliknutím na šipku vlevo ho rozbalíte, abychom viděli obsah našeho kontraktu. Zde je naše proměnná `count`, naše funkce `increment()` a getter `getCounter()`. + +Pokud kliknete na tlačítko `count` nebo `getCount`, ve skutečnosti se načte obsah proměnné `count` kontraktu a zobrazí se. Jelikož jsme funkci `increment` ještě nevolali, měla by se zobrazit 0. + +![Tlačítko funkce v kompilátoru Solidity v Remixu](./remix-function-button.png) + +Nyní zavoláme funkci `increment` kliknutím na tlačítko. Uvidíte záznamy o provedených transakcích, které se objeví ve spodní části okna. Uvidíte, že záznamy jsou odlišné, když stisknete tlačítko pro načtení dat namísto tlačítka `increment`. Je to proto, že čtení dat na blockchainu nevyžaduje žádné transakce (zápis) ani poplatky. Protože pouze úprava stavu blockchainu vyžaduje provedení transakce: + +![Záznam transakcí](./transaction-log.png) + +Po stisknutí tlačítka inkrementace, které vygeneruje transakci pro volání naší funkce `increment()`, přečteme při opětovném kliknutí na tlačítka `count` nebo `getCount` nově aktualizovaný stav našeho chytrého kontraktu, kde proměnná `count` bude větší než 0. + +![Nově aktualizovaný stav chytrého kontraktu](./updated-state.png) + +V dalším návodu si ukážeme, [jak můžete do svých chytrých kontraktů přidávat události](/developers/tutorials/logging-events-smart-contracts/). Zaznamenávání událostí je pohodlný způsob, jak ladit váš chytrý kontrakt a porozumět tomu, co se děje při volání funkce. diff --git a/public/content/translations/cs/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md b/public/content/translations/cs/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md new file mode 100644 index 00000000000..57653c2fdea --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md @@ -0,0 +1,372 @@ +--- +title: "Jak vyvíjet a testovat dApp na lokálním, multi-klientském testnetu" +description: "Tento průvodce vás nejprve provede vytvořením instance a konfigurací lokálního Ethereum testnetu s více klienty a následně použitím testnetu k nasazení a testování dApp." +author: "Tedi Mitiku" +tags: + [ + "klienti", + "uzly", + "smart kontrakt účty", + "složitelnost", + "konsensuální vrstva", + "exekuční vrstva", + "testování" + ] +skill: intermediate +lang: cs +published: 2023-04-11 +--- + +## Úvod {#introduction} + +Tato příručka vás provede procesem vytvoření instance konfigurovatelného lokálního Ethereum testnetu, nasazením chytrého kontraktu a použitím testnetu ke spuštění testů vaší dApp. Tato příručka je určena pro vývojáře dApps, kteří chtějí lokálně vyvíjet a testovat své dApps s různými konfiguracemi sítě před nasazením na živý testnet nebo mainnet. + +V této příručce: + +- Vytvoříte instanci lokálního Ethereum testnetu s [`eth-network-package`](https://github.com/kurtosis-tech/eth-network-package) pomocí [Kurtosis](https://www.kurtosis.com/), +- Připojíte své vývojové prostředí Hardhat dApp k lokálnímu testnetu pro kompilaci, nasazení a testování dApp a +- Nakonfigurujete lokální testnet, včetně parametrů, jako je počet uzlů a konkrétní párování EL/CL klientů, abyste umožnili vývoj a testování s různými konfiguracemi sítě. + +### Co je Kurtosis? {#what-is-kurtosis} + +[Kurtosis](https://www.kurtosis.com/) je skládací systém sestavení určený pro konfiguraci vícekontejnerových testovacích prostředí. Umožňuje vývojářům vytvářet reprodukovatelná prostředí, která vyžadují logiku dynamického nastavení, jako jsou například blockchainové testnety. + +V této příručce balíček Kurtosis eth-network-package spouští lokální Ethereum testnet s podporou klienta [`geth`](https://geth.ethereum.org/) exekuční vrstvy (EL) a také klientů [`teku`](https://consensys.io/teku), [`lighthouse`](https://lighthouse.sigmaprime.io/) a [`lodestar`](https://lodestar.chainsafe.io/) konsensuální vrstvy (CL). Tento balíček slouží jako konfigurovatelná a skládací alternativa k sítím v rámcích jako Hardhat Network, Ganache a Anvil. Kurtosis nabízí vývojářům větší kontrolu a flexibilitu nad testnety, které používají, což je hlavní důvod, proč [nadace Ethereum použila Kurtosis k testování Sloučení](https://www.kurtosis.com/blog/testing-the-ethereum-merge) a nadále ho používá k testování upgradů sítě. + +## Nastavení Kurtosis {#setting-up-kurtosis} + +Než budete pokračovat, ujistěte se, že máte: + +- [Nainstalovaný a spuštěný Docker engine](https://docs.kurtosis.com/install/#i-install--start-docker) na vašem lokálním počítači +- [Nainstalovaný Kurtosis CLI](https://docs.kurtosis.com/install#ii-install-the-cli) (nebo aktualizovaný na nejnovější verzi, pokud již máte CLI nainstalovaný) +- Nainstalovaný [Node.js](https://nodejs.org/en), [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable) a [npx](https://www.npmjs.com/package/npx) (pro vaše prostředí dApp) + +## Vytvoření instance lokálního Ethereum testnetu {#instantiate-testnet} + +Pro spuštění lokálního Ethereum testnetu spusťte: + +```python +kurtosis --enclave local-eth-testnet run github.com/kurtosis-tech/eth-network-package +``` + +Poznámka: Tento příkaz pojmenuje vaši síť: „local-eth-testnet“ pomocí příznaku `--enclave`. + +Kurtosis bude průběžně vypisovat kroky, které provádí, zatímco interpretuje, ověřuje a následně provádí pokyny. Na konci byste měli vidět výstup, který se podobá následujícímu: + +```python +INFO[2023-04-04T18:09:44-04:00] ====================================================== +INFO[2023-04-04T18:09:44-04:00] || Created enclave: local-eth-testnet || +INFO[2023-04-04T18:09:44-04:00] ====================================================== +Name: local-eth-testnet +UUID: 39372d756ae8 +Status: RUNNING +Creation Time: Tue, 04 Apr 2023 18:09:03 EDT + +========================================= Files Artifacts ========================================= +UUID Name +d4085a064230 cl-genesis-data +1c62cb792e4c el-genesis-data +bd60489b73a7 genesis-generation-config-cl +b2e593fe5228 genesis-generation-config-el +d552a54acf78 geth-prefunded-keys +5f7e661eb838 prysm-password +054e7338bb59 validator-keystore-0 + +========================================== User Services ========================================== +UUID Name Ports Status +e20f129ee0c5 cl-client-0-beacon http: 4000/tcp -> RUNNING + metrics: 5054/tcp -> + tcp-discovery: 9000/tcp -> 127.0.0.1:54263 + udp-discovery: 9000/udp -> 127.0.0.1:60470 +a8b6c926cdb4 cl-client-0-validator http: 5042/tcp -> 127.0.0.1:54267 RUNNING + metrics: 5064/tcp -> +d7b802f623e8 el-client-0 engine-rpc: 8551/tcp -> 127.0.0.1:54253 RUNNING + rpc: 8545/tcp -> 127.0.0.1:54251 + tcp-discovery: 30303/tcp -> 127.0.0.1:54254 + udp-discovery: 30303/udp -> 127.0.0.1:53834 + ws: 8546/tcp -> 127.0.0.1:54252 +514a829c0a84 prelaunch-data-generator-1680646157905431468 STOPPED +62bd62d0aa7a prelaunch-data-generator-1680646157915424301 STOPPED +05e9619e0e90 prelaunch-data-generator-1680646157922872635 STOPPED + +``` + +Gratulujeme! Použili jste Kurtosis k vytvoření instance lokálního Ethereum testnetu s klientem CL (`lighthouse`) a EL (`geth`) přes Docker. + +### Rekapitulace {#review-instantiate-testnet} + +V této části jste spustili příkaz, který nařídil Kurtosisu, aby použil [`eth-network-package` hostovaný vzdáleně na GitHubu](https://github.com/kurtosis-tech/eth-network-package) ke spuštění lokálního Ethereum testnetu v rámci Kurtosis [Enclave](https://docs.kurtosis.com/advanced-concepts/enclaves/). Uvnitř vaší enklávy najdete jak „souborové artefakty“, tak „uživatelské služby“. + +[Souborové artefakty](https://docs.kurtosis.com/advanced-concepts/files-artifacts/) ve vaší enklávě obsahují všechna data vygenerovaná a využitá k zavedení klientů EL a CL. Data byla vytvořena pomocí služby `prelaunch-data-generator` sestavené z tohoto [obrazu Dockeru](https://github.com/ethpandaops/ethereum-genesis-generator) + +Uživatelské služby zobrazují všechny kontejnerizované služby běžící ve vaší enklávě. Všimnete si, že byl vytvořen jediný uzel, který obsahuje klienta EL i klienta CL. + +## Připojení vývojového prostředí dApp k lokálnímu Ethereum testnetu {#connect-your-dapp} + +### Nastavení vývojového prostředí dApp {#set-up-dapp-env} + +Nyní, když máte spuštěný lokální testnet, můžete k němu připojit své vývojové prostředí dApp. V této příručce bude použit framework Hardhat k nasazení blackjack dApp na váš lokální testnet. + +Chcete-li nastavit vývojové prostředí dApp, naklonujte repozitář, který obsahuje naši ukázkovou dApp, a nainstalujte její závislosti spuštěním: + +```python +git clone https://github.com/kurtosis-tech/awesome-kurtosis.git && cd awesome-kurtosis/smart-contract-example && yarn +``` + +Složka [smart-contract-example](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example) použitá zde obsahuje typické nastavení pro vývojáře dApp používajícího framework [Hardhat](https://hardhat.org/): + +- [`contracts/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/contracts) obsahuje několik jednoduchých chytrých kontraktů pro Blackjack dApp +- [`scripts/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/scripts) obsahuje skript pro nasazení tokenového kontraktu do vaší lokální sítě Ethereum +- [`test/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/test) obsahuje jednoduchý .js test pro váš tokenový kontrakt, který potvrdí, že každý hráč v naší Blackjack dApp má pro sebe vyraženo 1000 tokenů +- [`hardhat.config.ts`](https://github.com/kurtosis-tech/awesome-kurtosis/blob/main/smart-contract-example/hardhat.config.ts) konfiguruje vaše nastavení Hardhat + +### Konfigurace Hardhatu pro použití lokálního testnetu {#configure-hardhat} + +S nastaveným vývojovým prostředím dApp nyní připojíte Hardhat k lokálnímu Ethereum testnetu vygenerovanému pomocí Kurtosis. Chcete-li toho dosáhnout, nahraďte `<$YOUR_PORT>` ve struktuře `localnet` v konfiguračním souboru `hardhat.config.ts` portem z výstupu RPC URI libovolné služby `el-client-`. V tomto ukázkovém případě by port byl `64248`. Váš port bude jiný. + +Příklad v `hardhat.config.ts`: + +```js +localnet: { +url: 'http://127.0.0.1:<$YOUR_PORT>',// TODO: NAHRAĎTE $YOUR_PORT PORTEM Z URI UZLU, KTERÝ VYTVOŘIL BALÍČEK KURTOSIS ETH-NETWORK-PACKAGE + +// Toto jsou soukromé klíče spojené s předem financovanými testovacími účty vytvořenými balíčkem eth-network-package +// +accounts: [ + "ef5177cd0b6b21c87db5a0bf35d4084a8a57a9d6a064f86d51ac85f2b873a4e2", + "48fcc39ae27a0e8bf0274021ae6ebd8fe4a0e12623d61464c498900b28feb567", + "7988b3a148716ff800414935b305436493e1f25237a2a03e5eebc343735e2f31", + "b3c409b6b0b3aa5e65ab2dc1930534608239a478106acf6f3d9178e9f9b00b35", + "df9bb6de5d3dc59595bcaa676397d837ff49441d211878c024eabda2cd067c9f", + "7da08f856b5956d40a72968f93396f6acff17193f013e8053f6fbb6c08c194d6", + ], +}, +``` + +Jakmile soubor uložíte, vaše vývojové prostředí Hardhat dApp je nyní připojeno k vašemu lokálnímu Ethereum testnetu! Funkčnost vašeho testnetu můžete ověřit spuštěním: + +```python +npx hardhat balances --network localnet +``` + +Výstup by měl vypadat přibližně takto: + +```python +0x878705ba3f8Bc32FCf7F4CAa1A35E72AF65CF766 has balance 10000000000000000000000000 +0x4E9A3d9D1cd2A2b2371b8b3F489aE72259886f1A has balance 10000000000000000000000000 +0xdF8466f277964Bb7a0FFD819403302C34DCD530A has balance 10000000000000000000000000 +0x5c613e39Fc0Ad91AfDA24587e6f52192d75FBA50 has balance 10000000000000000000000000 +0x375ae6107f8cC4cF34842B71C6F746a362Ad8EAc has balance 10000000000000000000000000 +0x1F6298457C5d76270325B724Da5d1953923a6B88 has balance 10000000000000000000000000 +``` + +To potvrzuje, že Hardhat používá váš lokální testnet a detekuje předfinancované účty vytvořené balíčkem `eth-network-package`. + +### Lokální nasazení a testování vaší dApp {#deploy-and-test-dapp} + +S vývojovým prostředím dApp plně připojeným k lokálnímu Ethereum testnetu nyní můžete spouštět vývojové a testovací pracovní postupy proti své dApp pomocí lokálního testnetu. + +Pro kompilaci a nasazení chytrého kontraktu `ChipToken.sol` pro lokální prototypování a vývoj spusťte: + +```python +npx hardhat compile +npx hardhat run scripts/deploy.ts --network localnet +``` + +Výstup by měl vypadat nějak takto: + +```python +ChipToken deployed to: 0xAb2A01BC351770D09611Ac80f1DE076D56E0487d +``` + +Nyní zkuste spustit test `simple.js` proti vaší lokální dApp, abyste potvrdili, že každý hráč v naší Blackjack dApp má pro sebe vyraženo 1000 tokenů: + +Výstup by měl vypadat přibližně takto: + +```python +npx hardhat test --network localnet +``` + +Výstup by měl vypadat přibližně takto: + +```python +ChipToken + mint + ✔ should mint 1000 chips for PLAYER ONE + + 1 passing (654ms) +``` + +### Rekapitulace {#review-dapp-workflows} + +V tomto bodě jste nastavili vývojové prostředí dApp, připojili jste ho k lokální síti Ethereum vytvořené pomocí Kurtosis a zkompilovali, nasadili a spustili jste jednoduchý test proti vaší dApp. + +Nyní se podívejme, jak můžete konfigurovat podkladovou síť pro testování našich dApps v různých konfiguracích sítě. + +## Konfigurace lokálního Ethereum testnetu {#configure-testnet} + +### Změna konfigurací klientů a počtu uzlů {#configure-client-config-and-num-nodes} + +Váš lokální Ethereum testnet lze nakonfigurovat tak, aby používal různé páry klientů EL a CL, stejně jako různý počet uzlů, v závislosti na scénáři a specifické konfiguraci sítě, kterou chcete vyvíjet nebo testovat. To znamená, že po nastavení můžete spustit přizpůsobený lokální testnet a použít jej ke spuštění stejných pracovních postupů (nasazení, testy atd.) v různých konfiguracích sítě, abyste se ujistili, že vše funguje podle očekávání. Chcete-li se dozvědět více o dalších parametrech, které můžete upravit, navštivte tento odkaz. + +Vyzkoušejte to! Prostřednictvím souboru JSON můžete balíčku `eth-network-package` předat různé možnosti konfigurace. Tento soubor JSON s parametry sítě poskytuje specifické konfigurace, které Kurtosis použije k nastavení lokální sítě Ethereum. + +Vezměte výchozí konfigurační soubor a upravte jej tak, aby se spustily dva uzly s různými páry EL/CL: + +- Uzel 1 s `geth`/`lighthouse` +- Uzel 2 s `geth`/`lodestar` +- Uzel 3 s `geth`/`teku` + +Tato konfigurace vytváří heterogenní síť implementací uzlů Ethereum pro testování vaší dApp. Váš konfigurační soubor by nyní měl vypadat takto: + +```yaml +{ + "participants": + [ + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "lighthouse", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "lodestar", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "teku", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + ], + "network_params": + { + "preregistered_validator_keys_mnemonic": "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete", + "num_validator_keys_per_node": 64, + "network_id": "3151908", + "deposit_contract_address": "0x4242424242424242424242424242424242424242", + "seconds_per_slot": 12, + "genesis_delay": 120, + "capella_fork_epoch": 5, + }, +} +``` + +Každá struktura `participants` odpovídá jednomu uzlu v síti, takže 3 struktury `participants` řeknou Kurtosisu, aby spustil 3 uzly ve vaší síti. Každá struktura `participants` vám umožní určit pár EL a CL použitý pro daný konkrétní uzel. + +Struktura `network_params` konfiguruje nastavení sítě, která se používají k vytvoření genesis souborů pro každý uzel, a také další nastavení, jako jsou sekundy na slot sítě. + +Uložte si upravený soubor parametrů do libovolného adresáře (v níže uvedeném příkladu je uložen na plochu) a poté ho použijte ke spuštění balíčku Kurtosis spuštěním: + +```python +kurtosis clean -a && kurtosis run --enclave local-eth-testnet github.com/kurtosis-tech/eth-network-package "$(cat ~/eth-network-params.json)" +``` + +Poznámka: příkaz `kurtosis clean -a` se zde používá k tomu, aby Kurtosis zničil starý testnet a jeho obsah před spuštěním nového. + +Kurtosis bude opět chvíli pracovat a vypisovat jednotlivé kroky, které probíhají. Nakonec by výstup měl vypadat nějak takto: + +```python +Starlark code successfully run. No output was returned. +INFO[2023-04-07T11:43:16-04:00] ========================================================== +INFO[2023-04-07T11:43:16-04:00] || Created enclave: local-eth-testnet || +INFO[2023-04-07T11:43:16-04:00] ========================================================== +Name: local-eth-testnet +UUID: bef8c192008e +Status: RUNNING +Creation Time: Fri, 07 Apr 2023 11:41:58 EDT + +========================================= Files Artifacts ========================================= +UUID Name +cc495a8e364a cl-genesis-data +7033fcdb5471 el-genesis-data +a3aef43fc738 genesis-generation-config-cl +8e968005fc9d genesis-generation-config-el +3182cca9d3cd geth-prefunded-keys +8421166e234f prysm-password +d9e6e8d44d99 validator-keystore-0 +23f5ba517394 validator-keystore-1 +4d28dea40b5c validator-keystore-2 + +========================================== User Services ========================================== +UUID Name Ports Status +485e6fde55ae cl-client-0-beacon http: 4000/tcp -> http://127.0.0.1:65010 RUNNING + metrics: 5054/tcp -> http://127.0.0.1:65011 + tcp-discovery: 9000/tcp -> 127.0.0.1:65012 + udp-discovery: 9000/udp -> 127.0.0.1:54455 +73739bd158b2 cl-client-0-validator http: 5042/tcp -> 127.0.0.1:65016 RUNNING + metrics: 5064/tcp -> http://127.0.0.1:65017 +1b0a233cd011 cl-client-1-beacon http: 4000/tcp -> 127.0.0.1:65021 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65023 + tcp-discovery: 9000/tcp -> 127.0.0.1:65024 + udp-discovery: 9000/udp -> 127.0.0.1:56031 + validator-metrics: 5064/tcp -> 127.0.0.1:65022 +949b8220cd53 cl-client-1-validator http: 4000/tcp -> 127.0.0.1:65028 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65030 + tcp-discovery: 9000/tcp -> 127.0.0.1:65031 + udp-discovery: 9000/udp -> 127.0.0.1:60784 + validator-metrics: 5064/tcp -> 127.0.0.1:65029 +c34417bea5fa cl-client-2 http: 4000/tcp -> 127.0.0.1:65037 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65035 + tcp-discovery: 9000/tcp -> 127.0.0.1:65036 + udp-discovery: 9000/udp -> 127.0.0.1:63581 +e19738e6329d el-client-0 engine-rpc: 8551/tcp -> 127.0.0.1:64986 RUNNING + rpc: 8545/tcp -> 127.0.0.1:64988 + tcp-discovery: 30303/tcp -> 127.0.0.1:64987 + udp-discovery: 30303/udp -> 127.0.0.1:55706 + ws: 8546/tcp -> 127.0.0.1:64989 +e904687449d9 el-client-1 engine-rpc: 8551/tcp -> 127.0.0.1:64993 RUNNING + rpc: 8545/tcp -> 127.0.0.1:64995 + tcp-discovery: 30303/tcp -> 127.0.0.1:64994 + udp-discovery: 30303/udp -> 127.0.0.1:58096 + ws: 8546/tcp -> 127.0.0.1:64996 +ad6f401126fa el-client-2 engine-rpc: 8551/tcp -> 127.0.0.1:65003 RUNNING + rpc: 8545/tcp -> 127.0.0.1:65001 + tcp-discovery: 30303/tcp -> 127.0.0.1:65000 + udp-discovery: 30303/udp -> 127.0.0.1:57269 + ws: 8546/tcp -> 127.0.0.1:65002 +12d04a9dbb69 prelaunch-data-generator-1680882122181135513 STOPPED +5b45f9c0504b prelaunch-data-generator-1680882122192182847 STOPPED +3d4aaa75e218 prelaunch-data-generator-1680882122201668972 STOPPED +``` + +Gratulujeme! Úspěšně jste nakonfigurovali svůj lokální testnet tak, aby měl 3 uzly místo 1. Chcete-li spustit stejné pracovní postupy jako dříve proti vaší dApp (nasazení a testování), proveďte stejné operace jako dříve tak, že nahradíte `<$YOUR_PORT>` ve struktuře `localnet` v konfiguračním souboru `hardhat.config.ts` portem z výstupu RPC URI libovolné služby `el-client-` ve vašem novém, 3uzlovém lokálním testnetu. + +## Závěr {#conclusion} + +A to je vše! Abychom shrnuli tuto krátkou příručku: + +- Vytvořili jste lokální Ethereum testnet přes Docker pomocí Kurtosis +- Připojili jste své lokální vývojové prostředí dApp k lokální síti Ethereum +- Nasadili jste dApp a spustili jste na ní jednoduchý test v lokální síti Ethereum +- Nakonfigurovali jste podkladovou síť Ethereum tak, aby měla 3 uzly + +Rádi bychom od vás slyšeli, co se vám povedlo, co by se dalo vylepšit, nebo abychom zodpověděli jakékoli vaše dotazy. Neváhejte se nám ozvat přes [GitHub](https://github.com/kurtosis-tech/kurtosis/issues/new/choose) nebo nám [napište e-mail](mailto:feedback@kurtosistech.com)! + +### Další příklady a průvodci {#other-examples-guides} + +Doporučujeme vám podívat se na náš [rychlý start](https://docs.kurtosis.com/quickstart) (kde si na něm postavíte databázi Postgres a API) a naše další příklady v našem [repozitáři awesome-kurtosis](https://github.com/kurtosis-tech/awesome-kurtosis), kde najdete několik skvělých příkladů, včetně balíčků pro: + +- Spuštění stejného lokálního Ethereum testnetu, ale s připojenými dalšími službami, jako je spammer transakcí (pro simulaci transakcí), monitor větví a připojená instance Grafana a Prometheus +- Provedení [testu podsíťování](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/ethereum-network-partition-test) proti stejné lokální síti Ethereum diff --git a/public/content/translations/cs/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md b/public/content/translations/cs/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md new file mode 100644 index 00000000000..307a81a9bd2 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md @@ -0,0 +1,144 @@ +--- +title: "Zmenšení kontraktů v boji proti limitu velikosti kontraktů" +description: "Co můžete udělat, abyste zabránili přílišnému zvětšení vašich chytrých kontraktů?" +author: Markus Waas +lang: cs +tags: [ "solidity", "smart kontrakt účty", "úložiště" ] +skill: intermediate +published: 2020-06-26 +source: soliditydeveloper.com +sourceUrl: https://soliditydeveloper.com/max-contract-size +--- + +## Proč existuje limit? {#why-is-there-a-limit} + +Dne [22. listopadu 2016](https://blog.ethereum.org/2016/11/18/hard-fork-no-4-spurious-dragon/) hard fork Spurious Dragon představil [EIP-170](https://eips.ethereum.org/EIPS/eip-170), který přidal limit velikosti chytrého kontraktu 24,576 kb. Pro vás jako pro vývojáře v jazyce Solidity to znamená, že když budete do svého kontraktu přidávat další a další funkce, v určitém okamžiku narazíte na limit a při nasazování se vám zobrazí chyba: + +`Warning: Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries.` + +Tento limit byl zaveden, aby se zabránilo útokům typu denial-of-service (DoS). Každé volání kontraktu je z hlediska spotřeby paliva relativně levné. Dopad volání kontraktu na uzly Etherea se však neúměrně zvyšuje v závislosti na velikosti kódu volaného kontraktu (čtení kódu z disku, předzpracování kódu, přidávání dat do Merkle proofu). Kdykoli nastane situace, kdy útočník potřebuje málo zdrojů, aby ostatním způsobil spoustu práce, vzniká potenciál pro útoky DoS. + +Původně to byl menší problém, protože jedním přirozeným limitem velikosti kontraktu je palivový limit bloku. Kontrakt musí být samozřejmě nasazen v rámci transakce, která obsahuje veškerý bytecode kontraktu. Pokud do bloku zahrnete pouze tuto jednu transakci, můžete spotřebovat všechno palivo, ale není ho nekonečně. Od [vylepšení London](/ethereum-forks/#london) se palivový limit bloku může měnit mezi 15 a 30 miliony jednotek v závislosti na poptávce sítě. + +V následujícím textu se podíváme na některé metody seřazené podle jejich potenciálního dopadu. Přemýšlejte o tom jako o hubnutí. Nejlepší strategií, jak dosáhnout cílové hmotnosti (v našem případě 24 kb), je zaměřit se nejprve na metody s velkým dopadem. Ve většině případů vás tam dostane pouhá úprava jídelníčku, ale někdy je potřeba trochu víc. Pak můžete přidat nějaké cvičení (střední dopad) nebo dokonce doplňky stravy (malý dopad). + +## Velký dopad {#big-impact} + +### Rozdělte své kontrakty {#separate-your-contracts} + +To by měl být vždy váš první přístup. Jak můžete kontrakt rozdělit na více menších? Obecně vás to donutí vymyslet pro své kontrakty dobrou architekturu. Z hlediska čitelnosti kódu jsou vždy upřednostňovány menší kontrakty. Při rozdělování kontraktů si položte následující otázky: + +- Které funkce patří k sobě? Každá sada funkcí může být nejlepší ve svém vlastním kontraktu. +- Které funkce nevyžadují čtení stavu kontraktu nebo jen jeho specifické podmnožiny? +- Můžete rozdělit úložiště a funkcionalitu? + +### Knihovny {#libraries} + +Jedním z jednoduchých způsobů, jak přesunout kód funkcionality mimo úložiště, je použití [knihovny](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#libraries). Funkce knihovny nedeklarujte jako interní (internal), protože ty budou během kompilace přímo [přidány do kontraktu](https://ethereum.stackexchange.com/questions/12975/are-internal-functions-in-libraries-not-covered-by-linking). Pokud však použijete veřejné (public) funkce, budou se ve skutečnosti nacházet v samostatném kontraktu knihovny. Zvažte použití [using for](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#using-for), aby bylo používání knihoven pohodlnější. + +### Proxy {#proxies} + +Pokročilejší strategií by byl systém proxy. Knihovny na pozadí používají `DELEGATECALL`, který jednoduše provede funkci jiného kontraktu se stavem volajícího kontraktu. Více informací o proxy systémech se dozvíte v [tomto příspěvku na blogu](https://hackernoon.com/how-to-make-smart-contracts-upgradable-2612e771d5a2). Poskytují vám více funkcí, např. umožňují upgradovatelnost, ale také přidávají velkou složitost. Nepřidával bych je jen kvůli zmenšení velikosti kontraktu, pokud to z jakéhokoli důvodu není vaše jediná možnost. + +## Střední dopad {#medium-impact} + +### Odstraňte funkce {#remove-functions} + +Tohle by mělo být zřejmé. Funkce poměrně značně zvětšují velikost kontraktu. + +- **Externí (external)**: Často přidáváme mnoho view funkcí z důvodu pohodlí. To je naprosto v pořádku, dokud nenarazíte na limit velikosti. Pak byste se mohli opravdu zamyslet nad odstraněním všech funkcí kromě těch naprosto nezbytných. +- **Interní (internal)**: Můžete také odstranit interní/privátní (internal/private) funkce a jednoduše vložit kód přímo, pokud je funkce volána pouze jednou. + +### Vyhněte se dalším proměnným {#avoid-additional-variables} + +```solidity +function get(uint id) returns (address,address) { + MyStruct memory myStruct = myStructs[id]; + return (myStruct.addr1, myStruct.addr2); +} +``` + +```solidity +function get(uint id) returns (address,address) { + return (myStructs[id].addr1, myStructs[id].addr2); +} +``` + +Takováto jednoduchá změna představuje rozdíl **0,28 kb**. Je pravděpodobné, že ve svých kontraktech najdete mnoho podobných situací, a ty se mohou opravdu nasčítat do významných hodnot. + +### Zkraťte chybové zprávy {#shorten-error-message} + +Dlouhé revertovací zprávy a zejména mnoho různých revertovacích zpráv může kontrakt nafouknout. Místo toho používejte krátké chybové kódy a dekódujte je ve svém kontraktu. Dlouhá zpráva by mohla být mnohem kratší: + +```solidity +require(msg.sender == owner, "Tuto funkci může volat pouze vlastník tohoto kontraktu"); +``` + +```solidity +require(msg.sender == owner, "OW1"); +``` + +### Používejte vlastní chyby namísto chybových zpráv + +Vlastní chyby byly zavedeny v [Solidity 0.8.4](https://blog.soliditylang.org/2021/04/21/custom-errors/). Jsou skvělým způsobem, jak zmenšit velikost vašich kontraktů, protože jsou kódovány v ABI jako selektory (stejně jako funkce). + +```solidity +error Unauthorized(); + +if (msg.sender != owner) { + revert Unauthorized(); +} +``` + +### Zvažte nízkou hodnotu runs v optimalizátoru {#consider-a-low-run-value-in-the-optimizer} + +Můžete také změnit nastavení optimalizátoru. Výchozí hodnota 200 znamená, že se snaží optimalizovat bytecode tak, jako by byla funkce volána 200krát. Pokud ji změníte na 1, v podstatě říkáte optimalizátoru, aby optimalizoval pro případ, že každá funkce bude spuštěna pouze jednou. Optimalizovaná funkce pro jednorázové spuštění znamená, že je optimalizována pro samotné nasazení. Mějte na paměti, že **to zvyšuje [náklady na palivo](/developers/docs/gas/) za spuštění funkcí**, takže to možná nebudete chtít udělat. + +## Malý dopad {#small-impact} + +### Vyhněte se předávání struktur (structs) funkcím {#avoid-passing-structs-to-functions} + +Pokud používáte [ABIEncoderV2](https://solidity.readthedocs.io/en/v0.6.10/layout-of-source-files.html#abiencoderv2), může pomoci nepředávat struktury funkci. Namísto předávání parametru jako struktury předejte požadované parametry přímo. V tomto příkladu jsme ušetřili dalších **0,1 kb**. + +```solidity +function get(uint id) returns (address,address) { + return _get(myStruct); +} + +function _get(MyStruct memory myStruct) private view returns(address,address) { + return (myStruct.addr1, myStruct.addr2); +} +``` + +```solidity +function get(uint id) returns(address,address) { + return _get(myStructs[id].addr1, myStructs[id].addr2); +} + +function _get(address addr1, address addr2) private view returns(address,address) { + return (addr1, addr2); +} +``` + +### Deklarujte správnou viditelnost pro funkce a proměnné {#declare-correct-visibility-for-functions-and-variables} + +- Funkce nebo proměnné, které jsou volány pouze zvenčí? Deklarujte je jako `external` namísto `public`. +- Funkce nebo proměnné volané pouze v rámci kontraktu? Deklarujte je jako `private` nebo `internal` namísto `public`. + +### Odstraňte modifikátory {#remove-modifiers} + +Modifikátory, zejména při intenzivním používání, mohou mít významný dopad na velikost kontraktu. Zvažte jejich odstranění a místo nich použijte funkce. + +```solidity +modifier checkStuff() {} + +function doSomething() checkStuff {} +``` + +```solidity +function checkStuff() private {} + +function doSomething() { checkStuff(); } +``` + +Tyto tipy by vám měly pomoci výrazně zmenšit velikost kontraktu. Ještě jednou zdůrazňuji, že pro co největší dopad se vždy zaměřte na rozdělení kontraktů, pokud je to možné. diff --git a/public/content/translations/cs/developers/tutorials/eip-1271-smart-contract-signatures/index.md b/public/content/translations/cs/developers/tutorials/eip-1271-smart-contract-signatures/index.md new file mode 100644 index 00000000000..19ade3a6023 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/eip-1271-smart-contract-signatures/index.md @@ -0,0 +1,129 @@ +--- +title: "EIP-1271: Podepisování a ověřování podpisů chytrých kontraktů" +description: "Přehled vytváření a ověřování podpisů chytrých kontraktů pomocí EIP-1271. Projdeme si také implementaci EIP-1271 použitou v Safe (dříve Gnosis Safe), abychom vývojářům chytrých kontraktů poskytli konkrétní příklad, na kterém mohou stavět." +author: Nathan H. Leung +lang: cs +tags: + [ + "eip-1271", + "chytré kontrakty", + "ověřování", + "podepisování" + ] +skill: intermediate +published: 2023-01-12 +--- + +Standard [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) umožňuje chytrým kontraktům ověřovat podpisy. + +V tomto tutoriálu poskytneme přehled digitálních podpisů, pozadí EIP-1271 a specifické implementace EIP-1271 používané službou [Safe](https://safe.global/) (dříve Gnosis Safe). Celkově to může sloužit jako výchozí bod pro implementaci EIP-1271 ve vašich vlastních kontraktech. + +## Co je to podpis? + +V tomto kontextu je podpis (přesněji „digitální podpis“) zpráva plus nějaký druh důkazu, že zpráva pochází od konkrétní osoby/odesílatele/adresy. + +Digitální podpis může vypadat například takto: + +1. Zpráva: „Chci se na tuto webovou stránku přihlásit pomocí své peněženky Ethereum.“ +2. Podepisující: Moje adresa je `0x000…` +3. Důkaz: Zde je důkaz, že já, `0x000...`, jsem skutečně vytvořil celou tuto zprávu (obvykle se jedná o něco kryptografického). + +Je důležité si uvědomit, že digitální podpis zahrnuje jak „zprávu“, tak „podpis“. + +Proč? Pokud byste mi například dali k podpisu smlouvu a já bych odtrhl stránku s podpisem a vrátil vám pouze své podpisy bez zbytku smlouvy, smlouva by nebyla platná. + +Stejně tak digitální podpis bez přidružené zprávy nic neznamená! + +## Proč existuje EIP-1271? + +Abyste mohli vytvořit digitální podpis pro použití na blockchainechech založených na Ethereu, obecně potřebujete tajný privátní klíč, který nikdo jiný nezná. Díky tomu je váš podpis skutečně váš (nikdo jiný nemůže vytvořit stejný podpis bez znalosti tajného klíče). + +Váš účet na Ethereu (tj. váš externě vlastněný účet / EOA) má s ním spojený privátní klíč, a to je privátní klíč, který se obvykle používá, když vás web nebo dapp požádá o podpis (např. pro „Přihlásit se pomocí Etherea“). + +Aplikace může [ověřit podpis](https://www.alchemy.com/docs/how-to-verify-a-message-signature-on-ethereum), který vytvoříte pomocí knihovny třetí strany, jako je ethers.js, [aniž by znala váš privátní klíč](https://en.wikipedia.org/wiki/Public-key_cryptography), a být si jistá, že jste to byli _vy_, kdo podpis vytvořil. + +> Ve skutečnosti, protože digitální podpisy EOA používají kryptografii s veřejným klíčem, mohou být generovány a ověřovány **mimo blockchain**! Takto funguje hlasování v DAO bez poplatků – místo odesílání hlasů na blockchainu lze digitální podpisy vytvářet a ověřovat mimo blockchain pomocí kryptografických knihoven. + +Zatímco účty EOA mají privátní klíč, účty chytrých kontraktů nemají žádný privátní ani tajný klíč (takže "Přihlásit se pomocí Etherea" atd. nemůže nativně fungovat s účty chytrých kontraktů). + +Problém, který se EIP-1271 snaží vyřešit: jak můžeme poznat, že podpis chytrého kontraktu je platný, pokud chytrý kontrakt nemá žádné „tajemství“, které by mohl do podpisu začlenit? + +## Jak EIP-1271 funguje? + +Chytré kontrakty nemají privátní klíče, které by se daly použít k podepisování zpráv. Jak tedy poznáme, zda je podpis autentický? + +Jedním z nápadů je, že se můžeme chytrého kontraktu jednoduše _zeptat_, zda je podpis autentický! + +EIP-1271 standardizuje myšlenku „zeptat se“ chytrého kontraktu, zda je daný podpis platný. + +Kontrakt, který implementuje EIP-1271, musí mít funkci s názvem `isValidSignature`, která přijímá zprávu a podpis. Kontrakt pak může spustit nějakou ověřovací logiku (specifikace zde nevynucuje nic konkrétního) a poté vrátit hodnotu označující, zda je podpis platný, či nikoli. + +Pokud `isValidSignature` vrátí platný výsledek, je to v podstatě jako by kontrakt říkal „ano, schvaluji tento podpis + zprávu!“ + +### Rozhraní + +Zde je přesné rozhraní ve specifikaci EIP-1271 (o parametru `_hash` budeme mluvit níže, ale prozatím si ho představte jako zprávu, která je ověřována): + +```jsx +pragma solidity ^0.5.0; + +contract ERC1271 { + + // bytes4(keccak256("isValidSignature(bytes32,bytes)") + bytes4 constant internal MAGICVALUE = 0x1626ba7e; + + /** + * @dev Měla by vrátit, zda je poskytnutý podpis platný pro poskytnutý haš + * @param _hash Haš dat k podepsání + * @param _signature Pole bajtů podpisu spojené s _hash + * + * MUSÍ vrátit magickou hodnotu bytes4 0x1626ba7e, když funkce projde. + * NESMÍ upravovat stav (pomocí STATICCALL pro solc < 0.5, modifikátor view pro solc > 0.5) + * MUSÍ povolit externí volání + */ + function isValidSignature( + bytes32 _hash, + bytes memory _signature) + public + view + returns (bytes4 magicValue); +} +``` + +## Příklad implementace EIP-1271: Safe + +Kontrakty mohou implementovat `isValidSignature` mnoha způsoby — specifikace neříká mnoho o přesné implementaci. + +Jedním z pozoruhodných kontraktů, který implementuje EIP-1271, je Safe (dříve Gnosis Safe). + +V kódu Safe je `isValidSignature` [implementována](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol) tak, že podpisy lze vytvářet a ověřovat [dvěma způsoby](https://ethereum.stackexchange.com/questions/122635/signing-messages-as-a-gnosis-safe-eip1271-support): + +1. Zprávy na blockchainu + 1. Vytvoření: vlastník Safe vytvoří novou transakci Safe k „podepsání“ zprávy a předá zprávu jako data do transakce. Jakmile transakci podepíše dostatek vlastníků k dosažení prahu pro multisig, transakce se odešle a spustí. V transakci je funkce Safe s názvem (`signMessage(bytes calldata _data)`), která přidá zprávu na seznam „schválených“ zpráv. + 2. Ověření: zavolejte `isValidSignature` na kontraktu Safe a předejte zprávu k ověření jako parametr zprávy a [prázdnou hodnotu pro parametr podpisu](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol#L32) (tj. `0x`). Safe uvidí, že parametr podpisu je prázdný, a místo kryptografického ověření podpisu bude vědět, že má pouze zkontrolovat, zda je zpráva na seznamu „schválených“ zpráv. +2. Zprávy mimo blockchain: + 1. Vytvoření: vlastník Safe vytvoří zprávu mimo blockchain, poté nechá ostatní vlastníky Safe podepsat zprávu jednotlivě, dokud nebude dostatek podpisů k překonání prahu schválení multisig. + 2. Ověření: zavolejte `isValidSignature`. Do parametru zprávy předejte zprávu k ověření. Do parametru podpisu předejte jednotlivé podpisy všech vlastníků Safe, všechny spojené za sebou. Safe zkontroluje, že je dostatek podpisů pro splnění prahu **a** že každý podpis je platný. Pokud ano, vrátí hodnotu označující úspěšné ověření podpisu. + +## Co přesně je parametr `_hash`? Proč nepředat celou zprávu? + +Možná jste si všimli, že funkce `isValidSignature` v [rozhraní EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) nepřijímá samotnou zprávu, ale místo toho parametr `_hash`. To znamená, že místo předání celé zprávy libovolné délky do `isValidSignature` předáme 32bajtový haš zprávy (obvykle keccak256). + +Každý bajt calldata — tj. data parametrů funkce předaná funkci chytrého kontraktu — [stojí 16 jednotek paliva (4 jednotky paliva, pokud je to nulový bajt)](https://eips.ethereum.org/EIPS/eip-2028), takže to může ušetřit spoustu paliva, pokud je zpráva dlouhá. + +### Předchozí specifikace EIP-1271 + +Existují specifikace EIP-1271, které mají funkci `isValidSignature` s prvním parametrem typu `bytes` (libovolná délka, místo pevné délky `bytes32`) a názvem parametru `message`. Toto je [starší verze](https://github.com/safe-global/safe-contracts/issues/391#issuecomment-1075427206) standardu EIP-1271. + +## Jak by měl být EIP-1271 implementován v mých vlastních kontraktech? + +Specifikace je v tomto ohledu velmi otevřená. Implementace Safe má několik dobrých nápadů: + +- Můžete považovat podpisy EOA od "vlastníka" kontraktu za platné. +- Můžete si uložit seznam schválených zpráv a pouze ty považovat za platné. + +Nakonec je to na vás jako na vývojáři kontraktu! + +## Závěr + +[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) je všestranný standard, který umožňuje chytrým kontraktům ověřovat podpisy. Otevírá dveře pro chytré kontrakty, aby se chovaly více jako EOA – například poskytuje způsob, jak "Přihlásit se pomocí Etherea" funguje s chytrými kontrakty – a může být implementován mnoha způsoby (Safe má netriviální, zajímavou implementaci k zvážení). diff --git a/public/content/translations/cs/developers/tutorials/erc-721-vyper-annotated-code/index.md b/public/content/translations/cs/developers/tutorials/erc-721-vyper-annotated-code/index.md new file mode 100644 index 00000000000..9b0c11391de --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/erc-721-vyper-annotated-code/index.md @@ -0,0 +1,713 @@ +--- +title: "Průchod kontraktem Vyper ERC-721" +description: Kontrakt ERC-721 od Ryuyi Nakamury a jak funguje +author: Ori Pomerantz +lang: cs +tags: [ "vyper", "erc-721", "python" ] +skill: beginner +published: 2021-04-01 +--- + +## Úvod {#introduction} + +Standard [ERC-721](/developers/docs/standards/tokens/erc-721/) se používá k držení vlastnictví nezaměnitelných tokenů (NFT). +Tokeny [ERC-20](/developers/docs/standards/tokens/erc-20/) se chovají jako komodita, protože mezi jednotlivými tokeny není žádný rozdíl. +Naproti tomu tokeny ERC-721 jsou navrženy pro aktiva, která jsou si podobná, ale ne totožná, jako jsou například různé [kreslené kočky](https://www.cryptokitties.co/) +nebo vlastnická práva k různým nemovitostem. + +V tomto článku budeme analyzovat [kontrakt ERC-721 od Ryuyi Nakamury](https://github.com/vyperlang/vyper/blob/master/examples/tokens/ERC721.vy). +Tento kontrakt je napsán v jazyce [Vyper](https://vyper.readthedocs.io/en/latest/index.html), kontraktovém jazyce podobném Pythonu, který je navržen tak, aby bylo +psaní nezabezpečeného kódu obtížnější než v Solidity. + +## Kontrakt {#contract} + +```python +# @dev Implementace standardu nezaměnitelného tokenu ERC-721. +# @author Ryuya Nakamura (@nrryuya) +# Upraveno z: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy +``` + +Komentáře ve Vyperu, stejně jako v Pythonu, začínají mřížkou (`#`) a pokračují až do konce řádku. Komentáře, které obsahují +`@`, se používají v [NatSpecu](https://vyper.readthedocs.io/en/latest/natspec.html) k vytvoření lidsky čitelné +dokumentace. + +```python +from vyper.interfaces import ERC721 + +implements: ERC721 +``` + +Rozhraní ERC-721 je zabudováno do jazyka Vyper. +[Definici kódu naleznete zde](https://github.com/vyperlang/vyper/blob/master/vyper/builtin_interfaces/ERC721.py). +Definice rozhraní je napsána v Pythonu, nikoli ve Vyperu, protože rozhraní se používají nejen v rámci +blockchainu, ale také při odesílání transakce do blockchainu z externího klienta, který může být napsán v +Pythonu. + +První řádek importuje rozhraní a druhý určuje, že ho zde implementujeme. + +### Rozhraní ERC721Receiver {#receiver-interface} + +```python +# Rozhraní pro kontrakt volaný funkcí safeTransferFrom() +interface ERC721Receiver: + def onERC721Received( +``` + +ERC-721 podporuje dva typy převodů: + +- `transferFrom`, který umožňuje odesílateli zadat jakoukoli cílovou adresu a přenáší odpovědnost + za převod na odesílatele. To znamená, že můžete provést převod na neplatnou adresu, v takovém případě + je NFT navždy ztraceno. +- `safeTransferFrom`, který kontroluje, zda je cílová adresa kontrakt. Pokud ano, kontrakt ERC-721 se + zeptá přijímajícího kontraktu, zda chce NFT přijmout. + +Aby mohl přijímající kontrakt odpovídat na požadavky `safeTransferFrom`, musí implementovat `ERC721Receiver`. + +```python + _operator: address, + _from: address, +``` + +Adresa `_from` je aktuální vlastník tokenu. Adresa `_operator` je ta, která +požadovala převod (tyto dvě adresy se mohou lišit z důvodu povolenek). + +```python + _tokenId: uint256, +``` + +ID tokenů ERC-721 jsou 256bitové. Obvykle se vytvářejí hašováním popisu toho, +co token představuje. + +```python + _data: Bytes[1024] +``` + +Požadavek může obsahovat až 1024 bajtů uživatelských dat. + +```python + ) -> bytes32: view +``` + +Aby se předešlo případům, kdy kontrakt omylem přijme převod, není návratová hodnota booleovská, +ale 256 bitů s konkrétní hodnotou. + +Tato funkce je `view`, což znamená, že může číst stav blockchainu, ale nemůže ho měnit. + +### Události {#events} + +[Události](https://media.consensys.net/technical-introduction-to-events-and-logs-in-ethereum-a074d65dd61e) +se vysílají za účelem informování uživatelů a serverů mimo blockchain o událostech. Všimněte si, že obsah událostí +není dostupný pro kontrakty na blockchainu. + +```python +# @dev Vysílá se, když se jakýmkoli mechanismem změní vlastnictví jakéhokoli NFT. Tato událost se vysílá, když jsou NFT +# vytvořeny (`from` == 0) a zničeny (`to` == 0). Výjimka: během vytváření kontraktu může být +# vytvořen a přiřazen libovolný počet NFT bez vyslání události Transfer. V okamžiku jakéhokoli +# převodu se schválená adresa pro dané NFT (pokud existuje) vynuluje. +# @param _from Odesílatel NFT (pokud je adresa nulová, značí to vytvoření tokenu). +# @param _to Příjemce NFT (pokud je adresa nulová, značí to zničení tokenu). +# @param _tokenId NFT, které bylo převedeno. +event Transfer: + sender: indexed(address) + receiver: indexed(address) + tokenId: indexed(uint256) +``` + +Je to podobné události Transfer v ERC-20 s tím rozdílem, že místo částky hlásíme `tokenId`. +Nikdo nevlastní nulovou adresu, takže ji zvykově používáme k hlášení o vytvoření a zničení tokenů. + +```python +# @dev Vysílá se, když je schválená adresa pro NFT změněna nebo znovu potvrzena. Nulová +# adresa značí, že neexistuje žádná schválená adresa. Když se vyšle událost Transfer, značí to +# také, že schválená adresa pro dané NFT (pokud existuje) se vynuluje. +# @param _owner Vlastník NFT. +# @param _approved Adresa, kterou schvalujeme. +# @param _tokenId NFT, které schvalujeme. +event Approval: + owner: indexed(address) + approved: indexed(address) + tokenId: indexed(uint256) +``` + +Schválení ERC-721 je podobné povolence v ERC-20. Konkrétní adresa má povoleno převést konkrétní +token. To dává kontraktům mechanismus, jak reagovat na přijetí tokenu. Kontrakty nemohou +naslouchat událostem, takže pokud jim token pouze převedete, \"neví\" o tom. Tímto způsobem +vlastník nejprve podá schválení a poté zašle kontraktu žádost: \"Schválil jsem vám převod tokenu +X, prosím, proveďte...\". + +Jedná se o návrhové rozhodnutí, aby byl standard ERC-721 podobný standardu ERC-20. Protože +tokeny ERC-721 nejsou zaměnitelné, může kontrakt také identifikovat, že získal konkrétní token, pohledem +na vlastnictví tokenu. + +```python +# @dev Vysílá se, když je operátor pro vlastníka povolen nebo zakázán. Operátor může spravovat +# všechny NFT vlastníka. +# @param _owner Vlastník NFT. +# @param _operator Adresa, které nastavujeme práva operátora. +# @param _approved Stav práv operátora (true, pokud jsou práva udělena, a false, pokud jsou +# odvolána). +event ApprovalForAll: + owner: indexed(address) + operator: indexed(address) + approved: bool +``` + +Někdy je užitečné mít _operátora_, který může spravovat všechny tokeny určitého typu na účtu (ty, které jsou spravovány +konkrétním kontraktem), podobně jako plná moc. Například bych mohl chtít takovou pravomoc udělit kontraktu, který kontroluje, zda +jsem ho nekontaktoval po dobu šesti měsíců, a pokud ano, rozdělí můj majetek mým dědicům (pokud o to některý z nich požádá; kontrakty +nemohou dělat nic, aniž by byly volány transakcí). V ERC-20 můžeme dědickému kontraktu dát vysokou povolenku, +ale to u ERC-721 nefunguje, protože tokeny nejsou zaměnitelné. Toto je ekvivalent. + +Hodnota `approved` nám říká, zda se událost týká schválení, nebo jeho odvolání. + +### Stavové proměnné {#state-vars} + +Tyto proměnné obsahují aktuální stav tokenů: které jsou dostupné a kdo je vlastní. Většina z nich +jsou objekty `HashMap`, [jednosměrná mapování, která existují mezi dvěma typy](https://vyper.readthedocs.io/en/latest/types.html#mappings). + +```python +# @dev Mapování z ID NFT na adresu, která jej vlastní. +idToOwner: HashMap[uint256, address] + +# @dev Mapování z ID NFT na schválenou adresu. +idToApprovals: HashMap[uint256, address] +``` + +Identity uživatelů a kontraktů v Ethereu jsou reprezentovány 160bitovými adresami. Tyto dvě proměnné mapují +ID tokenů na jejich vlastníky a ty, kteří mají schváleno je převést (maximálně jeden pro každý token). V Ethereu +jsou neinicializovaná data vždy nulová, takže pokud pro daný token neexistuje vlastník nebo schválený převodce, je jeho hodnota +nulová. + +```python +# @dev Mapování z adresy vlastníka na počet jeho tokenů. +ownerToNFTokenCount: HashMap[address, uint256] +``` + +Tato proměnná uchovává počet tokenů pro každého vlastníka. Neexistuje žádné mapování od vlastníků k tokenům, takže +jediný způsob, jak identifikovat tokeny, které konkrétní vlastník vlastní, je podívat se zpět do historie událostí blockchainu +a najít příslušné události `Transfer`. Tuto proměnnou můžeme použít k tomu, abychom věděli, kdy máme všechny NFT a nemusíme +se dívat ještě dále do minulosti. + +Všimněte si, že tento algoritmus funguje pouze pro uživatelská rozhraní a externí servery. Kód běžící na samotném blockchainu +nemůže číst minulé události. + +```python +# @dev Mapování z adresy vlastníka na mapování adres operátorů. +ownerToOperators: HashMap[address, HashMap[address, bool]] +``` + +Účet může mít více než jednoho operátora. Jednoduchá `HashMap` je pro +jejich sledování nedostatečná, protože každý klíč vede k jedné hodnotě. Místo toho můžete jako hodnotu použít +`HashMap[address, bool]`. Standardně je hodnota pro každou adresu `False`, což znamená, že +není operátorem. Podle potřeby můžete nastavit hodnoty na `True`. + +```python +# @dev Adresa mintera, který může razit tokeny +minter: address +``` + +Nové tokeny musí být nějakým způsobem vytvořeny. V tomto kontraktu existuje jediná entita, která to má povoleno, a to +`minter`. To je například pravděpodobně dostačující pro hru. Pro jiné účely může být nutné +vytvořit složitější obchodní logiku. + +```python +# @dev Mapování ID rozhraní na booleovskou hodnotu, zda je či není podporováno +supportedInterfaces: HashMap[bytes32, bool] + +# @dev ID rozhraní ERC165 standardu ERC165 +ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7 + +# @dev ID rozhraní ERC165 standardu ERC721 +ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cd +``` + +[ERC-165](https://eips.ethereum.org/EIPS/eip-165) specifikuje mechanismus, jak může kontrakt zveřejnit, jak s ním mohou aplikace +komunikovat a kterým standardům ERC odpovídá. V tomto případě kontrakt odpovídá standardům ERC-165 a ERC-721. + +### Funkce {#functions} + +Toto jsou funkce, které skutečně implementují ERC-721. + +#### Konstruktor {#constructor} + +```python +@external +def __init__(): +``` + +Ve Vyperu, stejně jako v Pythonu, se funkce konstruktoru nazývá `__init__`. + +```python + """ + @dev Konstruktor kontraktu. + """ +``` + +V Pythonu a ve Vyperu můžete také vytvořit komentář tak, že zadáte víceřádkový řetězec (který začíná a končí +`"""`) a nijak ho nepoužijete. Tyto komentáře mohou obsahovat také +[NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html). + +```python + self.supportedInterfaces[ERC165_INTERFACE_ID] = True + self.supportedInterfaces[ERC721_INTERFACE_ID] = True + self.minter = msg.sender +``` + +Pro přístup ke stavovým proměnným použijte `self.`(opět, stejně jako v Pythonu). + +#### Funkce view {#views} + +Jedná se o funkce, které nemění stav blockchainu, a proto mohou být provedeny zdarma, +pokud jsou volány externě. Pokud jsou funkce view volány kontraktem, stále musí být provedeny na +každém uzlu, a proto stojí palivo. + +```python +@view +@external +``` + +Tato klíčová slova před definicí funkce, která začínají zavináčem (`@`), se nazývají _dekorace_. Určují +okolnosti, za kterých lze funkci volat. + +- `@view` určuje, že tato funkce je view. +- `@external` určuje, že tato konkrétní funkce může být volána transakcemi a jinými kontrakty. + +```python +def supportsInterface(_interfaceID: bytes32) -> bool: +``` + +Na rozdíl od Pythonu je Vyper [jazyk se statickým typováním](https://wikipedia.org/wiki/Type_system#Static_type_checking). +Nelze deklarovat proměnnou nebo parametr funkce bez identifikace [datového typu](https://vyper.readthedocs.io/en/latest/types.html). V tomto případě je vstupní parametr `bytes32`, 256bitová hodnota +(256 bitů je nativní velikost slova [Ethereum Virtual Machine (EVM)](/developers/docs/evm/)). Výstupem je booleovská +hodnota. Názvy parametrů funkcí zvykově začínají podtržítkem (`_`). + +```python + """ + @dev Identifikace rozhraní je specifikována v ERC-165. + @param _interfaceID ID rozhraní + """ + return self.supportedInterfaces[_interfaceID] +``` + +Vrátí hodnotu z `self.supportedInterfaces` HashMap, která je nastavena v konstruktoru (`__init__`). + +```python +### FUNKCE VIEW ### + +``` + +Toto jsou funkce view, které zpřístupňují informace o tokenech uživatelům a jiným kontraktům. + +```python +@view +@external +def balanceOf(_owner: address) -> uint256: + """ + @dev Vrátí počet NFT vlastněných `_owner`. + Vrátí chybu, pokud je `_owner` nulová adresa. NFT přiřazené k nulové adrese jsou považovány za neplatné. + @param _owner Adresa, pro kterou se má dotazovat na zůstatek. + """ + assert _owner != ZERO_ADDRESS +``` + +Tento řádek [zajišťuje](https://vyper.readthedocs.io/en/latest/statements.html#assert), že `_owner` není +nulová adresa. Pokud ano, dojde k chybě a operace se vrátí zpět. + +```python + return self.ownerToNFTokenCount[_owner] + +@view +@external +def ownerOf(_tokenId: uint256) -> address: + """ + @dev Vrátí adresu vlastníka NFT. + Vrátí chybu, pokud `_tokenId` není platné NFT. + @param _tokenId Identifikátor NFT. + """ + owner: address = self.idToOwner[_tokenId] + # Vrátí chybu, pokud `_tokenId` není platné NFT + assert owner != ZERO_ADDRESS + return owner +``` + +V Ethereum Virtual Machine (EVM) je jakékoli úložiště, které v sobě nemá uloženou hodnotu, nulové. +Pokud na `_tokenId` není žádný token, pak je hodnota `self.idToOwner[_tokenId]` nulová. V takovém případě +se funkce vrátí zpět. + +```python +@view +@external +def getApproved(_tokenId: uint256) -> address: + """ + @dev Získá schválenou adresu pro jedno NFT. + Vrátí chybu, pokud `_tokenId` není platné NFT. + @param _tokenId ID NFT, pro které se má dotazovat na schválení. + """ + # Vrátí chybu, pokud `_tokenId` není platné NFT + assert self.idToOwner[_tokenId] != ZERO_ADDRESS + return self.idToApprovals[_tokenId] +``` + +Všimněte si, že `getApproved` _může_ vrátit nulu. Pokud je token platný, vrátí `self.idToApprovals[_tokenId]`. +Pokud neexistuje žádný schvalovatel, tato hodnota je nulová. + +```python +@view +@external +def isApprovedForAll(_owner: address, _operator: address) -> bool: + """ + @dev Zkontroluje, zda je `_operator` schválený operátor pro `_owner`. + @param _owner Adresa, která vlastní NFT. + @param _operator Adresa, která jedná jménem vlastníka. + """ + return (self.ownerToOperators[_owner])[_operator] +``` + +Tato funkce kontroluje, zda má `_operator` povoleno spravovat všechny tokeny `_owner` v tomto kontraktu. +Protože může existovat více operátorů, jedná se o dvouúrovňovou HashMap. + +#### Pomocné funkce pro převod {#transfer-helpers} + +Tyto funkce implementují operace, které jsou součástí převodu nebo správy tokenů. + +```python + +### POMOCNÉ FUNKCE PRO PŘEVOD ### + +@view +@internal +``` + +Tato dekorace, `@internal`, znamená, že funkce je přístupná pouze z jiných funkcí v rámci +stejného kontraktu. Názvy těchto funkcí zvykově také začínají podtržítkem (`_`). + +```python +def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool: + """ + @dev Vrátí, zda daný spender může převést dané ID tokenu + @param spender adresa spendera, na kterou se dotazujeme + @param tokenId uint256 ID tokenu, který má být převeden + @return bool zda je msg.sender schválen pro dané ID tokenu, + je operátorem vlastníka, nebo je vlastníkem tokenu + """ + owner: address = self.idToOwner[_tokenId] + spenderIsOwner: bool = owner == _spender + spenderIsApproved: bool = _spender == self.idToApprovals[_tokenId] + spenderIsApprovedForAll: bool = (self.ownerToOperators[owner])[_spender] + return (spenderIsOwner or spenderIsApproved) or spenderIsApprovedForAll +``` + +Existují tři způsoby, jak může adresa získat povolení k převodu tokenu: + +1. Adresa je vlastníkem tokenu +2. Adresa má schváleno utratit tento token +3. Adresa je operátorem pro vlastníka tokenu + +Výše uvedená funkce může být view, protože nemění stav. Pro snížení provozních nákladů by každá +funkce, která _může_ být view, _měla_ být view. + +```python +@internal +def _addTokenTo(_to: address, _tokenId: uint256): + """ + @dev Přidá NFT k dané adrese + Vrátí chybu, pokud je `_tokenId` vlastněno někým jiným. + """ + # Vrátí chybu, pokud je `_tokenId` vlastněno někým jiným + assert self.idToOwner[_tokenId] == ZERO_ADDRESS + # Změní vlastníka + self.idToOwner[_tokenId] = _to + # Změní sledování počtu + self.ownerToNFTokenCount[_to] += 1 + + +@internal +def _removeTokenFrom(_from: address, _tokenId: uint256): + """ + @dev Odebere NFT z dané adresy + Vrátí chybu, pokud `_from` není aktuální vlastník. + """ + # Vrátí chybu, pokud `_from` není aktuální vlastník + assert self.idToOwner[_tokenId] == _from + # Změní vlastníka + self.idToOwner[_tokenId] = ZERO_ADDRESS + # Změní sledování počtu + self.ownerToNFTokenCount[_from] -= 1 +``` + +Pokud se vyskytne problém s převodem, vrátíme volání zpět. + +```python +@internal +def _clearApproval(_owner: address, _tokenId: uint256): + """ + @dev Zruší schválení dané adresy + Vrátí chybu, pokud `_owner` není aktuální vlastník. + """ + # Vrátí chybu, pokud `_owner` není aktuální vlastník + assert self.idToOwner[_tokenId] == _owner + if self.idToApprovals[_tokenId] != ZERO_ADDRESS: + # Vynuluje schválení + self.idToApprovals[_tokenId] = ZERO_ADDRESS +``` + +Hodnotu změňte pouze v případě nutnosti. Stavové proměnné se nacházejí v úložišti. Zápis do úložiště je +jednou z nejdražších operací, které EVM (Ethereum Virtual Machine) provádí (z hlediska +[paliva](/developers/docs/gas/)). Proto je dobré ji minimalizovat, i zápis +existující hodnoty má vysoké náklady. + +```python +@internal +def _transferFrom(_from: address, _to: address, _tokenId: uint256, _sender: address): + """ + @dev Provede převod NFT. + Vrátí chybu, pokud `msg.sender` není aktuální vlastník, autorizovaný operátor nebo schválená + adresa pro toto NFT. (POZNÁMKA: `msg.sender` není povoleno v soukromé funkci, takže se předává `_sender`.) + Vrátí chybu, pokud je `_to` nulová adresa. + Vrátí chybu, pokud `_from` není aktuální vlastník. + Vrátí chybu, pokud `_tokenId` není platné NFT. + """ +``` + +Tuto interní funkci máme proto, že existují dva způsoby převodu tokenů (běžný a bezpečný), ale +chceme mít pouze jedno místo v kódu, kde to děláme, abychom usnadnili auditování. + +```python + # Zkontroluje požadavky + assert self._isApprovedOrOwner(_sender, _tokenId) + # Vrátí chybu, pokud je `_to` nulová adresa + assert _to != ZERO_ADDRESS + # Zruší schválení. Vrátí chybu, pokud `_from` není aktuální vlastník + self._clearApproval(_from, _tokenId) + # Odebere NFT. Vrátí chybu, pokud `_tokenId` není platné NFT + self._removeTokenFrom(_from, _tokenId) + # Přidá NFT + self._addTokenTo(_to, _tokenId) + # Zapíše převod do protokolu + log Transfer(_from, _to, _tokenId) +``` + +Pro vyslání události ve Vyperu použijete příkaz `log` ([více podrobností zde](https://vyper.readthedocs.io/en/latest/event-logging.html#event-logging)). + +#### Funkce pro převod {#transfer-funs} + +```python + +### FUNKCE PRO PŘEVOD ### + +@external +def transferFrom(_from: address, _to: address, _tokenId: uint256): + """ + @dev Vrátí chybu, pokud `msg.sender` není aktuální vlastník, autorizovaný operátor nebo schválená + adresa pro toto NFT. + Vrátí chybu, pokud `_from` není aktuální vlastník. + Vrátí chybu, pokud je `_to` nulová adresa. + Vrátí chybu, pokud `_tokenId` není platné NFT. + @notice Volající je odpovědný za potvrzení, že `_to` je schopno přijímat NFT, jinak + mohou být trvale ztraceny. + @param _from Aktuální vlastník NFT. + @param _to Nový vlastník. + @param _tokenId NFT k převodu. + """ + self._transferFrom(_from, _to, _tokenId, msg.sender) +``` + +Tato funkce umožňuje převod na libovolnou adresu. Pokud adresa není uživatel nebo kontrakt, který +ví, jak převádět tokeny, jakýkoli token, který převedete, zůstane na této adrese a bude k ničemu. + +```python +@external +def safeTransferFrom( + _from: address, + _to: address, + _tokenId: uint256, + _data: Bytes[1024]=b"" + ): + """ + @dev Přenáší vlastnictví NFT z jedné adresy na jinou. + Vrátí chybu, pokud `msg.sender` není aktuální vlastník, autorizovaný operátor nebo + schválená adresa pro toto NFT. + Vrátí chybu, pokud `_from` není aktuální vlastník. + Vrátí chybu, pokud je `_to` nulová adresa. + Vrátí chybu, pokud `_tokenId` není platné NFT. + Pokud je `_to` chytrý kontrakt, volá `onERC721Received` na `_to` a vrátí chybu, pokud + návratová hodnota není `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. + POZNÁMKA: bytes4 je reprezentováno jako bytes32 s výplní + @param _from Aktuální vlastník NFT. + @param _to Nový vlastník. + @param _tokenId NFT k převodu. + @param _data Dodatečná data bez specifikovaného formátu, odeslaná ve volání na `_to`. + """ + self._transferFrom(_from, _to, _tokenId, msg.sender) +``` + +Je v pořádku provést převod nejprve, protože pokud se vyskytne problém, stejně se vrátíme zpět, +takže vše provedené ve volání bude zrušeno. + +```python + if _to.is_contract: # zkontroluje, zda je `_to` účet kontraktu +``` + +Nejprve zkontrolujte, zda je adresa kontrakt (zda má kód). Pokud ne, předpokládejte, že se jedná o uživatelskou +adresu a uživatel bude moci token použít nebo ho převést. Ale nenechte se tím ukolébat +do falešného pocitu bezpečí. Můžete ztratit tokeny, i s `safeTransferFrom`, pokud je převedete +na adresu, ke které nikdo nezná privátní klíč. + +```python + returnValue: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) +``` + +Volejte cílový kontrakt, abyste zjistili, zda může přijímat tokeny ERC-721. + +```python + # Vrátí chybu, pokud je cílem převodu kontrakt, který neimplementuje 'onERC721Received' + assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes32) +``` + +Pokud je cílem kontrakt, ale takový, který nepřijímá tokeny ERC-721 (nebo který se rozhodl nepřijmout tento +konkrétní převod), vraťte se zpět. + +```python +@external +def approve(_approved: address, _tokenId: uint256): + """ + @dev Nastaví nebo znovu potvrdí schválenou adresu pro NFT. Nulová adresa značí, že neexistuje žádná schválená adresa. + Vrátí chybu, pokud `msg.sender` není aktuální vlastník NFT nebo autorizovaný operátor aktuálního vlastníka. + Vrátí chybu, pokud `_tokenId` není platné NFT. (POZNÁMKA: Toto není uvedeno v EIP) + Vrátí chybu, pokud je `_approved` aktuální vlastník. (POZNÁMKA: Toto není uvedeno v EIP) + @param _approved Adresa, která má být schválena pro dané ID NFT. + @param _tokenId ID tokenu, který má být schválen. + """ + owner: address = self.idToOwner[_tokenId] + # Vrátí chybu, pokud `_tokenId` není platné NFT + assert owner != ZERO_ADDRESS + # Vrátí chybu, pokud je `_approved` aktuální vlastník + assert _approved != owner +``` + +Pokud zvykově nechcete mít schvalovatele, jmenujte nulovou adresu, ne sebe. + +```python + # Zkontroluje požadavky + senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender + senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender] + assert (senderIsOwner or senderIsApprovedForAll) +``` + +Pro nastavení schválení můžete být buď vlastníkem, nebo operátorem autorizovaným vlastníkem. + +```python + # Nastaví schválení + self.idToApprovals[_tokenId] = _approved + log Approval(owner, _approved, _tokenId) + + +@external +def setApprovalForAll(_operator: address, _approved: bool): + """ + @dev Povolí nebo zakáže schválení pro třetí stranu („operátora“) ke správě všech + aktiv `msg.sender`. Také vysílá událost ApprovalForAll. + Vrátí chybu, pokud je `_operator` `msg.sender`. (POZNÁMKA: Toto není uvedeno v EIP) + @notice Toto funguje i v případě, že odesílatel v daném okamžiku nevlastní žádné tokeny. + @param _operator Adresa, která se má přidat do sady autorizovaných operátorů. + @param _approved True, pokud je operátor schválen, false pro odvolání schválení. + """ + # Vrátí chybu, pokud je `_operator` `msg.sender` + assert _operator != msg.sender + self.ownerToOperators[msg.sender][_operator] = _approved + log ApprovalForAll(msg.sender, _operator, _approved) +``` + +#### Ražba nových tokenů a zničení stávajících {#mint-burn} + +Účet, který vytvořil kontrakt, je `minter`, super uživatel, který je oprávněn razit +nové NFT. Ani on však nesmí pálit existující tokeny. To může udělat pouze vlastník nebo entita +autorizovaná vlastníkem. + +```python +### FUNKCE PRO RAŽBU A PÁLENÍ ### + +@external +def mint(_to: address, _tokenId: uint256) -> bool: +``` + +Tato funkce vždy vrátí `True`, protože pokud operace selže, vrátí se zpět. + +```python + """ + @dev Funkce pro ražbu tokenů + Vrátí chybu, pokud `msg.sender` není minter. + Vrátí chybu, pokud je `_to` nulová adresa. + Vrátí chybu, pokud je `_tokenId` vlastněno někým jiným. + @param _to Adresa, která obdrží ražené tokeny. + @param _tokenId ID tokenu k ražbě. + @return Booleovská hodnota, která značí, zda byla operace úspěšná. + """ +``` + +Pouze minter (účet, který vytvořil kontrakt ERC-721) může razit nové tokeny. To může být +v budoucnu problém, pokud budeme chtít změnit identitu mintera. V +produkčním kontraktu byste pravděpodobně chtěli funkci, která minterovi umožní převést +práva mintera na někoho jiného. + +```python + # Vrátí chybu, pokud je `_to` nulová adresa + assert _to != ZERO_ADDRESS + # Přidá NFT. Vrátí chybu, pokud je `_tokenId` vlastněno někým jiným + self._addTokenTo(_to, _tokenId) + log Transfer(ZERO_ADDRESS, _to, _tokenId) + return True +``` + +Ražba nových tokenů se zvykově počítá jako převod z nulové adresy. + +```python + +@external +def burn(_tokenId: uint256): + """ + @dev Spálí konkrétní token ERC721. + Vrátí chybu, pokud `msg.sender` není aktuální vlastník, autorizovaný operátor nebo schválená + adresa pro toto NFT. + Vrátí chybu, pokud `_tokenId` není platné NFT. + @param _tokenId uint256 id tokenu ERC721, který má být spálen. + """ + # Zkontroluje požadavky + assert self._isApprovedOrOwner(msg.sender, _tokenId) + owner: address = self.idToOwner[_tokenId] + # Vrátí chybu, pokud `_tokenId` není platné NFT + assert owner != ZERO_ADDRESS + self._clearApproval(owner, _tokenId) + self._removeTokenFrom(owner, _tokenId) + log Transfer(owner, ZERO_ADDRESS, _tokenId) +``` + +Každý, kdo má povoleno převést token, ho smí spálit. Ačkoli se pálení zdá být ekvivalentem +převodu na nulovou adresu, nulová adresa ve skutečnosti token neobdrží. To nám umožňuje +uvolnit veškeré úložiště, které bylo pro token použito, což může snížit náklady na palivo transakce. + +## Použití tohoto kontraktu {#using-contract} + +Na rozdíl od Solidity, Vyper nemá dědičnost. Jedná se o záměrné návrhové rozhodnutí, které má za cíl zpřehlednit kód, +a tím usnadnit jeho zabezpečení. Chcete-li tedy vytvořit svůj vlastní kontrakt Vyper ERC-721, vezměte tento +kontrakt a upravte ho +tak, aby implementoval obchodní logiku, kterou chcete. + +## Závěr {#conclusion} + +Pro shrnutí, zde jsou některé z nejdůležitějších myšlenek v tomto kontraktu: + +- Pro příjem tokenů ERC-721 s bezpečným převodem musí kontrakty implementovat rozhraní `ERC721Receiver`. +- I když použijete bezpečný převod, tokeny se mohou stále zaseknout, pokud je pošlete na adresu, jejíž privátní klíč + je neznámý. +- Když se vyskytne problém s operací, je dobré volání `vrátit`, nikoli jen vrátit + hodnotu selhání. +- Tokeny ERC-721 existují, když mají vlastníka. +- Existují tři způsoby, jak být oprávněn k převodu NFT. Můžete být vlastníkem, být schválen pro konkrétní token, + nebo být operátorem pro všechny tokeny vlastníka. +- Minulé události jsou viditelné pouze mimo blockchain. Kód běžící uvnitř blockchainu je nemůže zobrazit. + +Nyní jděte a implementujte bezpečné kontrakty Vyper. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). + diff --git a/public/content/translations/cs/developers/tutorials/erc20-annotated-code/index.md b/public/content/translations/cs/developers/tutorials/erc20-annotated-code/index.md new file mode 100644 index 00000000000..0b2658802a9 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/erc20-annotated-code/index.md @@ -0,0 +1,927 @@ +--- +title: "Podrobný průvodce kontraktem ERC-20" +description: "Co je v kontraktu OpenZeppelin ERC-20 a proč se tam nachází?" +author: Ori Pomerantz +lang: cs +tags: [ "solidity", "erc-20" ] +skill: beginner +published: 2021-03-09 +--- + +## Úvod {#introduction} + +Jedním z nejčastějších způsobů využití Etherea je vytvoření obchodovatelného tokenu pro skupinu, v podstatě jejich vlastní měny. Tyto tokeny obvykle dodržují standard +[ERC-20](/developers/docs/standards/tokens/erc-20/). Tento standard umožňuje psát nástroje, jako jsou pooly likvidity a peněženky, které fungují se všemi tokeny ERC-20. V tomto článku budeme analyzovat implementaci [OpenZeppelin Solidity ERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol) a také [definici rozhraní](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol). + +Jedná se o anotovaný zdrojový kód. Chcete-li implementovat ERC-20, +[přečtěte si tento návod](https://docs.openzeppelin.com/contracts/2.x/erc20-supply). + +## Rozhraní {#the-interface} + +Účelem standardu, jako je ERC-20, je umožnit mnoho implementací tokenů, které jsou interoperabilní napříč aplikacemi, jako jsou peněženky a decentralizované burzy. K tomu vytvoříme [rozhraní](https://www.geeksforgeeks.org/solidity/solidity-basics-of-interface/). Jakýkoli kód, který potřebuje použít kontrakt tokenu, +může použít stejné definice v rozhraní a být kompatibilní se všemi kontrakty tokenu, které jej používají, ať už je to peněženka, jako je MetaMask, dapp, jako je etherscan.io, nebo jiný kontrakt, jako je pool likvidity. + +![Ilustrace rozhraní ERC-20](erc20_interface.png) + +Pokud jste zkušený programátor, pravděpodobně si pamatujete, že jste podobné konstrukce viděli v [Javě](https://www.w3schools.com/java/java_interface.asp) +nebo dokonce v [hlavičkových souborech C](https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html). + +Toto je definice [rozhraní ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) +od OpenZeppelin. Jedná se o překlad [lidsky čitelného standardu](https://eips.ethereum.org/EIPS/eip-20) do kódu v Solidity. Samozřejmě samotné +rozhraní nedefinuje, _jak_ se má něco dělat. To je vysvětleno ve zdrojovém kódu kontraktu níže. + +  + +```solidity +// SPDX-License-Identifier: MIT +``` + +Soubory v Solidity by měly obsahovat identifikátor licence. [Seznam licencí naleznete zde](https://spdx.org/licenses/). Pokud potřebujete jinou +licenci, jednoduše to vysvětlete v komentářích. + +  + +```solidity +pragma solidity >=0.6.0 <0.8.0; +``` + +Jazyk Solidity se stále rychle vyvíjí a nové verze nemusí být kompatibilní se starým kódem +([viz zde](https://docs.soliditylang.org/en/v0.7.0/070-breaking-changes.html)). Proto je dobré specifikovat nejen minimální +verzi jazyka, ale také maximální verzi, nejnovější, se kterou jste kód testovali. + +  + +```solidity +/** + * @dev Rozhraní standardu ERC20, jak je definováno v EIP. + */ +``` + +Značka @dev v komentáři je součástí [formátu NatSpec](https://docs.soliditylang.org/en/develop/natspec-format.html), který se používá k vytváření +dokumentace ze zdrojového kódu. + +  + +```solidity +interface IERC20 { +``` + +Podle konvence začínají názvy rozhraní písmenem `I`. + +  + +```solidity + /** + * @dev Vrací množství existujících tokenů. + */ + function totalSupply() external view returns (uint256); +``` + +Tato funkce je externí (`external`), což znamená, že [ji lze volat pouze z vnějšku kontraktu](https://docs.soliditylang.org/en/v0.7.0/cheatsheet.html#index-2). +Vrací celkovou zásobu tokenů v kontraktu. Tato hodnota je vrácena pomocí nejběžnějšího typu v Ethereu, 256bitového celého čísla bez znaménka (256 bitů je +nativní velikost slova EVM). Tato funkce je také `view`, což znamená, že nemění stav, takže ji lze provést na jednom uzlu, místo aby ji musel +spouštět každý uzel v blockchainu. Tento druh funkce negeneruje transakci a nestojí žádné [palivo](/developers/docs/gas/). + +**Poznámka:** Teoreticky by se mohlo zdát, že tvůrce kontraktu by mohl podvádět vrácením menší celkové zásoby, než je skutečná hodnota, takže by se každý token zdál +cennější, než ve skutečnosti je. Tato obava však ignoruje skutečnou podstatu blockchainu. Vše, co se děje na blockchainu, může být ověřeno +každým uzlem. Aby toho bylo dosaženo, je na každém uzlu k dispozici strojový kód a úložiště každého kontraktu. I když nejste povinni zveřejnit kód vašeho kontraktu v +Solidity, nikdo by vás nebral vážně, pokud nezveřejníte zdrojový kód a verzi Solidity, se kterou byl zkompilován, aby mohl být +ověřen oproti strojovému kódu, který jste poskytli. +Podívejte se například na [tento kontrakt](https://eth.blockscout.com/address/0xa530F85085C6FE2f866E7FdB716849714a89f4CD?tab=contract). + +  + +```solidity + /** + * @dev Vrací množství tokenů, které vlastní `account`. + */ + function balanceOf(address account) external view returns (uint256); +``` + +Jak název napovídá, funkce `balanceOf` vrací zůstatek účtu. Účty Etherea jsou v Solidity identifikovány pomocí typu `address`, který obsahuje 160 bitů. +Je také `external` a `view`. + +  + +```solidity + /** + * @dev Přesune tokeny v množství `amount` z účtu volajícího na `recipient`. + * + * Vrací booleovskou hodnotu, která udává, zda operace proběhla úspěšně. + * + * Vyvolá událost {Transfer}. + */ + function transfer(address recipient, uint256 amount) external returns (bool); +``` + +Funkce `transfer` převádí tokeny od volajícího na jinou adresu. To zahrnuje změnu stavu, takže to není `view`. +Když uživatel zavolá tuto funkci, vytvoří se transakce, která stojí palivo. Rovněž vyvolá událost `Transfer`, aby informovala všechny na +blockchainu o této události. + +Funkce má dva typy výstupu pro dva různé typy volajících: + +- Uživatelé, kteří volají funkci přímo z uživatelského rozhraní. Uživatel obvykle odešle transakci + a nečeká na odpověď, což může trvat neurčitou dobu. Uživatel může zjistit, co se stalo, + vyhledáním potvrzení o transakci (které je identifikováno hašem transakce) nebo vyhledáním + události `Transfer`. +- Jiné kontrakty, které volají funkci jako součást celkové transakce. Tyto kontrakty dostanou výsledek okamžitě, + protože běží ve stejné transakci, takže mohou použít návratovou hodnotu funkce. + +Stejný typ výstupu je vytvořen ostatními funkcemi, které mění stav kontraktu. + +  + +Povolení umožňují účtu utratit některé tokeny, které patří jinému vlastníkovi. +To je užitečné například pro kontrakty, které fungují jako prodejci. Kontrakty nemohou +sledovat události, takže pokud by kupující převedl tokeny přímo na kontrakt prodejce, +tento kontrakt by nevěděl, že mu bylo zaplaceno. Místo toho kupující povolí +kontraktu prodejce utratit určitou částku a prodejce tuto částku převede. +To se děje prostřednictvím funkce, kterou kontrakt prodejce volá, takže kontrakt prodejce +může vědět, zda byl úspěšný. + +```solidity + /** + * @dev Vrací zbývající počet tokenů, které bude moci `spender` + * utratit jménem `owner` prostřednictvím {transferFrom}. Ve výchozím nastavení je + * tato hodnota nulová. + * + * Tato hodnota se mění při volání funkcí {approve} nebo {transferFrom}. + */ + function allowance(address owner, address spender) external view returns (uint256); +``` + +Funkce `allowance` umožňuje komukoli dotázat se, jaké je povolení, které jedna +adresa (`owner`) umožňuje utratit jiné adrese (`spender`). + +  + +```solidity + /** + * @dev Nastaví `amount` jako povolenou částku pro `spender` pro tokeny volajícího. + * + * Vrací booleovskou hodnotu, která udává, zda operace proběhla úspěšně. + * + * DŮLEŽITÉ: Dejte si pozor, že změna povolení touto metodou s sebou nese riziko, + * že někdo může nešťastným pořadím transakcí využít jak staré, tak i nové povolení. + * Jedním z možných řešení, jak tento souběh zmírnit, + * je nejprve snížit povolení pro utrácejícího na 0 a poté nastavit + * požadovanou hodnotu: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Vyvolá událost {Approval}. + */ + function approve(address spender, uint256 amount) external returns (bool); +``` + +Funkce `approve` vytváří povolení. Nezapomeňte si přečíst zprávu o tom, +jak může být zneužita. V Ethereu ovládáte pořadí svých vlastních transakcí, +ale nemůžete ovládat pořadí, v jakém budou provedeny transakce +jiných lidí, pokud neodešlete vlastní transakci až poté, co uvidíte, +že transakce druhé strany proběhla. + +  + +```solidity + /** + * @dev Přesune tokeny v množství `amount` z adresy `sender` na `recipient` pomocí + * mechanismu povolení. `amount` se poté odečte od povolení volajícího + * . + * + * Vrací booleovskou hodnotu, která udává, zda operace proběhla úspěšně. + * + * Vyvolá událost {Transfer}. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); +``` + +Nakonec `transferFrom` použije utrácející k samotnému utracení povolené částky. + +  + +```solidity + + /** + * @dev Vyvolá se, když se tokeny v hodnotě `value` přesunou z jednoho účtu (`from`) na + * jiný (`to`). + * + * Všimněte si, že `value` může být nula. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Vyvolá se, když je povolení `spendera` pro `ownera` nastaveno + * voláním {approve}. `value` je nové povolení. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} +``` + +Tyto události jsou emitovány, když se změní stav kontraktu ERC-20. + +## Skutečný kontrakt {#the-actual-contract} + +Toto je skutečný kontrakt, který implementuje standard ERC-20, [převzato odtud](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol). +Není určen k použití tak, jak je, ale můžete +z něj [dědit](https://www.tutorialspoint.com/solidity/solidity_inheritance.htm) a rozšířit jej na něco použitelného. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.8.0; +``` + +  + +### Importní příkazy {#import-statements} + +Kromě výše uvedených definic rozhraní importuje definice kontraktu dva další soubory: + +```solidity + +import "../../GSN/Context.sol"; +import "./IERC20.sol"; +import "../../math/SafeMath.sol"; +``` + +- `GSN/Context.sol` jsou definice potřebné k použití [OpenGSN](https://www.opengsn.org/), systému, který umožňuje uživatelům bez etheru + používat blockchain. Všimněte si, že se jedná o starou verzi, pokud chcete integrovat s OpenGSN, + [použijte tento návod](https://docs.opengsn.org/javascript-client/tutorial.html). +- [Knihovna SafeMath](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/), která zabraňuje + aritmetickému přetečení/podtečení pro verze Solidity **<0.8.0**. V Solidity ≥0.8.0 aritmetické operace automaticky + vracejí při přetečení/podtečení, takže SafeMath je zbytečná. Tento kontrakt používá SafeMath pro zpětnou kompatibilitu se + staršími verzemi kompilátoru. + +  + +Tento komentář vysvětluje účel kontraktu. + +```solidity +/** + * @dev Implementace rozhraní {IERC20}. + * + * Tato implementace je agnostická vůči způsobu vytváření tokenů. To znamená, + * že mechanismus dodávání musí být přidán v odvozeném kontraktu pomocí {_mint}. + * Obecný mechanismus viz {ERC20PresetMinterPauser}. + * + * TIP: Podrobný popis naleznete v našem průvodci + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[Jak + * implementovat mechanismy dodávání]. + * + * Dodržovali jsme obecné pokyny OpenZeppelin: funkce se při selhání vracejí, místo aby + * vracely `false`. Toto chování je nicméně konvenční + * a není v rozporu s očekáváními aplikací ERC20. + * + * Navíc se při volání {transferFrom} emituje událost {Approval}. + * To umožňuje aplikacím rekonstruovat povolení pro všechny účty jen + * nasloucháním zmíněných událostí. Jiné implementace EIP nemusí tyto události + * emitovat, protože to není vyžadováno specifikací. + * + * Nakonec byly přidány nestandardní funkce {decreaseAllowance} a {increaseAllowance}, + * aby se zmírnily známé problémy kolem nastavování + * povolení. Viz {IERC20-approve}. + */ + +``` + +### Definice kontraktu {#contract-definition} + +```solidity +contract ERC20 is Context, IERC20 { +``` + +Tento řádek specifikuje dědičnost, v tomto případě z `IERC20` z výše uvedeného a `Context`, pro OpenGSN. + +  + +```solidity + + using SafeMath for uint256; + +``` + +Tento řádek připojuje knihovnu `SafeMath` k typu `uint256`. Tuto knihovnu najdete +[zde](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol). + +### Definice proměnných {#variable-definitions} + +Tyto definice specifikují stavové proměnné kontraktu. Tyto proměnné jsou deklarovány jako soukromé (`private`), ale +to pouze znamená, že je nemohou číst jiné kontrakty na blockchainu. _Na blockchainu neexistují žádná +tajemství_, software na každém uzlu má stav každého kontraktu +v každém bloku. Podle konvence se stavové proměnné pojmenovávají `_`. + +První dvě proměnné jsou [mapování](https://www.tutorialspoint.com/solidity/solidity_mappings.htm), +což znamená, že se chovají zhruba stejně jako [asociativní pole](https://wikipedia.org/wiki/Associative_array), +s výjimkou toho, že klíče jsou číselné hodnoty. Úložiště je přiděleno pouze pro položky, které mají hodnoty odlišné +od výchozí (nula). + +```solidity + mapping (address => uint256) private _balances; +``` + +První mapování, `_balances`, jsou adresy a jejich příslušné zůstatky tohoto tokenu. Pro přístup +k zůstatku použijte tuto syntaxi: `_balances[]`. + +  + +```solidity + mapping (address => mapping (address => uint256)) private _allowances; +``` + +Tato proměnná, `_allowances`, ukládá povolení vysvětlená dříve. První index je vlastníkem +tokenů a druhý je kontrakt s povolením. Pro přístup k částce, kterou může adresa A +utratit z účtu adresy B, použijte `_allowances[B][A]`. + +  + +```solidity + uint256 private _totalSupply; +``` + +Jak název napovídá, tato proměnná sleduje celkovou zásobu tokenů. + +  + +```solidity + string private _name; + string private _symbol; + uint8 private _decimals; +``` + +Tyto tři proměnné se používají ke zlepšení čitelnosti. První dvě jsou samovysvětlující, ale `_decimals` +není. + +Na jedné straně Ethereum nemá proměnné s plovoucí desetinnou čárkou nebo zlomkové proměnné. Na druhé straně, +lidé rádi dělí tokeny. Jedním z důvodů, proč se lidé usadili na zlatě jako měně, bylo to, že +bylo těžké vrátit drobné, když si někdo chtěl koupit hodnotu kachny v kravě. + +Řešením je sledovat celá čísla, ale počítat místo skutečného tokenu zlomkový token, který je +téměř bezcenný. V případě etheru se zlomkový token nazývá wei a 10^18 wei se rovná jednomu +ETH. V době psaní tohoto článku je 10 000 000 000 000 wei přibližně jeden americký nebo eurový cent. + +Aplikace potřebují vědět, jak zobrazit zůstatek tokenu. Pokud má uživatel 3 141 000 000 000 000 000 wei, je to +3,14 ETH? 31,41 ETH? 3 141 ETH? V případě etheru je definováno 10^18 wei na ETH, ale pro váš +token můžete zvolit jinou hodnotu. Pokud dělení tokenu nemá smysl, můžete použít +hodnotu `_decimals` nula. Chcete-li použít stejný standard jako ETH, použijte hodnotu **18**. + +### Konstruktor {#the-constructor} + +```solidity + /** + * @dev Nastaví hodnoty pro {name} a {symbol}, inicializuje {decimals} s + * výchozí hodnotou 18. + * + * Chcete-li vybrat jinou hodnotu pro {decimals}, použijte {_setupDecimals}. + * + * Všechny tři z těchto hodnot jsou neměnné: lze je nastavit pouze jednou během + * konstrukce. + */ + constructor (string memory name_, string memory symbol_) public { + // V Solidity ≥0.7.0, 'public' je implicitní a může být vynechán. + + _name = name_; + _symbol = symbol_; + _decimals = 18; + } +``` + +Konstruktor se volá při prvním vytvoření kontraktu. Podle konvence jsou parametry funkce pojmenovány `_`. + +### Funkce uživatelského rozhraní {#user-interface-functions} + +```solidity + /** + * @dev Vrací název tokenu. + */ + function name() public view returns (string memory) { + return _name; + } + + /** + * @dev Vrací symbol tokenu, obvykle kratší verzi + * názvu. + */ + function symbol() public view returns (string memory) { + return _symbol; + } + + /** + * @dev Vrací počet desetinných míst použitých k získání jeho uživatelské reprezentace. + * Například, pokud se `decimals` rovná `2`, měl by být zůstatek `505` tokenů + * zobrazen uživateli jako `5,05` (`505 / 10 ** 2`). + * + * Tokeny obvykle volí hodnotu 18, napodobující vztah mezi + * etherem a wei. Toto je hodnota, kterou {ERC20} používá, pokud není + * volána {_setupDecimals}. + * + * POZNÁMKA: Tato informace se používá pouze pro účely _zobrazení_: v + * žádném případě neovlivňuje žádnou aritmetiku kontraktu, včetně + * {IERC20-balanceOf} a {IERC20-transfer}. + */ + function decimals() public view returns (uint8) { + return _decimals; + } +``` + +Tyto funkce, `name`, `symbol` a `decimals`, pomáhají uživatelským rozhraním poznat váš kontrakt, aby ho mohly správně zobrazit. + +Návratový typ je `string memory`, což znamená návrat řetězce, který je uložen v paměti. Proměnné, jako jsou +řetězce, mohou být uloženy na třech místech: + +| | Životnost | Přístup ke kontraktu | Náklady na palivo | +| -------- | ---------------- | -------------------- | ----------------------------------------------------------------- | +| Paměť | Volání funkce | Čtení/zápis | Desítky nebo stovky (vyšší pro vyšší umístění) | +| Calldata | Volání funkce | Pouze pro čtení | Nelze použít jako návratový typ, pouze jako typ parametru funkce | +| Úložiště | Dokud se nezmění | Čtení/zápis | Vysoké (800 pro čtení, 20k pro zápis) | + +V tomto případě je `memory` nejlepší volbou. + +### Čtení informací o tokenu {#read-token-information} + +Jedná se o funkce, které poskytují informace o tokenu, buď o celkové zásobě, nebo o +zůstatku na účtu. + +```solidity + /** + * @dev Viz {IERC20-totalSupply}. + */ + function totalSupply() public view override returns (uint256) { + return _totalSupply; + } +``` + +Funkce `totalSupply` vrací celkovou zásobu tokenů. + +  + +```solidity + /** + * @dev Viz {IERC20-balanceOf}. + */ + function balanceOf(address account) public view override returns (uint256) { + return _balances[account]; + } +``` + +Přečtěte si zůstatek na účtu. Všimněte si, že kdokoli může získat zůstatek na účtu kohokoli +jiného. Nemá smysl se snažit tuto informaci skrývat, protože je stejně dostupná na každém +uzlu. _Na blockchainu neexistují žádná tajemství._ + +### Převod tokenů {#transfer-tokens} + +```solidity + /** + * @dev Viz {IERC20-transfer}. + * + * Požadavky: + * + * - `recipient` nemůže být nulová adresa. + * - volající musí mít zůstatek alespoň `amount`. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { +``` + +Funkce `transfer` se volá k převodu tokenů z účtu odesílatele na jiný. Všimněte si, +že i když vrací booleovskou hodnotu, tato hodnota je vždy **true**. Pokud se převod +nepodaří, kontrakt vrátí volání zpět. + +  + +```solidity + _transfer(_msgSender(), recipient, amount); + return true; + } +``` + +Funkce `_transfer` dělá skutečnou práci. Jedná se o soukromou funkci, kterou mohou volat pouze +jiné funkce kontraktu. Podle konvence jsou soukromé funkce pojmenovány `_`, stejně jako stavové +proměnné. + +Normálně v Solidity používáme `msg.sender` pro odesílatele zprávy. To však narušuje +[OpenGSN](http://opengsn.org/). Chceme-li s naším tokenem povolit transakce bez etheru, musíme +použít `_msgSender()`. Pro běžné transakce vrací `msg.sender`, ale pro transakce bez etheru +vrací původního podepisujícího a ne kontrakt, který zprávu předal. + +### Funkce povolení {#allowance-functions} + +Jedná se o funkce, které implementují funkcionalitu povolení: `allowance`, `approve`, `transferFrom` +a `_approve`. Kromě toho implementace OpenZeppelin jde nad rámec základního standardu a obsahuje některé funkce, které zlepšují +bezpečnost: `increaseAllowance` a `decreaseAllowance`. + +#### Funkce povolení {#allowance} + +```solidity + /** + * @dev Viz {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } +``` + +Funkce `allowance` umožňuje každému zkontrolovat jakékoli povolení. + +#### Funkce schválení {#approve} + +```solidity + /** + * @dev Viz {IERC20-approve}. + * + * Požadavky: + * + * - `spender` nemůže být nulová adresa. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { +``` + +Tato funkce se volá pro vytvoření povolení. Je podobná výše uvedené funkci `transfer`: + +- Funkce pouze volá interní funkci (v tomto případě `_approve`), která provádí skutečnou práci. +- Funkce buď vrátí `true` (pokud je úspěšná), nebo se vrátí (pokud ne). + +  + +```solidity + _approve(_msgSender(), spender, amount); + return true; + } +``` + +Používáme interní funkce, abychom minimalizovali počet míst, kde dochází ke změnám stavu. _Každá_ funkce, která mění +stav, je potenciálním bezpečnostním rizikem, které je třeba zkontrolovat z hlediska bezpečnosti. Tímto způsobem máme menší šanci, že se spleteme. + +#### Funkce transferFrom {#transferFrom} + +Toto je funkce, kterou volá utrácející, aby utratil povolenou částku. To vyžaduje dvě operace: převést utracenou částku +a snížit povolenou částku o tuto částku. + +```solidity + /** + * @dev Viz {IERC20-transferFrom}. + * + * Vyvolá událost {Approval} udávající aktualizované povolení. To není + * vyžadováno EIP. Viz poznámka na začátku {ERC20}. + * + * Požadavky: + * + * - `sender` a `recipient` nemohou být nulová adresa. + * - `sender` musí mít zůstatek alespoň `amount`. + * - volající musí mít povolení pro tokeny ``sender`` alespoň + * `amount`. + */ + function transferFrom(address sender, address recipient, uint256 amount) public virtual + override returns (bool) { + _transfer(sender, recipient, amount); +``` + +  + +Volání funkce `a.sub(b, "zpráva")` provádí dvě věci. Nejprve vypočítá `a-b`, což je nové povolení. +Zadruhé zkontroluje, zda tento výsledek není záporný. Pokud je záporný, volání se vrátí s poskytnutou zprávou. Všimněte si, že když se volání vrátí, veškeré předchozí zpracování během tohoto volání se ignoruje, takže nemusíme +vracet `_transfer`. + +```solidity + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, + "ERC20: transfer amount exceeds allowance")); + return true; + } +``` + +#### Bezpečnostní doplňky OpenZeppelin {#openzeppelin-safety-additions} + +Je nebezpečné nastavit nenulové povolení na jinou nenulovou hodnotu, +protože ovládáte pouze pořadí svých vlastních transakcí, nikoli transakcí kohokoli jiného. Představte si, že +máte dva uživatele, Alici, která je naivní, a Billa, který je nečestný. Alice chce od +Billa nějakou službu, která si myslí, že stojí pět tokenů – takže dává Billovi povolení na pět tokenů. + +Pak se něco změní a Billova cena stoupne na deset tokenů. Alice, která stále chce službu, +pošle transakci, která nastaví Billovo povolení na deset. V okamžiku, kdy Bill uvidí tuto novou transakci +v poolu transakcí, pošle transakci, která utratí Aliciných pět tokenů a má mnohem +vyšší cenu paliva, takže bude vytěžena rychleji. Tímto způsobem může Bill nejprve utratit pět tokenů a poté, +jakmile je vytěženo nové povolení Alice, utratit dalších deset za celkovou cenu patnácti tokenů, což je více, než +Alice zamýšlela autorizovat. Tato technika se nazývá +[front-running](https://consensysdiligence.github.io/smart-contract-best-practices/attacks/#front-running) + +| Transakce Alice | Nonce Alice | Transakce Billa | Nonce Billa | Billovo povolení | Billův celkový příjem od Alice | +| ------------------------------------ | ----------- | ------------------------------------------------ | ----------- | ---------------- | ------------------------------ | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | +| approve(Bill, 10) | 11 | | | 10 | 5 | +| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 15 | + +Abyste se tomuto problému vyhnuli, tyto dvě funkce (`increaseAllowance` a `decreaseAllowance`) vám umožňují +upravit povolenou částku o určitou částku. Takže pokud Bill už utratil pět tokenů, bude moci utratit +jen dalších pět. V závislosti na načasování existují dva způsoby, jak to může fungovat, oba z +nichž končí tím, že Bill dostane pouze deset tokenů: + +A: + +| Transakce Alice | Nonce Alice | Transakce Billa | Nonce Billa | Billovo povolení | Billův celkový příjem od Alice | +| --------------------------------------------- | ----------: | ----------------------------------------------- | ----------: | ---------------: | ------------------------------ | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | +| increaseAllowance(Bill, 5) | 11 | | | 0+5 = 5 | 5 | +| | | transferFrom(Alice, Bill, 5) | 10,124 | 0 | 10 | + +B: + +| Transakce Alice | Nonce Alice | Transakce Billa | Nonce Billa | Billovo povolení | Billův celkový příjem od Alice | +| --------------------------------------------- | ----------: | ------------------------------------------------ | ----------: | ---------------: | -----------------------------: | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| increaseAllowance(Bill, 5) | 11 | | | 5+5 = 10 | 0 | +| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 10 | + +```solidity + /** + * @dev Atomicky zvyšuje povolení udělené `spenderu` volajícím. + * + * Toto je alternativa k {approve}, kterou lze použít jako zmírnění pro + * problémy popsané v {IERC20-approve}. + * + * Vyvolá událost {Approval} udávající aktualizované povolení. + * + * Požadavky: + * + * - `spender` nemůže být nulová adresa. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } +``` + +Funkce `a.add(b)` je bezpečné sčítání. V nepravděpodobném případě, že `a`+`b`>=`2^256`, se neobtočí +jako normální sčítání. + +```solidity + + /** + * @dev Atomicky snižuje povolení udělené `spenderu` volajícím. + * + * Toto je alternativa k {approve}, kterou lze použít jako zmírnění pro + * problémy popsané v {IERC20-approve}. + * + * Vyvolá událost {Approval} udávající aktualizované povolení. + * + * Požadavky: + * + * - `spender` nemůže být nulová adresa. + * - `spender` musí mít povolení pro volajícího alespoň + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, + "ERC20: decreased allowance below zero")); + return true; + } +``` + +### Funkce, které upravují informace o tokenu {#functions-that-modify-token-information} + +Toto jsou čtyři funkce, které provádějí skutečnou práci: `_transfer`, `_mint`, `_burn` a `_approve`. + +#### Funkce _transfer {#_transfer} + +```solidity + /** + * @dev Přesune tokeny v množství `amount` od `odesílatele` k `příjemci`. + * + * Tato interní funkce je ekvivalentní {transfer} a může být použita + * například k implementaci automatických poplatků za tokeny, mechanismů slashing atd. + * + * Vyvolá událost {Transfer}. + * + * Požadavky: + * + * - `sender` nemůže být nulová adresa. + * - `recipient` nemůže být nulová adresa. + * - `sender` musí mít zůstatek alespoň `amount`. + */ + function _transfer(address sender, address recipient, uint256 amount) internal virtual { +``` + +Tato funkce, `_transfer`, převádí tokeny z jednoho účtu na druhý. Volá se jak +`transfer` (pro převody z vlastního účtu odesílatele), tak `transferFrom` (pro použití povolení +k převodu z cizího účtu). + +  + +```solidity + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); +``` + +Nikdo ve skutečnosti nevlastní adresu nula v Ethereu (to znamená, že nikdo nezná soukromý klíč, jehož odpovídající veřejný klíč +je transformován na nulovou adresu). Když lidé používají tuto adresu, obvykle se jedná o softwarovou chybu – takže selžeme, +pokud je jako odesílatel nebo příjemce použita nulová adresa. + +  + +```solidity + _beforeTokenTransfer(sender, recipient, amount); + +``` + +Existují dva způsoby, jak použít tento kontrakt: + +1. Použijte jej jako šablonu pro svůj vlastní kód +2. [Dědit z něj](https://www.bitdegree.org/learn/solidity-inheritance) a přepsat pouze ty funkce, které potřebujete upravit + +Druhá metoda je mnohem lepší, protože kód OpenZeppelin ERC-20 již byl auditován a prokázal se jako bezpečný. Když používáte dědičnost, +je jasné, jaké funkce upravujete, a aby lidé důvěřovali vašemu kontraktu, stačí jim auditovat pouze tyto konkrétní funkce. + +Často je užitečné provést funkci pokaždé, když tokeny změní majitele. Nicméně `_transfer` je velmi důležitá funkce a je +možné ji napsat nezabezpečeně (viz níže), takže je nejlepší ji nepřepisovat. Řešením je `_beforeTokenTransfer`, [funkce hook](https://wikipedia.org/wiki/Hooking). Tuto funkci můžete přepsat a bude volána při každém převodu. + +  + +```solidity + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); +``` + +Toto jsou řádky, které skutečně provádějí převod. Všimněte si, že mezi nimi **nic** není a že odečítáme +převedenou částku od odesílatele před jejím přičtením k příjemci. To je důležité, protože kdyby uprostřed bylo +volání jiného kontraktu, mohlo by být použito k podvádění tohoto kontraktu. Tímto způsobem je převod +atomický, uprostřed se nemůže nic stát. + +  + +```solidity + emit Transfer(sender, recipient, amount); + } +``` + +Nakonec vyvoláme událost `Transfer`. Události nejsou přístupné chytrým kontraktům, ale kód spuštěný mimo blockchain +může naslouchat událostem a reagovat na ně. Například peněženka může sledovat, kdy majitel získá více tokenů. + +#### Funkce _mint a _burn {#_mint-and-_burn} + +Tyto dvě funkce (`_mint` a `_burn`) upravují celkovou zásobu tokenů. +Jsou interní a v tomto kontraktu je nevolá žádná funkce, +takže jsou užitečné pouze v případě, že z kontraktu dědíte a přidáte si vlastní +logiku k rozhodnutí, za jakých podmínek se mají nové tokeny razit nebo stávající +pálit. + +**POZNÁMKA:** Každý token ERC-20 má svou vlastní obchodní logiku, která určuje správu tokenů. +Například kontrakt s pevnou zásobou může volat `_mint` pouze +v konstruktoru a nikdy nevolat `_burn`. Kontrakt, který prodává tokeny, +volá `_mint`, když je zaplaceno, a pravděpodobně v určitém okamžiku volá `_burn`, +aby se zabránilo nekontrolovatelné inflaci. + +```solidity + /** @dev Vytvoří `amount` tokenů a přiřadí je `účtu`, čímž se zvýší + * celková zásoba. + * + * Vyvolá událost {Transfer} s `from` nastaveným na nulovou adresu. + * + * Požadavky: + * + * - `to` nemůže být nulová adresa. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + _beforeTokenTransfer(address(0), account, amount); + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } +``` + +Nezapomeňte aktualizovat `_totalSupply`, když se změní celkový počet tokenů. + +  + +```solidity + /** + * @dev Zničí `amount` tokenů z `účtu`, čímž se sníží + * celková zásoba. + * + * Vyvolá událost {Transfer} s `to` nastaveným na nulovou adresu. + * + * Požadavky: + * + * - `účet` nemůže být nulová adresa. + * - `účet` musí mít alespoň `amount` tokenů. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } +``` + +Funkce `_burn` je téměř identická s `_mint`, jen jde opačným směrem. + +#### Funkce _approve {#_approve} + +Toto je funkce, která skutečně specifikuje povolení. Všimněte si, že umožňuje vlastníkovi specifikovat +povolení, které je vyšší než aktuální zůstatek vlastníka. To je v pořádku, protože zůstatek se +kontroluje v okamžiku převodu, kdy se může lišit od zůstatku v době vytvoření povolení +. + +```solidity + /** + * @dev Nastaví `amount` jako povolení `spenderu` pro tokeny `vlastníka`. + * + * Tato interní funkce je ekvivalentní `approve` a může být použita + * například k nastavení automatických povolení pro určité subsystémy atd. + * + * Vyvolá událost {Approval}. + * + * Požadavky: + * + * - `owner` nemůže být nulová adresa. + * - `spender` nemůže být nulová adresa. + */ + function _approve(address owner, address spender, uint256 amount) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; +``` + +  + +Vyvolat událost `Approval`. V závislosti na tom, jak je aplikace napsána, může být kontraktu utrácejícího o +schválení sděleno buď vlastníkem, nebo serverem, který naslouchá těmto událostem. + +```solidity + emit Approval(owner, spender, amount); + } + +``` + +### Upravit proměnnou Decimals {#modify-the-decimals-variable} + +```solidity + + + /** + * @dev Nastaví {decimals} na jinou hodnotu než výchozí 18. + * + * VAROVÁNÍ: Tuto funkci byste měli volat pouze z konstruktoru. Většina + * aplikací, které interagují s tokenovými kontrakty, neočekává, + * že se {decimals} někdy změní, a mohou fungovat nesprávně, pokud ano. + */ + function _setupDecimals(uint8 decimals_) internal { + _decimals = decimals_; + } +``` + +Tato funkce upravuje proměnnou `_decimals`, která se používá k tomu, aby uživatelským rozhraním sdělila, jak interpretovat částku. +Měli byste ji volat z konstruktoru. Bylo by nečestné volat ji v jakémkoli následujícím bodě a aplikace +nejsou navrženy tak, aby to zvládly. + +### Háčky {#hooks} + +```solidity + + /** + * @dev Záchytný bod (hook), který se volá před jakýmkoli převodem tokenů. To zahrnuje + * ražbu a pálení. + * + * Podmínky volání: + * + * – když `from` a `to` jsou obě nenulové, bude převeden `amount` tokenů z adresy ``from`` + * na adresu `to`. + * – když je `from` nulové, bude pro `to` vyraženo `amount` tokenů. + * – když je `to` nulové, `amount` tokenů z adresy ``from`` bude spáleno. + * – `from` a `to` nejsou nikdy obě nulové. + * + * Více informací o záchytných bodech (hooks) najdete v xref:ROOT:extending-contracts.adoc#using-hooks[Používání záchytných bodů]. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } +} +``` + +Toto je funkce háku, která se má volat během převodů. Zde je prázdná, ale pokud potřebujete, +aby něco dělala, stačí ji přepsat. + +## Závěr {#conclusion} + +Pro přehled, zde jsou některé z nejdůležitějších myšlenek v tomto kontraktu (podle mého názoru se váš může lišit): + +- _Na blockchainu neexistují žádná tajemství_. Jakékoli informace, ke kterým má chytrý kontrakt přístup, + jsou dostupné celému světu. +- Můžete ovládat pořadí svých vlastních transakcí, ale ne to, kdy se uskuteční transakce + jiných lidí. To je důvod, proč může být změna povolení nebezpečná, protože umožňuje + utrácejícímu utratit součet obou povolení. +- Hodnoty typu `uint256` se obtáčejí. Jinými slovy, _0-1=2^256-1_. Pokud to není požadované + chování, musíte to zkontrolovat (nebo použít knihovnu SafeMath, která to udělá za vás). Všimněte si, že se to změnilo v + [Solidity 0.8.0](https://docs.soliditylang.org/en/breaking/080-breaking-changes.html). +- Provádějte všechny změny stavu určitého typu na určitém místě, protože to usnadňuje auditování. + To je důvod, proč máme například `_approve`, které je voláno `approve`, `transferFrom`, + `increaseAllowance` a `decreaseAllowance` +- Změny stavu by měly být atomické, bez jakékoli jiné akce uprostřed (jak můžete vidět + v `_transfer`). Je to proto, že během změny stavu máte nekonzistentní stav. Například + mezi dobou, kdy odečtete ze zůstatku odesílatele, a dobou, kdy přičtete k zůstatku + příjemce, existuje méně tokenů, než by mělo být. To by mohlo být potenciálně zneužito, pokud + mezi nimi probíhají operace, zejména volání jiného kontraktu. + +Nyní, když jste viděli, jak je napsán kontrakt OpenZeppelin ERC-20 a zejména jak je +zabezpečen, jděte a pište své vlastní bezpečné kontrakty a aplikace. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). diff --git a/public/content/translations/cs/developers/tutorials/erc20-with-safety-rails/index.md b/public/content/translations/cs/developers/tutorials/erc20-with-safety-rails/index.md new file mode 100644 index 00000000000..804b9ff7cc8 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/erc20-with-safety-rails/index.md @@ -0,0 +1,217 @@ +--- +title: "ERC-20 s bezpečnostními pojistkami" +description: "Jak pomoci lidem, aby se vyhnuli zbytečným chybám" +author: Ori Pomerantz +lang: cs +tags: [ "erc-20" ] +skill: beginner +published: 2022-08-15 +--- + +## Úvod {#introduction} + +Jednou ze skvělých věcí na Ethereu je to, že neexistuje žádná centrální autorita, která by mohla upravit nebo zvrátit vaše transakce. Jedním z velkých problémů Etherea je naopak to, že neexistuje žádná centrální autorita, která by měla pravomoc napravovat chyby uživatelů nebo nelegitimní transakce. V tomto článku se dozvíte o některých běžných chybách, kterých se uživatelé dopouštějí u [ERC-20](/developers/docs/standards/tokens/erc-20/) tokenů, a také o tom, jak vytvářet ERC-20 kontrakty, které uživatelům pomáhají se těmto chybám vyhnout nebo které dávají centrální autoritě určitou pravomoc (například zmrazit účty). + +Upozorňujeme, že ačkoli budeme používat [kontrakt tokenu ERC-20 od OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20), tento článek jej podrobně nevysvětluje. Tyto informace naleznete [zde](/developers/tutorials/erc20-annotated-code). + +Pokud chcete vidět kompletní zdrojový kód: + +1. Otevřete [Remix IDE](https://remix.ethereum.org/). +2. Klikněte na ikonu pro klonování z GitHubu (![ikona klonování z GitHubu](icon-clone.png)). +3. Naklonujte repozitář z GitHubu: `https://github.com/qbzzt/20220815-erc20-safety-rails`. +4. Otevřete **contracts > erc20-safety-rails.sol**. + +## Vytvoření ERC-20 kontraktu {#creating-an-erc-20-contract} + +Než budeme moci přidat funkcionalitu bezpečnostních pojistek, potřebujeme ERC-20 kontrakt. V tomto článku použijeme [průvodce kontrakty od OpenZeppelin](https://docs.openzeppelin.com/contracts/5.x/wizard). Otevřete jej v jiném prohlížeči a postupujte podle těchto pokynů: + +1. Vyberte **ERC20**. + +2. Zadejte tato nastavení: + + | Parametr | Hodnota | + | ---------------- | ---------------- | + | Název | SafetyRailsToken | + | Symbol | SAFE | + | Premint | 1000 | + | Funkce | Žádná | + | Řízení přístupu | Ownable | + | Upgradovatelnost | Žádná | + +3. Přejděte nahoru a klikněte na **Otevřít v Remixu** (pro Remix) nebo **Stáhnout** pro použití jiného prostředí. Budu předpokládat, že používáte Remix. Pokud používáte něco jiného, proveďte příslušné změny. + +4. Nyní máme plně funkční ERC-20 kontrakt. Importovaný kód uvidíte po rozbalení `.deps` > `npm`. + +5. Zkompilujte, nasaďte a vyzkoušejte si kontrakt, abyste viděli, že funguje jako ERC-20 kontrakt. Pokud se potřebujete naučit používat Remix, [použijte tento tutoriál](https://remix.ethereum.org/?#activate=udapp,solidity,LearnEth). + +## Běžné chyby {#common-mistakes} + +### Chyby {#the-mistakes} + +Uživatelé někdy posílají tokeny na špatnou adresu. Ačkoli jim nevidíme do hlavy, abychom věděli, co měli v úmyslu, existují dva typy chyb, které se stávají často a dají se snadno odhalit: + +1. Odeslání tokenů na vlastní adresu kontraktu. Například u [tokenu OP od Optimism](https://optimism.mirror.xyz/qvd0WfuLKnePm1Gxb9dpGchPf5uDz5NSMEFdgirDS4c) se za necelé dva měsíce podařilo nashromáždit [přes 120 000](https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000042) tokenů OP. To představuje značné jmění, o které lidé pravděpodobně jednoduše přišli. + +2. Odeslání tokenů na prázdnou adresu, která neodpovídá ani [externě vlastněnému účtu (EOA)](/developers/docs/accounts/#externally-owned-accounts-and-key-pairs), ani [chytrému kontraktu](/developers/docs/smart-contracts). Sice nemám statistiky o tom, jak často se to stává, ale [jeden incident mohl stát 20 000 000 tokenů](https://gov.optimism.io/t/message-to-optimism-community-from-wintermute/2595). + +### Zabránění převodům {#preventing-transfers} + +Kontrakt ERC-20 od OpenZeppelin obsahuje [„hook“ `_beforeTokenTransfer`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol#L364-L368), který je volán před převodem tokenu. Ve výchozím stavu tento hook nic nedělá, ale můžeme na něj navázat vlastní funkcionalitu, například kontroly, které v případě problému transakci vrátí. + +Chcete-li tento hook použít, přidejte za konstruktor tuto funkci: + +```solidity + function _beforeTokenTransfer(address from, address to, uint256 amount) + internal virtual + override(ERC20) + { + super._beforeTokenTransfer(from, to, amount); + } +``` + +Některé části této funkce pro vás mohou být nové, pokud nejste se Solidity příliš obeznámeni: + +```solidity + internal virtual +``` + +Klíčové slovo `virtual` znamená, že stejně jako jsme zdědili funkcionalitu z `ERC20` a přepsali tuto funkci, mohou i další kontrakty dědit z našeho kontraktu a tuto funkci přepsat. + +```solidity + override(ERC20) +``` + +Musíme explicitně uvést, že [přepisujeme](https://docs.soliditylang.org/en/v0.8.15/contracts.html#function-overriding) definici `_beforeTokenTransfer` z tokenu ERC20. Z hlediska bezpečnosti jsou explicitní definice obecně mnohem lepší než implicitní – nemůžete zapomenout, že jste něco udělali, když to máte přímo před očima. To je také důvod, proč musíme určit, kterou nadtřídu `_beforeTokenTransfer` přepisujeme. + +```solidity + super._beforeTokenTransfer(from, to, amount); +``` + +Tento řádek volá funkci `_beforeTokenTransfer` kontraktu nebo kontraktů, z nichž jsme dědili a které ji mají. V tomto případě je to pouze `ERC20`, kontrakt `Ownable` tento hook nemá. Přestože `ERC20._beforeTokenTransfer` v současné době nic nedělá, voláme ji pro případ, že by v budoucnu byla přidána nějaká funkcionalita (a my se pak rozhodli kontrakt znovu nasadit, protože kontrakty se po nasazení nemění). + +### Kódování požadavků {#coding-the-requirements} + +Do funkce chceme přidat tyto požadavky: + +- Adresa `to` se nesmí rovnat `address(this)`, tedy adrese samotného kontraktu ERC-20. +- Adresa `to` nesmí být prázdná, musí to být buď: + - Externě vlastněný účet (EOA). Nemůžeme přímo zkontrolovat, zda je adresa EOA, ale můžeme zkontrolovat zůstatek ETH na adrese. EOA mají téměř vždy nějaký zůstatek, i když se již nepoužívají – je těžké je vyčistit do posledního wei. + - Chytrý kontrakt. Otestovat, zda je adresa chytrý kontrakt, je trochu těžší. Existuje operační kód, který kontroluje délku externího kódu, nazývaný [`EXTCODESIZE`](https://www.evm.codes/#3b), ale není přímo dostupný v Solidity. Musíme pro to použít [Yul](https://docs.soliditylang.org/en/v0.8.15/yul.html), což je assemblér pro EVM. Existují i další hodnoty, které bychom mohli použít ze Solidity ([`
.code` a `
.codehash`](https://docs.soliditylang.org/en/v0.8.15/units-and-global-variables.html#members-of-address-types)), ale stojí více. + +Projděme si nový kód řádek po řádku: + +```solidity + require(to != address(this), "Tokeny nelze posílat na adresu kontraktu"); +``` + +Toto je první požadavek, zkontrolujte, že `to` a `this(address)` není totéž. + +```solidity + bool isToContract; + assembly { + isToContract := gt(extcodesize(to), 0) + } +``` + +Takto zkontrolujeme, zda je adresa kontrakt. Z Yulu nemůžeme přímo získat výstup, takže místo toho definujeme proměnnou, která bude obsahovat výsledek (v tomto případě `isToContract`). Yul funguje tak, že každý operační kód je považován za funkci. Nejprve tedy zavoláme [`EXTCODESIZE`](https://www.evm.codes/#3b), abychom získali velikost kontraktu, a poté použijeme [`GT`](https://www.evm.codes/#11), abychom zkontrolovali, že není nulová (pracujeme s neznaménkovými celými čísly, takže samozřejmě nemůže být záporná). Poté zapíšeme výsledek do `isToContract`. + +```solidity + require(to.balance != 0 || isToContract, "Tokeny nelze posílat na prázdnou adresu"); +``` + +A nakonec tu máme samotnou kontrolu prázdných adres. + +## Administrativní přístup {#admin-access} + +Někdy je užitečné mít administrátora, který může napravovat chyby. Aby se snížilo riziko zneužití, může být tento administrátor [multisig](https://blog.logrocket.com/security-choices-multi-signature-wallets/), takže na akci se musí shodnout více lidí. V tomto článku budeme mít dvě administrativní funkce: + +1. Zmrazování a rozmrazování účtů. To může být užitečné například v případě, že by účet mohl být kompromitován. +2. Vyčištění prostředků. + + Někdy podvodníci posílají podvodné tokeny do kontraktu skutečného tokenu, aby získali legitimitu. Příklad naleznete [zde](https://optimism.blockscout.com/token/0x2348B1a1228DDCd2dB668c3d30207c3E1852fBbe?tab=holders). Legitimní kontrakt ERC-20 je [0x4200....0042](https://optimism.blockscout.com/token/0x4200000000000000000000000000000000000042). Podvod, který se za něj vydává, je [0x234....bbe](https://optimism.blockscout.com/token/0x2348B1a1228DDCd2dB668c3d30207c3E1852fBbe). + + Je také možné, že lidé omylem pošlou legitimní ERC-20 tokeny na náš kontrakt, což je další důvod, proč je dobré mít způsob, jak je dostat ven. + +OpenZeppelin poskytuje dva mechanismy pro umožnění administrativního přístupu: + +- [`Ownable`](https://docs.openzeppelin.com/contracts/5.x/access-control#ownership-and-ownable) kontrakty mají jediného vlastníka. Funkce, které mají [modifikátor](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm) `onlyOwner`, může volat pouze tento vlastník. Vlastníci mohou převést vlastnictví na někoho jiného nebo se ho úplně vzdát. Práva všech ostatních účtů jsou obvykle totožná. +- [`AccessControl`](https://docs.openzeppelin.com/contracts/5.x/access-control#role-based-access-control) kontrakty mají [řízení přístupu na základě rolí (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control). + +Pro zjednodušení v tomto článku používáme `Ownable`. + +### Zmrazování a rozmrazování účtů {#freezing-and-thawing-contracts} + +Zmrazování a rozmrazování účtů vyžaduje několik změn: + +- [Mapování](https://www.tutorialspoint.com/solidity/solidity_mappings.htm) z adres na [booleovské hodnoty](https://en.wikipedia.org/wiki/Boolean_data_type) pro sledování, které adresy jsou zmrazené. Všechny hodnoty jsou na začátku nulové, což se u booleovských hodnot interpretuje jako false. To je to, co chceme, protože ve výchozím nastavení nejsou účty zmrazeny. + + ```solidity + mapping(address => bool) public frozenAccounts; + ``` + +- [Události](https://www.tutorialspoint.com/solidity/solidity_events.htm), které informují všechny zúčastněné o zmrazení nebo rozmrazení účtu. Technicky vzato nejsou události pro tyto akce vyžadovány, ale pomáhá to offchain kódu, aby mohl těmto událostem naslouchat a vědět, co se děje. Považuje se za slušnost, když je chytrý kontrakt emituje, když se stane něco, co by mohlo být pro někoho jiného relevantní. + + Události jsou indexovány, takže bude možné vyhledat všechny případy, kdy byl účet zmrazen nebo rozmrazen. + + ```solidity + // Když jsou účty zmrazeny nebo rozmrazeny + event AccountFrozen(address indexed _addr); + event AccountThawed(address indexed _addr); + ``` + +- Funkce pro zmrazování a rozmrazování účtů. Tyto dvě funkce jsou téměř totožné, takže si projdeme pouze funkci zmrazení. + + ```solidity + function freezeAccount(address addr) + public + onlyOwner + ``` + + Funkce označené jako [`public`](https://www.tutorialspoint.com/solidity/solidity_contracts.htm) lze volat z jiných chytrých kontraktů nebo přímo transakcí. + + ```solidity + { + require(!frozenAccounts[addr], "Účet je již zmrazen"); + frozenAccounts[addr] = true; + emit AccountFrozen(addr); + } // freezeAccount + ``` + + Pokud je účet již zmrazen, transakce se vrátí. V opačném případě ho zmrazte a `emit`ujte událost. + +- Změňte `_beforeTokenTransfer`, abyste zabránili přesunu prostředků ze zmrazeného účtu. Všimněte si, že prostředky lze stále převádět na zmrazený účet. + + ```solidity + require(!frozenAccounts[from], "Účet je zmrazen"); + ``` + +### Vyčištění prostředků {#asset-cleanup} + +K uvolnění ERC-20 tokenů držených tímto kontraktem musíme zavolat funkci na kontraktu tokenu, ke kterému patří, buď [`transfer`](https://eips.ethereum.org/EIPS/eip-20#transfer) nebo [`approve`](https://eips.ethereum.org/EIPS/eip-20#approve). V tomto případě nemá smysl plýtvat palivem na povolenky, můžeme rovnou provést přímý převod. + +```solidity + function cleanupERC20( + address erc20, + address dest + ) + public + onlyOwner + { + IERC20 token = IERC20(erc20); +``` + +Toto je syntaxe pro vytvoření objektu pro kontrakt, když obdržíme adresu. Můžeme to udělat, protože máme definici pro ERC-20 tokeny jako součást zdrojového kódu (viz řádek 4) a tento soubor obsahuje [definici pro IERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol), což je rozhraní pro kontrakt ERC-20 od OpenZeppelin. + +```solidity + uint balance = token.balanceOf(address(this)); + token.transfer(dest, balance); + } +``` + +Jedná se o funkci vyčištění, takže pravděpodobně nechceme zanechat žádné tokeny. Místo ručního získávání zůstatku od uživatele můžeme tento proces rovnou zautomatizovat. + +## Závěr {#conclusion} + +Toto není dokonalé řešení – na problém "uživatel udělal chybu" neexistuje dokonalé řešení. Použití těchto typů kontrol však může alespoň některým chybám zabránit. Schopnost zmrazit účty, i když je nebezpečná, může být použita k omezení škod způsobených některými útoky tím, že hackerovi odepře ukradené prostředky. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). diff --git a/public/content/translations/cs/developers/tutorials/ethereum-for-web2-auth/index.md b/public/content/translations/cs/developers/tutorials/ethereum-for-web2-auth/index.md new file mode 100644 index 00000000000..3219c82719e --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/ethereum-for-web2-auth/index.md @@ -0,0 +1,886 @@ +--- +title: "Použití Etherea pro web2 autentizaci" +description: "Po přečtení tohoto tutoriálu bude vývojář schopen integrovat přihlášení přes Ethereum (web3) s přihlášením SAML, což je standard používaný ve web2 k poskytování jednotného přihlášení a dalších souvisejících služeb. To umožňuje, aby byl přístup ke zdrojům web2 autentizován prostřednictvím podpisů Etherea, s uživatelskými atributy pocházejícími z atestací." +author: Ori Pomerantz +tags: [ "web2", "ověření", "eas" ] +skill: beginner +lang: cs +published: 2025-04-30 +--- + +## Úvod + +[SAML](https://www.onelogin.com/learn/saml) je standard používaný na web2, který umožňuje [poskytovateli identity (IdP)](https://en.wikipedia.org/wiki/Identity_provider#SAML_identity_provider) poskytovat informace o uživateli [poskytovatelům služeb (SP)](https://en.wikipedia.org/wiki/Service_provider_\(SAML\)). + +V tomto tutoriálu se dozvíte, jak integrovat podpisy Etherea se SAML, abyste uživatelům umožnili používat jejich peněženky Ethereum k vlastní autentizaci u služeb web2, které ještě nativně nepodporují Ethereum. + +Upozorňujeme, že tento tutoriál je napsán pro dvě samostatné skupiny čtenářů: + +- Lidi z komunity Etherea, kteří Ethereu rozumí a potřebují se naučit SAML +- Lidi z web2, kteří rozumí SAML a web2 autentizaci a potřebují se naučit o Ethereu + +V důsledku toho bude obsahovat spoustu úvodního materiálu, který již znáte. Klidně ho přeskočte. + +### SAML pro lidi z komunity Etherea + +SAML je centralizovaný protokol. Poskytovatel služeb (SP) přijímá tvrzení (například „toto je můj uživatel John, měl by mít oprávnění provádět A, B a C“) od poskytovatele identity (IdP) pouze pokud s ním má již existující vztah důvěry, nebo s [certifikační autoritou](https://www.ssl.com/article/what-is-a-certificate-authority-ca/), která podepsala certifikát daného IdP. + +Například SP může být cestovní kancelář poskytující cestovní služby firmám a IdP může být interní webová stránka firmy. Když si zaměstnanci potřebují rezervovat služební cestu, cestovní kancelář je odešle k autentizaci firmou, než jim skutečně umožní cestu rezervovat. + +![Proces SAML krok za krokem](./fig-01-saml.png) + +Tímto způsobem si tři entity, prohlížeč, SP a IdP, vyjednávají přístup. SP nemusí předem vědět nic o uživateli používajícím prohlížeč, stačí, když důvěřuje IdP. + +### Ethereum pro lidi znalé SAML + +Ethereum je decentralizovaný systém. + +![Přihlášení přes Ethereum](./fig-02-eth-logon.png) + +Uživatelé mají privátní klíč (obvykle uložený v rozšíření prohlížeče). Z privátního klíče můžete odvodit veřejný klíč a z něj 20bajtovou adresu. Když se uživatelé potřebují přihlásit do systému, jsou požádáni o podepsání zprávy s hodnotou nonce (jednorázově použitelná hodnota). Server může ověřit, že podpis byl vytvořen touto adresou. + +![Získávání dodatečných dat z atestací](./fig-03-eas-data.png) + +Podpis pouze ověřuje adresu Etherea. Chcete-li získat další atributy uživatele, obvykle používáte [atestace](https://attest.org/). Atestace má obvykle tato pole: + +- **Atestátor**, adresa, která provedla atestaci +- **Příjemce**, adresa, které se atestace týká +- **Data**, atestovaná data, jako je jméno, oprávnění atd. +- **Schéma**, ID schématu použitého k interpretaci dat. + +Vzhledem k decentralizované povaze Etherea může atestace provádět kterýkoli uživatel. Identita atestátora je důležitá pro identifikaci atestací, které považujeme za spolehlivé. + +## Nastavení + +Prvním krokem je zajistit komunikaci mezi SAML SP a SAML IdP. + +1. Stáhněte si software. Vzorový software pro tento článek je [na GitHubu](https://github.com/qbzzt/250420-saml-ethereum). Různé fáze jsou uloženy v různých větvích, pro tuto fázi chcete `saml-only` + + ```sh + git clone https://github.com/qbzzt/250420-saml-ethereum -b saml-only + cd 250420-saml-ethereum + pnpm install + ``` + +2. Vytvořte klíče s certifikáty s vlastním podpisem (self-signed). To znamená, že klíč je svou vlastní certifikační autoritou a je třeba jej ručně importovat do poskytovatele služeb. Více informací naleznete v [dokumentaci OpenSSL](https://docs.openssl.org/master/man1/openssl-req/). + + ```sh + mkdir keys + cd keys + openssl req -new -x509 -days 365 -nodes -sha256 -out saml-sp.crt -keyout saml-sp.pem -subj /CN=sp/ + openssl req -new -x509 -days 365 -nodes -sha256 -out saml-idp.crt -keyout saml-idp.pem -subj /CN=idp/ + cd .. + ``` + +3. Spusťte servery (SP i IdP) + + ```sh + pnpm start + ``` + +4. Přejděte na adresu URL SP [http://localhost:3000/](http://localhost:3000/) a kliknutím na tlačítko budete přesměrováni na IdP (port 3001). + +5. Poskytněte IdP svou e-mailovou adresu a klikněte na **Přihlásit se k poskytovateli služeb**. Uvidíte, že budete přesměrováni zpět k poskytovateli služeb (port 3000) a že vás pozná podle vaší e-mailové adresy. + +### Podrobné vysvětlení + +Toto se děje krok za krokem: + +![Běžné přihlášení SAML bez Etherea](./fig-04-saml-no-eth.png) + +#### src/config.mts + +Tento soubor obsahuje konfiguraci jak pro poskytovatele identity, tak pro poskytovatele služeb. Obvykle by se jednalo o různé entity, ale zde můžeme pro zjednodušení sdílet kód. + +```typescript +const fs = await import("fs") + +const protocol="http" +``` + +Zatím jen testujeme, takže je v pořádku používat HTTP. + +```typescript +export const spCert = fs.readFileSync("keys/saml-sp.crt").toString() +export const idpCert = fs.readFileSync("keys/saml-idp.crt").toString() +``` + +Načtení veřejných klíčů, které jsou normálně dostupné oběma komponentám (a jsou buď přímo důvěryhodné, nebo podepsané důvěryhodnou certifikační autoritou). + +```typescript +export const spPort = 3000 +export const spHostname = "localhost" +export const spDir = "sp" + +export const idpPort = 3001 +export const idpHostname = "localhost" +export const idpDir = "idp" + +export const spUrl = `${protocol}://${spHostname}:${spPort}/${spDir}` +export const idpUrl = `${protocol}://${idpHostname}:${idpPort}/${idpDir}` +``` + +Adresy URL pro obě komponenty. + +```typescript +export const spPublicData = { +``` + +Veřejná data pro poskytovatele služeb. + +```typescript + entityID: `${spUrl}/metadata`, +``` + +Podle konvence je v SAML `entityID` adresa URL, na které jsou dostupná metadata entity. Tato metadata odpovídají zdejším veřejným datům, s výjimkou toho, že jsou ve formátu XML. + +```typescript + wantAssertionsSigned: true, + authnRequestsSigned: false, + signingCert: spCert, + allowCreate: true, + assertionConsumerService: [{ + Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + Location: `${spUrl}/assertion`, + }] + } +``` + +Nejdůležitější definicí pro naše účely je `assertionConsumerServer`. To znamená, že abychom poskytovateli služeb mohli něco potvrdit (například, že „uživatel, který vám posílá tyto informace, je nekdo@example.com“), musíme použít [HTTP POST](https://www.w3schools.com/tags/ref_httpmethods.asp) na URL `http://localhost:3000/sp/assertion`. + +```typescript +export const idpPublicData = { + entityID: `${idpUrl}/metadata`, + signingCert: idpCert, + wantAuthnRequestsSigned: false, + singleSignOnService: [{ + Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", + Location: `${idpUrl}/login` + }], + singleLogoutService: [{ + Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", + Location: `${idpUrl}/logout` + }], + } +``` + +Veřejná data pro poskytovatele identity jsou podobná. Určuje, že pro přihlášení uživatele je třeba odeslat POST na `http://localhost:3001/idp/login` a pro odhlášení uživatele POST na `http://localhost:3001/idp/logout`. + +#### src/sp.mts + +Toto je kód, který implementuje poskytovatele služeb. + +```typescript +import * as config from "./config.mts" +const fs = await import("fs") +const saml = await import("samlify") +``` + +K implementaci SAML používáme knihovnu [`samlify`](https://www.npmjs.com/package/samlify). + +```typescript +import * as validator from "@authenio/samlify-node-xmllint" +saml.setSchemaValidator(validator) +``` + +Knihovna `samlify` očekává balíček, který ověří, že XML je správné, podepsané očekávaným veřejným klíčem atd. Pro tento účel používáme [`@authenio/samlify-node-xmllint`](https://www.npmjs.com/package/@authenio/samlify-node-xmllint). + +```typescript +const express = (await import("express")).default +const spRouter = express.Router() +const app = express() +``` + +[`Router`](https://expressjs.com/en/5x/api.html#router) v [`expressu`](https://expressjs.com/) je „mini webová stránka“, kterou lze připojit k webové stránce. V tomto případě jej používáme ke seskupení všech definic poskytovatele služeb dohromady. + +```typescript +const spPrivateKey = fs.readFileSync("keys/saml-sp.pem").toString() + +const sp = saml.ServiceProvider({ + privateKey: spPrivateKey, + ...config.spPublicData +}) +``` + +Vlastní reprezentace poskytovatele služeb se skládá ze všech veřejných dat a privátního klíče, který používá k podepisování informací. + +```typescript +const idp = saml.IdentityProvider(config.idpPublicData); +``` + +Veřejná data obsahují vše, co poskytovatel služeb potřebuje vědět o poskytovateli identity. + +```typescript +spRouter.get(`/metadata`, + (req, res) => res.header("Content-Type", "text/xml").send(sp.getMetadata()) +) +``` + +Pro zajištění interoperability s ostatními komponentami SAML by poskytovatelé služeb a identity měli mít svá veřejná data (nazývaná metadata) dostupná ve formátu XML na adrese `/metadata`. + +```typescript +spRouter.post(`/assertion`, +``` + +Toto je stránka, na kterou prohlížeč přistupuje, aby se identifikoval. Tvrzení obsahuje identifikátor uživatele (zde používáme e-mailovou adresu) a může obsahovat další atributy. Toto je obslužný program pro krok 7 ve výše uvedeném sekvenčním diagramu. + +```typescript + async (req, res) => { + // console.log(`SAML response:\n${Buffer.from(req.body.SAMLResponse, 'base64').toString('utf-8')}`) +``` + +Pomocí zakomentovaného příkazu můžete zobrazit data XML poskytnutá v tvrzení. Je [kódován v base64](https://en.wikipedia.org/wiki/Base64). + +```typescript + try { + const loginResponse = await sp.parseLoginResponse(idp, 'post', req); +``` + +Zpracujte požadavek na přihlášení od serveru identity. + +```typescript + res.send(` + + +

Dobrý den, ${loginResponse.extract.nameID}

+ + + `) + res.send(); +``` + +Odešlete odpověď HTML, abyste uživateli ukázali, že jsme obdrželi přihlášení. + +```typescript + } catch (err) { + console.error('Chyba při zpracování odpovědi SAML:', err); + res.status(400).send('Ověření SAML se nezdařilo'); + } + } +) +``` + +V případě selhání informujte uživatele. + +```typescript +spRouter.get('/login', +``` + +Vytvořte požadavek na přihlášení, když se prohlížeč pokusí o přístup na tuto stránku. Toto je obslužný program pro krok 1 ve výše uvedeném sekvenčním diagramu. + +```typescript + async (req, res) => { + const loginRequest = await sp.createLoginRequest(idp, "post") +``` + +Získejte informace pro odeslání požadavku na přihlášení. + +```typescript + res.send(` + + + +``` + +Tato stránka automaticky odešle formulář (viz níže). Díky tomu uživatel nemusí pro přesměrování nic dělat. Toto je krok 2 ve výše uvedeném sekvenčním diagramu. + +```typescript +
+``` + +Odešlete POST na `loginRequest.entityEndpoint` (adresa URL koncového bodu poskytovatele identity). + +```typescript + +``` + +Název vstupu je `loginRequest.type` (`SAMLRequest`). Obsah tohoto pole je `loginRequest.context`, což je opět XML kódované v base64. + +```typescript +
+ + + `) + } +) + +app.use(express.urlencoded({extended: true})) +``` + +[Tento middleware](https://expressjs.com/en/5x/api.html#express.urlencoded) čte tělo [požadavku HTTP](https://www.tutorialspoint.com/http/http_requests.htm). Ve výchozím nastavení ho Express ignoruje, protože většina požadavků ho nevyžaduje. Potřebujeme ho, protože POST používá tělo. + +```typescript +app.use(`/${config.spDir}`, spRouter) +``` + +Připojte router do adresáře poskytovatele služeb (`/sp`). + +```typescript +app.get("/", (req, res) => { + res.send(` + + + + + + `) +}) +``` + +Pokud se prohlížeč pokusí získat kořenový adresář, poskytněte mu odkaz na přihlašovací stránku. + +```typescript +app.listen(config.spPort, () => { + console.log(`poskytovatel služeb běží na http://${config.spHostname}:${config.spPort}`) +}) +``` + +Naslouchejte na portu `spPort` pomocí této aplikace Express. + +#### src/idp.mts + +Toto je poskytovatel identity. Je velmi podobný poskytovateli služeb, níže uvedená vysvětlení se týkají částí, které se liší. + +```typescript +const xmlParser = new (await import("fast-xml-parser")).XMLParser( + { + ignoreAttributes: false, // Zachovat atributy + attributeNamePrefix: "@_", // Prefix pro atributy + } +) +``` + +Musíme si přečíst a porozumět požadavku XML, který obdržíme od poskytovatele služeb. + +```typescript +const getLoginPage = requestId => ` +``` + +Tato funkce vytváří stránku s automaticky odesílaným formulářem, který je vrácen v kroku 4 výše uvedeného sekvenčního diagramu. + +```typescript + + + Přihlašovací stránka + + +

Přihlašovací stránka

+
+ + E-mailová adresa: +
+ +``` + +Poskytovateli služeb posíláme dvě pole: + +1. `requestId`, na které odpovídáme. +2. Identifikátor uživatele (prozatím používáme e-mailovou adresu, kterou uživatel poskytne). + +```typescript +
+ + + +const idpRouter = express.Router() + +idpRouter.post("/loginSubmitted", async (req, res) => { + const loginResponse = await idp.createLoginResponse( +``` + +Toto je obslužný program pro krok 5 ve výše uvedeném sekvenčním diagramu. [`idp.createLoginResponse`](https://github.com/tngan/samlify/blob/master/src/entity-idp.ts#L73-L125) vytváří odpověď na přihlášení. + +```typescript + sp, + { + authnContextClassRef: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport', + audience: sp.entityID, +``` + +Cílovou skupinou je poskytovatel služeb. + +```typescript + extract: { + request: { + id: req.body.requestId + } + }, +``` + +Informace extrahované z požadavku. Jediný parametr, který nás v požadavku zajímá, je requestId, který umožňuje poskytovateli služeb párovat požadavky a jejich odpovědi. + +```typescript + signingKey: { privateKey: idpPrivateKey, publicKey: config.idpCert } // Zajistit podepsání +``` + +Potřebujeme `signingKey`, abychom měli data k podepsání odpovědi. Poskytovatel služeb nedůvěřuje nepodepsaným požadavkům. + +```typescript + }, + "post", + { + email: req.body.email +``` + +Toto je pole s informacemi o uživateli, které posíláme zpět poskytovateli služeb. + +```typescript + } + ); + + res.send(` + + + + +
+ +
+ + + `) +}) +``` + +Opět použijte automaticky odesílaný formulář. Toto je krok 6 ve výše uvedeném sekvenčním diagramu. + +```typescript + +// Koncový bod IdP pro požadavky na přihlášení +idpRouter.post(`/login`, +``` + +Toto je koncový bod, který přijímá požadavek na přihlášení od poskytovatele služeb. Toto je obslužný program pro krok 3 ve výše uvedeném sekvenčním diagramu. + +```typescript + async (req, res) => { + try { + // Dočasné řešení, protože se mi nepodařilo zprovoznit parseLoginRequest. + // const loginRequest = await idp.parseLoginRequest(sp, 'post', req) + const samlRequest = xmlParser.parse(Buffer.from(req.body.SAMLRequest, 'base64').toString('utf-8')) + res.send(getLoginPage(samlRequest["samlp:AuthnRequest"]["@_ID"])) +``` + +Měli bychom být schopni použít [`idp.parseLoginRequest`](https://github.com/tngan/samlify/blob/master/src/entity-idp.ts#L127-L144) ke čtení ID požadavku na ověření. Nepodařilo se mi to však zprovoznit a nestálo za to tím trávit spoustu času, tak jsem prostě použil [univerzální XML parser](https://www.npmjs.com/package/fast-xml-parser). Informace, kterou potřebujeme, je atribut `ID` uvnitř značky ``, která je na nejvyšší úrovni XML. + +## Použití podpisů Etherea + +Nyní, když můžeme odeslat identitu uživatele poskytovateli služeb, dalším krokem je získat identitu uživatele důvěryhodným způsobem. Viem nám umožňuje jednoduše požádat peněženku o adresu uživatele, ale to znamená žádat o informace prohlížeč. Prohlížeč nemáme pod kontrolou, takže nemůžeme automaticky důvěřovat odpovědi, kterou od něj dostaneme. + +Místo toho IdP pošle prohlížeči řetězec k podepsání. Pokud peněženka v prohlížeči tento řetězec podepíše, znamená to, že se skutečně jedná o tuto adresu (tj. zná privátní klíč, který adrese odpovídá). + +Chcete-li to vidět v akci, zastavte stávající IdP a SP a spusťte tyto příkazy: + +```sh +git checkout eth-signatures +pnpm install +pnpm start +``` + +Poté přejděte [na SP](http://localhost:3000) a postupujte podle pokynů. + +Všimněte si, že v tomto okamžiku nevíme, jak získat e-mailovou adresu z adresy Etherea, takže místo toho hlásíme SP `@bad.email.address`. + +### Podrobné vysvětlení + +Změny jsou v krocích 4–5 v předchozím diagramu. + +![SAML s podpisem Etherea](./fig-05-saml-w-signature.png) + +Jediný soubor, který jsme změnili, je `idp.mts`. Zde jsou změněné části. + +```typescript +import { v4 as uuidv4 } from 'uuid' +import { verifyMessage } from 'viem' +``` + +Potřebujeme tyto dvě další knihovny. K vytvoření hodnoty [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce) používáme [`uuid`](https://www.npmjs.com/package/uuid). Na samotné hodnotě nezáleží, jen na tom, že je použita pouze jednou. + +Knihovna [`viem`](https://viem.sh/) nám umožňuje používat definice Etherea. Zde ji potřebujeme k ověření, že podpis je skutečně platný. + +```typescript +const loginPrompt = "Pro přístup k poskytovateli služeb podepište tuto hodnotu nonce: " +``` + +Peněženka požádá uživatele o povolení podepsat zprávu. Zpráva, která je pouze hodnotou nonce, by mohla uživatele zmást, proto uvádíme tuto výzvu. + +```typescript +// Zde si ponechte requestID +let nonces = {} +``` + +Potřebujeme informace o požadavku, abychom na něj mohli odpovědět. Mohli bychom ho poslat s požadavkem (krok 4) a přijmout ho zpět (krok 5). Nemůžeme však důvěřovat informacím, které získáváme z prohlížeče, který je pod kontrolou potenciálně nepřátelského uživatele. Je tedy lepší jej uložit zde s hodnotou nonce jako klíčem. + +Všimněte si, že pro zjednodušení to zde děláme jako proměnnou. To má však několik nevýhod: + +- Jsme zranitelní vůči útoku typu odepření služby (denial of service). Uživatel se zlými úmysly by se mohl pokusit přihlásit vícekrát a zaplnit tak naši paměť. +- Pokud je třeba proces IdP restartovat, ztratíme stávající hodnoty. +- Nemůžeme rozkládat zátěž mezi více procesů, protože každý by měl svou vlastní proměnnou. + +Na produkčním systému bychom použili databázi a implementovali nějaký mechanismus pro vypršení platnosti. + +```typescript +const getSignaturePage = requestId => { + const nonce = uuidv4() + nonces[nonce] = requestId +``` + +Vytvořte hodnotu nonce a uložte `requestId` pro budoucí použití. + +```typescript + return ` + + + + + +

Prosím podepište

+ +
+ + + +` +} +``` + +Zbytek je jen standardní HTML. + +```typescript +idpRouter.get("/signature/:nonce/:account/:signature", async (req, res) => { +``` + +Toto je obslužný program pro krok 5 v sekvenčním diagramu. + +```typescript + const requestId = nonces[req.params.nonce] + if (requestId === undefined) { + res.send("Špatná hodnota nonce") + return ; + } + + nonces[req.params.nonce] = undefined +``` + +Získejte ID požadavku a odstraňte hodnotu nonce z `nonces`, abyste se ujistili, že ji nelze znovu použít. + +```typescript + try { +``` + +Protože existuje mnoho způsobů, jak může být podpis neplatný, zabalíme to do bloku `try ...` `catch`, který zachytí jakékoli vyvolané chyby. + +```typescript + const validSignature = await verifyMessage({ + address: req.params.account, + message: `${loginPrompt}${req.params.nonce}`, + signature: req.params.signature + }) +``` + +Použijte [`verifyMessage`](https://viem.sh/docs/actions/public/verifyMessage#verifymessage) k implementaci kroku 5.5 v sekvenčním diagramu. + +```typescript + if (!validSignature) + throw("Špatný podpis") + } catch (err) { + res.send("Chyba:" + err) + return ; + } +``` + +Zbytek obslužného programu je ekvivalentní tomu, co jsme dělali v obslužném programu `/loginSubmitted` dříve, s výjimkou jedné malé změny. + +```typescript + const loginResponse = await idp.createLoginResponse( + . + . + . + { + email: req.params.account + "@bad.email.address" + } + ); +``` + +Nemáme skutečnou e-mailovou adresu (získáme ji v další části), takže prozatím vracíme adresu Etherea a jasně ji označujeme jako e-mailovou adresu. + +```typescript +// Koncový bod IdP pro požadavky na přihlášení +idpRouter.post(`/login`, + async (req, res) => { + try { + // Dočasné řešení, protože se mi nepodařilo zprovoznit parseLoginRequest. + // const loginRequest = await idp.parseLoginRequest(sp, 'post', req) + const samlRequest = xmlParser.parse(Buffer.from(req.body.SAMLRequest, 'base64').toString('utf-8')) + res.send(getSignaturePage(samlRequest["samlp:AuthnRequest"]["@_ID"])) + } catch (err) { + console.error('Chyba při zpracování odpovědi SAML:', err); + res.status(400).send('Ověření SAML se nezdařilo'); + } + } +) +``` + +Místo `getLoginPage` nyní v obslužném programu kroku 3 použijte `getSignaturePage`. + +## Získání e-mailové adresy + +Dalším krokem je získání e-mailové adresy, identifikátoru požadovaného poskytovatelem služeb. K tomu používáme [službu Ethereum Attestation Service (EAS)](https://attest.org/). + +Nejjednodušší způsob, jak získat atestace, je použít [GraphQL API](https://docs.attest.org/docs/developer-tools/api). Používáme tento dotaz: + +``` +query GetAttestationsByRecipient { + attestations( + where: { + recipient: { equals: "${getAddress(ethAddr)}" } + schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" } + } + take: 1 + ) { + data + id + attester + } +} +``` + +Toto [`schemaId`](https://optimism.easscan.org/schema/view/0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977) obsahuje pouze e-mailovou adresu. Tento dotaz žádá o atestace tohoto schématu. Subjekt atestace se nazývá `recipient`. Je to vždy adresa Etherea. + +Varování: Způsob, jakým zde získáváme atestace, má dva bezpečnostní problémy. + +- Přistupujeme ke koncovému bodu API `https://optimism.easscan.org/graphql`, což je centralizovaná komponenta. Můžeme získat atribut `id` a poté provést ověření na blockchainu, abychom ověřili, že je atestace skutečná, ale koncový bod API může stále cenzurovat atestace tím, že nám o nich neřekne. + + Tento problém není neřešitelný, mohli bychom spustit vlastní koncový bod GraphQL a získat atestace z protokolů řetězce, ale to je pro naše účely zbytečné. + +- Nebereme v úvahu identitu atestátora. Kdokoli nám může poskytnout nepravdivé informace. V reálné implementaci bychom měli sadu důvěryhodných atestátorů a zabývali bychom se pouze jejich atestacemi. + +Chcete-li to vidět v akci, zastavte stávající IdP a SP a spusťte tyto příkazy: + +```sh +git checkout email-address +pnpm install +pnpm start +``` + +Poté zadejte svou e-mailovou adresu. Máte dva způsoby, jak to udělat: + +- Importujte peněženku pomocí privátního klíče a použijte testovací privátní klíč `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80`. + +- Přidejte atestaci pro svou vlastní e-mailovou adresu: + + 1. Přejděte na [schéma v průzkumníku atestací](https://optimism.easscan.org/schema/view/0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977). + + 2. Klikněte na **Atestovat se schématem**. + + 3. Zadejte svou adresu Etherea jako příjemce, svou e-mailovou adresu jako e-mailovou adresu a vyberte **Onchain**. Poté klikněte na **Provést atestaci**. + + 4. Schvalte transakci ve své peněžence. K zaplacení za palivo budete potřebovat nějaké ETH na [blockchainu Optimism](https://app.optimism.io/bridge/deposit). + +Ať tak či onak, poté přejděte na [http://localhost:3000](http://localhost:3000) a postupujte podle pokynů. Pokud jste importovali testovací privátní klíč, e-mail, který obdržíte, je `test_addr_0@example.com`. Pokud jste použili vlastní adresu, měla by to být ta, kterou jste atestovali. + +### Podrobné vysvětlení + +![Získání e-mailu z adresy Etherea](./fig-06-saml-sig-n-email.png) + +Nové kroky jsou komunikace GraphQL, kroky 5.6 a 5.7. + +Opět zde jsou změněné části `idp.mts`. + +```typescript +import { GraphQLClient } from 'graphql-request' +import { SchemaEncoder } from '@ethereum-attestation-service/eas-sdk' +``` + +Importujte knihovny, které potřebujeme. + +```typescript +const graphqlEndpointUrl = "https://optimism.easscan.org/graphql" +``` + +Pro každý blockchain existuje [samostatný koncový bod](https://docs.attest.org/docs/developer-tools/api). + +```typescript +const graphqlClient = new GraphQLClient(graphqlEndpointUrl, { fetch }) +``` + +Vytvořte nového klienta `GraphQLClient`, kterého můžeme použít pro dotazování koncového bodu. + +```typescript +const graphqlSchema = 'string emailAddress' +const graphqlEncoder = new SchemaEncoder(graphqlSchema) +``` + +GraphQL nám poskytuje pouze neprůhledný datový objekt s bajty. Abychom mu porozuměli, potřebujeme schéma. + +```typescript +const ethereumAddressToEmail = async ethAddr => { +``` + +Funkce pro získání e-mailové adresy z adresy Etherea. + +```typescript + const query = ` + query GetAttestationsByRecipient { +``` + +Toto je dotaz GraphQL. + +```typescript + attestations( +``` + +Hledáme atestace. + +```typescript + where: { + recipient: { equals: "${getAddress(ethAddr)}" } + schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" } + } +``` + +Atestace, které chceme, jsou ty v našem schématu, kde je příjemce `getAddress(ethAddr)`. Funkce [`getAddress`](https://viem.sh/docs/utilities/getAddress#getaddress) zajišťuje, že naše adresa má správný [kontrolní součet](https://github.com/ethereum/ercs/blob/master/ERCS/erc-55.md). To je nutné, protože GraphQL rozlišuje velikost písmen. „0xBAD060A7“, „0xBad060A7“ a „0xbad060a7“ jsou různé hodnoty. + +```typescript + take: 1 +``` + +Bez ohledu na to, kolik atestací najdeme, chceme pouze tu první. + +```typescript + ) { + data + id + attester + } + }` +``` + +Pole, která chceme obdržet. + +- `attester`: Adresa, která atestaci odeslala. Obvykle se používá k rozhodnutí, zda atestaci důvěřovat, či nikoli. +- `id`: ID atestace. Tuto hodnotu můžete použít ke [čtení atestace na blockchainu](https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000021?tab=read_proxy&source_address=0x4E0275Ea5a89e7a3c1B58411379D1a0eDdc5b088#0xa3112a64), abyste ověřili, že informace z dotazu GraphQL jsou správné. +- `data`: Data schématu (v tomto případě e-mailová adresa). + +```typescript + const queryResult = await graphqlClient.request(query) + + if (queryResult.attestations.length == 0) + return "no_address@available.is" +``` + +Pokud neexistuje žádná atestace, vraťte hodnotu, která je zjevně nesprávná, ale která by se poskytovateli služeb jevila jako platná. + +```typescript + const attestationDataFields = graphqlEncoder.decodeData(queryResult.attestations[0].data) + return attestationDataFields[0].value.value +} +``` + +Pokud existuje hodnota, použijte k dekódování dat `decodeData`. Nepotřebujeme metadata, která poskytuje, pouze samotnou hodnotu. + +```typescript + const loginResponse = await idp.createLoginResponse( + sp, + { + . + . + . + }, + "post", + { + email: await ethereumAddressToEmail(req.params.account) + } + ); +``` + +Pro získání e-mailové adresy použijte novou funkci. + +## A co decentralizace? + +V této konfiguraci se uživatelé nemohou vydávat za někoho, kým nejsou, pokud se spoléháme na důvěryhodné atestátory pro mapování adres Etherea na e-mailové adresy. Náš poskytovatel identity je však stále centralizovanou komponentou. Kdokoli, kdo má privátní klíč poskytovatele identity, může poskytovateli služeb posílat nepravdivé informace. + +Může existovat řešení pomocí [více-stranného výpočtu (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation). Doufám, že o tom napíšu v budoucím tutoriálu. + +## Závěr + +Přijetí standardu pro přihlášení, jako jsou podpisy Etherea, se potýká s problémem slepice a vejce. Poskytovatelé služeb chtějí oslovit co nejširší trh. Uživatelé chtějí mít přístup ke službám, aniž by se museli starat o podporu svého standardu pro přihlášení. +Vytváření adaptérů, jako je Ethereum IdP, nám může pomoci překonat tuto překážku. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). diff --git a/public/content/translations/cs/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md b/public/content/translations/cs/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md new file mode 100644 index 00000000000..1ed5f892f7d --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md @@ -0,0 +1,156 @@ +--- +title: "Začínáme s vývojem pro Ethereum" +description: "Toto je příručka pro začátečníky, jak začít s vývojem pro Ethereum. Provedeme vás od vytvoření koncového bodu API přes vytvoření požadavku z příkazového řádku až po napsání vašeho prvního web3 skriptu! Nejsou nutné žádné zkušenosti s vývojem na blockchainu!" +author: "Elan Halpern" +tags: + [ + "javascript", + "ethers.js", + "uzly", + "dotazování", + "alchemy" + ] +skill: beginner +lang: cs +published: 2020-10-30 +source: Medium +sourceUrl: https://medium.com/alchemy-api/getting-started-with-ethereum-development-using-alchemy-c3d6a45c567f +--- + +![Loga Etherea a Alchemy](./ethereum-alchemy.png) + +Toto je příručka pro začátečníky, jak začít s vývojem pro Ethereum. V tomto tutoriálu budeme používat [Alchemy](https://alchemyapi.io/), přední vývojářskou platformu pro blockchain, která pohání miliony uživatelů ze 70 % nejlepších blockchainových aplikací, včetně Maker, 0x, MyEtherWallet, Dharma a Kyber. Alchemy nám poskytne přístup ke koncovému bodu API v řetězci Ethereum, abychom mohli číst a zapisovat transakce. + +Provedeme vás od registrace u Alchemy až po napsání vašeho prvního web3 skriptu! Nejsou nutné žádné zkušenosti s vývojem na blockchainu! + +## 1. Zaregistrujte si bezplatný účet Alchemy {#sign-up-for-a-free-alchemy-account} + +Vytvoření účtu u Alchemy je snadné, [zaregistrujte se zdarma zde](https://auth.alchemy.com/). + +## 2. Vytvořte aplikaci Alchemy {#create-an-alchemy-app} + +Pro komunikaci s řetězcem Ethereum a pro používání produktů Alchemy potřebujete API klíč k ověření vašich požadavků. + +API klíče můžete [vytvořit na řídicím panelu](https://dashboard.alchemy.com/). Chcete-li vytvořit nový klíč, přejděte na „Vytvořit aplikaci“, jak je ukázáno níže: + +Zvláštní poděkování patří [_ShapeShift_](https://shapeshift.com/) _za to, že nám umožnili ukázat jejich řídicí panel!_ + +![Řídicí panel Alchemy](./alchemy-dashboard.png) + +Vyplňte podrobnosti v sekci „Vytvořit aplikaci“, abyste získali svůj nový klíč. Můžete zde také vidět aplikace, které jste dříve vytvořili, a ty, které vytvořil váš tým. Stávající klíče získáte kliknutím na „Zobrazit klíč“ u kterékoli aplikace. + +![Snímek obrazovky vytvoření aplikace s Alchemy](./create-app.png) + +Stávající API klíče můžete také získat tak, že najedete kurzorem na „Aplikace“ a jednu vyberete. Zde můžete „zobrazit klíč“, a také „upravit aplikaci“ pro zařazení konkrétních domén na seznam povolených, prohlédnout si několik vývojářských nástrojů a zobrazit analytiku. + +![Gif, který ukazuje uživateli, jak získat API klíče](./pull-api-keys.gif) + +## 3. Vytvoření požadavku z příkazového řádku {#make-a-request-from-the-command-line} + +S blockchainem Etherea můžete prostřednictvím Alchemy interagovat pomocí JSON-RPC a curl. + +Pro ruční požadavky doporučujeme interagovat s `JSON-RPC` prostřednictvím požadavků `POST`. Jednoduše předejte hlavičku `Content-Type: application/json` a váš dotaz jako tělo `POST` s následujícími poli: + +- `jsonrpc`: Verze JSON-RPC – v současné době je podporována pouze verze `2.0`. +- `method`: Metoda ETH API. [Viz reference API.](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc) +- `params`: Seznam parametrů, které se mají předat metodě. +- `id`: ID vašeho požadavku. Bude vráceno odpovědí, abyste mohli sledovat, ke kterému požadavku odpověď patří. + +Zde je příklad, který můžete spustit z příkazového řádku pro získání aktuální ceny gasu: + +```bash +curl https://eth-mainnet.alchemyapi.io/v2/demo \ +-X POST \ +-H "Content-Type: application/json" \ +-d '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":73}' +``` + +_**POZNÁMKA:** Nahraďte [https://eth-mainnet.alchemyapi.io/v2/demo](https://eth-mainnet.alchemyapi.io/jsonrpc/demo) svým vlastním API klíčem `https://eth-mainnet.alchemyapi.io/v2/**your-api-key`._ + +**Výsledky:** + +```json +{ "id": 73,"jsonrpc": "2.0","result": "0x09184e72a000" // 10000000000000 } +``` + +## 4. Nastavte si svého Web3 klienta {#set-up-your-web3-client} + +**Pokud již máte existujícího klienta,** změňte URL adresu svého současného poskytovatele uzlů na URL adresu Alchemy s vaším API klíčem: `"https://eth-mainnet.alchemyapi.io/v2/your-api-key"` + +**_POZNÁMKA:_** Níže uvedené skripty je třeba spustit v **kontextu Node** nebo **uložit do souboru**, nikoli z příkazového řádku. Pokud ještě nemáte nainstalovaný Node nebo npm, podívejte se na tohoto rychlého [průvodce nastavením pro Mac](https://app.gitbook.com/@alchemyapi/s/alchemy/guides/alchemy-for-macs). + +Existuje spousta [knihoven Web3](https://docs.alchemyapi.io/guides/getting-started#other-web3-libraries), které můžete integrovat s Alchemy, my však doporučujeme používat [Alchemy Web3](https://docs.alchemy.com/reference/api-overview), přímou náhradu za web3.js, vytvořenou a nakonfigurovanou tak, aby bezproblémově fungovala s Alchemy. To poskytuje řadu výhod, jako jsou automatické opakované pokusy a robustní podpora WebSocketů. + +Chcete-li nainstalovat AlchemyWeb3.js, **přejděte do adresáře svého projektu** a spusťte: + +**S Yarn:** + +``` +yarn add @alch/alchemy-web3 +``` + +**S NPM:** + +``` +npm install @alch/alchemy-web3 +``` + +Chcete-li interagovat s infrastrukturou uzlů Alchemy, spusťte v NodeJS nebo přidejte toto do souboru JavaScript: + +```js +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3( + "https://eth-mainnet.alchemyapi.io/v2/your-api-key" +) +``` + +## 5. Napište svůj první Web3 skript! {#write-your-first-web3-script} + +Nyní si trochu „ušpiníme ruce“ programováním ve web3 a napíšeme jednoduchý skript, který vypíše číslo posledního bloku z hlavní sítě Ethereum (mainnetu). + +**1.** Pokud jste tak ještě neučinili, ve svém terminálu vytvořte nový adresář projektu a přejděte do něj:\*\* + +``` +mkdir web3-example +cd web3-example +``` + +**2.** Nainstalujte si do projektu závislost Alchemy web3 (nebo jakoukoli jinou web3), pokud jste tak ještě neučinili:\*\* + +``` +npm install @alch/alchemy-web3 +``` + +**3.** Vytvořte soubor s názvem `index.js` a přidejte do něj následující obsah:\*\* + +> Nakonec byste měli `demo` nahradit svým HTTP API klíčem Alchemy. + +```js +async function main() { + const { createAlchemyWeb3 } = require("@alch/alchemy-web3") + const web3 = createAlchemyWeb3("https://eth-mainnet.alchemyapi.io/v2/demo") + const blockNumber = await web3.eth.getBlockNumber() + console.log("Číslo posledního bloku je " + blockNumber) +} +main() +``` + +Nevyznáte se v asynchronních věcech? Podívejte se na tento [příspěvek na serveru Medium](https://medium.com/better-programming/understanding-async-await-in-javascript-1d81bb079b2c). + +**4. Spusťte jej ve svém terminálu pomocí node** + +``` +node index.js +``` + +**5. Nyní byste měli ve vaší konzoli vidět výstup s číslem posledního bloku!** + +``` +Číslo posledního bloku je 11043912 +``` + +**Paráda! Výborně! Právě jste napsali svůj první web3 skript pomocí Alchemy 🎉** + +Nevíte, co dál? Zkuste nasadit svůj první chytrý kontrakt a ponořte se do programování v Solidity v našem [Průvodci chytrým kontraktem Hello World](https://www.alchemy.com/docs/hello-world-smart-contract), nebo si otestujte své znalosti řídicího panelu s [Demo aplikací řídicího panelu](https://docs.alchemyapi.io/tutorials/demo-app)! + +_[Zaregistrujte se zdarma u Alchemy](https://auth.alchemy.com/), prohlédněte si naši [dokumentaci](https://www.alchemy.com/docs/) a pro nejnovější zprávy nás sledujte na [Twitteru](https://twitter.com/AlchemyPlatform)_. diff --git a/public/content/translations/cs/developers/tutorials/guide-to-smart-contract-security-tools/index.md b/public/content/translations/cs/developers/tutorials/guide-to-smart-contract-security-tools/index.md new file mode 100644 index 00000000000..ec5da90b436 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/guide-to-smart-contract-security-tools/index.md @@ -0,0 +1,102 @@ +--- +title: "Průvodce nástroji pro zabezpečení chytrých kontraktů" +description: "Přehled tří různých technik testování a analýzy programů" +author: "Trailofbits" +lang: cs +tags: [ "solidity", "smart kontrakt účty", "bezpečnost" ] +skill: intermediate +published: 2020-09-07 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis +--- + +Budeme používat tři různé techniky testování a analýzy programů: + +- **Statická analýza pomocí nástroje [Slither](/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/).** Všechny cesty programu jsou aproximovány a analyzovány současně prostřednictvím různých prezentací programu (např. graf řízení toku). +- **Fuzzing s nástrojem [Echidna](/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/).** Kód se provádí s pseudonáhodným generováním transakcí. Fuzzer se pokusí najít sekvenci transakcí, která poruší danou vlastnost. +- **Symbolické spuštění s nástrojem [Manticore](/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/).** Formální ověřovací technika, která převádí každou cestu spuštění na matematický vzorec, na němž lze kontrolovat omezení. + +Každá technika má své výhody a nevýhody a bude užitečná ve [specifických případech](#determining-security-properties): + +| Technika | Nástroj | Použití | Rychlost | Zmeškané chyby | Falešné poplachy | +| ------------------- | --------- | ----------------------------- | -------- | -------------- | ---------------- | +| Statická analýza | Slither | CLI a skripty | sekund | střední | nízká | +| Fuzzing | Echidna | Vlastnosti Solidity | minut | nízká | žádná | +| Symbolické spuštění | Manticore | Vlastnosti a skripty Solidity | hodin | žádná\* | žádná | + +\* pokud jsou všechny cesty prozkoumány bez vypršení časového limitu + +**Slither** analyzuje kontrakty během několika sekund, nicméně statická analýza může vést k falešným poplachům a bude méně vhodná pro složité kontroly (např. aritmetické kontroly). Spusťte Slither prostřednictvím rozhraní API pro jednoduchý přístup k vestavěným detektorům nebo prostřednictvím rozhraní API pro uživatelem definované kontroly. + +**Echidna** potřebuje běžet několik minut a bude produkovat pouze skutečné pozitivní nálezy. Echidna kontroluje uživatelem zadané bezpečnostní vlastnosti napsané v jazyce Solidity. Může přehlédnout chyby, protože je založena na náhodném prozkoumávání. + +**Manticore** provádí analýzu s "největší váhou". Stejně jako Echidna, Manticore ověřuje vlastnosti zadané uživatelem. Bude potřebovat více času na spuštění, ale může prokázat platnost vlastnosti a nebude hlásit falešné poplachy. + +## Doporučený pracovní postup {#suggested-workflow} + +Začněte s vestavěnými detektory nástroje Slither, abyste se ujistili, že v současné době neexistují žádné jednoduché chyby, ani nebudou zavedeny později. Použijte Slither ke kontrole vlastností souvisejících s dědičností, závislostmi proměnných a strukturálními problémy. Jak se kódová základna rozrůstá, použijte nástroj Echidna k testování složitějších vlastností stavového automatu. Vraťte se k nástroji Slither a vyviňte si vlastní kontroly ochrany, které nejsou v Solidity dostupné, například ochranu proti přepsání funkce. Nakonec použijte Manticore k provedení cíleného ověření kritických bezpečnostních vlastností, např. aritmetických operací. + +- Použijte CLI nástroje Slither k zachycení běžných problémů +- Použijte Echidna k testování bezpečnostních vlastností vašeho kontraktu na vysoké úrovni +- Použijte Slither k psaní vlastních statických kontrol +- Použijte Manticore, jakmile budete chtít hloubkové ujištění o kritických bezpečnostních vlastnostech + +**Poznámka k jednotkovým testům**. Jednotkové testy jsou nezbytné pro vytváření vysoce kvalitního softwaru. Tyto techniky však nejsou nejvhodnější k nalezení bezpečnostních chyb. Obvykle se používají k testování pozitivního chování kódu (tj. kód funguje podle očekávání v normálním kontextu), zatímco bezpečnostní chyby se obvykle vyskytují v okrajových případech, které vývojáři nezohlednili. V naší studii desítek bezpečnostních auditů chytrých kontraktů nemělo [pokrytí jednotkovými testy žádný vliv na počet ani závažnost bezpečnostních chyb](https://blog.trailofbits.com/2019/08/08/246-findings-from-our-smart-contract-audits-an-executive-summary/), které jsme našli v kódu našich klientů. + +## Určení bezpečnostních vlastností {#determining-security-properties} + +Chcete-li efektivně testovat a ověřovat svůj kód, musíte identifikovat oblasti, které vyžadují pozornost. Vzhledem k tomu, že vaše zdroje vynaložené na bezpečnost jsou omezené, je důležité určit slabé nebo vysoce hodnotné části vaší kódové základny, abyste optimalizovali své úsilí. Pomoci může modelování hrozeb. Zvažte prostudování: + +- [Rychlé posouzení rizik](https://infosec.mozilla.org/guidelines/risk/rapid_risk_assessment.html) (náš preferovaný přístup, když je málo času) +- [Průvodce modelováním hrozeb datově orientovaných systémů](https://csrc.nist.gov/pubs/sp/800/154/ipd) (aka NIST 800-154) +- [Modelování hrozeb podle Shostacka](https://www.amazon.com/Threat-Modeling-Designing-Adam-Shostack/dp/1118809998) +- [STRIDE](https://wikipedia.org/wiki/STRIDE_\(security\)) / [DREAD](https://wikipedia.org/wiki/DREAD_\(risk_assessment_model\)) +- [PASTA](https://wikipedia.org/wiki/Threat_model#P.A.S.T.A.) +- [Použití tvrzení](https://blog.regehr.org/archives/1091) + +### Komponenty {#components} + +Vědět, co chcete kontrolovat, vám také pomůže vybrat správný nástroj. + +Mezi široké oblasti, které jsou často relevantní pro chytré kontrakty, patří: + +- **Stavový automat.** Většinu kontraktů lze reprezentovat jako stavový automat. Zvažte kontrolu, že (1) nelze dosáhnout žádného neplatného stavu, (2) pokud je stav platný, lze ho dosáhnout a (3) žádný stav kontrakt nezablokuje. + + - Echidna a Manticore jsou nástroje, které je třeba upřednostnit pro testování specifikací stavových automatů. + +- **Řízení přístupu.** Pokud má váš systém privilegované uživatele (např. vlastníka, správce, ...) musíte zajistit, že (1) každý uživatel může provádět pouze povolené akce a (2) žádný uživatel nemůže blokovat akce privilegovanějšího uživatele. + + - Slither, Echidna a Manticore mohou kontrolovat správné řízení přístupu. Například Slither může zkontrolovat, že pouze funkce na bílé listině postrádají modifikátor onlyOwner. Echidna a Manticore jsou užitečné pro složitější řízení přístupu, jako je oprávnění udělené pouze v případě, že kontrakt dosáhne daného stavu. + +- **Aritmetické operace.** Kontrola správnosti aritmetických operací je kritická. Používání `SafeMath` všude je dobrým krokem k zabránění přetečení/podtečení, nicméně stále musíte zvážit další aritmetické chyby, včetně problémů se zaokrouhlováním a chyb, které kontrakt zablokují. + + - Manticore je zde nejlepší volbou. Echidna může být použita, pokud je aritmetika mimo rozsah SMT solveru. + +- **Správnost dědičnosti.** Kontrakty v jazyce Solidity se silně spoléhají na vícenásobnou dědičnost. Snadno se mohou vyskytnout chyby, jako je stínící funkce, které chybí volání `super`, a nesprávně interpretované pořadí c3 linearizace. + + - Slither je nástroj, který zajistí odhalení těchto problémů. + +- **Externí interakce.** Kontrakty vzájemně interagují a některým externím kontraktům by se nemělo důvěřovat. Například, pokud váš kontrakt spoléhá na externí orákula, zůstane bezpečný, pokud bude polovina dostupných orákul kompromitována? + + - Manticore a Echidna jsou nejlepší volbou pro testování externích interakcí s vašimi kontrakty. Manticore má vestavěný mechanismus pro stubování externích kontraktů. + +- **Shoda se standardy.** Standardy Etherea (např. ERC20) mají v historii svého návrhu chyby. Buďte si vědomi omezení standardu, na kterém stavíte. + - Slither, Echidna a Manticore vám pomohou odhalit odchylky od daného standardu. + +### Tahák pro výběr nástrojů {#tool-selection-cheatsheet} + +| Komponenta | Nástroje | Příklady | +| -------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Stavový automat | Echidna, Manticore | | +| Řízení přístupu | Slither, Echidna, Manticore | [Cvičení 2 nástroje Slither](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise2.md), [Cvičení 2 nástroje Echidna](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-2.md) | +| Aritmetické operace | Manticore, Echidna | [Cvičení 1 nástroje Echidna](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-1.md), [Cvičení 1–3 nástroje Manticore](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore/exercises) | +| Správnost dědičnosti | Slither | [Cvičení 1 nástroje Slither](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise1.md) | +| Externí interakce | Manticore, Echidna | | +| Shoda se standardy | Slither, Echidna, Manticore | [`slither-erc`](https://github.com/crytic/slither/wiki/ERC-Conformance) | + +V závislosti na vašich cílech bude třeba zkontrolovat i další oblasti, ale tyto hrubozrnné oblasti zaměření jsou dobrým začátkem pro jakýkoli systém chytrých kontraktů. + +Naše veřejné audity obsahují příklady ověřených nebo testovaných vlastností. Zvažte přečtení sekcí `Automated Testing and Verification` v následujících zprávách, abyste si prohlédli reálné bezpečnostní vlastnosti: + +- [0x](https://github.com/trailofbits/publications/blob/master/reviews/0x-protocol.pdf) +- [Balancer](https://github.com/trailofbits/publications/blob/master/reviews/BalancerCore.pdf) diff --git a/public/content/translations/cs/developers/tutorials/hello-world-smart-contract-fullstack/index.md b/public/content/translations/cs/developers/tutorials/hello-world-smart-contract-fullstack/index.md new file mode 100644 index 00000000000..0f37ba4dc20 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/hello-world-smart-contract-fullstack/index.md @@ -0,0 +1,1542 @@ +--- +title: "Chytrý kontrakt Hello World pro začátečníky – Fullstack" +description: "Úvodní tutoriál k psaní a nasazení jednoduchého chytrého kontraktu na Ethereum." +author: "nstrike2" +tags: + [ + "solidity", + "hardhat", + "alchemy", + "smart kontrakt účty", + "nasazování", + "průzkumník bloků", + "frontend", + "transakce" + ] +skill: beginner +lang: cs +published: 2021-10-25 +--- + +Tento průvodce je pro vás, pokud jste v blockchainovém vývoji nováčkem a nevíte, kde začít nebo jak nasadit chytré kontrakty a interagovat s nimi. Projdeme si vytvoření a nasazení jednoduchého chytrého kontraktu v testovací síti Goerli pomocí [MetaMask](https://metamask.io), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org) a [Alchemy](https://alchemy.com/eth). + +K dokončení tohoto tutoriálu budete potřebovat účet Alchemy. [Zaregistrujte si bezplatný účet](https://www.alchemy.com/). + +Pokud budete mít kdykoli nějaké otázky, neváhejte se na nás obrátit na [Discordu Alchemy](https://discord.gg/gWuC7zB)! + +## Část 1 – Vytvoření a nasazení chytrého kontraktu pomocí Hardhat {#part-1} + +### Připojení k síti Ethereum {#connect-to-the-ethereum-network} + +Existuje mnoho způsobů, jak posílat požadavky na blockchain Etherea. Pro zjednodušení použijeme bezplatný účet na Alchemy, vývojářské platformě a API pro blockchain, která nám umožňuje komunikovat s ethereovým chainem, aniž bychom museli sami provozovat uzel. Alchemy má také vývojářské nástroje pro monitorování a analýzu; v tomto tutoriálu je využijeme, abychom pochopili, co se děje pod pokličkou při nasazení našeho chytrého kontraktu. + +### Vytvoření aplikace a klíče API {#create-your-app-and-api-key} + +Jakmile si vytvoříte účet Alchemy, můžete si vygenerovat API klíč vytvořením aplikace. To vám umožní zadávat požadavky do testovací sítě Goerli. Pokud testovací sítě neznáte, můžete si přečíst [průvodce Alchemy výběrem sítě](https://www.alchemy.com/docs/choosing-a-web3-network). + +Na řídicím panelu Alchemy najděte v navigační liště rozevírací seznam **Aplikace** a klikněte na **Vytvořit aplikaci**. + +![Vytvoření aplikace Hello World](./hello-world-create-app.png) + +Pojmenujte svou aplikaci „_Hello World_“ a napište krátký popis. Jako prostředí vyberte **Staging** a jako síť **Goerli**. + +![Pohled na vytvoření aplikace Hello World](./create-app-view-hello-world.png) + +_Poznámka: Nezapomeňte vybrat **Goerli**, jinak tento tutoriál nebude fungovat._ + +Klikněte na **Vytvořit aplikaci**. Vaše aplikace se objeví v tabulce níže. + +### Vytvoření účtu Ethereum {#create-an-ethereum-account} + +Potřebujete účet Ethereum pro odesílání a přijímání transakcí. Použijeme MetaMask, virtuální peněženku v prohlížeči, která umožňuje uživatelům spravovat adresu svého ethereového účtu. + +Účet MetaMask si můžete zdarma stáhnout a vytvořit [zde](https://metamask.io/download). Při vytváření účtu nebo pokud již účet máte, nezapomeňte se vpravo nahoře přepnout na „testovací síť Goerli“ (abychom nepracovali se skutečnými penězi). + +### Krok 4: Přidejte ether z Faucetu {#step-4-add-ether-from-a-faucet} + +Chcete-li nasadit chytrý kontrakt do testovací sítě, budete potřebovat nějaké falešné ETH. Chcete-li získat ETH v síti Goerli, přejděte na faucet Goerli a zadejte adresu svého účtu Goerli. Upozorňujeme, že faucety Goerli mohou být v poslední době trochu nespolehlivé – podívejte se na [stránku testovacích sítí](/developers/docs/networks/#goerli), kde najdete seznam možností k vyzkoušení: + +_Poznámka: Kvůli přetížení sítě to může chvíli trvat._ +`` + +### Krok 5: Zkontrolujte si zůstatek {#step-5-check-your-balance} + +Chcete-li si dvakrát ověřit, že máte ETH v peněžence, zadejte požadavek [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) pomocí [nástroje Alchemy Composer](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). Tím se vrátí množství ETH v naší peněžence. Chcete-li se dozvědět více, podívejte se na [krátký tutoriál od Alchemy o tom, jak používat nástroj Composer](https://youtu.be/r6sjRxBZJuU). + +Zadejte adresu svého účtu MetaMask a klikněte na **Odeslat požadavek**. Zobrazí se odpověď, která vypadá jako úryvek kódu níže. + +```json +{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" } +``` + +> _Poznámka: Tento výsledek je ve wei, nikoliv v ETH. Wei je nejmenší jednotkou etheru._ + +Uf! Naše falešné peníze jsou všechny tam. + +### Krok 6: Inicializace našeho projektu {#step-6-initialize-our-project} + +Nejprve musíme pro náš projekt vytvořit složku. Přejděte na příkazový řádek a zadejte následující. + +``` +mkdir hello-world +cd hello-world +``` + +Nyní, když jsme uvnitř složky našeho projektu, použijeme `npm init` k inicializaci projektu. + +> Pokud ještě nemáte nainstalovaný npm, postupujte podle [těchto pokynů k instalaci Node.js a npm](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm). + +Pro účely tohoto tutoriálu nezáleží na tom, jak na inicializační otázky odpovíte. Zde je pro referenci, jak jsme to udělali my: + +``` +název balíčku: (hello-world) +verze: (1.0.0) +popis: chytrý kontrakt hello world +vstupní bod: (index.js) +příkaz k testování: +repositář git: +klíčová slova: +autor: +licence: (ISC) + +Chystá se zápis do /Users/.../.../.../hello-world/package.json: + +{ + "name": "hello-world", + "version": "1.0.0", + "description": "chytrý kontrakt hello world", + "main": "index.js", + "scripts": { + "test": "echo \"Chyba: není zadán žádný test\" && exit 1" + }, + "author": "", + "license": "ISC" +} +``` + +Schvalte package.json a můžeme pokračovat! + +### Krok 7: Stažení nástroje Hardhat {#step-7-download-hardhat} + +Hardhat je vývojové prostředí pro kompilaci, nasazení, testování a ladění vašeho softwaru pro Ethereum. Pomáhá vývojářům při lokálním budování chytrých kontraktů a dapps před jejich nasazením na živý řetězec. + +Uvnitř našeho projektu `hello-world` spusťte: + +``` +npm install --save-dev hardhat +``` + +Další podrobnosti o [instalačních pokynech](https://hardhat.org/getting-started/#overview) naleznete na této stránce. + +### Krok 8: Vytvoření projektu Hardhat {#step-8-create-hardhat-project} + +Uvnitř složky projektu `hello-world` spusťte: + +``` +npx hardhat +``` + +Poté by se vám měla zobrazit uvítací zpráva a možnost vybrat si, co chcete dělat. Vyberte „create an empty hardhat.config.js“: + +``` +888 888 888 888 888 +888 888 888 888 888 +888 888 888 888 888 +8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 +888 888 "88b 888P" d88" 888 888 "88b "88b 888 +888 888 .d888888 888 888 888 888 888 .d888888 888 +888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. +888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 + +👷 Vítejte v Hardhat v2.0.11 👷‍ + +Co chcete udělat? … +Vytvořit vzorový projekt +❯ Vytvořit prázdný soubor hardhat.config.js +Ukončit +``` + +Tím se v projektu vygeneruje soubor `hardhat.config.js`. Ten použijeme později v tomto tutoriálu k určení nastavení našeho projektu. + +### Krok 9: Přidání složek projektu {#step-9-add-project-folders} + +Aby byl projekt přehledný, vytvoříme dvě nové složky. V příkazovém řádku přejděte do kořenového adresáře projektu `hello-world` a zadejte: + +``` +mkdir contracts +mkdir scripts +``` + +- `contracts/` je místo, kam uložíme soubor s kódem našeho chytrého kontraktu Hello World +- `scripts/` je místo, kam uložíme skripty pro nasazení našeho kontraktu a interakci s ním + +### Krok 10: Napsání našeho kontraktu {#step-10-write-our-contract} + +Možná si říkáte, kdy budeme psát kód? Je čas! + +Otevřete si projekt hello-world ve svém oblíbeném editoru. Chytré kontrakty se nejčastěji píší v Solidity, který použijeme i my.‌ + +1. Přejděte do složky `contracts` a vytvořte nový soubor s názvem `HelloWorld.sol` +2. Níže je ukázka chytrého kontraktu Hello World, který budeme v tomto tutoriálu používat. Zkopírujte níže uvedený obsah do souboru `HelloWorld.sol`. + +_Poznámka: Nezapomeňte si přečíst komentáře, abyste pochopili, co tento kontrakt dělá._ + +``` +// Určuje verzi Solidity pomocí sémantického verzování. +// Více informací: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +pragma solidity >=0.7.3; + +// Definuje kontrakt s názvem `HelloWorld`. +// Kontrakt je soubor funkcí a dat (jeho stav). Po nasazení se kontrakt nachází na určité adrese na blockchainu Ethereum. Více informací: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +contract HelloWorld { + + // Emituje se při zavolání funkce update + // Události chytrého kontraktu jsou způsob, jakým může váš kontrakt sdělit vašemu front-endu, že se na blockchainu něco stalo, což může „naslouchat“ určitým událostem a při jejich výskytu provést akci. + event UpdatedMessages(string oldStr, string newStr); + + // Deklaruje stavovou proměnnou `message` typu `string`. + // Stavové proměnné jsou proměnné, jejichž hodnoty jsou trvale uloženy v úložišti kontraktu. Klíčové slovo `public` zpřístupňuje proměnné zvenčí kontraktu a vytváří funkci, kterou mohou volat jiné kontrakty nebo klienti pro přístup k hodnotě. + string public message; + + // Podobně jako v mnoha třídních objektově orientovaných jazycích je konstruktor speciální funkce, která se provádí pouze při vytvoření kontraktu. + // Konstruktory se používají k inicializaci dat kontraktu. Více informací:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + constructor(string memory initMessage) { + + // Přijímá řetězcový argument `initMessage` a nastavuje hodnotu do úložné proměnné kontraktu `message`). + message = initMessage; + } + + // Veřejná funkce, která přijímá řetězcový argument a aktualizuje úložnou proměnnou `message`. + function update(string memory newMessage) public { + string memory oldMsg = message; + message = newMessage; + emit UpdatedMessages(oldMsg, newMessage); + } +} +``` + +Jedná se o základní chytrý kontrakt, který při vytvoření ukládá zprávu. Lze jej aktualizovat voláním funkce `update`. + +### Krok 11: Připojení MetaMask a Alchemy k vašemu projektu {#step-11-connect-metamask-alchemy-to-your-project} + +Vytvořili jsme si peněženku MetaMask, účet Alchemy a napsali jsme náš chytrý kontrakt, nyní je čas je všechny tři propojit. + +Každá transakce odeslaná z vaší peněženky vyžaduje podpis s použitím vašeho jedinečného privátního klíče. Abychom programu toto oprávnění poskytli, můžeme náš privátní klíč bezpečně uložit do souboru prostředí. Zde také uložíme API klíč pro Alchemy. + +> Chcete-li se dozvědět více o odesílání transakcí, podívejte se na [tento tutoriál](https://www.alchemy.com/docs/hello-world-smart-contract#step-11-connect-metamask--alchemy-to-your-project) o odesílání transakcí pomocí Web3. + +Nejprve nainstalujte balíček dotenv do adresáře vašeho projektu: + +``` +npm install dotenv --save +``` + +Poté vytvořte v kořenovém adresáři projektu soubor `.env`. Přidejte do něj svůj privátní klíč MetaMask a URL adresa Alchemy API pro HTTP. + +Váš soubor prostředí se musí jmenovat `.env`, jinak nebude rozpoznán jako soubor prostředí. + +Nenazývejte jej `process.env` ani `.env-custom` ani nijak jinak. + +- Postupujte podle [těchto pokynů](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key) a exportujte svůj privátní klíč +- Níže naleznete postup, jak získat URL pro HTTP API Alchemy + +![](./get-alchemy-api-key.gif) + +Váš soubor `.env` by měl vypadat takto: + +``` +API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" +PRIVATE_KEY = "your-metamask-private-key" +``` + +Abychom je skutečně propojili s naším kódem, budeme na tyto proměnné odkazovat v našem souboru `hardhat.config.js` v kroku 13. + +### Krok 12: Instalace Ethers.js {#step-12-install-ethersjs} + +Ethers.js je knihovna, která usnadňuje interakci a zadávání požadavků na Ethereum tím, že obaluje [standardní metody JSON-RPC](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc) uživatelsky přívětivějšími metodami. + +Hardhat umožňuje integrovat [pluginy](https://hardhat.org/plugins/) pro další nástroje a rozšířenou funkčnost. Pro nasazení kontraktu využijeme [plugin Ethers](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers). + +V adresáři projektu zadejte: + +```bash +npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0" +``` + +### Krok 13: Aktualizace souboru hardhat.config.js {#step-13-update-hardhat-configjs} + +Zatím jsme přidali několik závislostí a pluginů, nyní musíme aktualizovat `hardhat.config.js`, aby o nich náš projekt věděl. + +Aktualizujte svůj soubor `hardhat.config.js`, aby vypadal takto: + +```javascript +/** + * @type import('hardhat/config').HardhatUserConfig + */ + +require("dotenv").config() +require("@nomiclabs/hardhat-ethers") + +const { API_URL, PRIVATE_KEY } = process.env + +module.exports = { + solidity: "0.7.3", + defaultNetwork: "goerli", + networks: { + hardhat: {}, + goerli: { + url: API_URL, + accounts: [`0x${PRIVATE_KEY}`], + }, + }, +} +``` + +### Krok 14: Kompilace našeho kontraktu {#step-14-compile-our-contract} + +Abychom se ujistili, že zatím vše funguje, zkompilujeme si náš kontrakt. Úkol `compile` je jedním z vestavěných úkolů Hardhatu. + +Z příkazového řádku spusťte: + +```bash +npx hardhat compile +``` + +Může se zobrazit varování o `SPDX license identifier not provided in source file`, ale nemusíte se tím znepokojovat – doufejme, že vše ostatní vypadá dobře! Pokud ne, vždy můžete napsat zprávu na [discordu Alchemy](https://discord.gg/u72VCg3). + +### Krok 15: Napsání našeho skriptu pro nasazení {#step-15-write-our-deploy-script} + +Nyní, když je náš kontrakt napsán a náš konfigurační soubor je připraven, je čas napsat náš skript pro nasazení kontraktu. + +Přejděte do složky `scripts/`, vytvořte nový soubor s názvem `deploy.js` a přidejte do něj následující obsah: + +```javascript +async function main() { + const HelloWorld = await ethers.getContractFactory("HelloWorld") + + // Spusťte nasazení, vrátí se promise, která se vyřeší na objekt kontraktu + const hello_world = await HelloWorld.deploy("Hello World!") + console.log("Contract deployed to address:", hello_world.address) +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) +``` + +Hardhat skvěle vysvětluje, co každý z těchto řádků kódu dělá ve svém [výukovém programu Kontrakty](https://hardhat.org/tutorial/testing-contracts.html#writing-tests), a my jsme zde jejich vysvětlení převzali. + +```javascript +const HelloWorld = await ethers.getContractFactory("HelloWorld") +``` + +`ContractFactory` v ethers.js je abstrakce používaná k nasazení nových chytrých kontraktů, takže `HelloWorld` je zde [továrna](https://en.wikipedia.org/wiki/Factory_\(object-oriented_programming\)) pro instance našeho kontraktu hello world. Při použití pluginu `hardhat-ethers` jsou instance `ContractFactory` a `Contract` ve výchozím nastavení připojeny k prvnímu podepisujícímu (vlastníkovi). + +```javascript +const hello_world = await HelloWorld.deploy() +``` + +Volání `deploy()` na `ContractFactory` spustí nasazení a vrátí `Promise`, která se vyřeší na objekt `Contract`. Toto je objekt, který má metodu pro každou z funkcí našeho chytrého kontraktu. + +### Krok 16: Nasazení našeho kontraktu {#step-16-deploy-our-contract} + +Konečně jsme připraveni nasadit náš chytrý kontrakt! Přejděte na příkazový řádek a spusťte: + +```bash +npx hardhat run scripts/deploy.js --network goerli +``` + +Měli byste pak vidět něco takového: + +```bash +Kontrakt nasazen na adresu: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 +``` + +**Tuto adresu si prosím uložte**. Budeme ji používat později v tomto tutoriálu. + +Pokud přejdeme na [Goerli Etherscan](https://goerli.etherscan.io) a vyhledáme adresu našeho kontraktu, měli bychom vidět, že byl úspěšně nasazen. Transakce bude vypadat nějak takto: + +![](./etherscan-contract.png) + +Adresa `From` by se měla shodovat s adresou vašeho účtu MetaMask a v adrese `To` bude uvedeno **Vytvoření kontraktu**. Pokud klikneme na transakci, uvidíme v poli `To` adresu našeho kontraktu. + +![](./etherscan-transaction.png) + +Výborně! Právě jste nasadili chytrý kontrakt do testovací sítě Ethereum. + +Abyste pochopili, co se děje pod pokličkou, přejděte na kartu Průzkumník v našem [řídicím panelu Alchemy](https://dashboard.alchemy.com/explorer). Pokud máte více aplikací Alchemy, nezapomeňte filtrovat podle aplikace a vybrat **Hello World**. + +![](./hello-world-explorer.png) + +Zde uvidíte několik metod JSON-RPC, které pro nás Hardhat/Ethers vytvořil pod pokličkou, když jsme volali funkci `.deploy()`. Dvě důležité metody jsou zde [`eth_sendRawTransaction`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction), což je požadavek na zapsání našeho kontraktu do chainu Goerli, a [`eth_getTransactionByHash`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_gettransactionbyhash), což je požadavek na přečtení informací o naší transakci na základě daného haše. Chcete-li se dozvědět více o odesílání transakcí, podívejte se na [náš tutoriál o odesílání transakcí pomocí Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/). + +## Část 2: Interakce s vaším chytrým kontraktem {#part-2-interact-with-your-smart-contract} + +Nyní, když jsme úspěšně nasadili chytrý kontrakt do sítě Goerli, se naučíme, jak s ním interagovat. + +### Vytvoření souboru interact.js {#create-a-interactjs-file} + +Toto je soubor, do kterého napíšeme náš interakční skript. Budeme používat knihovnu Ethers.js, kterou jste si nainstalovali v části 1. + +Uvnitř složky `scripts/` vytvořte nový soubor s názvem `interact.js` a přidejte následující kód: + +```javascript +// interact.js + +const API_KEY = process.env.API_KEY +const PRIVATE_KEY = process.env.PRIVATE_KEY +const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS +``` + +### Aktualizujte svůj soubor .env {#update-your-env-file} + +Budeme používat nové proměnné prostředí, takže je musíme definovat v souboru `.env`, který [jsme vytvořili dříve](#step-11-connect-metamask-&-alchemy-to-your-project). + +Budeme muset přidat definici pro náš Alchemy `API_KEY` a `CONTRACT_ADDRESS`, kde byl váš chytrý kontrakt nasazen. + +Váš soubor `.env` by měl vypadat nějak takto: + +```bash +# .env + +API_URL = "https://eth-goerli.alchemyapi.io/v2/" +API_KEY = "" +PRIVATE_KEY = "" +CONTRACT_ADDRESS = "0x" +``` + +### Získání ABI kontraktu {#grab-your-contract-ABI} + +Naše [ABI (Application Binary Interface)](/glossary/#abi) kontraktu je rozhraní pro interakci s naším chytrým kontraktem. Hardhat automaticky generuje ABI a ukládá ho do `HelloWorld.json`. Pro použití ABI budeme muset analyzovat obsah přidáním následujících řádků kódu do našeho souboru `interact.js`: + +```javascript +// interact.js +const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json") +``` + +Pokud chcete vidět ABI, můžete si ho vytisknout do konzole: + +```javascript +console.log(JSON.stringify(contract.abi)) +``` + +Abyste viděli své ABI vytištěné v konzoli, přejděte do terminálu a spusťte: + +```bash +npx hardhat run scripts/interact.js +``` + +### Vytvoření instance vašeho kontraktu {#create-an-instance-of-your-contract} + +Pro interakci s naším kontraktem musíme v našem kódu vytvořit instanci kontraktu. Abychom tak učinili s Ethers.js, budeme muset pracovat se třemi koncepty: + +1. Provider - poskytovatel uzlu, který vám dává přístup ke čtení a zápisu do blockchainu +2. Signer - představuje účet Ethereum, který může podepisovat transakce +3. Contract - objekt Ethers.js představující konkrétní kontrakt nasazený na blockchainu + +K vytvoření naší instance kontraktu použijeme ABI kontraktu z předchozího kroku: + +```javascript +// interact.js + +// Provider +const alchemyProvider = new ethers.providers.AlchemyProvider( + (network = "goerli"), + API_KEY +) + +// Signer +const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider) + +// Contract +const helloWorldContract = new ethers.Contract( + CONTRACT_ADDRESS, + contract.abi, + signer +) +``` + +Více se o Providerech, Signerech a Kontraktech dozvíte v [dokumentaci ethers.js](https://docs.ethers.io/v5/). + +### Přečtěte si úvodní zprávu {#read-the-init-message} + +Pamatujete si, když jsme nasadili náš kontrakt s `initMessage = "Hello world!"`? Nyní se chystáme přečíst zprávu uloženou v našem chytrém kontraktu a vytisknout ji do konzole. + +V JavaScriptu se při interakci se sítěmi používají asynchronní funkce. Chcete-li se dozvědět více o asynchronních funkcích, [přečtěte si tento článek na Medium](https://blog.bitsrc.io/understanding-asynchronous-javascript-the-event-loop-74cd408419ff). + +Použijte níže uvedený kód pro volání funkce `message` v našem chytrém kontraktu a přečtení úvodní zprávy: + +```javascript +// interact.js + +// ... + +async function main() { + const message = await helloWorldContract.message() + console.log("Zpráva zní: " + message) +} +main() +``` + +Po spuštění souboru pomocí `npx hardhat run scripts/interact.js` v terminálu bychom měli vidět tuto odpověď: + +``` +Zpráva zní: Hello world! +``` + +Výborně! Právě jste úspěšně přečetli data chytrého kontraktu z blockchainu Ethereum, skvělá práce! + +### Aktualizovat zprávu {#update-the-message} + +Místo pouhého čtení zprávy můžeme také aktualizovat zprávu uloženou v našem chytrém kontraktu pomocí funkce `update`! Docela super, že? + +Pro aktualizaci zprávy můžeme přímo zavolat funkci `update` na našem instancovaném objektu Contract: + +```javascript +// interact.js + +// ... + +async function main() { + const message = await helloWorldContract.message() + console.log("Zpráva zní: " + message) + + console.log("Aktualizace zprávy...") + const tx = await helloWorldContract.update("Toto je nová zpráva.") + await tx.wait() +} +main() +``` + +Všimněte si, že na řádku 11 voláme `.wait()` na vráceném objektu transakce. To zajišťuje, že náš skript počká, až se transakce vytěží na blockchainu, než opustí funkci. Pokud není zahrnuto volání `.wait()`, skript nemusí vidět aktualizovanou hodnotu `message` v kontraktu. + +### Přečtěte si novou zprávu {#read-the-new-message} + +Měli byste být schopni zopakovat [předchozí krok](#read-the-init-message) a přečíst aktualizovanou hodnotu `message`. Chvilku se zamyslete a zkuste provést změny potřebné k vytištění této nové hodnoty! + +Pokud potřebujete nápovědu, takto by měl v tuto chvíli vypadat váš soubor `interact.js`: + +```javascript +// interact.js + +const API_KEY = process.env.API_KEY +const PRIVATE_KEY = process.env.PRIVATE_KEY +const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS + +const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json") + +// provider - Alchemy +const alchemyProvider = new ethers.providers.AlchemyProvider( + (network = "goerli"), + API_KEY +) + +// signer - you +const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider) + +// instance kontraktu +const helloWorldContract = new ethers.Contract( + CONTRACT_ADDRESS, + contract.abi, + signer +) + +async function main() { + const message = await helloWorldContract.message() + console.log("Zpráva zní: " + message) + + console.log("Aktualizace zprávy...") + const tx = await helloWorldContract.update("toto je nová zpráva") + await tx.wait() + + const newMessage = await helloWorldContract.message() + console.log("Nová zpráva zní: " + newMessage) +} + +main() +``` + +Nyní stačí spustit skript a měli byste vidět starou zprávu, stav aktualizace a novou zprávu vytištěnou ve vašem terminálu! + +`npx hardhat run scripts/interact.js --network goerli` + +``` +Zpráva zní: Hello World! +Aktualizace zprávy... +Nová zpráva zní: Toto je nová zpráva. +``` + +Při spouštění tohoto skriptu si můžete všimnout, že krok `Updating the message...` (Aktualizace zprávy...) chvíli trvá, než se načte nová zpráva. Je to kvůli procesu těžby; pokud jste zvědaví na sledování transakcí během jejich těžby, navštivte [mempool Alchemy](https://dashboard.alchemyapi.io/mempool) a podívejte se na stav transakce. Pokud je transakce zrušena, je také užitečné zkontrolovat [Goerli Etherscan](https://goerli.etherscan.io) a vyhledat haš vaší transakce. + +## Část 3: Zveřejnění vašeho chytrého kontraktu na Etherscanu {#part-3-publish-your-smart-contract-to-etherscan} + +Odvedli jste veškerou těžkou práci, abyste svůj chytrý kontrakt přivedli k životu; nyní je čas podělit se o něj se světem! + +Ověřením vašeho chytrého kontraktu na Etherscanu si může kdokoli prohlédnout váš zdrojový kód a interagovat s vaším chytrým kontraktem. Začínáme! + +### Krok 1: Vygenerování API klíče na vašem účtu Etherscan {#step-1-generate-an-api-key-on-your-etherscan-account} + +Klíč Etherscan API je nezbytný k ověření, že vlastníte chytrý kontrakt, který se pokoušíte publikovat. + +Pokud ještě nemáte účet Etherscan, [zaregistrujte si jej](https://etherscan.io/register). + +Po přihlášení najděte své uživatelské jméno v navigační liště, najeďte na něj a vyberte tlačítko **Můj profil**. + +Na stránce vašeho profilu byste měli vidět boční navigační lištu. Z boční navigační lišty vyberte **API klíče**. Dále stiskněte tlačítko „Přidat“ pro vytvoření nového API klíče, pojmenujte svou aplikaci **hello-world** a stiskněte tlačítko **Vytvořit nový API klíč**. + +Váš nový API klíč by se měl objevit v tabulce API klíčů. Zkopírujte API klíč do schránky. + +Dále musíme přidat Etherscan API klíč do našeho souboru `.env`. + +Po jeho přidání by měl váš soubor `.env` vypadat takto: + +```javascript +API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" +PUBLIC_KEY = "your-public-account-address" +PRIVATE_KEY = "your-private-account-address" +CONTRACT_ADDRESS = "your-contract-address" +ETHERSCAN_API_KEY = "your-etherscan-key" +``` + +### Chytré kontrakty nasazené pomocí Hardhat {#hardhat-deployed-smart-contracts} + +#### Instalace hardhat-etherscan {#install-hardhat-etherscan} + +Publikování vašeho kontraktu na Etherscan pomocí Hardhat je jednoduché. Nejprve budete muset nainstalovat plugin `hardhat-etherscan`, abyste mohli začít. `hardhat-etherscan` automaticky ověří zdrojový kód a ABI chytrého kontraktu na Etherscanu. Pro jeho přidání spusťte v adresáři `hello-world`: + +```text +npm install --save-dev @nomiclabs/hardhat-etherscan +``` + +Po instalaci vložte následující příkaz na začátek souboru `hardhat.config.js` a přidejte možnosti konfigurace Etherscanu: + +```javascript +// hardhat.config.js + +require("dotenv").config() +require("@nomiclabs/hardhat-ethers") +require("@nomiclabs/hardhat-etherscan") + +const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env + +module.exports = { + solidity: "0.7.3", + defaultNetwork: "goerli", + networks: { + hardhat: {}, + goerli: { + url: API_URL, + accounts: [`0x${PRIVATE_KEY}`], + }, + }, + etherscan: { + // Váš API klíč pro Etherscan + // Získejte jej na https://etherscan.io/ + apiKey: ETHERSCAN_API_KEY, + }, +} +``` + +#### Ověření vašeho chytrého kontraktu na Etherscanu {#verify-your-smart-contract-on-etherscan} + +Ujistěte se, že jsou všechny soubory uloženy a všechny proměnné v souboru `.env` jsou správně nakonfigurovány. + +Spusťte úlohu `verify`, předejte adresu kontraktu a síť, kde je nasazen: + +```text +npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!' +``` + +Ujistěte se, že `DEPLOYED_CONTRACT_ADDRESS` je adresa vašeho nasazeného chytrého kontraktu v testovací síti Goerli. Také poslední argument (`'Hello World!'`) musí být stejná řetězcová hodnota, která byla použita [během kroku nasazení v části 1](#write-our-deploy-script). + +Pokud vše půjde dobře, uvidíte ve svém terminálu následující zprávu: + +```text +Zdrojový kód pro kontrakt byl úspěšně odeslán +contracts/HelloWorld.sol:HelloWorld na 0xdeployed-contract-address +k ověření na Etherscanu. Čekání na výsledek ověření... + + +Kontrakt HelloWorld byl úspěšně ověřen na Etherscanu. +https://goerli.etherscan.io/address/#contracts +``` + +Výborně! Kód vašeho chytrého kontraktu je na Etherscanu! + +### Podívejte se na svůj chytrý kontrakt na Etherscanu! {#check-out-your-smart-contract-on-etherscan} + +Když přejdete na odkaz uvedený ve vašem terminálu, měli byste vidět svůj kód chytrého kontraktu a ABI publikované na Etherscanu! + +**Paráda – dokázali jste to, šampione! Nyní může kdokoli volat nebo zapisovat do vašeho chytrého kontraktu! Těšíme se, co postavíte příště!** + +## Část 4 – Integrace vašeho chytrého kontraktu s frontendem {#part-4-integrating-your-smart-contract-with-the-frontend} + +Na konci tohoto tutoriálu budete vědět, jak: + +- Připojit peněženku MetaMask k vaší dapp +- Číst data z vašeho chytrého kontraktu pomocí [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) API +- Podepisovat transakce Ethereum pomocí MetaMask + +Pro tuto dapp budeme jako frontendový framework používat [React](https://react.dev/); je však důležité si uvědomit, že nebudeme trávit mnoho času rozebíráním jeho základů, protože se budeme soustředit hlavně na přenesení funkčnosti Web3 do našeho projektu. + +Jako předpoklad byste měli mít základní znalosti Reactu. Pokud ne, doporučujeme dokončit oficiální [tutoriál Úvod do Reactu](https://react.dev/learn). + +### Naklonujte si startovací soubory {#clone-the-starter-files} + +Nejprve přejděte do [GitHub repozitáře hello-world-part-four](https://github.com/alchemyplatform/hello-world-part-four-tutorial), abyste získali startovací soubory pro tento projekt, a naklonujte tento repozitář na váš lokální počítač. + +Otevřete si naklonovaný repozitář lokálně. Všimněte si, že obsahuje dvě složky: `starter-files` a `completed`. + +- `starter-files`- **budeme pracovat v tomto adresáři**, propojíme UI s vaší peněženkou Ethereum a s chytrým kontraktem, který jsme publikovali na Etherscanu v [Části 3](#part-3). +- `completed` obsahuje celý dokončený tutoriál a měl by být použit pouze jako reference, pokud se zaseknete. + +Dále si otevřete vaši kopii `starter-files` ve vašem oblíbeném editoru kódu a poté přejděte do složky `src`. + +Veškerý kód, který napíšeme, bude umístěn ve složce `src`. Budeme upravovat komponentu `HelloWorld.js` a javascriptové soubory `util/interact.js`, abychom našemu projektu dodali funkčnost Web3. + +### Prozkoumejte startovací soubory {#check-out-the-starter-files} + +Než začneme kódovat, prozkoumejme, co nám startovací soubory poskytují. + +#### Spusťte svůj projekt v Reactu {#get-your-react-project-running} + +Začněme spuštěním projektu React v našem prohlížeči. Krása Reactu spočívá v tom, že jakmile náš projekt běží v prohlížeči, veškeré uložené změny se v prohlížeči projeví v reálném čase. + +Aby projekt fungoval, přejděte do kořenového adresáře složky `starter-files` a spusťte v terminálu `npm install` pro instalaci závislostí projektu: + +```bash +cd starter-files +npm install +``` + +Jakmile se dokončí instalace, spusťte v terminálu `npm start`: + +```bash +npm start +``` + +Tím by se měla ve vašem prohlížeči otevřít adresa [http://localhost:3000/](http://localhost:3000/), kde uvidíte frontend našeho projektu. Měl by se skládat z jednoho pole (místo pro aktualizaci zprávy uložené ve vašem chytrém kontraktu), tlačítka „Připojit peněženku“ a tlačítka „Aktualizovat“. + +Pokud zkusíte kliknout na kterékoli tlačítko, zjistíte, že nefungují – je to proto, že jejich funkčnost musíme teprve naprogramovat. + +#### Komponenta `HelloWorld.js` {#the-helloworld-js-component} + +Vraťme se zpět do složky `src` v našem editoru a otevřeme si soubor `HelloWorld.js`. Je nesmírně důležité, abychom všemu v tomto souboru rozuměli, protože se jedná o primární komponentu Reactu, na které budeme pracovat. + +Na začátku tohoto souboru si všimnete, že máme několik importních příkazů, které jsou nezbytné pro spuštění našeho projektu, včetně knihovny React, hooků useEffect a useState, některých položek z `./util/interact.js` (popíšeme je podrobněji brzy!) a loga Alchemy. + +```javascript +// HelloWorld.js + +import React from "react" +import { useEffect, useState } from "react" +import { + helloWorldContract, + connectWallet, + updateMessage, + loadCurrentMessage, + getCurrentWalletConnected, +} from "./util/interact.js" + +import alchemylogo from "./alchemylogo.svg" +``` + +Dále máme naše stavové proměnné, které budeme aktualizovat po konkrétních událostech. + +```javascript +// HelloWorld.js + +//Stavové proměnné +const [walletAddress, setWallet] = useState("") +const [status, setStatus] = useState("") +const [message, setMessage] = useState("Žádné spojení se sítí.") +const [newMessage, setNewMessage] = useState("") +``` + +Zde je, co každá proměnná představuje: + +- `walletAddress` – řetězec, který ukládá adresu peněženky uživatele +- `status` – řetězec, který uchovává užitečnou zprávu, jež uživatele navádí, jak s dapp interagovat +- `message` - řetězec, který ukládá aktuální zprávu v chytrém kontraktu +- `newMessage` - řetězec, který ukládá novou zprávu, která bude zapsána do chytrého kontraktu + +Po stavových proměnných uvidíte pět neimplementovaných funkcí: `useEffect` ,`addSmartContractListener`, `addWalletListener` , `connectWalletPressed` a `onUpdatePressed`. Níže vysvětlíme, co dělají: + +```javascript +// HelloWorld.js + +//voláno pouze jednou +useEffect(async () => { + //TODO: implement +}, []) + +function addSmartContractListener() { + //TODO: implement +} + +function addWalletListener() { + //TODO: implement +} + +const connectWalletPressed = async () => { + //TODO: implement +} + +const onUpdatePressed = async () => { + //TODO: implement +} +``` + +- [`useEffect`](https://legacy.reactjs.org/docs/hooks-effect.html)- toto je React hook, který se volá po vykreslení vaší komponenty. Protože má prázdné pole `[]` jako prop (viz řádek 4), bude volán pouze při _prvním_ vykreslení komponenty. Zde načteme aktuální zprávu uloženou v našem chytrém kontraktu, zavoláme naše posluchače chytrého kontraktu a peněženky a aktualizujeme naše UI tak, aby odráželo, zda je peněženka již připojena. +- `addSmartContractListener`- tato funkce nastaví posluchače, který bude sledovat událost `UpdatedMessages` našeho kontraktu HelloWorld a aktualizuje naše UI, když se zpráva v našem chytrém kontraktu změní. +- `addWalletListener`- tato funkce nastaví posluchače, který detekuje změny ve stavu peněženky MetaMask uživatele, například když uživatel odpojí svou peněženku nebo přepne adresy. +- `connectWalletPressed`- tato funkce bude volána pro připojení peněženky MetaMask uživatele k naší dapp. +- `onUpdatePressed` – tato funkce bude volána, když uživatel bude chtít aktualizovat zprávu uloženou v chytrém kontraktu. + +Na konci tohoto souboru máme uživatelské rozhraní naší komponenty. + +```javascript +// HelloWorld.js + +//UI naší komponenty +return ( +
+ + + +

Aktuální zpráva:

+

{message}

+ +

Nová zpráva:

+ +
+ setNewMessage(e.target.value)} + value={newMessage} + /> +

{status}

+ + +
+ +
+) +``` + +Pokud si tento kód pozorně prostudujete, zjistíte, kde v našem UI používáme naše různé stavové proměnné: + +- Na řádcích 6-12, pokud je peněženka uživatele připojena (tj. `walletAddress.length > 0`), zobrazíme zkrácenou verzi adresy uživatele `walletAddress` v tlačítku s ID „walletButton;“ jinak se jednoduše zobrazí „Připojit peněženku“. +- Na řádku 17 zobrazujeme aktuální zprávu uloženou v chytrém kontraktu, která je zachycena v řetězci `message`. +- Na řádcích 23-26 používáme [řízenou komponentu](https://legacy.reactjs.org/docs/forms.html#controlled-components) k aktualizaci naší stavové proměnné `newMessage`, když se změní vstup v textovém poli. + +Kromě našich stavových proměnných také uvidíte, že funkce `connectWalletPressed` a `onUpdatePressed` jsou volány při kliknutí na tlačítka s ID `publishButton` a `walletButton`. + +Nakonec se podívejme, kam se tato komponenta `HelloWorld.js` přidává. + +Pokud přejdete do souboru `App.js`, který je hlavní komponentou v Reactu a slouží jako kontejner pro všechny ostatní komponenty, uvidíte, že naše komponenta `HelloWorld.js` je vložena na řádku 7. + +V neposlední řadě se podívejme na ještě jeden soubor, který máte k dispozici, a to `interact.js`. + +#### Soubor `interact.js` {#the-interact-js-file} + +Protože se chceme řídit paradigmatem [M-V-C](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller), budeme chtít samostatný soubor, který bude obsahovat všechny naše funkce pro správu logiky, dat a pravidel naší dapp, a poté budeme moci tyto funkce exportovat do našeho frontendu (naší komponenty `HelloWorld.js`). + +👆🏽Toto je přesně účel našeho souboru `interact.js`! + +Přejděte do složky `util` ve vašem adresáři `src` a všimnete si, že jsme zahrnuli soubor s názvem `interact.js`, který bude obsahovat všechny naše funkce a proměnné pro interakci s chytrým kontraktem a peněženkou. + +```javascript +// interact.js + +//export const helloWorldContract; + +export const loadCurrentMessage = async () => {} + +export const connectWallet = async () => {} + +const getCurrentWalletConnected = async () => {} + +export const updateMessage = async (message) => {} +``` + +Na začátku souboru si všimnete, že jsme zakomentovali objekt `helloWorldContract`. Později v tomto tutoriálu tento objekt odkomentujeme a instancujeme náš chytrý kontrakt do této proměnné, kterou pak exportujeme do naší komponenty `HelloWorld.js`. + +Čtyři neimplementované funkce za naším objektem `helloWorldContract` dělají následující: + +- `loadCurrentMessage` – tato funkce se stará o logiku načítání aktuální zprávy uložené v chytrém kontraktu. Provede volání _čtení_ na chytrý kontrakt Hello World pomocí [Alchemy Web3 API](https://github.com/alchemyplatform/alchemy-web3). +- `connectWallet` - tato funkce připojí peněženku MetaMask uživatele k naší dapp. +- `getCurrentWalletConnected` – tato funkce zkontroluje, zda je účet Ethereum již připojen k naší dapp při načítání stránky a podle toho aktualizuje naše UI. +- `updateMessage` – tato funkce aktualizuje zprávu uloženou v chytrém kontraktu. Provede _zápisové_ volání na chytrý kontrakt Hello World, takže peněženka MetaMask uživatele bude muset podepsat transakci Ethereum k aktualizaci zprávy. + +Nyní, když rozumíme tomu, s čím pracujeme, pojďme zjistit, jak číst z našeho chytrého kontraktu! + +### Krok 3: Čtení z vašeho chytrého kontraktu {#step-3-read-from-your-smart-contract} + +Chcete-li číst ze svého chytrého kontraktu, musíte úspěšně nastavit: + +- API připojení k řetězci Ethereum +- Načtenou instanci vašeho chytrého kontraktu +- Funkci pro volání funkce vašeho chytrého kontraktu +- Posluchače pro sledování aktualizací, když se změní data, která čtete z chytrého kontraktu + +To může znít jako mnoho kroků, ale nebojte se! Provedeme vás každým z nich krok za krokem! :\) + +#### Vytvoření API spojení s Ethereum chainem {#establish-an-api-connection-to-the-ethereum-chain} + +Vzpomínáte si, jak jsme v části 2 tohoto tutoriálu použili náš [Alchemy Web3 klíč k čtení z našeho chytrého kontraktu](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract/interacting-with-a-smart-contract#step-1-install-web3-library)? Ve své dapp budete také potřebovat klíč Alchemy Web3, abyste mohli číst z chainu. + +Pokud jej ještě nemáte, nejprve si nainstalujte [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) tak, že přejdete do kořenového adresáře vašich `starter-files` a spustíte v terminálu následující příkaz: + +```text +npm install @alch/alchemy-web3 +``` + +[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) je nadstavba nad [Web3.js](https://docs.web3js.org/), která poskytuje vylepšené metody API a další klíčové výhody, které vám usnadní život vývojáře web3. Je navržen tak, aby vyžadoval minimální konfiguraci, takže jej můžete ve své aplikaci začít používat okamžitě! + +Poté nainstalujte balíček [dotenv](https://www.npmjs.com/package/dotenv) do adresáře svého projektu, abychom měli bezpečné místo pro uložení našeho API klíče po jeho získání. + +```text +npm install dotenv --save +``` + +Pro naši dapp **budeme používat náš API klíč pro Websockets** namísto našeho API klíče pro HTTP, protože nám to umožní nastavit posluchače, který detekuje, kdy se změní zpráva uložená v chytrém kontraktu. + +Jakmile budete mít svůj API klíč, vytvořte v kořenovém adresáři soubor `.env` a přidejte do něj svou Alchemy Websockets URL. Poté by měl váš soubor `.env` vypadat takto: + +```javascript +REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/ +``` + +Nyní jsme připraveni nastavit náš Alchemy Web3 endpoint v naší dapp! Vraťme se k našemu souboru `interact.js`, který je vnořený do naší složky `util` a přidejme následující kód na začátek souboru: + +```javascript +// interact.js + +require("dotenv").config() +const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(alchemyKey) + +//export const helloWorldContract; +``` + +Výše jsme nejprve importovali klíč Alchemy z našeho souboru `.env` a poté jsme předali náš `alchemyKey` do `createAlchemyWeb3`, abychom vytvořili náš Alchemy Web3 endpoint. + +S tímto připraveným koncovým bodem je čas načíst náš chytrý kontrakt! + +#### Načítání vašeho chytrého kontraktu Hello World {#loading-your-hello-world-smart-contract} + +K načtení vašeho chytrého kontraktu Hello World budete potřebovat jeho adresu kontraktu a ABI, obojí lze najít na Etherscanu, pokud jste dokončili [Část 3 tohoto tutoriálu.](/developers/tutorials/hello-world-smart-contract-fullstack/#part-3-publish-your-smart-contract-to-etherscan-part-3-publish-your-smart-contract-to-etherscan) + +#### Jak získat ABI kontraktu z Etherscanu {#how-to-get-your-contract-abi-from-etherscan} + +Pokud jste přeskočili Část 3 tohoto tutoriálu, můžete použít kontrakt HelloWorld s adresou [0x6f3f635A9762B47954229Ea479b4541eAF402A6A](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code). Jeho ABI lze nalézt [zde](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code). + +ABI kontraktu je nezbytné pro určení, kterou funkci bude kontrakt volat, a také pro zajištění, že funkce vrátí data ve formátu, který očekáváte. Jakmile jsme zkopírovali naše ABI kontraktu, uložme ho jako JSON soubor s názvem `contract-abi.json` do vašeho adresáře `src`. + +Váš contract-abi.json by měl být uložen ve složce src. + +Vyzbrojeni adresou kontraktu, ABI a Alchemy Web3 koncovým bodem můžeme použít [metodu kontraktu](https://docs.web3js.org/api/web3-eth-contract/class/Contract) k načtení instance našeho chytrého kontraktu. Importujte ABI vašeho kontraktu do souboru `interact.js` a přidejte adresu svého kontraktu. + +```javascript +// interact.js + +const contractABI = require("../contract-abi.json") +const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A" +``` + +Nyní můžeme konečně odkomentovat naši proměnnou `helloWorldContract` a načíst chytrý kontrakt pomocí našeho koncového bodu AlchemyWeb3: + +```javascript +// interact.js +export const helloWorldContract = new web3.eth.Contract( + contractABI, + contractAddress +) +``` + +Abychom to shrnuli, prvních 12 řádků vašeho souboru `interact.js` by nyní mělo vypadat takto: + +```javascript +// interact.js + +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 = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A" + +export const helloWorldContract = new web3.eth.Contract( + contractABI, + contractAddress +) +``` + +Nyní, když máme náš kontrakt načtený, můžeme implementovat naši funkci `loadCurrentMessage`! + +#### Implementace `loadCurrentMessage` ve vašem souboru `interact.js` {#implementing-loadCurrentMessage-in-your-interact-js-file} + +Tato funkce je super jednoduchá. Provedeme jednoduché asynchronní volání web3 pro čtení z našeho kontraktu. Naše funkce vrátí zprávu uloženou v chytrém kontraktu: + +Aktualizujte `loadCurrentMessage` ve vašem souboru `interact.js` na následující: + +```javascript +// interact.js + +export const loadCurrentMessage = async () => { + const message = await helloWorldContract.methods.message().call() + return message +} +``` + +Protože chceme zobrazit tento chytrý kontrakt v našem UI, aktualizujme funkci `useEffect` v naší komponentě `HelloWorld.js` na následující: + +```javascript +// HelloWorld.js + +//voláno pouze jednou +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) +}, []) +``` + +Všimněte si, že chceme, aby byla naše funkce `loadCurrentMessage` volána pouze jednou během prvního vykreslení komponenty. Brzy implementujeme `addSmartContractListener` pro automatickou aktualizaci UI po změně zprávy v chytrém kontraktu. + +Než se pustíme do našeho posluchače, podívejme se, co jsme zatím dokázali! Uložte si soubory `HelloWorld.js` a `interact.js` a přejděte na [http://localhost:3000/](http://localhost:3000/) + +Všimnete si, že aktuální zpráva již neříká „Žádné spojení se sítí“. Místo toho odráží zprávu uloženou v chytrém kontraktu. Super! + +#### Vaše UI by nyní mělo odrážet zprávu uloženou v chytrém kontraktu {#your-UI-should-now-reflect-the-message-stored-in-the-smart-contract} + +A teď k tomu posluchači... + +#### Implementace `addSmartContractListener` {#implement-addsmartcontractlistener} + +Pokud si vzpomenete na soubor `HelloWorld.sol`, který jsme napsali v [části 1 této série tutoriálů](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract#step-10-write-our-contract), vzpomenete si, že existuje událost chytrého kontraktu s názvem `UpdatedMessages`, která je emitována po vyvolání funkce `update` našeho chytrého kontraktu (viz řádky 9 a 27): + +```javascript +// HelloWorld.sol + +// Určuje verzi Solidity pomocí sémantického verzování. +// Více informací: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +pragma solidity ^0.7.3; + +// Definuje kontrakt s názvem `HelloWorld`. +// Kontrakt je soubor funkcí a dat (jeho stav). Po nasazení se kontrakt nachází na určité adrese na blockchainu Ethereum. Více informací: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +contract HelloWorld { + + // Emituje se při zavolání funkce update + // Události chytrého kontraktu jsou způsob, jakým může váš kontrakt sdělit vašemu front-endu, že se na blockchainu něco stalo, což může „naslouchat“ určitým událostem a při jejich výskytu provést akci. + event UpdatedMessages(string oldStr, string newStr); + + // Deklaruje stavovou proměnnou `message` typu `string`. + // Stavové proměnné jsou proměnné, jejichž hodnoty jsou trvale uloženy v úložišti kontraktu. Klíčové slovo `public` zpřístupňuje proměnné zvenčí kontraktu a vytváří funkci, kterou mohou volat jiné kontrakty nebo klienti pro přístup k hodnotě. + string public message; + + // Podobně jako v mnoha třídních objektově orientovaných jazycích je konstruktor speciální funkce, která se provádí pouze při vytvoření kontraktu. + // Konstruktory se používají k inicializaci dat kontraktu. Více informací:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + constructor(string memory initMessage) { + + // Přijímá řetězcový argument `initMessage` a nastavuje hodnotu do úložné proměnné kontraktu `message`). + message = initMessage; + } + + // Veřejná funkce, která přijímá řetězcový argument a aktualizuje úložnou proměnnou `message`. + function update(string memory newMessage) public { + string memory oldMsg = message; + message = newMessage; + emit UpdatedMessages(oldMsg, newMessage); + } +} +``` + +Události chytrého kontraktu jsou způsob, jakým může váš kontrakt komunikovat, že se na blockchainu něco stalo (tj. došlo k _události_) vaší front-endové aplikaci, která může „naslouchat“ konkrétním událostem a při jejich výskytu provádět akce. + +Funkce `addSmartContractListener` bude specificky naslouchat události `UpdatedMessages` našeho chytrého kontraktu Hello World a aktualizovat naše UI, aby zobrazilo novou zprávu. + +Upravte `addSmartContractListener` na následující: + +```javascript +// HelloWorld.js + +function addSmartContractListener() { + helloWorldContract.events.UpdatedMessages({}, (error, data) => { + if (error) { + setStatus("😥 " + error.message) + } else { + setMessage(data.returnValues[1]) + setNewMessage("") + setStatus("🎉 Vaše zpráva byla aktualizována!") + } + }) +} +``` + +Pojďme si rozebrat, co se stane, když posluchač detekuje událost: + +- Pokud dojde k chybě při emisi události, projeví se to v UI prostřednictvím naší stavové proměnné `status`. +- V opačném případě použijeme vrácený objekt `data`. `data.returnValues` je pole indexované od nuly, kde první prvek pole ukládá předchozí zprávu a druhý prvek ukládá aktualizovanou. Celkově při úspěšné události nastavíme náš řetězec `message` na aktualizovanou zprávu, vymažeme řetězec `newMessage` a aktualizujeme naši stavovou proměnnou `status`, aby odrážela, že byla na našem chytrém kontraktu publikována nová zpráva. + +Nakonec zavoláme našeho posluchače ve funkci `useEffect`, aby byl inicializován při prvním vykreslení komponenty `HelloWorld.js`. Celkově by vaše funkce `useEffect` měla vypadat takto: + +```javascript +// HelloWorld.js + +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) + addSmartContractListener() +}, []) +``` + +Nyní, když umíme číst z našeho chytrého kontraktu, bylo by skvělé zjistit, jak do něj také zapisovat! Abychom však mohli do naší dapp zapisovat, musíme k ní mít nejprve připojenou peněženku Ethereum. + +Takže dále se budeme zabývat nastavením naší peněženky Ethereum (MetaMask) a jejím připojením k naší dapp! + +### Krok 4: Nastavení vaší peněženky Ethereum {#step-4-set-up-your-ethereum-wallet} + +Aby mohli uživatelé cokoliv zapsat na Ethereum chain, musí podepisovat transakce pomocí privátních klíčů své virtuální peněženky. Pro tento tutoriál použijeme [MetaMask](https://metamask.io/), virtuální peněženku v prohlížeči, která slouží ke správě vaší adresy účtu Ethereum, protože to pro koncového uživatele velmi usnadňuje podepisování transakcí. + +Pokud chcete lépe porozumět tomu, jak fungují transakce na Ethereu, podívejte se na [tuto stránku](/developers/docs/transactions/) od Nadace Ethereum. + +#### Stáhněte si MetaMask {#download-metamask} + +Účet MetaMask si můžete zdarma stáhnout a vytvořit [zde](https://metamask.io/download). Při vytváření účtu nebo pokud již účet máte, nezapomeňte se vpravo nahoře přepnout na „testovací síť Goerli“ (abychom nepracovali se skutečnými penězi). + +#### Přidání etheru z faucetu {#add-ether-from-a-faucet} + +K podepsání transakce na blockchainu Ethereum budeme potřebovat nějaký falešný ETH. Pro získání ETH můžete jít na [FaucETH](https://fauceth.komputing.org) a zadat adresu svého účtu Goerli, kliknout na „Požádat o prostředky“, poté v rozevíracím seznamu vybrat „Ethereum Testnet Goerli“ a nakonec znovu kliknout na tlačítko „Požádat o prostředky“. Krátce poté byste měli vidět Eth ve svém účtu MetaMask! + +#### Zkontrolujte si zůstatek {#check-your-balance} + +Abychom si ověřili, že náš zůstatek je k dispozici, proveďte požadavek [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) pomocí [nástroje Composer od 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). Tím získáte množství Eth ve vaší peněžence. Po zadání adresy vašeho účtu MetaMask a kliknutí na „Send Request“ byste měli vidět takovouto odpověď: + +```text +{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"} +``` + +**POZNÁMKA:** Tento výsledek je ve wei, nikoli v eth. Wei se používá jako nejmenší denominace etheru. Převod z wei na eth je: 1 eth = 10¹⁸ wei. Takže pokud převedeme 0xde0b6b3a7640000 na desetinné číslo, dostaneme 1\*10¹⁸, což se rovná 1 eth. + +Uf! Naše falešné peníze jsou všechny tam! 🤑 + +### Krok 5: Připojení MetaMask k vašemu UI {#step-5-connect-metamask-to-your-UI} + +Nyní, když je naše peněženka MetaMask nastavena, připojme k ní naši dapp! + +#### Funkce `connectWallet` {#the-connectWallet-function} + +V našem souboru `interact.js` implementujeme funkci `connectWallet`, kterou pak můžeme zavolat v naší komponentě `HelloWorld.js`. + +Upravme `connectWallet` na následující: + +```javascript +// interact.js + +export const connectWallet = async () => { + if (window.ethereum) { + try { + const addressArray = await window.ethereum.request({ + method: "eth_requestAccounts", + }) + const obj = { + status: "👆🏽 Napište zprávu do textového pole výše.", + address: addressArray[0], + } + return obj + } catch (err) { + return { + address: "", + status: "😥 " + err.message, + } + } + } else { + return { + address: "", + status: ( + +

+ {" "} + 🦊 + Musíte si do prohlížeče nainstalovat MetaMask, virtuální peněženku Ethereum. + +

+ + ), + } + } +} +``` + +Co přesně tedy tento obrovský blok kódu dělá? + +Nejprve zkontroluje, zda je ve vašem prohlížeči povoleno `window.ethereum`. + +`window.ethereum` je globální API, které vkládá MetaMask a další poskytovatelé peněženek a které webovým stránkám umožňuje žádat o účty uživatelů Etherea. Pokud je schváleno, může číst data z blockchainů, ke kterým je uživatel připojen, a navrhovat uživateli podepisování zpráv a transakcí. Pro více informací se podívejte do [dokumentace MetaMasku](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents)! + +Pokud `window.ethereum` _není_ přítomno, znamená to, že MetaMask není nainstalován. Výsledkem je vrácení objektu JSON, kde vrácená `adresa` je prázdný řetězec a objekt JSX `status` sděluje, že uživatel si musí nainstalovat MetaMask. + +Pokud `window.ethereum` _je_ přítomno, pak to začne být zajímavé. + +Pomocí smyčky try/catch se pokusíme připojit k MetaMasku voláním [`window.ethereum.request({ method: \"eth_requestAccounts\" });`](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts). Volání této funkce otevře MetaMask v prohlížeči, kde bude uživatel vyzván k připojení své peněženky k vaší dapp. + +- Pokud se uživatel rozhodne připojit, `method: "eth_requestAccounts"` vrátí pole obsahující všechny adresy účtů uživatele, které se k dapp připojily. Celkově naše funkce `connectWallet` vrátí objekt JSON, který obsahuje _první_ `adresu` v tomto poli (viz řádek 9) a zprávu `status`, která vyzve uživatele k napsání zprávy do chytrého kontraktu. +- Pokud uživatel spojení odmítne, objekt JSON bude obsahovat prázdný řetězec pro vrácenou `adresu` a zprávu `status`, která odráží, že uživatel spojení odmítl. + +Nyní, když jsme napsali tuto funkci `connectWallet`, je dalším krokem její zavolání v naší komponentě `HelloWorld.js`. + +#### Přidání funkce `connectWallet` do vaší UI komponenty `HelloWorld.js` {#add-the-connectWallet-function-to-your-HelloWorld-js-ui-component} + +Přejděte na funkci `connectWalletPressed` v `HelloWorld.js` a aktualizujte ji na následující: + +```javascript +// HelloWorld.js + +const connectWalletPressed = async () => { + const walletResponse = await connectWallet() + setStatus(walletResponse.status) + setWallet(walletResponse.address) +} +``` + +Všimli jste si, jak je většina naší funkčnosti abstrahována od naší komponenty `HelloWorld.js` do souboru `interact.js`? To proto, abychom dodrželi paradigma M-V-C! + +V `connectWalletPressed` jednoduše provedeme await volání naší importované funkce `connectWallet` a pomocí její odpovědi aktualizujeme naše proměnné `status` a `walletAddress` prostřednictvím jejich stavových háků (hooks). + +Nyní si oba soubory (`HelloWorld.js` a `interact.js`) uložme a otestujme naše UI. + +Otevřete si prohlížeč na stránce [http://localhost:3000/](http://localhost:3000/) a stiskněte tlačítko „Připojit peněženku“ vpravo nahoře. + +Pokud máte nainstalovaný MetaMask, měli byste být vyzváni k připojení své peněženky k vaší dapp. Přijměte výzvu k připojení. + +Měli byste vidět, že tlačítko peněženky nyní ukazuje, že je vaše adresa připojena! Super! 🔥 + +Dále zkuste obnovit stránku... to je divné. Naše tlačítko peněženky nás vyzývá k připojení MetaMasku, i když už je připojen... + +Ale žádný strach! To snadno vyřešíme (chápete?). implementací `getCurrentWalletConnected`, která zkontroluje, zda je adresa již připojena k naší dapp, a podle toho aktualizuje naše UI! + +#### Funkce `getCurrentWalletConnected` {#the-getcurrentwalletconnected-function} + +Aktualizujte svou funkci `getCurrentWalletConnected` v souboru `interact.js` na následující: + +```javascript +// interact.js + +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: "👆🏽 Napište zprávu do textového pole výše.", + } + } else { + return { + address: "", + status: "🦊 Připojte se k MetaMask pomocí tlačítka vpravo nahoře.", + } + } + } catch (err) { + return { + address: "", + status: "😥 " + err.message, + } + } + } else { + return { + address: "", + status: ( + +

+ {" "} + 🦊 + Musíte si do prohlížeče nainstalovat MetaMask, virtuální peněženku Ethereum. + +

+ + ), + } + } +} +``` + +Tento kód je _velmi_ podobný funkci `connectWallet`, kterou jsme právě napsali v předchozím kroku. + +Hlavní rozdíl je v tom, že místo volání metody `eth_requestAccounts`, která otevře MetaMask, aby si uživatel mohl připojit svou peněženku, zde voláme metodu `eth_accounts`, která jednoduše vrací pole obsahující adresy MetaMask aktuálně připojené k naší dapp. + +Abychom tuto funkci viděli v akci, zavoláme ji ve funkci `useEffect` naší komponenty `HelloWorld.js`: + +```javascript +// HelloWorld.js + +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) + addSmartContractListener() + + const { address, status } = await getCurrentWalletConnected() + setWallet(address) + setStatus(status) +}, []) +``` + +Všimněte si, že odpověď z našeho volání `getCurrentWalletConnected` používáme k aktualizaci našich stavových proměnných `walletAddress` a `status`. + +Nyní, když jste přidali tento kód, zkusme obnovit okno našeho prohlížeče. + +Paráda! Tlačítko by mělo hlásit, že jste připojeni, a zobrazovat náhled adresy vaší připojené peněženky – i po obnovení! + +#### Implementace `addWalletListener` {#implement-addwalletlistener} + +Posledním krokem v nastavení peněženky naší dapp je implementace posluchače peněženky, aby se naše uživatelské rozhraní aktualizovalo, když se změní stav naší peněženky, například když se uživatel odpojí nebo přepne účty. + +Ve vašem souboru `HelloWorld.js` upravte svou funkci `addWalletListener` na následující: + +```javascript +// HelloWorld.js + +function addWalletListener() { + if (window.ethereum) { + window.ethereum.on("accountsChanged", (accounts) => { + if (accounts.length > 0) { + setWallet(accounts[0]) + setStatus("👆🏽 Napište zprávu do textového pole výše.") + } else { + setWallet("") + setStatus("🦊 Připojte se k MetaMask pomocí tlačítka vpravo nahoře.") + } + }) + } else { + setStatus( +

+ {" "} + 🦊 + Musíte si do prohlížeče nainstalovat MetaMask, virtuální peněženku Ethereum. + +

+ ) + } +} +``` + +Vsadím se, že už ani nepotřebujete naši pomoc, abyste pochopili, co se zde děje, ale pro úplnost si to rychle rozebereme: + +- Nejprve naše funkce zkontroluje, zda je `window.ethereum` povoleno (tj. zda je MetaMask nainstalován). + - Pokud není, jednoduše nastavíme naši stavovou proměnnou `status` na řetězec JSX, který uživatele vyzve k instalaci MetaMasku. + - Pokud je povoleno, nastavíme na řádku 3 posluchače `window.ethereum.on(\"accountsChanged\")`, který naslouchá změnám stavu v peněžence MetaMask, což zahrnuje případy, kdy uživatel připojí další účet k dapp, přepne účty nebo odpojí účet. Pokud je připojen alespoň jeden účet, stavová proměnná `walletAddress` se aktualizuje jako první účet v poli `accounts` vráceném posluchačem. V opačném případě je `walletAddress` nastaveno jako prázdný řetězec. + +V neposlední řadě ji musíme zavolat v naší funkci `useEffect`: + +```javascript +// HelloWorld.js + +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) + addSmartContractListener() + + const { address, status } = await getCurrentWalletConnected() + setWallet(address) + setStatus(status) + + addWalletListener() +}, []) +``` + +A to je vše! Úspěšně jsme dokončili programování veškeré funkčnosti naší peněženky! Nyní k našemu poslednímu úkolu: aktualizaci zprávy uložené v našem chytrém kontraktu! + +### Krok 6: Implementace funkce `updateMessage` {#step-6-implement-the-updateMessage-function} + +Tak jo, lidi, jsme v cílové rovince! Ve funkci `updateMessage` vašeho souboru `interact.js` uděláme následující: + +1. Ujistěte se, že zpráva, kterou chceme publikovat v našem chytrém kontraktu, je platná +2. Podepište naši transakci pomocí MetaMask +3. Zavolejte tuto funkci z naší frontendové komponenty `HelloWorld.js` + +Nebude to trvat dlouho; pojďme tuto dapp dokončit! + +#### Zpracování chyb na vstupu {#input-error-handling} + +Je přirozené, že na začátku funkce je nějaké zpracování chyb vstupů. + +Budeme chtít, aby se naše funkce vrátila dříve, pokud není nainstalováno rozšíření MetaMask, není připojena žádná peněženka (tj. předaná `adresa` je prázdný řetězec) nebo je `zpráva` prázdný řetězec. Přidejme následující zpracování chyb do `updateMessage`: + +```javascript +// interact.js + +export const updateMessage = async (address, message) => { + if (!window.ethereum || address === null) { + return { + status: + "💡 Připojte svou peněženku MetaMask pro aktualizaci zprávy na blockchainu.", + } + } + + if (message.trim() === "") { + return { + status: "❌ Vaše zpráva nemůže být prázdný řetězec.", + } + } +} +``` + +Nyní, když máme správné zpracování chyb vstupů, je čas podepsat transakci přes MetaMask! + +#### Podepisování naší transakce {#signing-our-transaction} + +Pokud jste již obeznámeni s tradičními web3 Ethereum transakcemi, kód, který napíšeme dále, vám bude velmi povědomý. Pod kód pro zpracování chyb vstupů přidejte do `updateMessage` následující: + +```javascript +// interact.js + +//nastavení parametrů transakce +const transactionParameters = { + to: contractAddress, // Vyžadováno kromě publikací kontraktů. + from: address, // musí se shodovat s aktivní adresou uživatele. + data: helloWorldContract.methods.update(message).encodeABI(), +} + +//podepsání transakce +try { + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [transactionParameters], + }) + return { + status: ( + + ✅{" "} + + Zobrazte stav své transakce na Etherscanu! + +
+ ℹ️ Jakmile bude transakce ověřena sítí, zpráva bude + aktualizována automaticky. + + ), + } +} catch (error) { + return { + status: "😥 " + error.message, + } +} +``` + +Rozeberme si, co se děje. Nejprve nastavíme parametry naší transakce, kde: + +- `to` určuje adresu příjemce (náš chytrý kontrakt) +- `from` určuje podepisujícího transakce, proměnnou `address`, kterou jsme předali do naší funkce +- `data` obsahuje volání metody `update` našeho chytrého kontraktu Hello World, která přijímá naši řetězcovou proměnnou `message` jako vstup + +Poté provedeme await volání `window.ethereum.request`, kde požádáme MetaMask o podepsání transakce. Všimněte si, že na řádcích 11 a 12 určujeme naši metodu eth, `eth_sendTransaction`, a předáváme naše `transactionParameters`. + +V tomto okamžiku se v prohlížeči otevře MetaMask a vyzve uživatele k podepsání nebo zamítnutí transakce. + +- Pokud je transakce úspěšná, funkce vrátí JSON objekt, kde `status` JSX řetězec vyzve uživatele, aby se podíval na Etherscan pro více informací o své transakci. +- Pokud transakce selže, funkce vrátí JSON objekt, kde řetězec `status` předá chybovou zprávu. + +Celkově by naše funkce `updateMessage` měla vypadat takto: + +```javascript +// interact.js + +export const updateMessage = async (address, message) => { + //zpracování chyb vstupů + if (!window.ethereum || address === null) { + return { + status: + "💡 Připojte svou peněženku MetaMask pro aktualizaci zprávy na blockchainu.", + } + } + + if (message.trim() === "") { + return { + status: "❌ Vaše zpráva nemůže být prázdný řetězec.", + } + } + + //nastavení parametrů transakce + const transactionParameters = { + to: contractAddress, // Vyžadováno kromě publikací kontraktů. + from: address, // musí se shodovat s aktivní adresou uživatele. + data: helloWorldContract.methods.update(message).encodeABI(), + } + + //podepsání transakce + try { + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [transactionParameters], + }) + return { + status: ( + + ✅{" "} +
+ Zobrazte stav své transakce na Etherscanu! + +
+ ℹ️ Jakmile bude transakce ověřena sítí, zpráva bude + aktualizována automaticky. + + ), + } + } catch (error) { + return { + status: "😥 " + error.message, + } + } +} +``` + +V neposlední řadě musíme naši funkci `updateMessage` propojit s naší komponentou `HelloWorld.js`. + +#### Propojení `updateMessage` s frontendem `HelloWorld.js` {#connect-updatemessage-to-the-helloworld-js-frontend} + +Naše funkce `onUpdatePressed` by měla provést await volání na importovanou funkci `updateMessage` a upravit stavovou proměnnou `status`, aby odrážela, zda naše transakce uspěla nebo selhala: + +```javascript +// HelloWorld.js + +const onUpdatePressed = async () => { + const { status } = await updateMessage(walletAddress, newMessage) + setStatus(status) +} +``` + +Je to super čisté a jednoduché. A hádejte co... VAŠE DAPP JE HOTOVÁ!!! + +Jděte do toho a otestujte tlačítko **Aktualizovat**! + +### Vytvořte si vlastní dapp {#make-your-own-custom-dapp} + +Skvělé, dostali jste se až na konec tutoriálu! Abychom to shrnuli, naučili jste se: + +- Připojit peněženku MetaMask k vašemu projektu dapp +- Číst data z vašeho chytrého kontraktu pomocí [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) API +- Podepisovat transakce Ethereum pomocí MetaMask + +Nyní jste plně vybaveni k tomu, abyste dovednosti z tohoto tutoriálu uplatnili při vytváření vlastního projektu dapp! Jako vždy, pokud máte nějaké otázky, neváhejte se na nás obrátit s žádostí o pomoc na [Discordu Alchemy](https://discord.gg/gWuC7zB). 🧙‍♂️ + +Jakmile dokončíte tento tutoriál, dejte nám vědět, jaké byly vaše zkušenosti, nebo pokud máte nějakou zpětnou vazbu, tak nás označte na Twitteru [@alchemyplatform](https://twitter.com/AlchemyPlatform)! diff --git a/public/content/translations/cs/developers/tutorials/hello-world-smart-contract/index.md b/public/content/translations/cs/developers/tutorials/hello-world-smart-contract/index.md new file mode 100644 index 00000000000..9a50e94a08e --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/hello-world-smart-contract/index.md @@ -0,0 +1,367 @@ +--- +title: "Chytrý kontrakt Hello World pro začátečníky" +description: "Úvodní tutoriál k psaní a nasazení jednoduchého chytrého kontraktu na Ethereum." +author: "elanh" +tags: + [ + "solidity", + "hardhat", + "alchemy", + "smart kontrakt účty", + "nasazování" + ] +skill: beginner +lang: cs +published: 2021-03-31 +--- + +Pokud s vývojem na blockchainu teprve začínáte a nevíte, kde začít, nebo pokud jen chcete pochopit, jak nasadit chytré kontrakty a interagovat s nimi, je tento průvodce určen právě vám. Provedeme vás vytvořením a nasazením jednoduchého chytrého kontraktu na testovací síti Sepolia pomocí virtuální peněženky [MetaMask](https://metamask.io/), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/) a [Alchemy](https://www.alchemy.com/eth) (nebojte se, pokud ještě ničemu z toho nerozumíte, všechno si vysvětlíme). + +Ve [2. části](https://docs.alchemy.com/docs/interacting-with-a-smart-contract) tohoto tutoriálu si projdeme, jak můžeme s naším nasazeným chytrým kontraktem interagovat, a ve [3. části](https://www.alchemy.com/docs/submitting-your-smart-contract-to-etherscan) si ukážeme, jak ho publikovat na Etherscanu. + +Pokud budete mít v kterémkoliv bodě dotazy, neváhejte se zeptat na [Discordu Alchemy](https://discord.gg/gWuC7zB)! + +## Krok 1: Připojení k síti Ethereum {#step-1} + +Existuje mnoho způsobů, jak posílat požadavky na blockchain Etherea. Pro zjednodušení použijeme bezplatný účet na Alchemy, vývojářské platformy a API pro blockchain, která nám umožňuje komunikovat s blockchainem Etherea, aniž bychom museli provozovat vlastní uzly. Tato platforma má také vývojářské nástroje pro monitorování a analýzu, které v tomto tutoriálu využijeme, abychom pochopili, co se děje „pod kapotou“ při nasazení našeho chytrého kontraktu. Pokud ještě nemáte účet Alchemy, [můžete se zdarma zaregistrovat zde](https://dashboard.alchemy.com/signup). + +## Krok 2: Vytvořte svou aplikaci (a klíč API) {#step-2} + +Jakmile si vytvoříte účet na Alchemy, můžete si vygenerovat klíč API vytvořením aplikace. To nám umožní posílat požadavky do testovací sítě Sepolia. Pokud neznáte testovací sítě, podívejte se na [tuto stránku](/developers/docs/networks/). + +1. Přejděte na stránku „Vytvořit novou aplikaci“ na vašem řídicím panelu Alchemy tak, že na navigační liště vyberete možnost „Vybrat aplikaci“ a kliknete na „Vytvořit novou aplikaci“ + +![Vytvoření aplikace Hello World](./hello-world-create-app.png) + +2. Pojmenujte svou aplikaci „Hello World“, uveďte krátký popis a vyberte případ použití, např. „Infra & nástroje“. Dále vyhledejte „Ethereum“ a vyberte síť. + +![Pohled na vytvoření aplikace Hello World](./create-app-view-hello-world.png) + +3. Kliknutím na „Další“ pokračujte, poté na „Vytvořit aplikaci“ a je to! Vaše aplikace by se měla objevit v rozevírací nabídce na navigační liště s klíčem API, který si můžete zkopírovat. + +## Krok 3: Vytvoření účtu Ethereum (adresy) {#step-3} + +K odesílání a přijímání transakcí potřebujeme ethereový účet. Pro tento výukový program použijeme MetaMask, virtuální peněženku v prohlížeči, která slouží ke správě adresy vašeho ethereového účtu. Více o [transakcích](/developers/docs/transactions/). + +Můžete si stáhnout MetaMask a zdarma si vytvořit účet Ethereum [zde](https://metamask.io/download). Při vytváření účtu, nebo pokud již účet máte, se ujistěte, že jste se přepnuli na testovací síť „Sepolia“ pomocí rozevírací nabídky sítě (abychom nepracovali se skutečnými penězi). + +Pokud síť Sepolia v seznamu nevidíte, přejděte do nabídky, poté do „Pokročilé“ a sjeďte dolů, abyste zapnuli možnost „Zobrazit testovací sítě“. V nabídce pro výběr sítě zvolte záložku „Vlastní“, kde najdete seznam testovacích sítí a vyberete „Sepolia“. + +![Příklad sítě Sepolia v MetaMask](./metamask-sepolia-example.png) + +## Krok 4: Přidání etheru z faucetu {#step-4} + +Abychom mohli náš chytrý kontrakt nasadit na testovací síť, budeme potřebovat nějaké falešné ETH. Pro získání ETH na síti Sepolia můžete přejít na [podrobnosti o síti Sepolia](/developers/docs/networks/#sepolia) a zobrazit si seznam různých faucetů. Pokud jeden nefunguje, zkuste jiný, protože jim někdy mohou dojít prostředky. Může chvíli trvat, než obdržíte své falešné ETH, kvůli vytížení sítě. Brzy poté byste měli vidět ETH ve svém účtu MetaMask! + +## Krok 5: Kontrola zůstatku {#step-5} + +Abychom si ověřili, že máme zůstatek, provedeme požadavek [eth_getBalance](/developers/docs/apis/json-rpc/#eth_getbalance) pomocí [nástroje Composer od Alchemy](https://sandbox.alchemy.com/?network=ETH_SEPOLIA&method=eth_getBalance&body.id=1&body.jsonrpc=2.0&body.method=eth_getBalance&body.params%5B0%5D=&body.params%5B1%5D=latest). Tím se vrátí množství ETH v naší peněžence. Po zadání adresy vašeho účtu MetaMask a kliknutí na „Send Request“ byste měli vidět takovouto odpověď: + +```json +{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" } +``` + +> **POZNÁMKA:** Tento výsledek je ve wei, nikoliv v ETH. Wei se používá jako nejmenší denominace etheru. Převod z wei na ETH je: 1 ETH = 1018 wei. Takže když převedeme 0x2B5E3AF16B1880000 na desetinné číslo, dostaneme 5\*10¹⁸, což se rovná 5 ETH. +> +> Uf! Naše falešné peníze jsou všechny tady . + +## Krok 6: Inicializace našeho projektu {#step-6} + +Nejprve budeme muset vytvořit složku pro náš projekt. Přejděte na příkazový řádek a zadejte: + +``` +mkdir hello-world +cd hello-world +``` + +Nyní, když jsme uvnitř složky našeho projektu, použijeme `npm init` k inicializaci projektu. Pokud ještě nemáte nainstalovaný npm, postupujte podle [těchto pokynů](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm) (budeme také potřebovat Node.js, takže si ho také stáhněte!). + +``` +npm init +``` + +Nezáleží na tom, jak odpovíte na instalační otázky, zde je pro referenci, jak jsme to udělali my: + +``` +název balíčku: (hello-world) +verze: (1.0.0) +popis: chytrý kontrakt hello world +vstupní bod: (index.js) +testovací příkaz: +git repozitář: +klíčová slova: +autor: +licence: (ISC) +Chystáte se zapsat do /Users/.../.../.../hello-world/package.json: + +{ + "name": "hello-world", + "version": "1.0.0", + "description": "chytrý kontrakt hello world", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} +``` + +Schvalte package.json a můžeme pokračovat! + +## Krok 7: Stažení [Hardhat](https://hardhat.org/getting-started/#overview) {#step-7} + +Hardhat je vývojové prostředí pro kompilaci, nasazení, testování a ladění vašeho softwaru pro Ethereum. Pomáhá vývojářům při lokálním budování chytrých kontraktů a dapps před jejich nasazením na živý řetězec. + +Uvnitř našeho projektu `hello-world` spusťte: + +``` +npm install --save-dev hardhat +``` + +Další podrobnosti o [instalačních pokynech](https://hardhat.org/getting-started/#overview) naleznete na této stránce. + +## Krok 8: Vytvoření projektu Hardhat {#step-8} + +Uvnitř složky našeho projektu spusťte: + +``` +npx hardhat +``` + +Poté by se vám měla zobrazit uvítací zpráva a možnost vybrat si, co chcete dělat. Vyberte „create an empty hardhat.config.js“: + +``` +888 888 888 888 888 +888 888 888 888 888 +888 888 888 888 888 +8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 +888 888 "88b 888P" d88" 888 888 "88b "88b 888 +888 888 .d888888 888 888 888 888 888 .d888888 888 +888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. +888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 + +👷 Vítejte v Hardhat v2.0.11 👷‍? + +Co chcete udělat? … +Vytvořit vzorový projekt +❯ Vytvořit prázdný hardhat.config.js +Ukončit +``` + +Tím se nám vygeneruje soubor `hardhat.config.js`, ve kterém specifikujeme veškeré nastavení našeho projektu (v kroku 13). + +## Krok 9: Přidání složek projektu {#step-9} + +Abychom si v našem projektu udrželi pořádek, vytvoříme dvě nové složky. Přejděte do kořenového adresáře projektu v příkazovém řádku a zadejte: + +``` +mkdir contracts +mkdir scripts +``` + +- `contracts/` je místo, kam uložíme soubor s kódem našeho chytrého kontraktu Hello World +- `scripts/` je místo, kam uložíme skripty pro nasazení našeho kontraktu a interakci s ním + +## Krok 10: Napsání našeho kontraktu {#step-10} + +Možná si říkáte, kdy už konečně budeme psát nějaký kód?? Tak jsme tady, v kroku 10. + +Otevřete projekt hello-world ve svém oblíbeném editoru (my máme rádi [VSCode](https://code.visualstudio.com/)). Chytré kontrakty se píší v jazyce zvaném Solidity, který použijeme k napsání našeho chytrého kontraktu HelloWorld.sol.‌ + +1. Přejděte do složky „contracts“ a vytvořte nový soubor s názvem HelloWorld.sol +2. Níže je ukázkový chytrý kontrakt Hello World od Nadace Ethereum, který budeme používat pro tento tutoriál. Zkopírujte a vložte níže uvedený obsah do souboru HelloWorld.sol a nezapomeňte si přečíst komentáře, abyste pochopili, co tento kontrakt dělá: + +```solidity +// Určuje verzi Solidity pomocí sémantického verzování. +// Více informací: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +pragma solidity ^0.7.0; + +// Definuje kontrakt s názvem `HelloWorld`. +// Kontrakt je soubor funkcí a dat (jeho stav). Po nasazení se kontrakt nachází na určité adrese na blockchainu Etherea. Více informací: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +contract HelloWorld { + + // Deklaruje stavovou proměnnou `message` typu `string`. + // Stavové proměnné jsou proměnné, jejichž hodnoty jsou trvale uloženy v úložišti kontraktu. Klíčové slovo `public` zpřístupňuje proměnné z vnějšku kontraktu a vytváří funkci, kterou mohou jiné kontrakty nebo klienti volat pro přístup k hodnotě. + string public message; + + // Podobně jako v mnoha objektově orientovaných jazycích založených na třídách je konstruktor speciální funkce, která se provede pouze při vytvoření kontraktu. + // Konstruktory se používají k inicializaci dat kontraktu. Více informací:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + constructor(string memory initMessage) { + + // Přijímá argument řetězce `initMessage` a nastavuje hodnotu do úložné proměnné `message` kontraktu). + message = initMessage; + } + + // Veřejná funkce, která přijímá argument řetězce a aktualizuje úložnou proměnnou `message`. + function update(string memory newMessage) public { + message = newMessage; + } +} +``` + +Jedná se o super jednoduchý chytrý kontrakt, který při vytvoření uloží zprávu a lze jej aktualizovat voláním funkce `update`. + +## Krok 11: Připojení MetaMask a Alchemy k vašemu projektu {#step-11} + +Vytvořili jsme si peněženku MetaMask, účet Alchemy a napsali jsme náš chytrý kontrakt, nyní je čas je všechny tři propojit. + +Každá transakce odeslaná z vaší virtuální peněženky vyžaduje podpis pomocí vašeho jedinečného privátního klíče. Abychom našemu programu poskytli toto oprávnění, můžeme bezpečně uložit náš privátní klíč (a klíč API od Alchemy) do souboru prostředí. + +> Chcete-li se dozvědět více o odesílání transakcí, podívejte se na [tento výukový program](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) o odesílání transakcí pomocí web3. + +Nejprve nainstalujte balíček dotenv do adresáře vašeho projektu: + +``` +npm install dotenv --save +``` + +Poté vytvořte soubor `.env` v kořenovém adresáři našeho projektu a přidejte do něj svůj privátní klíč MetaMask a adresu URL rozhraní HTTP API od Alchemy. + +- Pro exportování soukromého klíče postupujte podle [těchto pokynů](https://support.metamask.io/configure/accounts/how-to-export-an-accounts-private-key/) +- Níže naleznete postup, jak získat URL pro HTTP API Alchemy + +![Získání klíče API Alchemy](./get-alchemy-api-key.png) + +Zkopírujte URL API Alchemy + +Váš soubor `.env` by měl vypadat takto: + +``` +API_URL = "https://eth-sepolia.g.alchemy.com/v2/váš-api-klíč" +PRIVATE_KEY = "váš-soukromý-klíč-metamask" +``` + +Abychom je skutečně propojili s naším kódem, budeme na tyto proměnné odkazovat v našem souboru `hardhat.config.js` v kroku 13. + + + + +Nenahrávejte `.env`! Prosím, ujistěte se, že nikdy nesdílíte ani nezveřejňujete svůj soubor `.env` s nikým, protože tím ohrožujete svá tajemství. Pokud používáte správu verzí, přidejte svůj soubor `.env` do souboru
gitignore. + + + + +## Krok 12: Instalace Ethers.js {#step-12-install-ethersjs} + +Ethers.js je knihovna, která usnadňuje interakci a zadávání požadavků na Ethereum tím, že obaluje [standardní metody JSON-RPC](/developers/docs/apis/json-rpc/) uživatelsky přívětivějšími metodami. + +Hardhat velmi usnadňuje integraci [pluginů](https://hardhat.org/plugins/) pro další nástroje a rozšířenou funkčnost. Pro nasazení kontraktu využijeme [plugin Ethers](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) ([Ethers.js](https://github.com/ethers-io/ethers.js/) má několik velmi čistých metod pro nasazení kontraktu). + +V adresáři projektu zadejte: + +``` +npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0" +``` + +V dalším kroku také budeme vyžadovat Ethers v našem souboru `hardhat.config.js`. + +## Krok 13: Aktualizace souboru hardhat.config.js {#step-13-update-hardhatconfigjs} + +Zatím jsme přidali několik závislostí a pluginů, nyní musíme aktualizovat `hardhat.config.js`, aby o nich náš projekt věděl. + +Aktualizujte svůj soubor `hardhat.config.js`, aby vypadal takto: + +``` +require('dotenv').config(); + +require("@nomiclabs/hardhat-ethers"); +const { API_URL, PRIVATE_KEY } = process.env; + +/** +* @type import('hardhat/config').HardhatUserConfig +*/ +module.exports = { + solidity: "0.7.3", + defaultNetwork: "sepolia", + networks: { + hardhat: {}, + sepolia: { + url: API_URL, + accounts: [`0x${PRIVATE_KEY}`] + } + }, +} +``` + +## Krok 14: Kompilace našeho kontraktu {#step-14-compile-our-contracts} + +Abychom se ujistili, že zatím vše funguje, zkompilujeme si náš kontrakt. Úkol `compile` je jedním z vestavěných úkolů Hardhatu. + +Z příkazového řádku spusťte: + +``` +npx hardhat compile +``` + +Může se vám zobrazit varování `SPDX license identifier not provided in source file`, ale nemusíte se tím znepokojovat – doufejme, že všechno ostatní vypadá dobře! Pokud ne, vždy můžete napsat zprávu na [discordu Alchemy](https://discord.gg/u72VCg3). + +## Krok 15: Napsání našeho skriptu pro nasazení {#step-15-write-our-deploy-scripts} + +Nyní, když je náš kontrakt napsán a náš konfigurační soubor je připraven, je čas napsat náš skript pro nasazení kontraktu. + +Přejděte do složky `scripts/`, vytvořte nový soubor s názvem `deploy.js` a přidejte do něj následující obsah: + +``` +async function main() { + const HelloWorld = await ethers.getContractFactory("HelloWorld"); + + // Spustí nasazení a vrátí příslib, který se vyřeší na objekt kontraktu + const hello_world = await HelloWorld.deploy("Hello World!"); + console.log("Kontrakt nasazen na adresu:", hello_world.address);} + +main() + .then(() => process.exit(0)) + .catch(error => { + console.error(error); + process.exit(1); + }); +``` + +Hardhat skvěle vysvětluje, co každý z těchto řádků kódu dělá ve svém [výukovém programu Kontrakty](https://hardhat.org/tutorial/testing-contracts.html#writing-tests), a my jsme zde jejich vysvětlení převzali. + +``` +const HelloWorld = await ethers.getContractFactory("HelloWorld"); +``` + +`ContractFactory` v ethers.js je abstrakce, která se používá k nasazování nových chytrých kontraktů, takže `HelloWorld` je zde továrna pro instance našeho kontraktu hello world. Při použití pluginu `hardhat-ethers` jsou instance `ContractFactory` a `Contract` ve výchozím nastavení připojeny k prvnímu podepisujícímu. + +``` +const hello_world = await HelloWorld.deploy(); +``` + +Volání `deploy()` na `ContractFactory` spustí nasazení a vrátí `Promise`, který se vyřeší na `Contract`. Toto je objekt, který má metodu pro každou z funkcí našeho chytrého kontraktu. + +## Krok 16: Nasazení našeho kontraktu {#step-16-deploy-our-contract} + +Konečně jsme připraveni nasadit náš chytrý kontrakt! Přejděte na příkazový řádek a spusťte: + +``` +npx hardhat run scripts/deploy.js --network sepolia +``` + +Měli byste pak vidět něco takového: + +``` +Kontrakt nasazen na adresu: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 +``` + +Pokud přejdeme na [Etherscan sítě Sepolia](https://sepolia.etherscan.io/) a vyhledáme adresu našeho kontraktu, měli bychom vidět, že byl úspěšně nasazen. Transakce bude vypadat nějak takto: + +![Kontrakt na Etherscanu](./etherscan-contract.png) + +Adresa `From` by se měla shodovat s adresou vašeho účtu MetaMask a u adresy „To“ bude uvedeno „Vytvoření kontraktu“, ale pokud klikneme na transakci, uvidíme adresu našeho kontraktu v poli `To`: + +![Transakce na Etherscanu](./etherscan-transaction.png) + +Výborně! Právě jste nasadili chytrý kontrakt na blockchain Etherea 🎉 + +Abychom pochopili, co se děje „pod pokličkou“, přejděme na kartu Explorer v našem [ovládacím panelu Alchemy](https://dashboard.alchemyapi.io/explorer). Pokud máte více aplikací Alchemy, ujistěte se, že filtrujete podle aplikace a vyberete „Hello World“. +![Průzkumník Hello World](./hello-world-explorer.png) + +Zde uvidíte několik volání JSON-RPC, které za nás Hardhat/Ethers provedly „pod kapotou“, když jsme zavolali funkci `.deploy()`. Dvě důležité, které je třeba zmínit, jsou [`eth_sendRawTransaction`](https://www.alchemy.com/docs/node/abstract/abstract-api-endpoints/eth-send-raw-transaction), což je požadavek na skutečný zápis našeho kontraktu do blockchainu Sepolia, a [`eth_getTransactionByHash`](https://www.alchemy.com/docs/node/abstract/abstract-api-endpoints/eth-get-transaction-by-hash), což je požadavek na čtení informací o naší transakci na základě haše (typický vzor u +transakcí). Chcete-li se dozvědět více o odesílání transakcí, podívejte se na tento tutoriál o [odesílání transakcí pomocí Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) + +To je vše k 1. části tohoto tutoriálu, ve 2. části budeme s naším chytrým kontraktem skutečně [interagovat](https://www.alchemy.com/docs/interacting-with-a-smart-contract) aktualizací naší původní zprávy a ve 3. části náš chytrý kontrakt [publikujeme na Etherscanu](https://www.alchemy.com/docs/submitting-your-smart-contract-to-etherscan), aby každý věděl, jak s ním interagovat. + +**Chcete se o Alchemy dozvědět více?** Podívejte se na náš [web](https://www.alchemy.com/eth). Nechcete si nechat ujít žádnou aktualizaci? Přihlaste se k odběru našeho newsletteru [zde](https://www.alchemy.com/newsletter)! Nezapomeňte se také připojit na náš [Discord](https://discord.gg/u72VCg3).\*\*. diff --git a/public/content/translations/cs/developers/tutorials/how-to-implement-an-erc721-market/index.md b/public/content/translations/cs/developers/tutorials/how-to-implement-an-erc721-market/index.md new file mode 100644 index 00000000000..a8efe2104ef --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/how-to-implement-an-erc721-market/index.md @@ -0,0 +1,145 @@ +--- +title: Jak implementovat trh ERC-721 +description: "Jak dát tokenizované položky k prodeji na decentralizovanou inzertní tabuli" +author: "Alberto Cuesta Cañada" +tags: [ "chytré kontrakty", "erc-721", "solidity", "tokeny" ] +skill: intermediate +lang: cs +published: 2020-03-19 +source: Hackernoon +sourceUrl: https://hackernoon.com/how-to-implement-an-erc721-market-1e1a32j9 +--- + +V tomto článku vám ukážu, jak naprogramovat Craigslist pro blockchain Etherea. + +Před Gumtree, Ebay a Craigslist byly inzertní tabule většinou z korku nebo papíru. Inzertní tabule byly na školních chodbách, v novinách, na pouličních lampách, ve výlohách obchodů. + +To vše se změnilo s internetem. Počet lidí, kteří mohli vidět konkrétní inzertní tabuli, se znásobil o mnoho řádů. Díky tomu se trhy, které představují, staly mnohem efektivnějšími a rozšířily se do globální velikosti. Ebay je obrovský byznys, který má původ v těchto fyzických inzertních tabulích. + +S blockchainem se tyto trhy opět změní, dovolte mi ukázat vám jak. + +## Monetizace {#monetization} + +Obchodní model veřejné blockchainové inzertní tabule se bude muset lišit od modelu Ebay a podobných společností. + +Zaprvé je tu [úhel decentralizace](/developers/docs/web2-vs-web3/). Stávající platformy musí udržovat své vlastní servery. Decentralizovaná platforma je udržována jejími uživateli, takže náklady na provoz základní platformy pro vlastníka platformy klesají na nulu. + +Pak je tu front-end, webová stránka nebo rozhraní, které umožňuje přístup k platformě. Zde existuje mnoho možností. Vlastníci platformy mohou omezit přístup a nutit všechny používat jejich rozhraní a účtovat si poplatek. Vlastníci platformy se také mohou rozhodnout otevřít přístup (Moc lidem!). a nechat kohokoli vytvářet rozhraní k platformě. Nebo se majitelé mohli rozhodnout pro jakýkoli přístup uprostřed těchto extrémů. + +_Obchodní lídři s větší vizí než já budou vědět, jak to zpeněžit. Vše, co vidím, je, že je to odlišné od současného stavu a pravděpodobně ziskové._ + +Dále je tu úhel automatizace a plateb. Některé věci mohou být velmi [efektivně tokenizovány](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com) a obchodovány na inzertní tabuli. Tokenizovaná aktiva se v blockchainu snadno převádějí. Velmi složité platební metody lze v blockchainu snadno implementovat. + +Jen zde cítím obchodní příležitost. Inzertní tabuli bez provozních nákladů lze snadno implementovat, se složitými platebními cestami zahrnutými v každé transakci. Jsem si jistý, že někdo přijde s nápadem, na co to použít. + +Jsem prostě šťastný, že to stavím. Podívejme se na kód. + +## Implementace {#implementation} + +Před nějakou dobou jsme založili [open-source repozitář](https://github.com/HQ20/contracts?ref=hackernoon.com) s příklady implementací obchodních případů a dalšími vychytávkami, prosím, podívejte se. + +Kód pro tuto [inzertní tabuli Etherea](https://github.com/HQ20/contracts/tree/master/contracts/classifieds?ref=hackernoon.com) je tam, prosím, používejte ho a nebojte se ho využít na maximum. Jen si uvědomte, že kód nebyl auditován a musíte provést vlastní hloubkovou kontrolu, než do něj vložíte peníze. + +Základy tabule nejsou složité. Všechny inzeráty na tabuli budou jen strukturou s několika poli: + +```solidity +struct Trade { + address poster; + uint256 item; + uint256 price; + bytes32 status; // Otevřený, provedený, zrušený +} +``` + +Takže je tu někdo, kdo inzerát zveřejňuje. Položka na prodej. Cena za položku. Stav obchodu, který může být otevřený, provedený nebo zrušený. + +Všechny tyto obchody budou uloženy v mapování. Protože všechno v Solidity se zdá být mapování. Také proto, že je to pohodlné. + +```solidity +mapping(uint256 => Trade) public trades; +``` + +Použití mapování jen znamená, že musíme před zveřejněním přijít s ID pro každý inzerát a budeme muset znát ID inzerátu, než s ním budeme moci pracovat. Existuje několik způsobů, jak se s tím vypořádat, a to buď v chytrém kontraktu, nebo ve front-endu. Pokud potřebujete nějaké rady, zeptejte se. + +Dále přichází otázka, s jakými položkami se zabýváme a jaká je měna, která se používá k placení za transakci. + +U položek budeme jen požadovat, aby implementovaly rozhraní [ERC-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol?ref=hackernoon.com), což je ve skutečnosti jen způsob, jak reprezentovat položky z reálného světa v blockchainu, i když to [nejlépe funguje s digitálními aktivy](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com). V konstruktoru zadáme náš vlastní kontrakt ERC721, což znamená, že jakákoli aktiva v naší inzertní tabuli musí být předem tokenizována. + +U plateb uděláme něco podobného. Většina blockchainových projektů definuje svou vlastní kryptoměnu [ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol?ref=hackernoon.com). Některé jiné preferují použití mainstreamové kryptoměny jako DAI. V této inzertní tabuli se stačí při konstrukci rozhodnout, jaká bude vaše měna. Snadné. + +```solidity +constructor ( + address _currencyTokenAddress, address _itemTokenAddress +) public { + currencyToken = IERC20(_currencyTokenAddress); + itemToken = IERC721(_itemTokenAddress); + tradeCounter = 0; +} +``` + +Už se k tomu dostáváme. Máme inzeráty, položky k obchodu a měnu pro platby. Vytvořit inzerát znamená vložit položku do úschovy (escrow), abyste ukázali, že ji máte, a že jste ji nezveřejnili dvakrát, případně na jiné tabuli. + +Níže uvedený kód dělá přesně to. Vloží položku do úschovy (escrow), vytvoří inzerát, provede nějaké úklidové práce. + +```solidity +function openTrade(uint256 _item, uint256 _price) + public +{ + itemToken.transferFrom(msg.sender, address(this), _item); + trades[tradeCounter] = Trade({ + poster: msg.sender, + item: _item, + price: _price, + status: "Open" + }); + tradeCounter += 1; + emit TradeStatusChange(tradeCounter - 1, "Open"); +} +``` + +Přijmout obchod znamená vybrat inzerát (obchod), zaplatit cenu, obdržet položku. Níže uvedený kód načte obchod. Zkontroluje, zda je k dispozici. Zaplatí za položku. Načte položku. Aktualizuje inzerát. + +```solidity +function executeTrade(uint256 _trade) + public +{ + Trade memory trade = trades[_trade]; + require(trade.status == "Open", "Obchod není otevřený."); + currencyToken.transferFrom(msg.sender, trade.poster, trade.price); + itemToken.transferFrom(address(this), msg.sender, trade.item); + trades[_trade].status = "Executed"; + emit TradeStatusChange(_trade, "Executed"); +} +``` + +Nakonec máme pro prodejce možnost odstoupit od obchodu dříve, než ho kupující přijme. V některých modelech by inzeráty byly místo toho aktivní po určitou dobu, než vyprší jejich platnost. Vaše volba v závislosti na návrhu vašeho trhu. + +Kód je velmi podobný tomu, který se používá k provedení obchodu, pouze s tím rozdílem, že nedochází k výměně měny a položka se vrací autorovi inzerátu. + +```solidity +function cancelTrade(uint256 _trade) + public +{ + Trade memory trade = trades[_trade]; + require( + msg.sender == trade.poster, + "Obchod může zrušit pouze autor." + ); + require(trade.status == "Open", "Obchod není otevřený."); + itemToken.transferFrom(address(this), trade.poster, trade.item); + trades[_trade].status = "Cancelled"; + emit TradeStatusChange(_trade, "Cancelled"); +} +``` + +A je to. Dostali jste se na konec implementace. Je docela překvapivé, jak kompaktní jsou některé obchodní koncepty, když jsou vyjádřeny v kódu, a toto je jeden z těch případů. Podívejte se na kompletní kontrakt [v našem repozitáři](https://github.com/HQ20/contracts/blob/master/contracts/classifieds/Classifieds.sol). + +## Závěr {#conclusion} + +Inzertní tabule jsou běžnou konfigurací trhu, která se s internetem masivně rozšířila a stala se nesmírně populárním obchodním modelem s několika monopolními vítězi. + +Inzertní tabule jsou také nástrojem, který lze snadno replikovat v blockchainovém prostředí, se velmi specifickými funkcemi, které umožní vyzvat stávající giganty. + +V tomto článku jsem se pokusil propojit obchodní realitu inzertního byznysu s technologickou implementací. Tyto znalosti by vám měly pomoci vytvořit vizi a plán implementace, pokud máte správné dovednosti. + +Jako vždy, pokud se chystáte vytvořit něco zábavného a uvítali byste radu, prosím, [napište mi](https://albertocuesta.es/)! Vždy rád pomohu. diff --git a/public/content/translations/cs/developers/tutorials/how-to-mint-an-nft/index.md b/public/content/translations/cs/developers/tutorials/how-to-mint-an-nft/index.md new file mode 100644 index 00000000000..0edf29471d4 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/how-to-mint-an-nft/index.md @@ -0,0 +1,335 @@ +--- +title: "Jak vyrazit NFT (část 2/3 série tutoriálů o NFT)" +description: "Tento tutoriál popisuje, jak vyrazit NFT na blockchainu Etherea pomocí našeho chytrého kontraktu a Web3." +author: "Sumi Mudgil" +tags: + [ + "ERC-721", + "alchemy", + "solidity", + "smart kontrakt účty" + ] +skill: beginner +lang: cs +published: 2021-04-22 +--- + +[Beeple](https://www.nytimes.com/2021/03/11/arts/design/nft-auction-christies-beeple.html): 69 milionů +[3LAU](https://www.forbes.com/sites/abrambrown/2021/03/03/3lau-nft-nonfungible-tokens-justin-blau/?sh=5f72ef64643b): 11 milionů +[Grimes](https://www.theguardian.com/music/2021/mar/02/grimes-sells-digital-art-collection-non-fungible-tokens): 6 milionů + +Všichni z nich vyrazili svá NFT pomocí výkonného API od Alchemy. V tomto tutoriálu vás naučíme, jak udělat to samé za \<10 minut. + +„Ražba NFT“ je akt publikování jedinečné instance vašeho tokenu ERC-721 na blockchainu. Pomocí našeho chytrého kontraktu z [1. části této série tutoriálů o NFT](/developers/tutorials/how-to-write-and-deploy-an-nft/) si procvičíme naše dovednosti s Web3 a vyrazíme si NFT. Na konci tohoto tutoriálu si budete moci vyrazit tolik NFT, kolik jen vaše srdce (a peněženka) bude chtít! + +Pojďme na to! + +## Krok 1: Nainstalujte si Web3 {#install-web3} + +Pokud jste postupovali podle prvního tutoriálu o vytváření vašeho chytrého kontraktu pro NFT, máte již zkušenosti s používáním Ethers.js. Web3 je podobné Ethers, protože se jedná o knihovnu, která usnadňuje vytváření požadavků na blockchain Etherea. V tomto tutoriálu budeme používat [Alchemy Web3](https://docs.alchemyapi.io/alchemy/documentation/alchemy-web3), což je vylepšená knihovna Web3, která nabízí automatické opakování pokusů a robustní podporu WebSocket. + +V domovském adresáři vašeho projektu spusťte: + +``` +npm install @alch/alchemy-web3 +``` + +## Krok 2: Vytvořte soubor `mint-nft.js` {#create-mintnftjs} + +V adresáři `scripts` vytvořte soubor `mint-nft.js` a přidejte následující řádky kódu: + +```js +require("dotenv").config() +const API_URL = process.env.API_URL +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(API_URL) +``` + +## Krok 3: Získejte ABI svého kontraktu {#contract-abi} + +Naše ABI kontraktu (Application Binary Interface) je rozhraní pro interakci s naším chytrým kontraktem. Více informací o ABI kontraktů se můžete dozvědět [zde](https://docs.alchemyapi.io/alchemy/guides/eth_getlogs#what-are-ab-is). Hardhat pro nás automaticky generuje ABI a ukládá ho do souboru `MyNFT.json`. Abychom to mohli použít, budeme muset analyzovat obsah přidáním následujících řádků kódu do našeho souboru `mint-nft.js`: + +```js +const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json") +``` + +Pokud chcete vidět ABI, můžete si ho vytisknout do konzole: + +```js +console.log(JSON.stringify(contract.abi)) +``` + +Chcete-li spustit `mint-nft.js` a vidět své ABI vytištěné v konzoli, přejděte do terminálu a spusťte: + +```js +node scripts/mint-nft.js +``` + +## Krok 4: Nakonfigurujte metadata pro své NFT pomocí IPFS {#config-meta} + +Pokud si pamatujete z našeho tutoriálu v části 1, naše funkce chytrého kontraktu `mintNFT` přijímá parametr tokenURI, který by se měl přeložit na dokument JSON popisující metadata NFT – což je to, co NFT skutečně oživuje a umožňuje mu mít konfigurovatelné vlastnosti, jako je název, popis, obrázek a další atributy. + +> _Interplanetary File System (IPFS) je decentralizovaný protokol a peer-to-peer síť pro ukládání a sdílení dat v distribuovaném souborovém systému._ + +Použijeme Pinata, pohodlné IPFS API a sadu nástrojů, k uložení našeho NFT aktiva a metadat, abychom zajistili, že naše NFT je skutečně decentralizované. Pokud nemáte účet Pinata, zaregistrujte si bezplatný účet [zde](https://app.pinata.cloud) a dokončete kroky pro ověření e-mailu. + +Jakmile si vytvoříte účet: + +- Přejděte na stránku „Soubory“ a klikněte na modré tlačítko „Nahrát“ v levém horním rohu stránky. + +- Nahrajte obrázek do Pinaty – to bude obrazové aktivum pro vaše NFT. Aktivum si můžete pojmenovat, jak chcete + +- Po nahrání uvidíte informace o souboru v tabulce na stránce „Soubory“. Uvidíte také sloupec CID. CID můžete zkopírovat kliknutím na tlačítko kopírovat vedle něj. Váš nahraný soubor si můžete prohlédnout na: `https://gateway.pinata.cloud/ipfs/`. Obrázek, který jsme použili, najdete na IPFS například [zde](https://gateway.pinata.cloud/ipfs/QmZdd5KYdCFApWn7eTZJ1qgJu18urJrP9Yh1TZcZrZxxB5). + +Pro vizuálněji založené studenty jsou výše uvedené kroky shrnuty zde: + +![Jak nahrát obrázek do Pinaty](./instructionsPinata.gif) + +Nyní budeme chtít nahrát do Pinaty ještě jeden dokument. Ale než to uděláme, musíme ho vytvořit! + +Ve svém kořenovém adresáři vytvořte nový soubor s názvem `nft-metadata.json` a přidejte následující kód json: + +```json +{ + "attributes": [ + { + "trait_type": "Breed", + "value": "Maltipoo" + }, + { + "trait_type": "Eye color", + "value": "Mocha" + } + ], + "description": "The world's most adorable and sensitive pup.", + "image": "ipfs://QmWmvTJmJU3pozR9ZHFmQC2DNDwi2XJtf3QGyYiiagFSWb", + "name": "Ramses" +} +``` + +Data v jsonu si můžete libovolně měnit. Do sekce atributů můžete přidávat nebo z ní odebírat. Nejdůležitější je, abyste se ujistili, že pole s obrázkem ukazuje na umístění vašeho obrázku na IPFS – jinak bude vaše NFT obsahovat fotku (velmi roztomilého!) psa. + +Jakmile dokončíte úpravu souboru JSON, uložte ho a nahrajte na Pinata podle stejných kroků, jaké jsme provedli při nahrávání obrázku. + +![Jak nahrát soubor nft-metadata.json do Pinaty](./uploadPinata.gif) + +## Krok 5: Vytvořte instanci svého kontraktu {#instance-contract} + +Nyní, abychom mohli s naším kontraktem interagovat, musíme v našem kódu vytvořit jeho instanci. K tomu budeme potřebovat účet našeho kontraktu, který můžeme získat z nasazení nebo na [Blockscoutu](https://eth-sepolia.blockscout.com/) vyhledáním adresy, kterou jste použili k nasazení kontraktu. + +![Zobrazení účtu vašeho kontraktu na Etherscanu](./view-contract-etherscan.png) + +Ve výše uvedeném příkladu je účet našeho kontraktu 0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778. + +Dále použijeme metodu [kontraktu](https://docs.web3js.org/api/web3-eth-contract/class/Contract) Web3 k vytvoření našeho kontraktu pomocí ABI a adresy. Do souboru `mint-nft.js` přidejte následující: + +```js +const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778" + +const nftContract = new web3.eth.Contract(contract.abi, contractAddress) +``` + +## Krok 6: Aktualizujte soubor `.env` {#update-env} + +Nyní, abychom mohli vytvářet a odesílat transakce do řetězce Etherea, použijeme adresu vašeho veřejného účtu Ethereum k získání nonce účtu (vysvětlíme níže). + +Přidejte svůj veřejný klíč do souboru `.env` – pokud jste dokončili 1. část tutoriálu, náš soubor `.env` by nyní měl vypadat takto: + +```js +API_URL = "https://eth-sepolia.g.alchemy.com/v2/váš-api-klíč" +PRIVATE_KEY = "adresa-vašeho-privátního-účtu" +PUBLIC_KEY = "adresa-vašeho-veřejného-účtu" +``` + +## Krok 7: Vytvořte transakci {#create-txn} + +Nejprve definujme funkci s názvem `mintNFT(tokenData)` a vytvořme naši transakci následujícím postupem: + +1. Získejte svůj _PRIVATE_KEY_ a _PUBLIC_KEY_ ze souboru `.env`. + +2. Dále budeme muset zjistit nonce účtu. Specifikace nonce se používá ke sledování počtu transakcí odeslaných z vaší adresy – což potřebujeme z bezpečnostních důvodů a k zabránění [útokům opětovného přehrání](https://docs.alchemyapi.io/resources/blockchain-glossary#account-nonce). K získání počtu transakcí odeslaných z vaší adresy použijeme [getTransactionCount](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc#eth_gettransactioncount). + +3. Nakonec nastavíme naši transakci s následujícími informacemi: + +- `'from': PUBLIC_KEY` – Původ naší transakce je naše veřejná adresa + +- `'to': contractAddress` – Kontrakt, se kterým chceme interagovat a odeslat transakci + +- `'nonce': nonce` – Nonce účtu s počtem transakcí odeslaných z naší adresy + +- `'gas': estimatedGas` – Odhadované palivo potřebné k dokončení transakce + +- `'data': nftContract.methods.mintNFT(PUBLIC_KEY, md).encodeABI()` – Výpočet, který chceme v této transakci provést – což je v tomto případě ražba NFT + +Váš soubor `mint-nft.js` by nyní měl vypadat takto: + +```js + require('dotenv').config(); + const API_URL = process.env.API_URL; + const PUBLIC_KEY = process.env.PUBLIC_KEY; + const PRIVATE_KEY = process.env.PRIVATE_KEY; + + const { createAlchemyWeb3 } = require("@alch/alchemy-web3"); + const web3 = createAlchemyWeb3(API_URL); + + const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json"); + 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'); //získat nejnovější nonce + + //transakce + const tx = { + 'from': PUBLIC_KEY, + 'to': contractAddress, + 'nonce': nonce, + 'gas': 500000, + 'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI() + }; + }​ +``` + +## Krok 8: Podepište transakci {#sign-txn} + +Nyní, když jsme vytvořili naši transakci, musíme ji podepsat, abychom ji mohli odeslat. Zde použijeme náš privátní klíč. + +`web3.eth.sendSignedTransaction` nám poskytne haš transakce, který můžeme použít k ujištění, že naše transakce byla vytěžena a nebyla sítí zahozená. Všimnete si, že v sekci podepisování transakcí jsme přidali kontrolu chyb, abychom věděli, zda naše transakce proběhla úspěšně. + +```js +require("dotenv").config() +const API_URL = process.env.API_URL +const PUBLIC_KEY = process.env.PUBLIC_KEY +const PRIVATE_KEY = process.env.PRIVATE_KEY + +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(API_URL) + +const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json") +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") //získat nejnovější nonce + + //transakce + const tx = { + from: PUBLIC_KEY, + to: contractAddress, + nonce: nonce, + gas: 500000, + data: nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI(), + } + + const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY) + signPromise + .then((signedTx) => { + web3.eth.sendSignedTransaction( + signedTx.rawTransaction, + function (err, hash) { + if (!err) { + console.log( + "Haš vaší transakce je: ", + hash, + "\nZkontrolujte Mempool Alchemy a zobrazte stav vaší transakce!" + ) + } else { + console.log( + "Při odesílání transakce se něco pokazilo:", + err + ) + } + } + ) + }) + .catch((err) => { + console.log(" Promise selhal:", err) + }) +} +``` + +## Krok 9: Zavolejte `mintNFT` a spusťte node `mint-nft.js` {#call-mintnft-fn} + +Pamatujete si na `metadata.json`, který jste nahráli do Pinaty? Získejte jeho hašovací kód z Pinaty a předejte následující jako parametr funkci `mintNFT` `https://gateway.pinata.cloud/ipfs/` + +Zde je návod, jak získat hašovací kód: + +![Jak získat hašovací kód metadat vašeho NFT na Pinatě](./metadataPinata.gif)_Jak získat hašovací kód metadat vašeho NFT na Pinatě_ + +> Dvakrát zkontrolujte, že hašovací kód, který jste zkopírovali, odkazuje na váš **metadata.json** načtením `https://gateway.pinata.cloud/ipfs/` do samostatného okna. Stránka by měla vypadat podobně jako na snímku obrazovky níže: + +![Vaše stránka by měla zobrazovat metadata json](./metadataJSON.png)_Vaše stránka by měla zobrazovat metadata json_ + +Celkově by váš kód měl vypadat nějak takto: + +```js +require("dotenv").config() +const API_URL = process.env.API_URL +const PUBLIC_KEY = process.env.PUBLIC_KEY +const PRIVATE_KEY = process.env.PRIVATE_KEY + +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(API_URL) + +const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json") +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") //získat nejnovější nonce + + //transakce + const tx = { + from: PUBLIC_KEY, + to: contractAddress, + nonce: nonce, + gas: 500000, + data: nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI(), + } + + const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY) + signPromise + .then((signedTx) => { + web3.eth.sendSignedTransaction( + signedTx.rawTransaction, + function (err, hash) { + if (!err) { + console.log( + "Haš vaší transakce je: ", + hash, + "\nZkontrolujte Mempool Alchemy a zobrazte stav vaší transakce!" + ) + } else { + console.log( + "Při odesílání transakce se něco pokazilo:", + err + ) + } + } + ) + }) + .catch((err) => { + console.log("Promise selhal:", err) + }) +} + +mintNFT("ipfs://QmYueiuRNmL4MiA2GwtVMm6ZagknXnSpQnB3z2gWbz36hP") +``` + +Nyní spusťte `node scripts/mint-nft.js` pro nasazení vašeho NFT. Po několika sekundách byste měli v terminálu vidět odpověď podobnou této: + + ``` + Haš vaší transakce je: 0x301791fdf492001fcd9d5e5b12f3aa1bbbea9a88ed24993a8ab2cdae2d06e1e8 + + Zkontrolujte Mempool Alchemy a zobrazte stav vaší transakce! + ``` + +Dále navštivte svůj [mempool Alchemy](https://dashboard.alchemyapi.io/mempool) a podívejte se na stav vaší transakce (zda je čekající, vytěžená, nebo ji síť zahodila). Pokud byla vaše transakce zahozená, je také užitečné zkontrolovat [Blockscout](https://eth-sepolia.blockscout.com/) a vyhledat haš vaší transakce. + +![Zobrazení haše vaší NFT transakce na Etherscanu](./view-nft-etherscan.png)_Zobrazení haše vaší NFT transakce na Etherscanu_ + +A to je vše! Nyní jste nasadili A vyrazili NFT na blockchainu Etherea + +Pomocí `mint-nft.js` můžete vyrazit tolik NFT, kolik jen vaše srdce (a peněženka) bude chtít! Jen se ujistěte, že předáváte nový tokenURI popisující metadata NFT (jinak skončíte výrobou spousty identických s různými ID). + +Pravděpodobně byste si chtěli své NFT vystavit ve své peněžence – takže se určitě podívejte na [3. část: Jak si zobrazit své NFT ve vaší peněžence](/developers/tutorials/how-to-view-nft-in-metamask/)! diff --git a/public/content/translations/cs/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md b/public/content/translations/cs/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md new file mode 100644 index 00000000000..3498c5c56fa --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md @@ -0,0 +1,108 @@ +--- +title: "Jak vytvořit maketu chytrých kontraktů Solidity pro testování" +description: "Proč byste si při testování měli dělat legraci ze svých kontraktů" +author: Markus Waas +lang: cs +tags: + [ + "solidity", + "smart kontrakt účty", + "testování", + "mocking" + ] +skill: intermediate +published: 2020-05-02 +source: soliditydeveloper.com +sourceUrl: https://soliditydeveloper.com/mocking-contracts +--- + +[Mock objekty](https://wikipedia.org/wiki/Mock_object) jsou běžným návrhovým vzorem v objektově orientovaném programování. Pochází ze starého francouzského slova „mocquer“ s významem „dělat si legraci“ a vyvinulo se ve význam „napodobování něčeho skutečného“, což je vlastně to, co v programování děláme. Dělejte si prosím legraci ze svých chytrých kontraktů, jen pokud chcete, ale vytvářejte pro ně makety, kdykoli můžete. Usnadní vám to život. + +## Unit testování kontraktů pomocí maket {#unit-testing-contracts-with-mocks} + +Vytvoření makety kontraktu v podstatě znamená vytvoření druhé verze tohoto kontraktu, která se chová velmi podobně jako původní, ale způsobem, který může vývojář snadno ovládat. Často skončíte u složitých kontraktů, kde chcete pouze [testovat malé části kontraktu](/developers/docs/smart-contracts/testing/). Problém je, co když testování této malé části vyžaduje velmi specifický stav kontraktu, do kterého je obtížné se dostat? + +Pokaždé byste mohli napsat složitou logiku nastavení testu, která kontrakt uvede do požadovaného stavu, nebo napíšete maketu. Vytvoření makety kontraktu je s dědičností snadné. Jednoduše vytvořte druhý mock kontrakt, který dědí z původního. Nyní můžete do své makety přepsat funkce. Ukažme si to na příkladu. + +## Příklad: Privátní ERC20 {#example-private-erc20} + +Použijeme příklad kontraktu ERC-20, který má počáteční soukromé období. Vlastník může spravovat soukromé uživatele a pouze ti budou moci na začátku přijímat tokeny. Jakmile uplyne určitá doba, bude moci tokeny používat každý. Pokud vás to zajímá, používáme hook [`_beforeTokenTransfer`](https://docs.openzeppelin.com/contracts/5.x/extending-contracts#using-hooks) z nových kontraktů OpenZeppelin 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: invalid recipient"); + } + + function _validRecipient(address to) private view returns (bool) { + if (isPublic()) { + return true; + } + + return isPrivateUser[to]; + } +} +``` + +A teď si vytvoříme maketu. + +```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; + } +} +``` + +Dostanete jednu z následujících chybových zpráv: + +- `PrivateERC20Mock.sol: TypeError: Overriding function is missing "override" specifier.` +- `PrivateERC20.sol: TypeError: Trying to override non-virtual function. Did you forget to add "virtual"?.` + +Protože používáme novou verzi Solidity 0.6, musíme přidat klíčové slovo `virtual` pro funkce, které lze přepsat, a `override` pro přepisující funkci. Přidejme je tedy k oběma funkcím `isPublic`. + +Nyní ve svých unit testech můžete místo toho použít `PrivateERC20Mock`. Pokud chcete testovat chování během doby soukromého používání, použijte `setIsPublic(false)` a podobně `setIsPublic(true)` pro testování doby veřejného používání. V našem příkladu bychom samozřejmě mohli také použít [pomocné funkce pro čas](https://docs.openzeppelin.com/test-helpers/0.5/api#increase), abychom odpovídajícím způsobem změnili časy. Ale myšlenka mockingu by teď měla být jasná a dokážete si představit scénáře, kde to není tak snadné jako pouhé posunutí času. + +## Vytváření maket mnoha kontraktů {#mocking-many-contracts} + +Může to být nepřehledné, pokud musíte pro každou jednu maketu vytvářet další kontrakt. Pokud vám to vadí, můžete se podívat na knihovnu [MockContract](https://github.com/gnosis/mock-contract). Umožňuje přepisovat a měnit chování kontraktů za chodu. Funguje to však pouze pro mockování volání jiného kontraktu, takže by to pro náš příklad nefungovalo. + +## Mocking může být ještě mocnější {#mocking-can-be-even-more-powerful} + +Možnosti mockingu tím nekončí. + +- Přidávání funkcí: Užitečné je nejen přepsání konkrétní funkce, ale také pouhé přidání dalších funkcí. Dobrým příkladem pro tokeny je mít pouze dodatečnou funkci `mint`, která umožní každému uživateli získat zdarma nové tokeny. +- Použití na testnetech: Když nasazujete a testujete své kontrakty na testnetech společně s vaší dapp, zvažte použití mock verze. Vyhněte se přepisování funkcí, pokud to opravdu nemusíte. Koneckonců chcete testovat skutečnou logiku. Ale přidání například resetovací funkce může být užitečné, která jednoduše resetuje stav kontraktu na začátek, není vyžadováno žádné nové nasazení. To byste samozřejmě nechtěli mít v kontraktu na hlavní síti. diff --git a/public/content/translations/cs/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md b/public/content/translations/cs/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md new file mode 100644 index 00000000000..897996e6f7b --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md @@ -0,0 +1,710 @@ +--- +title: "Jak používat Echidnu k testování chytrých kontraktů" +description: "Jak používat Echidnu k automatickému testování chytrých kontraktů" +author: "Trailofbits" +lang: cs +tags: + [ + "solidity", + "smart kontrakt účty", + "bezpečnost", + "testování", + "fuzzing" + ] +skill: advanced +published: 2020-04-10 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/echidna +--- + +## Instalace {#installation} + +Echidnu lze nainstalovat prostřednictvím dockeru nebo pomocí předkompilovaného binárního souboru. + +### Echidna prostřednictvím dockeru {#echidna-through-docker} + +```bash +docker pull trailofbits/eth-security-toolbox +docker run -it -v "$PWD":/home/training trailofbits/eth-security-toolbox +``` + +_Poslední příkaz spustí eth-security-toolbox v dockeru, který má přístup k vašemu aktuálnímu adresáři. Můžete měnit soubory z vašeho hostitele a spouštět nástroje na souborech z dockeru_ + +Uvnitř dockeru spusťte: + +```bash +solc-select 0.5.11 +cd /home/training +``` + +### Binární soubor {#binary} + +[https://github.com/crytic/echidna/releases/tag/v1.4.0.0](https://github.com/crytic/echidna/releases/tag/v1.4.0.0) + +## Úvod do fuzzingu založeného na vlastnostech {#introduction-to-property-based-fuzzing} + +Echidna je fuzzer založený na vlastnostech, který jsme popsali v našich předchozích příspěvcích na blogu ([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) je dobře známá technika v bezpečnostní komunitě. Spočívá v generování více či méně náhodných vstupů pro nalezení chyb v programu. Fuzzery pro tradiční software (jako je [AFL](http://lcamtuf.coredump.cx/afl/) nebo [LibFuzzer](https://llvm.org/docs/LibFuzzer.html)) jsou známé jako účinné nástroje pro hledání chyb. + +Kromě čistě náhodného generování vstupů existuje mnoho technik a strategií pro generování dobrých vstupů, včetně: + +- Získávání zpětné vazby z každého spuštění a její použití k řízení generování. Například, pokud nově vygenerovaný vstup vede k objevení nové cesty, může mít smysl generovat nové vstupy, které jsou mu blízké. +- Generování vstupu respektujícího strukturální omezení. Například, pokud váš vstup obsahuje hlavičku s kontrolním součtem, bude mít smysl nechat fuzzer generovat vstup ověřující kontrolní součet. +- Použití známých vstupů pro generování nových vstupů: pokud máte přístup k velkému souboru dat platných vstupů, váš fuzzer může z nich generovat nové vstupy, místo aby začínal generování od nuly. Ty se obvykle nazývají _seeds_. + +### Fuzzing založený na vlastnostech {#property-based-fuzzing} + +Echidna patří do specifické rodiny fuzzerů: fuzzing založený na vlastnostech, silně inspirovaný [QuickCheck](https://wikipedia.org/wiki/QuickCheck). Na rozdíl od klasického fuzzeru, který se snaží najít pády, se Echidna snaží narušit uživatelem definované invarianty. + +V chytrých kontraktech jsou invarianty funkce v Solidity, které mohou představovat jakýkoli nesprávný nebo neplatný stav, kterého může kontrakt dosáhnout, včetně: + +- Nesprávná kontrola přístupu: útočník se stal vlastníkem kontraktu. +- Nesprávný stavový automat: tokeny mohou být převáděny, i když je kontrakt pozastaven. +- Nesprávná aritmetika: uživatel může způsobit podtečení svého zůstatku a získat neomezené množství tokenů zdarma. + +### Testování vlastnosti s Echidnou {#testing-a-property-with-echidna} + +Podíváme se, jak testovat chytrý kontrakt s Echidnou. Cílem je následující chytrý kontrakt [`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; + } +} +``` + +Budeme předpokládat, že tento token musí mít následující vlastnosti: + +- Každý může mít maximálně 1000 tokenů +- Token nelze převést (nejedná se o token ERC20) + +### Napsání vlastnosti {#write-a-property} + +Vlastnosti Echidny jsou funkce v Solidity. Vlastnost musí: + +- Nemít žádný argument +- Vrátit `true`, pokud je úspěšná +- Mít jméno začínající na `echidna` + +Echidna bude: + +- Automaticky generovat libovolné transakce pro testování vlastnosti. +- Hlásit jakékoli transakce, které vedou k tomu, že vlastnost vrátí `false` nebo vyhodí chybu. +- Zahodit vedlejší účinky při volání vlastnosti (tzn. pokud vlastnost změní stavovou proměnnou, je to po testu zahozeno) + +Následující vlastnost kontroluje, že volající nemá více než 1000 tokenů: + +```solidity +function echidna_balance_under_1000() public view returns(bool){ + return balances[msg.sender] <= 1000; +} +``` + +Použijte dědičnost k oddělení vašeho kontraktu od vašich vlastností: + +```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) implementuje vlastnost a dědí z tokenu. + +### Inicializace kontraktu {#initiate-a-contract} + +Echidna potřebuje [konstruktor](/developers/docs/smart-contracts/anatomy/#constructor-functions) bez argumentu. Pokud váš kontrakt potřebuje specifickou inicializaci, musíte ji provést v konstruktoru. + +V Echidně existují některé specifické adresy: + +- `0x00a329c0648769A73afAc7F9381E08FB43dBEA72`, která volá konstruktor. +- `0x10000`, `0x20000` a `0x00a329C0648769a73afAC7F9381e08fb43DBEA70`, které náhodně volají ostatní funkce. + +V našem aktuálním příkladu nepotřebujeme žádnou zvláštní inicializaci, proto je náš konstruktor prázdný. + +### Spuštění Echidny {#run-echidna} + +Echidna se spouští pomocí: + +```bash +echidna-test contract.sol +``` + +Pokud contract.sol obsahuje více kontraktů, můžete specifikovat cíl: + +```bash +echidna-test contract.sol --contract MyContract +``` + +### Shrnutí: Testování vlastnosti {#summary-testing-a-property} + +Následující text shrnuje spuštění Echidny na našem příkladu: + +```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 zjistila, že vlastnost je narušena, pokud je volána funkce `backdoor`. + +## Filtrování funkcí pro volání během fuzzingové kampaně {#filtering-functions-to-call-during-a-fuzzing-campaign} + +Ukážeme si, jak filtrovat funkce, které mají být fuzzovány. +Cílem je následující chytrý kontrakt: + +```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); + } +} +``` + +Tento malý příklad nutí Echidnu najít určitou sekvenci transakcí ke změně stavové proměnné. +Pro fuzzer je to obtížné (doporučuje se použít nástroj pro symbolické provádění, jako je [Manticore](https://github.com/trailofbits/manticore)). +Můžeme spustit Echidnu, abychom to ověřili: + +```bash +echidna-test multi.sol +... +echidna_state4: passed! 🎉 +Seed: -3684648582249875403 +``` + +### Filtrování funkcí {#filtering-functions} + +Echidna má potíže s nalezením správné sekvence pro testování tohoto kontraktu, protože dvě resetovací funkce (`reset1` a `reset2`) nastaví všechny stavové proměnné na `false`. +Můžeme však použít speciální funkci Echidny a buď dát resetovací funkce na černou listinu, nebo na bílou listinu pouze funkce `f`, `g`, +`h` a `i`. + +Chcete-li dát funkce na černou listinu, můžeme použít tento konfigurační soubor: + +```yaml +filterBlacklist: true +filterFunctions: ["reset1", "reset2"] +``` + +Dalším přístupem k filtrování funkcí je vypsání funkcí na bílé listině. K tomu můžeme použít tento konfigurační soubor: + +```yaml +filterBlacklist: false +filterFunctions: ["f", "g", "h", "i"] +``` + +- `filterBlacklist` je ve výchozím nastavení `true`. +- Filtrování bude provedeno pouze podle jména (bez parametrů). Pokud máte `f()` a `f(uint256)`, filtr `"f"` bude odpovídat oběma funkcím. + +### Spuštění Echidny {#run-echidna-1} + +Chcete-li spustit Echidnu s konfiguračním souborem `blacklist.yaml`: + +```bash +echidna-test multi.sol --config blacklist.yaml +... +echidna_state4: failed!💥 + Call sequence: + f(12) + g(8) + h(42) + i() +``` + +Echidna téměř okamžitě najde sekvenci transakcí, která vlastnost zneplatní. + +### Shrnutí: Filtrování funkcí {#summary-filtering-functions} + +Echidna může během fuzzingové kampaně buď dát funkce na černou listinu, nebo na bílou listinu pomocí: + +```yaml +filterBlacklist: true +filterFunctions: ["f1", "f2", "f3"] +``` + +```bash +echidna-test contract.sol --config config.yaml +... +``` + +Echidna spustí fuzzingovou kampaň buď s funkcemi `f1`, `f2` a `f3` na černé listině, nebo voláním pouze těchto funkcí, podle +hodnoty booleovské proměnné `filterBlacklist`. + +## Jak testovat assert v Solidity pomocí Echidny {#how-to-test-soliditys-assert-with-echidna} + +V tomto krátkém návodu si ukážeme, jak používat Echidnu k testování kontroly tvrzení (assertions) v kontraktech. Předpokládejme, že máme kontrakt jako je tento: + +```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); + } +} +``` + +### Napsání tvrzení (assertion) {#write-an-assertion} + +Chceme se ujistit, že `tmp` je menší nebo rovno `counter` po vrácení jejich rozdílu. Mohli bychom napsat +vlastnost pro Echidnu, ale museli bychom hodnotu `tmp` někam uložit. Místo toho bychom mohli použít tvrzení (assertion) jako je toto: + +```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); + } +} +``` + +### Spuštění Echidny {#run-echidna-2} + +Chcete-li povolit testování selhání tvrzení (assertion), vytvořte [konfigurační soubor Echidny](https://github.com/crytic/echidna/wiki/Config) `config.yaml`: + +```yaml +checkAsserts: true +``` + +Když spustíme tento kontrakt v Echidně, získáme očekávané výsledky: + +```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 +``` + +Jak můžete vidět, Echidna hlásí selhání tvrzení (assertion) ve funkci `inc`. Přidání více než jednoho tvrzení (assertion) na funkci je možné, ale Echidna nedokáže říct, které tvrzení selhalo. + +### Kdy a jak používat tvrzení (assertions) {#when-and-how-use-assertions} + +Tvrzení (assertions) lze použít jako alternativu k explicitním vlastnostem, zejména pokud jsou podmínky ke kontrole přímo spojeny se správným použitím nějaké operace `f`. Přidání tvrzení (assertions) za nějaký kód vynutí, že kontrola proběhne okamžitě po jeho vykonání: + +```solidity +function f(..) public { + // nějaký složitý kód + ... + assert (condition); + ... +} + +``` + +Naopak, použití explicitní vlastnosti echidna bude náhodně provádět transakce a neexistuje snadný způsob, jak vynutit, kdy přesně bude zkontrolována. Stále je možné použít toto řešení: + +```solidity +function echidna_assert_after_f() public returns (bool) { + f(..); + return(condition); +} +``` + +Existují však některé problémy: + +- Selže, pokud je `f` deklarováno jako `internal` nebo `external`. +- Není jasné, které argumenty by se měly použít k volání `f`. +- Pokud se `f` vrátí, vlastnost selže. + +Obecně doporučujeme řídit se [doporučením Johna Regehra](https://blog.regehr.org/archives/1091) o tom, jak používat tvrzení (assertions): + +- Nevynucujte žádný vedlejší účinek během kontroly tvrzení. Například: `assert(ChangeStateAndReturn() == 1)` +- Netvrďte zjevné výroky. Například `assert(var >= 0)`, kde `var` je deklarováno jako `uint`. + +Nakonec, prosím, **nepoužívejte** `require` místo `assert`, protože Echidna to nebude schopna detekovat (ale kontrakt se stejně vrátí). + +### Shrnutí: Kontrola tvrzení (Assertion Checking) {#summary-assertion-checking} + +Následující text shrnuje spuštění Echidny na našem příkladu: + +```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 zjistila, že tvrzení v `inc` může selhat, pokud je tato funkce volána vícekrát s velkými argumenty. + +## Sběr a úprava korpusu Echidna {#collecting-and-modifying-an-echidna-corpus} + +Ukážeme si, jak sbírat a používat korpus transakcí s Echidnou. Cílem je následující chytrý kontrakt [`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; + } + +} +``` + +Tento malý příklad nutí Echidnu najít určité hodnoty pro změnu stavové proměnné. Pro fuzzer je to obtížné +(doporučuje se použít nástroj pro symbolické provádění, jako je [Manticore](https://github.com/trailofbits/manticore)). +Můžeme spustit Echidnu, abychom to ověřili: + +```bash +echidna-test magic.sol +... + +echidna_magic_values: passed! 🎉 + +Seed: 2221503356319272685 +``` + +Můžeme však stále používat Echidnu ke sběru korpusu při spouštění této fuzzingové kampaně. + +### Sběr korpusu {#collecting-a-corpus} + +Chcete-li povolit sběr korpusu, vytvořte adresář korpusu: + +```bash +mkdir corpus-magic +``` + +A [konfigurační soubor Echidny](https://github.com/crytic/echidna/wiki/Config) `config.yaml`: + +```yaml +coverage: true +corpusDir: "corpus-magic" +``` + +Nyní můžeme spustit náš nástroj a zkontrolovat shromážděný korpus: + +```bash +echidna-test magic.sol --config config.yaml +``` + +Echidna stále nemůže najít správné magické hodnoty, ale můžeme se podívat na korpus, který shromáždila. +Například jeden z těchto souborů byl: + +```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" + } +] +``` + +Je zřejmé, že tento vstup nespustí selhání v naší vlastnosti. V dalším kroku si však ukážeme, jak jej pro tento účel upravit. + +### Nasazení korpusu {#seeding-a-corpus} + +Echidna potřebuje pomoc, aby si poradila s funkcí `magic`. Zkopírujeme a upravíme vstup tak, aby používal vhodné +parametry: + +```bash +cp corpus/2712688662897926208.txt corpus/new.txt +``` + +Upravíme `new.txt` tak, aby volal `magic(42,129,333,0)`. Nyní můžeme Echidnu znovu spustit: + +```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 + +``` + +Tentokrát zjistila, že vlastnost je okamžitě narušena. + +## Hledání transakcí s vysokou spotřebou paliva {#finding-transactions-with-high-gas-consumption} + +Ukážeme si, jak s Echidnou najít transakce s vysokou spotřebou paliva. Cílem je následující chytrý kontrakt: + +```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; + } + +} +``` + +Zde může mít `expensive` velkou spotřebu paliva. + +V současné době Echidna vždy potřebuje vlastnost k testování: zde `echidna_test` vždy vrací `true`. +Můžeme spustit Echidnu, abychom to ověřili: + +``` +echidna-test gas.sol +... +echidna_test: passed! 🎉 + +Seed: 2320549945714142710 +``` + +### Měření spotřeby paliva {#measuring-gas-consumption} + +Chcete-li s Echidnou povolit spotřebu paliva, vytvořte konfigurační soubor `config.yaml`: + +```yaml +estimateGas: true +``` + +V tomto příkladu také zmenšíme velikost sekvence transakcí, aby byly výsledky snáze pochopitelné: + +```yaml +seqLen: 2 +estimateGas: true +``` + +### Spuštění Echidny {#run-echidna-3} + +Jakmile máme vytvořený konfigurační soubor, můžeme Echidnu spustit takto: + +```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 + +``` + +- Zobrazené palivo je odhad poskytnutý [HEVM](https://github.com/dapphub/dapptools/tree/master/src/hevm#hevm-). + +### Odfiltrování volání snižujících spotřebu paliva {#filtering-out-gas-reducing-calls} + +Výše uvedený návod **Filtrování funkcí pro volání během fuzzingové kampaně** ukazuje, jak +odebrat některé funkce z testování. +To může být zásadní pro získání přesného odhadu paliva. +Zvažte následující příklad: + +```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; + } +} +``` + +Pokud může Echidna volat všechny funkce, nenajde snadno transakce s vysokými náklady na palivo: + +``` +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 +``` + +Je to proto, že náklady závisí na velikosti `addrs` a náhodná volání mají tendenci ponechat pole téměř prázdné. +Vyloučení funkcí `pop` a `clear` nám však dává mnohem lepší výsledky: + +```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 +``` + +### Shrnutí: Hledání transakcí s vysokou spotřebou paliva {#summary-finding-transactions-with-high-gas-consumption} + +Echidna může najít transakce s vysokou spotřebou paliva pomocí konfigurační volby `estimateGas`: + +```yaml +estimateGas: true +``` + +```bash +echidna-test contract.sol --config config.yaml +... +``` + +Jakmile bude fuzzingová kampaň ukončena, Echidna nahlásí sekvenci s maximální spotřebou paliva pro každou funkci.