diff --git a/public/content/translations/cs/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md b/public/content/translations/cs/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md new file mode 100644 index 00000000000..77faef9a8d9 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md @@ -0,0 +1,526 @@ +--- +title: "Jak používat Manticore k vyhledávání chyb v chytrých kontraktech" +description: "Jak používat Manticore k automatickému vyhledávání chyb v chytrých kontraktech" +author: Trailofbits +lang: cs +tags: + [ + "solidity", + "smart kontrakt účty", + "bezpečnost", + "testování", + "formální verifikace" + ] +skill: advanced +published: 2020-01-13 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore +--- + +Cílem tohoto tutoriálu je ukázat, jak používat Manticore k automatickému vyhledávání chyb v chytrých kontraktech. + +## Instalace {#installation} + +Manticore vyžaduje python >= 3.6. Lze jej nainstalovat pomocí pip nebo s použitím dockeru. + +### Manticore přes docker {#manticore-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/trufflecon/ +``` + +### Manticore přes pip {#manticore-through-pip} + +```bash +pip3 install --user manticore +``` + +Doporučuje se solc 0.5.11. + +### Spuštění skriptu {#running-a-script} + +Pro spuštění pythonového skriptu v pythonu 3: + +```bash +python3 script.py +``` + +## Úvod do dynamického symbolického provádění {#introduction-to-dynamic-symbolic-execution} + +### Dynamické symbolické provádění v kostce {#dynamic-symbolic-execution-in-a-nutshell} + +Dynamické symbolické provádění (DSE) je technika analýzy programů, která prozkoumává stavový prostor s vysokou mírou sémantického povědomí. Tato technika je založena na objevování „cest programu“, které jsou reprezentovány jako matematické vzorce zvané `path predicates`. Koncepčně tato technika pracuje s predikáty cest ve dvou krocích: + +1. Jsou vytvořeny pomocí omezení na vstupu programu. +2. Používají se ke generování vstupů programu, které způsobí provedení příslušných cest. + +Tento přístup neprodukuje žádné falešně pozitivní výsledky v tom smyslu, že všechny identifikované stavy programu mohou být spuštěny během konkrétního provádění. Pokud například analýza najde celočíselné přetečení, je zaručeno, že bude reprodukovatelné. + +### Příklad predikátu cesty {#path-predicate-example} + +Pro lepší představu o tom, jak DSE funguje, zvažte následující příklad: + +```solidity +function f(uint a){ + + if (a == 65) { + // Je přítomna chyba + } + +} +``` + +Protože `f()` obsahuje dvě cesty, DSE vytvoří dva různé predikáty cest: + +- Cesta 1: `a == 65` +- Cesta 2: `Not (a == 65)` + +Každý predikát cesty je matematický vzorec, který lze předat takzvanému [řešiči SMT](https://wikipedia.org/wiki/Satisfiability_modulo_theories), který se pokusí rovnici vyřešit. Pro `Cestu 1` řešič řekne, že cestu lze prozkoumat s `a = 65`. Pro `Cestu 2` může řešič dát `a` jakoukoli jinou hodnotu než 65, například `a = 0`. + +### Ověřování vlastností {#verifying-properties} + +Manticore umožňuje plnou kontrolu nad veškerým prováděním každé cesty. V důsledku toho vám umožňuje přidat libovolná omezení téměř k čemukoli. Tato kontrola umožňuje vytváření vlastností kontraktu. + +Zvažte následující příklad: + +```solidity +function unsafe_add(uint a, uint b) returns(uint c){ + c = a + b; // žádná ochrana proti přetečení + return c; +} +``` + +Zde je ve funkci k prozkoumání pouze jedna cesta: + +- Cesta 1: `c = a + b` + +Pomocí Manticore můžete zkontrolovat přetečení a přidat omezení k predikátu cesty: + +- `c = a + b AND (c < a OR c < b)` + +Pokud je možné najít ohodnocení `a` a `b`, pro které je výše uvedený predikát cesty proveditelný, znamená to, že jste našli přetečení. Řešič může například vygenerovat vstup `a = 10, b = MAXUINT256`. + +Pokud zvážíte opravenou verzi: + +```solidity +function safe_add(uint a, uint b) returns(uint c){ + c = a + b; + require(c>=a); + require(c>=b); + return c; +} +``` + +Přidružený vzorec s kontrolou přetečení by byl: + +- `c = a + b AND (c >= a) AND (c=>b) AND (c < a OR c < b)` + +Tento vzorec nelze vyřešit; jinými slovy, je to **důkaz**, že v `safe_add` se `c` vždy zvýší. + +DSE je proto mocný nástroj, který dokáže ověřit libovolná omezení ve vašem kódu. + +## Spuštění pod Manticore {#running-under-manticore} + +Podíváme se, jak prozkoumat chytrý kontrakt s Manticore API. Cílem je následující chytrý kontrakt [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol): + +```solidity +pragma solidity >=0.4.24 <0.6.0; + +contract Simple { + function f(uint a) payable public{ + if (a == 65) { + revert(); + } + } +} +``` + +### Spustit samostatné prozkoumávání {#run-a-standalone-exploration} + +Manticore můžete spustit přímo na chytrém kontraktu pomocí následujícího příkazu (`project` může být soubor Solidity nebo adresář projektu): + +```bash +$ manticore project +``` + +Získáte výstup testovacích případů, jako je tento (pořadí se může změnit): + +``` +... +... m.c.manticore:INFO: Generated testcase No. 0 - STOP +... m.c.manticore:INFO: Generated testcase No. 1 - REVERT +... m.c.manticore:INFO: Generated testcase No. 2 - RETURN +... m.c.manticore:INFO: Generated testcase No. 3 - REVERT +... m.c.manticore:INFO: Generated testcase No. 4 - STOP +... m.c.manticore:INFO: Generated testcase No. 5 - REVERT +... m.c.manticore:INFO: Generated testcase No. 6 - REVERT +... m.c.manticore:INFO: Results in /home/ethsec/workshops/Automated Smart Contracts Audit - TruffleCon 2018/manticore/examples/mcore_t6vi6ij3 +... +``` + +Bez dalších informací bude Manticore prozkoumávat kontrakt s novými symbolickými +transakcemi, dokud neprozkoumá nové cesty v kontraktu. Manticore nespouští nové transakce po neúspěšné (např. po revertu). + +Manticore vypíše informace do adresáře `mcore_*`. V tomto adresáři mimo jiné najdete: + +- `global.summary`: pokrytí a varování kompilátoru +- `test_XXXXX.summary`: pokrytí, poslední instrukce, zůstatky na účtech pro každý testovací případ +- `test_XXXXX.tx`: podrobný seznam transakcí pro každý testovací případ + +Zde Manticore našel 7 testovacích případů, které odpovídají (pořadí souborů se může změnit): + +| | Transakce 0 | Transakce 1 | Transakce 2 | Výsledek | +| :-------------------------------------------------------: | :-----------------: | :------------------------: | -------------------------- | :------: | +| **test_00000000.tx** | Vytvoření kontraktu | f(!=65) | f(!=65) | STOP | +| **test_00000001.tx** | Vytvoření kontraktu | záložní funkce | | REVERT | +| **test_00000002.tx** | Vytvoření kontraktu | | | RETURN | +| **test_00000003.tx** | Vytvoření kontraktu | f(65) | | REVERT | +| **test_00000004.tx** | Vytvoření kontraktu | f(!=65) | | STOP | +| **test_00000005.tx** | Vytvoření kontraktu | f(!=65) | f(65) | REVERT | +| **test_00000006.tx** | Vytvoření kontraktu | f(!=65) | záložní funkce | REVERT | + +_Shrnutí prozkoumávání f(!=65) označuje volání f s jakoukoliv hodnotou jinou než 65._ + +Jak si můžete všimnout, Manticore generuje jedinečný testovací případ pro každou úspěšnou nebo vrácenou transakci. + +Použijte příznak `--quick-mode`, pokud chcete rychlé prozkoumání kódu (vypíná detektory chyb, výpočet gasu atd.) + +### Manipulace s chytrým kontraktem přes API {#manipulate-a-smart-contract-through-the-api} + +Tato část popisuje podrobnosti o tom, jak manipulovat s chytrým kontraktem prostřednictvím Python API Manticore. Můžete vytvořit nový soubor s příponou pythonu `*.py` a napsat potřebný kód přidáním příkazů API (jejichž základy budou popsány níže) do tohoto souboru a poté jej spustit příkazem `$ python3 *.py`. Níže uvedené příkazy můžete také provést přímo v konzoli pythonu. Konzoli spustíte příkazem `$ python3`. + +### Vytváření účtů {#creating-accounts} + +První věc, kterou byste měli udělat, je inicializovat nový blockchain pomocí následujících příkazů: + +```python +from manticore.ethereum import ManticoreEVM + +m = ManticoreEVM() +``` + +Účet, který není kontraktem, se vytváří pomocí [m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account): + +```python +user_account = m.create_account(balance=1000) +``` + +Kontrakt v Solidity lze nasadit pomocí [m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract): + +```solidity +source_code = ''' +pragma solidity >=0.4.24 <0.6.0; +contract Simple { + function f(uint a) payable public{ + if (a == 65) { + revert(); + } + } +} +''' +# Inicializace kontraktu +contract_account = m.solidity_create_contract(source_code, owner=user_account) +``` + +#### Shrnutí {#summary} + +- Uživatelské účty a účty kontraktů můžete vytvořit pomocí [m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account) a [m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract). + +### Provádění transakcí {#executing-transactions} + +Manticore podporuje dva typy transakcí: + +- Nezpracovaná transakce: prozkoumány jsou všechny funkce +- Pojmenovaná transakce: je prozkoumána pouze jedna funkce + +#### Nezpracovaná transakce {#raw-transaction} + +Nezpracovaná transakce se provádí pomocí [m.transaction](https://manticore.readthedocs.io/en/latest/evm.html?highlight=transaction#manticore.ethereum.ManticoreEVM.transaction): + +```python +m.transaction(caller=user_account, + address=contract_account, + data=data, + value=value) +``` + +Volající, adresa, data nebo hodnota transakce mohou být buď konkrétní, nebo symbolické: + +- [m.make_symbolic_value](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_value#manticore.ethereum.ManticoreEVM.make_symbolic_value) vytvoří symbolickou hodnotu. +- [m.make_symbolic_buffer(size)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_buffer#manticore.ethereum.ManticoreEVM.make_symbolic_buffer) vytvoří symbolické bajtové pole. + +Například: + +```python +symbolic_value = m.make_symbolic_value() +symbolic_data = m.make_symbolic_buffer(320) +m.transaction(caller=user_account, + address=contract_address, + data=symbolic_data, + value=symbolic_value) +``` + +Pokud jsou data symbolická, Manticore prozkoumá všechny funkce kontraktu během provádění transakce. Pro pochopení toho, jak funguje výběr funkcí, bude užitečné podívat se na vysvětlení záložní funkce v článku [Hands on the Ethernaut CTF](https://blog.trailofbits.com/2017/11/06/hands-on-the-ethernaut-ctf/). + +#### Pojmenovaná transakce {#named-transaction} + +Funkce lze spouštět prostřednictvím jejich názvu. +Pro spuštění `f(uint var)` se symbolickou hodnotou z user_account a s 0 ethery použijte: + +```python +symbolic_var = m.make_symbolic_value() +contract_account.f(symbolic_var, caller=user_account, value=0) +``` + +Pokud není `hodnota` transakce specifikována, je ve výchozím nastavení 0. + +#### Shrnutí {#summary-1} + +- Argumenty transakce mohou být konkrétní nebo symbolické +- Nezpracovaná transakce prozkoumá všechny funkce +- Funkci lze volat podle jejího jména + +### Pracovní prostor {#workspace} + +`m.workspace` je adresář používaný jako výstupní adresář pro všechny generované soubory: + +```python +print("Výsledky jsou v {}".format(m.workspace)) +``` + +### Ukončení prozkoumávání {#terminate-the-exploration} + +Pro zastavení prozkoumávání použijte [m.finalize()](https://manticore.readthedocs.io/en/latest/evm.html?highlight=finalize#manticore.ethereum.ManticoreEVM.finalize). Po zavolání této metody by se neměly odesílat žádné další transakce a Manticore vygeneruje testovací případy pro každou prozkoumanou cestu. + +### Shrnutí: Spuštění pod Manticore {#summary-running-under-manticore} + +Když spojíme všechny předchozí kroky, získáme: + +```python +from manticore.ethereum import ManticoreEVM + +m = ManticoreEVM() + +with open('example.sol') as f: + source_code = f.read() + +user_account = m.create_account(balance=1000) +contract_account = m.solidity_create_contract(source_code, owner=user_account) + +symbolic_var = m.make_symbolic_value() +contract_account.f(symbolic_var) + +print("Výsledky jsou v {}".format(m.workspace)) +m.finalize() # zastavení prozkoumávání +``` + +Veškerý výše uvedený kód najdete v [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) + +## Získání cest s výjimkami {#getting-throwing-paths} + +Nyní vygenerujeme specifické vstupy pro cesty, které vyvolávají výjimku v `f()`. Cílem je stále následující chytrý kontrakt [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol): + +```solidity +pragma solidity >=0.4.24 <0.6.0; +contract Simple { + function f(uint a) payable public{ + if (a == 65) { + revert(); + } + } +} +``` + +### Použití informací o stavu {#using-state-information} + +Každá provedená cesta má svůj stav blockchainu. Stav je buď připraven, nebo je ukončen, což znamená, že dosáhne instrukce THROW nebo REVERT: + +- [m.ready_states](https://manticore.readthedocs.io/en/latest/states.html#accessing): seznam stavů, které jsou připraveny (neprovedly REVERT/INVALID) +- [m.killed_states](https://manticore.readthedocs.io/en/latest/states.html#accessings): seznam stavů, které jsou ukončeny +- [m.all_states](https://manticore.readthedocs.io/en/latest/states.html#accessings): všechny stavy + +```python +for state in m.all_states: + # udělat něco se stavem +``` + +Můžete přistupovat k informacím o stavu. Například: + +- `state.platform.get_balance(account.address)`: zůstatek účtu +- `state.platform.transactions`: seznam transakcí +- `state.platform.transactions[-1].return_data`: data vrácená poslední transakcí + +Data vrácená poslední transakcí jsou pole, které lze převést na hodnotu pomocí ABI.deserialize, například: + +```python +data = state.platform.transactions[0].return_data +data = ABI.deserialize("uint", data) +``` + +### Jak generovat testovací případ {#how-to-generate-testcase} + +Pro generování testovacího případu použijte [m.generate_testcase(state, name)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=generate_testcase#manticore.ethereum.ManticoreEVM.generate_testcase): + +```python +m.generate_testcase(state, 'BugFound') +``` + +### Shrnutí {#summary-2} + +- Stav můžete iterovat pomocí m.all_states +- `state.platform.get_balance(account.address)` vrací zůstatek účtu +- `state.platform.transactions` vrací seznam transakcí +- `transaction.return_data` jsou vrácená data +- `m.generate_testcase(state, name)` generuje vstupy pro daný stav + +### Shrnutí: Získání cesty s výjimkou {#summary-getting-throwing-path} + +```python +from manticore.ethereum import ManticoreEVM + +m = ManticoreEVM() + +with open('example.sol') as f: + source_code = f.read() + +user_account = m.create_account(balance=1000) +contract_account = m.solidity_create_contract(source_code, owner=user_account) + +symbolic_var = m.make_symbolic_value() +contract_account.f(symbolic_var) + +## Zkontrolujte, zda provádění končí s REVERT nebo INVALID + +for state in m.terminated_states: + last_tx = state.platform.transactions[-1] + if last_tx.result in ['REVERT', 'INVALID']: + print('Nalezena výjimka {}'.format(m.workspace)) + m.generate_testcase(state, 'ThrowFound') +``` + +Veškerý výše uvedený kód naleznete v [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) + +_Poznámka: mohli jsme vygenerovat mnohem jednodušší skript, protože všechny stavy vrácené terminated_state mají ve svém výsledku REVERT nebo INVALID: tento příklad měl pouze demonstrovat, jak manipulovat s API._ + +## Přidávání omezení {#adding-constraints} + +Podíváme se, jak omezit prozkoumávání. Budeme vycházet z předpokladu, že +dokumentace funkce `f()` uvádí, že funkce se nikdy nevolá s `a == 65`, takže jakákoli chyba s `a == 65` není skutečnou chybou. Cílem je stále následující chytrý kontrakt [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol): + +```solidity +pragma solidity >=0.4.24 <0.6.0; +contract Simple { + function f(uint a) payable public{ + if (a == 65) { + revert(); + } + } +} +``` + +### Operátory {#operators} + +Modul [Operators](https://github.com/trailofbits/manticore/blob/master/manticore/core/smtlib/operators.py) usnadňuje manipulaci s omezeními, mimo jiné poskytuje: + +- Operators.AND, +- Operators.OR, +- Operators.UGT (větší než bez znaménka), +- Operators.UGE (větší nebo rovno bez znaménka), +- Operators.ULT (menší než bez znaménka), +- Operators.ULE (menší nebo rovno bez znaménka). + +Pro import modulu použijte následující příkaz: + +```python +from manticore.core.smtlib import Operators +``` + +`Operators.CONCAT` se používá ke zřetězení pole na hodnotu. Například return_data transakce musí být změněna na hodnotu, která se má porovnat s jinou hodnotou: + +```python +last_return = Operators.CONCAT(256, *last_return) +``` + +### Omezení {#state-constraint} + +Omezení můžete použít globálně nebo pro určitý stav. + +#### Globální omezení {#state-constraint} + +Použijte `m.constrain(constraint)` pro přidání globálního omezení. +Můžete například zavolat kontrakt ze symbolické adresy a omezit tuto adresu na konkrétní hodnoty: + +```python +symbolic_address = m.make_symbolic_value() +m.constraint(Operators.OR(symbolic == 0x41, symbolic_address == 0x42)) +m.transaction(caller=user_account, + address=contract_account, + data=m.make_symbolic_buffer(320), + value=0) +``` + +#### Omezení stavu {#state-constraint} + +Použijte [state.constrain(constraint)](https://manticore.readthedocs.io/en/latest/states.html?highlight=StateBase#manticore.core.state.StateBase.constrain) k přidání omezení ke konkrétnímu stavu. +Lze jej použít k omezení stavu po jeho prozkoumání a ke kontrole nějaké jeho vlastnosti. + +### Kontrola omezení {#checking-constraint} + +Použijte `solver.check(state.constraints)`, abyste zjistili, zda je omezení stále proveditelné. +Následující příklad například omezí symbolic_value tak, aby se lišila od 65, a zkontroluje, zda je stav stále proveditelný: + +```python +state.constrain(symbolic_var != 65) +if solver.check(state.constraints): + # stav je proveditelný +``` + +### Shrnutí: Přidání omezení {#summary-adding-constraints} + +Přidáním omezení k předchozímu kódu získáme: + +```python +from manticore.ethereum import ManticoreEVM +from manticore.core.smtlib.solver import Z3Solver + +solver = Z3Solver.instance() + +m = ManticoreEVM() + +with open("example.sol") as f: + source_code = f.read() + +user_account = m.create_account(balance=1000) +contract_account = m.solidity_create_contract(source_code, owner=user_account) + +symbolic_var = m.make_symbolic_value() +contract_account.f(symbolic_var) + +no_bug_found = True + +## Zkontrolujte, zda provádění končí s REVERT nebo INVALID + +for state in m.terminated_states: + last_tx = state.platform.transactions[-1] + if last_tx.result in ['REVERT', 'INVALID']: + # nebereme v úvahu cestu, kde a == 65 + condition = symbolic_var != 65 + if m.generate_testcase(state, name="BugFound", only_if=condition): + print(f'Byla nalezena chyba, výsledky jsou v {m.workspace}') + no_bug_found = False + +if no_bug_found: + print(f'Nebyla nalezena žádná chyba') +``` + +Veškerý výše uvedený kód naleznete v [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) diff --git a/public/content/translations/cs/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md b/public/content/translations/cs/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md new file mode 100644 index 00000000000..6686a67ce21 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md @@ -0,0 +1,239 @@ +--- +title: "Jak používat Slither k hledání chyb ve smart kontraktech" +description: "Jak používat Slither k automatickému hledání chyb ve smart kontraktech" +author: Trailofbits +lang: cs +tags: + [ + "solidity", + "smart kontrakt účty", + "bezpečnost", + "testování" + ] +skill: advanced +published: 2020-06-09 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither +--- + +## Jak používat Slither {#how-to-use-slither} + +Cílem tohoto tutoriálu je ukázat, jak používat Slither k automatickému hledání chyb ve smart kontraktech. + +- [Instalace](#installation) +- [Použití příkazového řádku](#command-line) +- [Úvod do statické analýzy](#static-analysis): Stručný úvod do statické analýzy +- [API](#api-basics): Popis Python API + +## Instalace {#installation} + +Slither vyžaduje Python >= 3.6. Lze jej nainstalovat pomocí pip nebo s použitím dockeru. + +Slither přes pip: + +```bash +pip3 install --user slither-analyzer +``` + +Slither přes docker: + +```bash +docker pull trailofbits/eth-security-toolbox +docker run -it -v "$PWD":/home/trufflecon 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/trufflecon/ +``` + +### Spuštění skriptu {#running-a-script} + +Pro spuštění pythonového skriptu v pythonu 3: + +```bash +python3 script.py +``` + +### Příkazový řádek {#command-line} + +**Příkazový řádek versus uživatelsky definované skripty.** Slither je dodáván se sadou předdefinovaných detektorů, které nacházejí mnoho běžných chyb. Zavolání Slitheru z příkazového řádku spustí všechny detektory, není potřeba žádná podrobná znalost statické analýzy: + +```bash +slither project_paths +``` + +Kromě detektorů má Slither také možnosti revize kódu prostřednictvím svých [výpisů](https://github.com/crytic/slither#printers) a [nástrojů](https://github.com/crytic/slither#tools). + +Použijte [crytic.io](https://github.com/crytic) pro získání přístupu k soukromým detektorům a integraci s GitHub. + +## Statická analýza {#static-analysis} + +Schopnosti a design frameworku pro statickou analýzu Slither byly popsány v blogových příspěvcích ([1](https://blog.trailofbits.com/2018/10/19/slither-a-solidity-static-analysis-framework/), [2](https://blog.trailofbits.com/2019/05/27/slither-the-leading-static-analyzer-for-smart-contracts/)) a v [akademickém článku](https://github.com/trailofbits/publications/blob/master/papers/wetseb19.pdf). + +Statická analýza existuje v různých variantách. S největší pravděpodobností si uvědomujete, že kompilátory jako [clang](https://clang-analyzer.llvm.org/) a [gcc](https://lwn.net/Articles/806099/) závisí na těchto výzkumných technikách, ale jsou také základem pro ([Infer](https://fbinfer.com/), [CodeClimate](https://codeclimate.com/), [FindBugs](http://findbugs.sourceforge.net/) a nástroje založené na formálních metodách, jako jsou [Frama-C](https://frama-c.com/) a [Polyspace](https://www.mathworks.com/products/polyspace.html). + +Nebudeme zde vyčerpávajícím způsobem procházet techniky statické analýzy a výzkum. Místo toho se zaměříme na to, co je potřeba k pochopení fungování Slitheru, abyste jej mohli efektivněji používat k hledání chyb a porozumění kódu. + +- [Reprezentace kódu](#code-representation) +- [Analýza kódu](#analysis) +- [Mezilehlá reprezentace](#intermediate-representation) + +### Reprezentace kódu {#code-representation} + +Na rozdíl od dynamické analýzy, která uvažuje o jedné cestě spuštění, statická analýza uvažuje o všech cestách najednou. K tomu se spoléhá na jinou reprezentaci kódu. Dvě nejběžnější jsou abstraktní syntaktický strom (AST) a graf řízení toku (CFG). + +### Abstraktní syntaktické stromy (AST) {#abstract-syntax-trees-ast} + +AST se používají pokaždé, když kompilátor parsuje kód. Je to pravděpodobně nejzákladnější struktura, na které lze provádět statickou analýzu. + +V kostce, AST je strukturovaný strom, kde obvykle každý list obsahuje proměnnou nebo konstantu a vnitřní uzly jsou operandy nebo operace řízení toku. Zvažte následující kód: + +```solidity +function safeAdd(uint a, uint b) pure internal returns(uint){ + if(a + b <= a){ + revert(); + } + return a + b; +} +``` + +Odpovídající AST je zobrazen v: + +![AST](./ast.png) + +Slither používá AST exportovaný kompilátorem solc. + +I když je AST jednoduché sestavit, jedná se o vnořenou strukturu. Někdy to není nejjednodušší analyzovat. Například pro identifikaci operací použitých ve výrazu `a + b <= a` musíte nejprve analyzovat `<=` a pak `+`. Běžným přístupem je použití takzvaného vzoru návštěvník, který rekurzivně prochází stromem. Slither obsahuje obecného návštěvníka v [`ExpressionVisitor`](https://github.com/crytic/slither/blob/master/slither/visitors/expression/expression.py). + +Následující kód používá `ExpressionVisitor` k detekci, zda výraz obsahuje sčítání: + +```python +from slither.visitors.expression.expression import ExpressionVisitor +from slither.core.expressions.binary_operation import BinaryOperationType + +class HasAddition(ExpressionVisitor): + + def result(self): + return self._result + + def _post_binary_operation(self, expression): + if expression.type == BinaryOperationType.ADDITION: + self._result = True + +visitor = HasAddition(expression) # expression je výraz, který se má testovat +print(f'Výraz {expression} obsahuje sčítání: {visitor.result()}') +``` + +### Graf řízení toku (CFG) {#control-flow-graph-cfg} + +Druhou nejběžnější reprezentací kódu je graf řízení toku (CFG). Jak název napovídá, jedná se o grafovou reprezentaci, která odhaluje všechny cesty spuštění. Každý uzel obsahuje jednu nebo více instrukcí. Hrany v grafu představují operace řízení toku (if/then/else, smyčka atd.). CFG našeho předchozího příkladu je: + +![CFG](./cfg.png) + +CFG je reprezentace, na které je postavena většina analýz. + +Existuje mnoho dalších reprezentací kódu. Každá reprezentace má výhody a nevýhody v závislosti na analýze, kterou chcete provést. + +### Analýza {#analysis} + +Nejjednodušším typem analýz, které můžete se Slitherem provádět, jsou syntaktické analýzy. + +### Syntaktická analýza {#syntax-analysis} + +Slither může procházet různými komponenty kódu a jejich reprezentací, aby našel nekonzistence a nedostatky pomocí přístupu podobného porovnávání vzorů. + +Například následující detektory hledají problémy související se syntaxí: + +- [Stínování stavové proměnné](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing): iteruje přes všechny stavové proměnné a kontroluje, zda některá nestíní proměnnou ze zděděného kontraktu ([state.py#L51-L62](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/shadowing/state.py#L51-L62)) + +- [Nesprávné rozhraní ERC20](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface): hledá nesprávné podpisy funkcí ERC20 ([incorrect_erc20_interface.py#L34-L55](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/erc/incorrect_erc20_interface.py#L34-L55)) + +### Sémantická analýza {#semantic-analysis} + +Na rozdíl od syntaktické analýzy jde sémantická analýza hlouběji a analyzuje „význam“ kódu. Tato rodina zahrnuje několik širokých typů analýz. Vedou k výkonnějším a užitečnějším výsledkům, ale jsou také složitější na psaní. + +Sémantické analýzy se používají pro nejpokročilejší detekce zranitelností. + +#### Analýza závislosti dat {#fixed-point-computation} + +O proměnné `variable_a` se říká, že je datově závislá na `variable_b`, pokud existuje cesta, na které je hodnota `variable_a` ovlivněna `variable_b`. + +V následujícím kódu je `variable_a` závislá na `variable_b`: + +```solidity +// ... +variable_a = variable_b + 1; +``` + +Slither je dodáván s vestavěnými schopnostmi [datové závislosti](https://github.com/crytic/slither/wiki/data-dependency), díky své mezilehlé reprezentaci (diskutované v pozdější sekci). + +Příklad použití datové závislosti lze nalézt v [detektoru nebezpečné striktní rovnosti](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities). Zde Slither bude hledat porovnání striktní rovnosti s nebezpečnou hodnotou ([incorrect_strict_equality.py#L86-L87](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L86-L87)), a informuje uživatele, že by měl použít `>=` nebo `<=` místo `==`, aby zabránil útočníkovi uvěznit kontrakt. Mimo jiné bude detektor považovat za nebezpečnou návratovou hodnotu volání `balanceOf(address)` ([incorrect_strict_equality.py#L63-L64](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L63-L64)) a použije engine datové závislosti ke sledování jejího použití. + +#### Výpočet pevného bodu {#fixed-point-computation} + +Pokud vaše analýza prochází CFG a sleduje hrany, je pravděpodobné, že uvidíte již navštívené uzly. Například, pokud je smyčka představena, jak je uvedeno níže: + +```solidity +for(uint i; i < range; ++){ + variable_a += 1 +} +``` + +Vaše analýza bude muset vědět, kdy se zastavit. Existují zde dvě hlavní strategie: (1) iterovat na každém uzlu konečný počet krát, (2) vypočítat takzvaný _pevný bod_. Pevný bod v podstatě znamená, že analýza tohoto uzlu již neposkytuje žádné smysluplné informace. + +Příklad použití pevného bodu lze nalézt v detektorech reentrancy: Slither prozkoumává uzly a hledá externí volání, zápisy do úložiště a čtení z něj. Jakmile dosáhne pevného bodu ([reentrancy.py#L125-L131](https://github.com/crytic/slither/blob/master/slither/detectors/reentrancy/reentrancy.py#L125-L131)), zastaví průzkum a analyzuje výsledky, aby zjistil, zda je přítomna reentrancy, a to prostřednictvím různých vzorců reentrancy ([reentrancy_benign.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_benign.py), [reentrancy_read_before_write.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_read_before_write.py), [reentrancy_eth.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_eth.py)). + +Psaní analýz využívajících efektivní výpočet pevného bodu vyžaduje dobré porozumění tomu, jak analýza šíří své informace. + +### Mezilehlá reprezentace {#intermediate-representation} + +Mezilehlá reprezentace (IR) je jazyk, který má být pro statickou analýzu vhodnější než ten původní. Slither překládá Solidity do své vlastní IR: [SlithIR](https://github.com/crytic/slither/wiki/SlithIR). + +Porozumění SlithIR není nutné, pokud chcete psát pouze základní kontroly. Bude se však hodit, pokud plánujete psát pokročilé sémantické analýzy. [Výpisy](https://github.com/crytic/slither/wiki/Printer-documentation#slithir) SlithIR a [SSA](https://github.com/crytic/slither/wiki/Printer-documentation#slithir-ssa) vám pomohou pochopit, jak je kód přeložen. + +## Základy API {#api-basics} + +Slither má API, které vám umožňuje prozkoumat základní atributy kontraktu a jeho funkcí. + +Pro načtení kódové báze: + +```python +from slither import Slither +slither = Slither('/path/to/project') + +``` + +### Prozkoumávání kontraktů a funkcí {#exploring-contracts-and-functions} + +Objekt `Slither` má: + +- `contracts (list(Contract)`: seznam kontraktů +- `contracts_derived (list(Contract)`: seznam kontraktů, které nejsou zděděny jiným kontraktem (podmnožina kontraktů) +- `get_contract_from_name (str)`: Vrátí kontrakt podle jeho jména + +Objekt `Contract` má: + +- `name (str)`: Jméno kontraktu +- `functions (list(Function))`: Seznam funkcí +- `modifiers (list(Modifier))`: Seznam funkcí +- `all_functions_called (list(Function/Modifier))`: Seznam všech interních funkcí dosažitelných kontraktem +- `inheritance (list(Contract))`: Seznam zděděných kontraktů +- `get_function_from_signature (str)`: Vrátí funkci podle jejího podpisu +- `get_modifier_from_signature (str)`: Vrátí modifikátor podle jeho podpisu +- `get_state_variable_from_name (str)`: Vrátí stavovou proměnnou podle jejího jména + +Objekt `Function` nebo `Modifier` má: + +- `name (str)`: Jméno funkce +- `contract (contract)`: kontrakt, kde je funkce deklarována +- `nodes (list(Node))`: Seznam uzlů tvořících CFG funkce/modifikátoru +- `entry_point (Node)`: Vstupní bod CFG +- `variables_read (list(Variable))`: Seznam přečtených proměnných +- `variables_written (list(Variable))`: Seznam zapsaných proměnných +- `state_variables_read (list(StateVariable))`: Seznam přečtených stavových proměnných (podmnožina proměnných `read`) +- `state_variables_written (list(StateVariable))`: Seznam zapsaných stavových proměnných (podmnožina proměnných `written`) diff --git a/public/content/translations/cs/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md b/public/content/translations/cs/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md new file mode 100644 index 00000000000..3bc65e0dbd5 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md @@ -0,0 +1,81 @@ +--- +title: "Jak nastavit Tellor jako vaše orákulum" +description: "Průvodce, jak začít s integrací orákula Tellor do vašeho protokolu" +author: "Tellor" +lang: cs +tags: [ "solidity", "smart kontrakt účty", "orákula" ] +skill: beginner +published: 2021-06-29 +source: Tellor Docs +sourceUrl: https://docs.tellor.io/tellor/ +--- + +Rychlý kvíz: Váš protokol je téměř hotový, ale potřebuje orákulum, aby získal přístup k offchainovým datům... Co uděláte? + +## Doporučené předpoklady {#soft-prerequisites} + +Cílem tohoto příspěvku je, aby byl přístup ke zdroji dat z orákula co nejjednodušší a nejsrozumitelnější. Nicméně předpokládáme následující úroveň vašich programátorských dovedností, abychom se mohli soustředit na aspekt orákula. + +Předpoklady: + +- umíte se orientovat v terminálu +- máte nainstalovaný npm +- víte, jak používat npm ke správě závislostí + +Tellor je funkční open-source orákulum připravené k implementaci. Tento průvodce pro začátečníky je zde, aby ukázal, jak snadno lze Tellor zprovoznit a poskytnout tak vašemu projektu plně decentralizované orákulum odolné vůči cenzuře. + +## Přehled {#overview} + +Tellor je systém orákul, kde strany mohou požadovat hodnotu offchainového datového bodu (např. BTC/USD) a reportéři soutěží o přidání této hodnoty do onchainové datové banky, která je přístupná všem chytrým kontraktům na Ethereu. Vstupy do této datové banky jsou zabezpečeny sítí reportérů, kteří stakují. Tellor využívá kryptoekonomické motivační mechanismy, odměňuje poctivé reportéry za předkládání dat a trestá podvodníky prostřednictvím vydávání tokenu Telloru, Tributes (TRB), a mechanismu pro řešení sporů. + +V tomto tutoriálu si projdeme: + +- Nastavení počáteční sady nástrojů, kterou budete potřebovat pro zprovoznění. +- Projdeme si jednoduchý příklad. +- Vypíšeme si adresy testnetů, na kterých můžete v současné době Tellor testovat. + +## UsingTellor {#usingtellor} + +První věc, kterou budete chtít udělat, je nainstalovat základní nástroje potřebné k použití Telloru jako vašeho orákula. Použijte [tento balíček](https://github.com/tellor-io/usingtellor) k instalaci uživatelských kontraktů Tellor: + +`npm install usingtellor` + +Po instalaci to umožní vašim kontraktům dědit funkce z kontraktu ‚UsingTellor‘. + +Skvělé! Nyní, když máte připravené nástroje, projděme si jednoduché cvičení, ve kterém získáme cenu bitcoinu: + +### Příklad BTC/USD {#btcusd-example} + +Děděte kontrakt UsingTellor a předejte adresu Telloru jako argument konstruktoru: + +Toto je příklad: + +```solidity +import "usingtellor/contracts/UsingTellor.sol"; + +contract PriceContract is UsingTellor { + uint256 public btcPrice; + + //Tento kontrakt má nyní přístup ke všem funkcím v UsingTellor + +constructor(address payable _tellorAddress) UsingTellor(_tellorAddress) public {} + +function setBtcPrice() public { + bytes memory _b = abi.encode("SpotPrice",abi.encode("btc","usd")); + bytes32 _queryId = keccak256(_b); + + uint256 _timestamp; + bytes _value; + + (_value, _timestamp) = getDataBefore(_queryId, block.timestamp - 15 minutes); + + btcPrice = abi.decode(_value,(uint256)); + } +} +``` + +Úplný seznam adres kontraktů naleznete [zde](https://docs.tellor.io/tellor/the-basics/contracts-reference). + +Pro usnadnění použití je repozitář UsingTellor dodáván s verzí kontraktu [Tellor Playground](https://github.com/tellor-io/TellorPlayground) pro snazší integraci. Seznam užitečných funkcí naleznete [zde](https://github.com/tellor-io/sampleUsingTellor#tellor-playground). + +Pro robustnější implementaci orákula Tellor se podívejte na úplný seznam dostupných funkcí [zde](https://github.com/tellor-io/usingtellor/blob/master/README.md). diff --git a/public/content/translations/cs/developers/tutorials/how-to-view-nft-in-metamask/index.md b/public/content/translations/cs/developers/tutorials/how-to-view-nft-in-metamask/index.md new file mode 100644 index 00000000000..e16b0455822 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/how-to-view-nft-in-metamask/index.md @@ -0,0 +1,33 @@ +--- +title: "Jak si zobrazit své NFT ve své peněžence (3. část ze 3 v sérii návodů o NFT)" +description: "Tento návod popisuje, jak si zobrazit stávající NFT v peněžence MetaMask!" +author: "Sumi Mudgil" +tags: [ "ERC-721", "Alchemy", "Solidity" ] +skill: beginner +lang: cs +published: 2021-04-22 +--- + +Tento návod je 3. část ze 3 v sérii návodů o NFT, ve které si zobrazíme své nově vyražené NFT. Tento obecný návod však můžete použít pro jakýkoli token ERC-721 s použitím peněženky MetaMask, a to jak na hlavní síti, tak na jakékoli testovací síti. Pokud se chcete naučit, jak si vyrazit vlastní NFT na Ethereu, podívejte se na [1. část o tom, jak napsat a nasadit chytrý kontrakt pro NFT](/developers/tutorials/how-to-write-and-deploy-an-nft)! + +Výborně! Dostali jste se k nejkratší a nejjednodušší části naší série návodů o NFT – jak si zobrazit své čerstvě vyražené NFT ve virtuální peněžence. V tomto příkladu budeme používat MetaMask, protože jsme ho používali i v předchozích dvou částech. + +Jako předpoklad byste měli mít již nainstalovanou mobilní aplikaci MetaMask, a ta by měla obsahovat účet, na který jste si své NFT vyrazili – aplikaci si můžete zdarma stáhnout pro [iOS](https://apps.apple.com/us/app/metamask-blockchain-wallet/id1438144202) nebo [Android](https://play.google.com/store/apps/details?id=io.metamask&hl=en_US&gl=US). + +## Krok 1: Nastavte síť na Sepolia {#set-network-to-sepolia} + +V horní části aplikace stiskněte tlačítko „Peněženka“, poté budete vyzváni k výběru sítě. Protože naše NFT bylo vyraženo na síti Sepolia, budete chtít jako síť vybrat Sepolia. + +![Jak nastavit Sepolia jako síť v mobilní aplikaci MetaMask](./goerliMetamask.gif) + +## Krok 2: Přidejte svůj sběratelský předmět do MetaMask {#add-nft-to-metamask} + +Jakmile budete na síti Sepolia, vyberte vpravo kartu „Sběratelské předměty“ a přidejte adresu chytrého kontraktu NFT a ID tokenu ERC-721 vašeho NFT – které byste měli najít na Etherscanu na základě hashe transakce z vašeho NFT nasazeného v II. části našeho návodu. + +![Jak najít hash transakce a ID tokenu ERC-721](./findNFTEtherscan.png) + +Možná budete muset několikrát obnovit stránku, abyste své NFT viděli – ale bude tam ! + +![Jak nahrát své NFT do MetaMask](./findNFTMetamask.gif) + +Výborně! Úspěšně jste vyrazili NFT a nyní si ho můžete prohlédnout! Nemůžeme se dočkat, až uvidíme, jak vezmete svět NFT útokem! diff --git a/public/content/translations/cs/developers/tutorials/how-to-write-and-deploy-an-nft/index.md b/public/content/translations/cs/developers/tutorials/how-to-write-and-deploy-an-nft/index.md new file mode 100644 index 00000000000..a6be7ea78e5 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/how-to-write-and-deploy-an-nft/index.md @@ -0,0 +1,382 @@ +--- +title: "Jak napsat a nasadit NFT (část 1/3 ze série výukových programů o NFT)" +description: "Tento výukový program je první částí série o NFT, která vás krok za krokem provede tím, jak napsat a nasadit chytrý kontrakt nefunkčního tokenu (token ERC-721) pomocí Etherea a systému souborů Inter Planetary File System (IPFS)." +author: "Sumi Mudgil" +tags: [ "ERC-721", "Alchemy", "Solidity", "chytré kontrakty" ] +skill: beginner +lang: cs +published: 2021-04-22 +--- + +Jelikož NFT přibližují blockchain široké veřejnosti, je to skvělá příležitost, abyste pochopili ten humbuk kolem a sami si publikovali svůj vlastní NFT kontrakt (token ERC-721) na ethereovém blockchainu! + +Společnost Alchemy je nesmírně hrdá na to, že pohání největší jména v oblasti NFT, včetně Makersplace (nedávno stanovila rekord v prodeji digitálních uměleckých děl v aukční síni Christie's za 69 milionů dolarů), Dapper Labs (tvůrci NBA Top Shot a Crypto Kitties), OpenSea (největší světové NFT tržiště), Zora, Super Rare, NFTfi, Foundation, Enjin, Origin Protocol, Immutable a dalších. + +V tomto výukovém programu si projdeme vytvoření a nasazení chytrého kontraktu ERC-721 v testovací síti Sepolia pomocí [MetaMask](https://metamask.io/), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/), [Pinata](https://pinata.cloud/) a [Alchemy](https://alchemy.com/signup/eth) (nebojte se, pokud zatím nechápete, co z toho co znamená – všechno vám vysvětlíme!). + +Ve 2. části tohoto výukového programu si projdeme, jak můžeme pomocí našeho chytrého kontraktu razit NFT, a ve 3. části si vysvětlíme, jak si své NFT zobrazit v MetaMask. + +A samozřejmě, pokud budete mít v kterémkoli bodě dotazy, neváhejte se ozvat na [Alchemy Discord](https://discord.gg/gWuC7zB) nebo navštivte [dokumentaci NFT API od Alchemy](https://docs.alchemy.com/alchemy/enhanced-apis/nft-api)! + +## Krok 1: Připojte se k síti Ethereum {#connect-to-ethereum} + +Existuje spousta způsobů, jak posílat požadavky na ethereový blockchain, ale abychom si to zjednodušili, použijeme bezplatný účet na [Alchemy](https://alchemy.com/signup/eth), což je blockchainová vývojářská platforma a API, která nám umožňuje komunikovat s řetězcem Etherea, aniž bychom museli provozovat vlastní uzly. + +V tomto výukovém programu také využijeme vývojářské nástroje společnosti Alchemy pro monitorování a analýzu, abychom pochopili, co se děje „pod pokličkou“ při nasazování našeho chytrého kontraktu. Pokud ještě nemáte účet Alchemy, můžete se zdarma zaregistrovat [zde](https://alchemy.com/signup/eth). + +## Krok 2: Vytvořte si aplikaci (a klíč API) {#make-api-key} + +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 se chcete o testovacích sítích dozvědět více, podívejte se na [tuto příručku](https://docs.alchemyapi.io/guides/choosing-a-network). + +1. Přejděte na stránku „Create App“ ve svém ovládacím panelu Alchemy tak, že v navigační liště najedete na „Apps“ a kliknete na „Create App“ + +![Vytvořte si svou aplikaci](./create-your-app.png) + +2. Pojmenujte svou aplikaci (my jsme zvolili „Moje první NFT!“), nabídněte krátký popis, pro řetězec vyberte „Ethereum“ a pro síť zvolte „Sepolia“. Od Sloučení jsou ostatní testovací sítě zastaralé. + +![Nakonfigurujte a publikujte svou aplikaci](./alchemy-explorer-sepolia.png) + +3. Klikněte na „Create app“ a to je vše! Vaše aplikace by se měla objevit v tabulce níže. + +## Krok 3: Vytvořte si ethereový účet (adresu) {#create-eth-address} + +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. 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. + +Úč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, se ujistěte, že jste se vpravo nahoře přepnuli na „Sepolia Test Network“ (abychom nepracovali se skutečnými penězi). + +![Nastavte Sepolia jako vaši síť](./metamask-goerli.png) + +## Krok 4: Přidejte ether z Faucetu {#step-4-add-ether-from-a-faucet} + +Abychom mohli náš chytrý kontrakt nasadit do testovací sítě, budeme potřebovat nějaké falešné ETH. Pro získání ETH můžete jít na [Sepolia Faucet](https://sepoliafaucet.com/) hostovaný společností Alchemy, přihlásit se, zadat adresu svého účtu a kliknout na „Send Me ETH“. Krátce poté byste měli vidět ETH ve svém účtu MetaMask! + +## Krok 5: Zkontrolujte svůj zůstatek {#check-balance} + +Abychom si dvakrát zkontrolovali, že tam náš zůstatek je, udělejme požadavek [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) pomocí [nástroje pro sestavování 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 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": "0xde0b6b3a7640000"} + ``` + ```` + +> **Poznámka** Tento výsledek je ve wei, ne v ETH. Wei se používá jako nejmenší denominace etheru. Převod z wei na ETH je 1 eth = 1018 wei. Pokud tedy převedeme 0xde0b6b3a7640000 na desetinné číslo, dostaneme 1\*1018 wei, což se rovná 1 ETH. + +Uf! Naše falešné peníze jsou všechny tam. + +## Krok 6: Inicializujte náš projekt {#initialize-project} + +Nejprve budeme muset vytvořit složku pro náš projekt. Přejděte na příkazový řádek a zadejte: + + ``` + mkdir my-nft + cd my-nft + ``` + +Nyní, když jsme uvnitř složky našeho projektu, použijeme k inicializaci projektu npm init. 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 potřebovat také [Node.js](https://nodejs.org/en/download/), 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 informaci, jak jsme to udělali my: + +```json + název balíčku: (my-nft) + verze: (1.0.0) + popis: Moje první NFT! + vstupní bod: (index.js) + testovací příkaz: + repozitář git: + klíčová slova: + autor: + licence: (ISC) + Chystáte se zapsat do /Users/thesuperb1/Desktop/my-nft/package.json: + + { + "name": "my-nft", + "version": "1.0.0", + "description": "Moje první NFT!", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" + } +``` + +Schvalte soubor package.json a můžeme pokračovat! + +## Krok 7: Instalace [Hardhat](https://hardhat.org/getting-started/#overview) {#install-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 my-nft 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 {#create-hardhat-project} + +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 dělat? … + Vytvořit vzorový projekt + ❯ Vytvořit prázdný hardhat.config.js + Ukončit + ``` + +Tím se nám vygeneruje soubor hardhat.config.js, kde v kroku 13 specifikujeme všechna nastavení našeho projektu. + +## Krok 9: Přidání složek projektu {#add-project-folders} + +Abychom měli v projektu pořádek, vytvoříme si 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, kde budeme uchovávat kód našeho NFT chytrého kontraktu + +- scripts/ je místo, kde budeme uchovávat skripty pro nasazení a interakci s naším chytrým kontraktem + +## Krok 10: Napište náš kontrakt {#write-contract} + +Nyní, když máme nastavené prostředí, přejděme k zajímavějším věcem: _psaní kódu našeho chytrého kontraktu!_ + +Otevřete projekt my-nft 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 MyNFT.sol.‌ + +1. Přejděte do složky `contracts` a vytvořte nový soubor s názvem MyNFT.sol + +2. Níže je kód našeho NFT chytrého kontraktu, který jsme založili na implementaci ERC-721 z knihovny [OpenZeppelin](https://docs.openzeppelin.com/contracts/3.x/erc721). Zkopírujte a vložte níže uvedený obsah do souboru MyNFT.sol. + + ```solidity + //Kontrakt založený na [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721) + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.0; + + import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; + import "@openzeppelin/contracts/utils/Counters.sol"; + import "@openzeppelin/contracts/access/Ownable.sol"; + import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; + + contract MyNFT is ERC721URIStorage, Ownable { + using Counters for Counters.Counter; + Counters.Counter private _tokenIds; + + constructor() ERC721("MyNFT", "NFT") {} + + function mintNFT(address recipient, string memory tokenURI) + public onlyOwner + returns (uint256) + { + _tokenIds.increment(); + + uint256 newItemId = _tokenIds.current(); + _mint(recipient, newItemId); + _setTokenURI(newItemId, tokenURI); + + return newItemId; + } + } + ``` + +3. Protože dědíme třídy z knihovny kontraktů OpenZeppelin, spusťte v příkazovém řádku `npm install @openzeppelin/contracts^4.0.0`, abyste knihovnu nainstalovali do naší složky. + +Co přesně tedy tento kód _dělá_? Pojďme si to rozebrat, řádek po řádku. + +V horní části našeho chytrého kontraktu importujeme tři třídy chytrých kontraktů [OpenZeppelin](https://openzeppelin.com/): + +- @openzeppelin/contracts/token/ERC721/ERC721.sol obsahuje implementaci standardu ERC-721, kterou bude náš NFT chytrý kontrakt dědit. (Aby byl chytrý kontrakt platným NFT, musí implementovat všechny metody standardu ERC-721.) Chcete-li se dozvědět více o zděděných funkcích ERC-721, podívejte se na definici rozhraní [zde](https://eips.ethereum.org/EIPS/eip-721). + +- @openzeppelin/contracts/utils/Counters.sol poskytuje čítače, které lze zvýšit nebo snížit pouze o jedna. Náš chytrý kontrakt používá čítač ke sledování celkového počtu ražených NFT a k nastavení jedinečného ID na našem novém NFT. (Každému NFT raženému pomocí chytrého kontraktu musí být přiděleno jedinečné ID – zde je naše jedinečné ID určeno pouze celkovým počtem existujících NFT. Například první NFT, které razíme s naším chytrým kontraktem, má ID „1“, naše druhé NFT má ID „2“ atd.) + +- @openzeppelin/contracts/access/Ownable.sol nastavuje [řízení přístupu](https://docs.openzeppelin.com/contracts/3.x/access-control) k našemu chytrému kontraktu, takže NFT může razit pouze vlastník chytrého kontraktu (vy). (Poznámka: Zahrnutí řízení přístupu je zcela na preferencích. Pokud chcete, aby kdokoli mohl razit NFT pomocí vašeho chytrého kontraktu, odstraňte slovo Ownable na řádku 10 a onlyOwner na řádku 17.) + +Po našich příkazech pro import máme náš vlastní NFT chytrý kontrakt, který je překvapivě krátký – obsahuje pouze čítač, konstruktor a jednu funkci! Je to díky našim zděděným kontraktům OpenZeppelin, které implementují většinu metod, které potřebujeme k vytvoření NFT, jako je `ownerOf`, která vrací vlastníka NFT, a `transferFrom`, která převádí vlastnictví NFT z jednoho účtu na druhý. + +V našem konstruktoru ERC-721 si všimnete, že předáváme 2 řetězce, „MyNFT“ a „NFT“. První proměnná je název chytrého kontraktu a druhá je jeho symbol. Každou z těchto proměnných si můžete pojmenovat, jak chcete! + +Nakonec máme naši funkci `mintNFT(address recipient, string memory tokenURI)`, která nám umožňuje razit NFT! Všimnete si, že tato funkce přebírá dvě proměnné: + +- `address recipient` určuje adresu, která obdrží vaše čerstvě ražené NFT + +- `string memory tokenURI` je řetězec, který by se měl přeložit na dokument JSON, který popisuje metadata NFT. Metadata NFT jsou to, co ho skutečně oživuje a umožňuje mu mít konfigurovatelné vlastnosti, jako je název, popis, obrázek a další atributy. Ve 2. části tohoto výukového programu si popíšeme, jak tato metadata nakonfigurovat. + +`mintNFT` volá některé metody ze zděděné knihovny ERC-721 a nakonec vrací číslo, které představuje ID čerstvě raženého NFT. + +## Krok 11: Připojte MetaMask a Alchemy ke svému projektu {#connect-metamask-and-alchemy} + +Nyní, když jsme vytvořili peněženku MetaMask, účet Alchemy a napsali náš chytrý kontrakt, je čas tyto tři věci 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. + +- Postupujte podle [těchto pokynů](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key) pro export vašeho privátního klíče z MetaMask + +- Níže se podívejte, jak získat URL API HTTP od Alchemy, a zkopírujte si ji do schránky + +![Zkopírujte URL vašeho API od Alchemy](./copy-alchemy-api-url.gif) + +Váš soubor `.env` by nyní měl vypadat takto: + + ``` + API_URL="https://eth-sepolia.g.alchemy.com/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 {#install-ethers} + +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 budeme také vyžadovat ethers v našem souboru hardhat.config.js. + +## Krok 13: Aktualizace souboru hardhat.config.js {#update-hardhat-config} + +Zatím jsme přidali několik závislostí a pluginů, nyní musíme aktualizovat hardhat.config.js, aby o nich všech náš projekt věděl. + +Aktualizujte svůj soubor hardhat.config.js tak, aby vypadal takto: + +```js + /** + * @type import('hardhat/config').HardhatUserConfig + */ + require('dotenv').config(); + require("@nomiclabs/hardhat-ethers"); + const { API_URL, PRIVATE_KEY } = process.env; + module.exports = { + solidity: "0.8.1", + defaultNetwork: "sepolia", + networks: { + hardhat: {}, + sepolia: { + url: API_URL, + accounts: [`0x${PRIVATE_KEY}`] + } + }, + } +``` + +## Krok 14: Zkompilujte náš kontrakt {#compile-contract} + +Abychom se ujistili, že zatím vše funguje, zkompilujeme si náš kontrakt. Úkol kompilace je jedním z vestavěných úkolů Hardhat. + +Z příkazového řádku spusťte: + + ``` + npx hardhat compile + ``` + +Můžete dostat varování o tom, že v zdrojovém souboru není uveden identifikátor licence SPDX, ale s tím si nemusíte dělat starosti – 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: Napište náš skript pro nasazení {#write-deploy} + +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/` a vytvořte nový soubor s názvem `deploy.js` a přidejte do něj následující obsah: + +```js +async function main() { + const MyNFT = await ethers.getContractFactory("MyNFT") + + // Spusťte nasazení, vrátí se promise, která se vyřeší na objekt kontraktu + const myNFT = await MyNFT.deploy() + await myNFT.deployed() + console.log("Contract deployed to address:", myNFT.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 MyNFT = await ethers.getContractFactory("MyNFT"); + ``` + +ContractFactory v ethers.js je abstrakce používaná k nasazení nových chytrých kontraktů, takže MyNFT je zde továrnou pro instance našeho NFT kontraktu. 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 myNFT = await MyNFT.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: Nasaďte náš kontrakt {#deploy-contract} + +Konečně jsme připraveni nasadit náš chytrý kontrakt! Vraťte se do kořenového adresáře projektu a v příkazovém řádku spusťte: + + ``` + npx hardhat --network sepolia run scripts/deploy.js + ``` + +Měli byste pak vidět něco takového: + + ``` + Contract deployed to address: 0x4C5266cCc4b3F426965d2f51b6D910325a0E7650 + ``` + +Pokud přejdeme na [Sepolia etherscan](https://sepolia.etherscan.io/) a vyhledáme adresu našeho kontraktu, měli bychom vidět, že byl úspěšně nasazen. Pokud ji nevidíte okamžitě, chvíli prosím počkejte, protože to může nějakou dobu trvat. Transakce bude vypadat nějak takto: + +![Zobrazení adresy transakce na Etherscanu](./etherscan-sepoila-contract-creation.png) + +Adresa „Od“ by měla odpovídat adrese vašeho účtu MetaMask a adresa „Pro“ bude říkat „Contract Creation“. Pokud klikneme na transakci, uvidíme adresu našeho kontraktu v poli „Pro“: + +![Zobrazte adresu svého kontraktu na Etherscanu](./etherscan-sepolia-tx-details.png) + +Paráda! Právě jste nasadili svůj NFT chytrý kontrakt na řetězec Ethereum (testnet)! + +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 „MyNFT“. + +![Zobrazení volání provedených „pod pokličkou“ pomocí ovládacího panelu Explorer od Alchemy](./alchemy-explorer-goerli.png) + +Zde uvidíte hrstku volání JSON-RPC, která pro nás Hardhat/Ethers provedly „pod pokličkou“, když jsme volali funkci .deploy(). Dvě důležité, které je třeba zde zmínit, jsou [eth_sendRawTransaction](/developers/docs/apis/json-rpc/#eth_sendrawtransaction), což je požadavek na skutečný zápis našeho chytrého kontraktu na řetězec Sepolia, a [eth_getTransactionByHash](/developers/docs/apis/json-rpc/#eth_gettransactionbyhash), což je požadavek na přečtení informací o naší transakci na základě haše (typický vzorec při odesílání transakcí). Chcete-li se dozvědět více o odesílání transakcí, podívejte se na tento výukový program o [odesílání transakcí pomocí Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/). + +To je vše k 1. části tohoto výukového programu. V [části 2 budeme skutečně interagovat s naším chytrým kontraktem ražbou NFT](/developers/tutorials/how-to-mint-an-nft/) a v [části 3 si ukážeme, jak si své NFT zobrazit ve své ethereové peněžence](/developers/tutorials/how-to-view-nft-in-metamask/)! diff --git a/public/content/translations/cs/developers/tutorials/interact-with-other-contracts-from-solidity/index.md b/public/content/translations/cs/developers/tutorials/interact-with-other-contracts-from-solidity/index.md new file mode 100644 index 00000000000..487d5121e85 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/interact-with-other-contracts-from-solidity/index.md @@ -0,0 +1,179 @@ +--- +title: "Interakce s dalšími kontrakty ze Solidity" +description: "Jak nasadit chytrý kontrakt z existujícího kontraktu a interagovat s ním" +author: "jdourlens" +tags: + [ + "smart kontrakt účty", + "solidity", + "remix", + "nasazování", + "složitelnost" + ] +skill: advanced +lang: cs +published: 2020-04-05 +source: EthereumDev +sourceUrl: https://ethereumdev.io/interact-with-other-contracts-from-solidity/ +address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +--- + +V předchozích návodech jsme se toho hodně naučili [jak nasadit svůj první chytrý kontrakt](/developers/tutorials/deploying-your-first-smart-contract/) a přidat do něj některé funkce, jako je [řízení přístupu pomocí modifikátorů](https://ethereumdev.io/organize-your-code-and-control-access-to-your-smart-contract-with-modifiers/) nebo [zpracování chyb v Solidity](https://ethereumdev.io/handle-errors-in-solidity-with-require-and-revert/). V tomto návodu se naučíme, jak nasadit chytrý kontrakt z existujícího kontraktu a jak s ním interagovat. + +Vytvoříme kontrakt, který komukoli umožní mít svůj vlastní `Counter` chytrý kontrakt tím, že pro něj vytvoříme továrnu (factory). Její název bude `CounterFactory`. Nejdříve si ukážeme kód našeho původního `Counter` chytrého kontraktu: + +```solidity +pragma solidity 0.5.17; + +contract Counter { + + uint256 private _count; + address private _owner; + address private _factory; + + + modifier onlyOwner(address caller) { + require(caller == _owner, "You're not the owner of the contract"); + _; + } + + modifier onlyFactory() { + require(msg.sender == _factory, "You need to use the factory"); + _; + } + + constructor(address owner) public { + _owner = owner; + _factory = msg.sender; + } + + function getCount() public view returns (uint256) { + return _count; + } + + function increment(address caller) public onlyFactory onlyOwner(caller) { + _count++; + } + +} +``` + +Všimni si, že jsme mírně upravili kód kontraktu, abychom mohli sledovat adresu továrny a adresu vlastníka kontraktu. Když voláš kód kontraktu z jiného kontraktu, msg.sender bude odkazovat na adresu naší továrny na kontrakty. Toto je **opravdu důležitý bod k pochopení**, protože použití kontraktu k interakci s jinými kontrakty je běžnou praxí. Proto by sis měl v komplexních případech dávat pozor, kdo je odesílatel. + +Z tohoto důvodu jsme také přidali modifikátor `onlyFactory`, který zajišťuje, že funkci měnící stav může volat pouze továrna, která předá původního volajícího jako parametr. + +Do naší nové `CounterFactory`, která bude spravovat všechny ostatní čítače (Counters), přidáme mapování, které přiřadí vlastníka k adrese jeho kontraktu čítače: + +```solidity +mapping(address => Counter) _counters; +``` + +V Ethereu je mapování ekvivalentem objektů v javascriptu, umožňují mapovat klíč typu A na hodnotu typu B. V tomto případě mapujeme adresu vlastníka s instancí jeho Čítače (Counter). + +Vytvoření instance nového čítače (Counter) pro někoho bude vypadat takto: + +```solidity + function createCounter() public { + require (_counters[msg.sender] == Counter(0)); + _counters[msg.sender] = new Counter(msg.sender); + } +``` + +Nejdříve zkontrolujeme, zda daná osoba již nějaký čítač (counter) vlastní. Pokud čítač nevlastní, vytvoříme instanci nového čítače předáním jeho adresy do konstruktoru `Counter` a přiřadíme nově vytvořenou instanci do mapování. + +Získání počtu pro konkrétní čítač (Counter) bude vypadat takto: + +```solidity +function getCount(address account) public view returns (uint256) { + require (_counters[account] != Counter(0)); + return (_counters[account].getCount()); +} + +function getMyCount() public view returns (uint256) { + return (getCount(msg.sender)); +} +``` + +První funkce zkontroluje, zda kontrakt Čítače (Counter) existuje pro danou adresu, a poté zavolá metodu `getCount` z instance. Druhá funkce: `getMyCount` je jen zkratka pro přímé předání msg.sender do funkce `getCount`. + +Funkce `increment` je poměrně podobná, ale předává původního odesílatele transakce do kontraktu `Counter`: + +```solidity +function increment() public { + require (_counters[msg.sender] != Counter(0)); + Counter(_counters[msg.sender]).increment(msg.sender); + } +``` + +Všimni si, že pokud se bude volat příliš často, může se náš čítač stát obětí přetečení (overflow). Pro ochranu před tímto možným případem bys měl co nejvíce používat [knihovnu SafeMath](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/). + +Pro nasazení našeho kontraktu budeš muset poskytnout kód jak pro `CounterFactory`, tak pro `Counter`. Při nasazování například v Remixu budeš muset vybrat CounterFactory. + +Zde je celý kód: + +```solidity +pragma solidity 0.5.17; + +contract Counter { + + uint256 private _count; + address private _owner; + address private _factory; + + + modifier onlyOwner(address caller) { + require(caller == _owner, "You're not the owner of the contract"); + _; + } + + modifier onlyFactory() { + require(msg.sender == _factory, "You need to use the factory"); + _; + } + + constructor(address owner) public { + _owner = owner; + _factory = msg.sender; + } + + function getCount() public view returns (uint256) { + return _count; + } + + function increment(address caller) public onlyFactory onlyOwner(caller) { + _count++; + } + +} + +contract CounterFactory { + + mapping(address => Counter) _counters; + + function createCounter() public { + require (_counters[msg.sender] == Counter(0)); + _counters[msg.sender] = new Counter(msg.sender); + } + + function increment() public { + require (_counters[msg.sender] != Counter(0)); + Counter(_counters[msg.sender]).increment(msg.sender); + } + + function getCount(address account) public view returns (uint256) { + require (_counters[account] != Counter(0)); + return (_counters[account].getCount()); + } + + function getMyCount() public view returns (uint256) { + return (getCount(msg.sender)); + } + +} +``` + +Po zkompilování v sekci pro nasazení v Remixu vybereš továrnu (factory), která se má nasadit: + +![Výběr továrny (factory) k nasazení v Remixu](./counterfactory-deploy.png) + +Poté si můžeš hrát s továrnou na kontrakty a sledovat, jak se mění hodnota. Pokud bys chtěl volat chytrý kontrakt z jiné adresy, budeš muset změnit adresu ve výběru účtu (Account) v Remixu. diff --git a/public/content/translations/cs/developers/tutorials/ipfs-decentralized-ui/index.md b/public/content/translations/cs/developers/tutorials/ipfs-decentralized-ui/index.md new file mode 100644 index 00000000000..9d69479bc7b --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/ipfs-decentralized-ui/index.md @@ -0,0 +1,73 @@ +--- +title: "IPFS pro decentralizovaná uživatelská rozhraní" +description: "Tento návod čtenáře naučí, jak pomocí IPFS uložit uživatelské rozhraní pro dapp. I když jsou data a obchodní logika aplikace decentralizované, bez uživatelského rozhraní odolného proti cenzuře by k němu uživatelé mohli stejně ztratit přístup." +author: Ori Pomerantz +tags: [ "ipfs" ] +skill: beginner +lang: cs +published: 2024-06-29 +--- + +Napsali jste neuvěřitelnou novou dapp. Dokonce jste pro ni napsali [uživatelské rozhraní](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/). Ale teď se bojíte, že se ho někdo pokusí cenzurovat tím, že shodí vaše uživatelské rozhraní, což je jen jeden server v cloudu. V tomto návodu se naučíte, jak se vyhnout cenzuře tím, že své uživatelské rozhraní umístíte na **[Interplanetární souborový systém (IPFS)](https://ipfs.tech/developers/)**, aby si ho každý zájemce mohl připnout na server pro budoucí přístup. + +Veškerou práci můžete udělat pomocí služby třetí strany, jako je [Fleek](https://resources.fleek.xyz/docs/). Tento návod je určen pro lidi, kteří chtějí udělat dost pro to, aby pochopili, co dělají, i když je to více práce. + +## Začínáme lokálně {#getting-started-locally} + +Existuje několik [poskytovatelů IPFS třetích stran](https://docs.ipfs.tech/how-to/work-with-pinning-services/#use-a-third-party-pinning-service), ale pro testování je nejlepší začít se spuštěním IPFS lokálně. + +1. Nainstalujte si [uživatelské rozhraní IPFS](https://docs.ipfs.tech/install/ipfs-desktop/#install-instructions). + +2. Vytvořte si adresář s webovou stránkou. Pokud používáte [Vite](https://vite.dev/), použijte tento příkaz: + + ```sh + pnpm vite build + ``` + +3. V aplikaci IPFS Desktop klikněte na **Importovat > Složka** a vyberte adresář, který jste vytvořili v předchozím kroku. + +4. Vyberte právě nahranou složku a klikněte na **Přejmenovat**. Dejte jí smysluplnější název. + +5. Znovu ji vyberte a klikněte na **Sdílet odkaz**. Zkopírujte URL adresu do schránky. Odkaz bude podobný tomuto: `https://ipfs.io/ipfs/QmaCuQ7yN6iyBjLmLGe8YiFuCwnePoKfVu6ue8vLBsLJQJ`. + +6. Klikněte na **Stav**. Rozbalte kartu **Pokročilé**, abyste viděli adresu brány. Například v mém systému je adresa `http://127.0.0.1:8080`. + +7. Zkombinujte cestu z kroku s odkazem s adresou brány, abyste našli svou adresu. Například pro výše uvedený příklad je URL `http://127.0.0.1:8080/ipfs/QmaCuQ7yN6iyBjLmLGe8YiFuCwnePoKfVu6ue8vLBsLJQJ`. Otevřete tuto URL adresu v prohlížeči a podívejte se na své stránky. + +## Nahrávání {#uploading} + +Takže teď můžete používat IPFS k lokálnímu poskytování souborů, což není příliš vzrušující. Dalším krokem je zpřístupnit je světu, když jste offline. + +Existuje řada známých [služeb pro připínání](https://docs.ipfs.tech/concepts/persistence/#pinning-services). Vyberte si jednu z nich. Ať už používáte jakoukoli službu, musíte si vytvořit účet a poskytnout jí **identifikátor obsahu (CID)** ve vaší aplikaci IPFS Desktop. + +Osobně jsem zjistil, že nejjednodušší je používat [4EVERLAND](https://docs.4everland.org/storage/4ever-pin/guides). Zde jsou pokyny: + +1. Přejděte na [nástěnku](https://dashboard.4everland.org/overview) a přihlaste se pomocí své peněženky. + +2. V levém postranním panelu klikněte na **Úložiště > 4EVER Pin**. + +3. Klikněte na **Nahrát > Vybrané CID**. Pojmenujte svůj obsah a zadejte CID z aplikace IPFS Desktop. V současné době je CID řetězec, který začíná na `Qm`, za nímž následuje 44 písmen a číslic, které představují [kódovaný](https://medium.com/bootdotdev/base64-vs-base58-encoding-c25553ff4524) haš v base58, například `QmaCuQ7yN6iyBjLmLGe8YiFuCwnePoKfVu6ue8vLBsLJQJ`, ale [to se pravděpodobně změní](https://docs.ipfs.tech/concepts/content-addressing/#version-1-v1). + +4. Počáteční stav je **Ve frontě**. Načítejte znovu, dokud se nezmění na **Připnuto**. + +5. Kliknutím na své CID získáte odkaz. Mou aplikaci si můžete prohlédnout [zde](https://bafybeifqka2odrne5b6l5guthqvbxu4pujko2i6rx2zslvr3qxs6u5o7im/). + +6. Možná budete muset aktivovat svůj účet, aby byl připnutý déle než měsíc. Aktivace účtu stojí asi 1 dolar. Pokud jste ji zavřeli, odhlaste se a znovu se přihlaste, abyste byli znovu požádáni o aktivaci. + +## Používání z IPFS {#using-from-ipfs} + +V tomto okamžiku máte odkaz na centralizovanou bránu, která poskytuje váš obsah z IPFS. Stručně řečeno, vaše uživatelské rozhraní může být o něco bezpečnější, ale stále není odolné vůči cenzuře. Pro skutečnou odolnost proti cenzuře musí uživatelé používat IPFS [přímo z prohlížeče](https://docs.ipfs.tech/install/ipfs-companion/#prerequisites). + +Jakmile máte toto nainstalováno (a funguje desktopová aplikace IPFS), můžete přejít na [/ipfs/``](https://any.site/ipfs/bafybeifqka2odrne5b6l5guthqvbxu4pujko2i6rx2zslvr3qxs6u5o7im) na jakékoli stránce a získáte tento obsah, poskytovaný decentralizovaným způsobem. + +## Nevýhody {#drawbacks} + +Soubory IPFS nelze spolehlivě mazat, takže dokud upravujete své uživatelské rozhraní, je pravděpodobně nejlepší jej buď nechat centralizovaný, nebo použít [Interplanetární jmenný systém (IPNS)](https://docs.ipfs.tech/concepts/ipns/#mutability-in-ipfs), systém, který poskytuje proměnlivost nad IPFS. Samozřejmě, vše, co je proměnlivé, může být cenzurováno, v případě IPNS nátlakem na osobu s privátním klíčem, kterému odpovídá. + +Některé balíčky mají navíc s IPFS problém, takže pokud je vaše webová stránka velmi složitá, nemusí to být dobré řešení. A samozřejmě cokoli, co se spoléhá na integraci serveru, nelze decentralizovat jen tím, že klientská strana bude na IPFS. + +## Závěr {#conclusion} + +Stejně jako vám Ethereum umožňuje decentralizovat databázové a obchodní logické aspekty vaší dapp, IPFS vám umožňuje decentralizovat uživatelské rozhraní. To vám umožní uzavřít další vektor útoku proti vaší dapp. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). diff --git a/public/content/translations/cs/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md b/public/content/translations/cs/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md new file mode 100644 index 00000000000..1629ab7b6e4 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md @@ -0,0 +1,111 @@ +--- +title: "Nastartujte vývoj frontendu vaší dapp pomocí create-eth-app" +description: "Přehled použití create-eth-app a jeho funkcí" +author: "Markus Waas" +tags: + [ + "frontend", + "javascript", + "ethers.js", + "the graph", + "defi" + ] +skill: beginner +lang: cs +published: 2020-04-27 +source: soliditydeveloper.com +sourceUrl: https://soliditydeveloper.com/create-eth-app +--- + +Minule jsme se podívali na [celkový obraz Solidity](https://soliditydeveloper.com/solidity-overview-2020) a již zmínili [create-eth-app](https://github.com/PaulRBerg/create-eth-app). Nyní se dozvíte, jak jej používat, jaké funkce jsou integrovány a jaké jsou další nápady na jeho rozšíření. Tato aplikace, kterou založil Paul Razvan Berg, zakladatel [Sablier](http://sablier.com/), nastartuje váš vývoj frontendu a přináší několik volitelných integrací, ze kterých si můžete vybrat. + +## Instalace {#installation} + +Instalace vyžaduje Yarn 0.25 nebo vyšší (`npm install yarn --global`). Je to tak jednoduché jako spuštění: + +```bash +yarn create eth-app my-eth-app +cd my-eth-app +yarn react-app:start +``` + +Pod kapotou používá [create-react-app](https://github.com/facebook/create-react-app). Chcete-li zobrazit svou aplikaci, otevřete `http://localhost:3000/`. Až budete připraveni k nasazení do produkčního prostředí, vytvořte minifikovaný balíček pomocí yarn build. Jedním ze snadných způsobů, jak to hostovat, je [Netlify](https://www.netlify.com/). Můžete si vytvořit repozitář na GitHubu, přidat ho do Netlify, nastavit příkaz pro sestavení a máte hotovo! Vaše aplikace bude hostovaná a použitelná pro všechny. A to vše zdarma. + +## Vlastnosti {#features} + +### React a create-react-app {#react--create-react-app} + +Především srdce aplikace: React a všechny další funkce, které přináší _create-react-app_. Použití pouze tohoto je skvělá volba, pokud nechcete integrovat Ethereum. Samotný [React](https://react.dev/) velmi usnadňuje vytváření interaktivních uživatelských rozhraní. Možná není tak přívětivý pro začátečníky jako [Vue](https://vuejs.org/), ale stále je nejpoužívanější, má více funkcí a hlavně tisíce dalších knihoven, ze kterých si můžete vybrat. _create-react-app_ také velmi usnadňuje začátek a zahrnuje: + +- Podporu syntaxe React, JSX, ES6, TypeScript a Flow. +- Jazyková rozšíření nad rámec ES6, jako je operátor rozšiřování objektů (object spread operator). +- Automaticky prefixované CSS, takže nepotřebujete -webkit- ani jiné prefixy. +- Rychlý interaktivní spouštěč jednotkových testů s vestavěnou podporou pro reportování pokrytí. +- Živý vývojový server, který varuje před běžnými chybami. +- Sestavovací skript pro sbalení JS, CSS a obrázků pro produkci, s haši a sourcemapy. + +Konkrétně _create-eth-app_ využívá nové [hooks effects](https://legacy.reactjs.org/docs/hooks-effect.html). Metoda pro psaní výkonných, ale velmi malých takzvaných funkcionálních komponent. Jak se používají v _create-eth-app_ se dozvíte níže v sekci o Apollo. + +### Yarn Workspaces {#yarn-workspaces} + +[Yarn Workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) vám umožňují mít více balíčků, ale spravovat je všechny z kořenového adresáře a instalovat závislosti pro všechny najednou pomocí `yarn install`. To dává smysl zejména pro menší doplňkové balíčky, jako je správa adres/ABI chytrých kontraktů (informace o tom, kde jste nasadili které chytré kontrakty a jak s nimi komunikovat) nebo integrace The Graph, které jsou obě součástí `create-eth-app`. + +### ethers.js {#ethersjs} + +Zatímco [Web3](https://docs.web3js.org/) se stále používá nejvíce, [ethers.js](https://docs.ethers.io/) se v posledním roce stává stále populárnější alternativou a je integrován do _create-eth-app_. Můžete s ním pracovat, změnit ho na Web3 nebo zvážit upgrade na [ethers.js v5](https://docs.ethers.org/v5/), který je téměř venku z beta verze. + +### The Graph {#the-graph} + +[GraphQL](https://graphql.org/) je alternativní způsob práce s daty ve srovnání s [Restful API](https://restfulapi.net/). Mají několik výhod oproti Restful API, zejména pro decentralizovaná blockchainová data. Pokud vás zajímají důvody, podívejte se na [GraphQL Will Power the Decentralized Web](https://medium.com/graphprotocol/graphql-will-power-the-decentralized-web-d7443a69c69a). + +Obvykle byste získávali data přímo z vašeho chytrého kontraktu. Chcete si přečíst čas posledního obchodu? Stačí zavolat `MyContract.methods.latestTradeTime().call()`, které získá data z uzlu Ethereum do vaší dapp. Ale co když potřebujete stovky různých datových bodů? To by vedlo ke stovkám načítání dat z uzlu, přičemž každé by vyžadovalo [RTT](https://wikipedia.org/wiki/Round-trip_delay_time), což by vaši dapp zpomalilo a zneefektivnilo. Jedním z řešení může být funkce pro hromadné načítání (fetcher call) uvnitř vašeho kontraktu, která vrací více dat najednou. Ne vždy je to ale ideální. + +A pak by vás mohla zajímat i historická data. Chcete znát nejen čas posledního obchodu, ale časy všech obchodů, které jste kdy sami uskutečnili. Použijte balíček subgraphu _create-eth-app_, přečtěte si [dokumentaci](https://thegraph.com/docs/en/subgraphs/developing/creating/starting-your-subgraph) a přizpůsobte jej svým vlastním kontraktům. Pokud hledáte oblíbené chytré kontrakty, může pro ně již existovat subgraph. Podívejte se na [průzkumníka subgraphů](https://thegraph.com/explorer/). + +Jakmile máte subgraph, můžete ve své dapp napsat jeden jednoduchý dotaz, který načte všechna důležitá blockchainová data, která potřebujete, včetně těch historických, a to pouze jedním načtením. + +### Apollo {#apollo} + +Díky integraci [Apollo Boost](https://www.apollographql.com/docs/react/get-started/) můžete snadno integrovat The Graph do své React dapp. Zejména při použití [React hooks and Apollo](https://www.apollographql.com/blog/apollo-client-now-with-react-hooks) je načítání dat tak jednoduché jako napsání jediného dotazu GraphQL ve vaší komponentě: + +```js +const { loading, error, data } = useQuery(myGraphQlQuery) + +React.useEffect(() => { + if (!loading && !error && data) { + console.log({ data }) + } +}, [loading, error, data]) +``` + +## Šablony {#templates} + +Navíc si můžete vybrat z několika různých šablon. Zatím můžete použít integraci Aave, Compound, UniSwap nebo Sablier. Všechny přidávají důležité adresy servisních chytrých kontraktů spolu s předpřipravenými integracemi subgraphů. Stačí přidat šablonu do příkazu pro vytvoření, například `yarn create eth-app my-eth-app --with-template aave`. + +### Aave {#aave} + +[Aave](https://aave.com/) je decentralizovaný trh s půjčováním peněz. Vkladatelé poskytují trhu likviditu, aby získali pasivní příjem, zatímco dlužníci si mohou půjčovat pomocí kolaterálu. Jednou z jedinečných funkcí Aave jsou [rychlé půjčky (flash loans)](https://aave.com/docs/developers/flash-loans), které vám umožní půjčit si peníze bez jakéhokoli kolaterálu, pokud půjčku vrátíte v rámci jedné transakce. To může být užitečné například pro získání dodatečné hotovosti při arbitrážním obchodování. + +Obchodované tokeny, které vám vydělávají úroky, se nazývají _aTokens_. + +Když se rozhodnete integrovat Aave s _create-eth-app_, získáte [integraci subgraphu](https://docs.aave.com/developers/getting-started/using-graphql). Aave používá The Graph a již vám poskytuje několik subgraphů připravených k použití na [Ropstenu](https://thegraph.com/explorer/subgraph/aave/protocol-ropsten) a [mainnetu](https://thegraph.com/explorer/subgraph/aave/protocol) v [surové](https://thegraph.com/explorer/subgraph/aave/protocol-raw) nebo [formátované](https://thegraph.com/explorer/subgraph/aave/protocol) podobě. + +![Mem o rychlé půjčce Aave – "Jo, kdybych si mohl nechat rychlou půjčku déle než 1 transakci, bylo by to skvělé"](./flashloan-meme.png) + +### Compound {#compound} + +[Compound](https://compound.finance/) je podobný Aave. Integrace již zahrnuje nový [Compound v2 Subgraph](https://medium.com/graphprotocol/https-medium-com-graphprotocol-compound-v2-subgraph-highlight-a5f38f094195). Tokeny, které zde vydělávají úroky, se překvapivě nazývají _cTokens_. + +### Uniswap {#uniswap} + +[Uniswap](https://uniswap.exchange/) je decentralizovaná burza (DEX). Poskytovatelé likvidity mohou vydělávat poplatky poskytováním požadovaných tokenů nebo etheru pro obě strany obchodu. Je široce používána a proto má jednu z nejvyšších likvidit pro velmi širokou škálu tokenů. Můžete ji snadno integrovat do své dapp, abyste například umožnili uživatelům směnit jejich ETH za DAI. + +Bohužel v době psaní tohoto článku je integrace pouze pro Uniswap v1, a nikoli pro [právě vydanou v2](https://uniswap.org/blog/uniswap-v2/). + +### Sablier {#sablier} + +[Sablier](https://sablier.com/) umožňuje uživatelům streamování plateb. Místo jediné výplaty dostáváte peníze neustále bez další administrace po úvodním nastavení. Integrace zahrnuje [vlastní subgraph](https://thegraph.com/explorer/subgraph/sablierhq/sablier). + +## Co dál? {#whats-next} + +Pokud máte dotazy ohledně _create-eth-app_, přejděte na [komunitní server Sablier](https://discord.gg/bsS8T47), kde se můžete spojit s autory _create-eth-app_. Jako jedny z prvních dalších kroků můžete chtít integrovat UI framework jako [Material UI](https://mui.com/material-ui/), napsat GraphQL dotazy pro data, která skutečně potřebujete, a nastavit nasazení. diff --git a/public/content/translations/cs/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md b/public/content/translations/cs/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md new file mode 100644 index 00000000000..11177bfe302 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md @@ -0,0 +1,269 @@ +--- +title: "Naučte se základní témata Etherea pomocí SQL" +description: "Tento tutoriál pomáhá čtenářům pochopit základní koncepty Etherea, včetně transakcí, bloků a paliva, a to pomocí dotazování na onchain data pomocí jazyka SQL (Structured Query Language)." +author: "Paul Apivat" +tags: [ "SQL", "Dotazování", "Transakce" ] +skill: beginner +lang: cs +published: 2021-05-11 +source: paulapivat.com +sourceUrl: https://paulapivat.com/post/query_ethereum/ +--- + +Mnoho tutoriálů o Ethereu se zaměřuje na vývojáře, ale chybí vzdělávací zdroje pro datové analytiky nebo pro lidi, kteří chtějí vidět onchain data bez spuštění klienta nebo uzlu. + +Tento tutoriál pomáhá čtenářům pochopit základní koncepty Etherea, včetně transakcí, bloků a paliva, a to pomocí dotazování na onchain data pomocí jazyka SQL (structured query language) přes rozhraní poskytované [Dune Analytics](https://dune.com/). + +Onchain data nám může pomoci pochopit Ethereum, síť a jako ekonomiku pro výpočetní výkon a mělo by sloužit jako základ pro pochopení výzev, kterým Ethereum dnes čelí (tj. rostoucí ceny paliva) a co je důležitější, diskusí o řešeních škálování. + +### Transakce {#transactions} + +Cesta uživatele v síti Ethereum začíná inicializací účtu ovládaného uživatelem nebo entity se zůstatkem v ETH. Existují dva typy účtů – ovládané uživatelem nebo chytrým kontraktem (viz [ethereum.org](/developers/docs/accounts/)). + +Jakýkoli účet lze zobrazit v prohlížeči bloků, jako je [Etherscan](https://etherscan.io/) nebo [Blockscout](https://eth.blockscout.com/). Prohlížeče bloků jsou portálem k datům Etherea. Zobrazují v reálném čase data o blocích, transakcích, těžařích, účtech a dalších onchain aktivitách (viz [zde](/developers/docs/data-and-analytics/block-explorers/)). + +Uživatel si však může přát dotazovat se na data přímo, aby si ověřil informace poskytované externími prohlížeči bloků. [Dune Analytics](https://dune.com/) poskytuje tuto možnost každému, kdo má alespoň nějaké znalosti SQL. + +Pro informaci, účet chytrého kontraktu Nadace Ethereum (EF) si můžete prohlédnout na [Blockscout](https://eth.blockscout.com/address/0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe). + +Jedna věc, kterou je třeba poznamenat, je, že všechny účty, včetně účtu EF, mají veřejnou adresu, kterou lze použít k odesílání a přijímání transakcí. + +Zůstatek na účtu na Etherscanu se skládá z běžných transakcí a interních transakcí. Interní transakce, navzdory svému názvu, nejsou _skutečnými_ transakcemi, které mění stav řetězce. Jedná se o převody hodnot iniciované provedením kontraktu ([zdroj](https://ethereum.stackexchange.com/questions/3417/how-to-get-contract-internal-transactions)). Vzhledem k tomu, že interní transakce nemají podpis, **nejsou** zahrnuty v blockchainu a nelze je dotazovat pomocí Dune Analytics. + +Tento tutoriál se proto zaměří na běžné transakce. Lze se na ně dotazovat takto: + +```sql +WITH temp_table AS ( +SELECT + hash, + block_number, + block_time, + "from", + "to", + value / 1e18 AS ether, + gas_used, + gas_price / 1e9 AS gas_price_gwei +FROM ethereum."transactions" +WHERE "to" = '\xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe' +ORDER BY block_time DESC +) +SELECT + hash, + block_number, + block_time, + "from", + "to", + ether, + (gas_used * gas_price_gwei) / 1e9 AS txn_fee +FROM temp_table +``` + +Tím získáte stejné informace, jaké jsou uvedeny na stránce transakcí na Etherscanu. Pro srovnání, zde jsou dva zdroje: + +#### Etherscan {#etherscan} + +![](./etherscan_view.png) + +[Stránka kontraktu EF na Blockscoutu.](https://eth.blockscout.com/address/0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe) + +#### Dune Analytics {#dune-analytics} + +![](./dune_view.png) + +Řídicí panel najdete [zde](https://dune.com/paulapivat/Learn-Ethereum). Kliknutím na tabulku zobrazíte dotaz (viz také výše). + +### Rozbor transakcí {#breaking_down_transactions} + +Odeslaná transakce obsahuje několik informací, včetně ([zdroj](/developers/docs/transactions/)): + +- **Příjemce**: Přijímající adresa (v dotazu jako \"to\") +- **Podpis**: Zatímco transakci podepisují soukromé klíče odesílatele, my můžeme pomocí SQL dotazovat veřejnou adresu odesílatele (\"from\"). +- **Hodnota**: Jedná se o množství převedených ETH (viz sloupec `ether`). +- **Data**: Jedná se o libovolná data, která byla zahašována (viz sloupec `data`) +- **gasLimit** – maximální množství jednotek paliva, které může být transakcí spotřebováno. Jednotky paliva představují výpočetní kroky +- **maxPriorityFeePerGas** – maximální množství paliva, které bude zahrnuto jako spropitné pro těžaře +- **maxFeePerGas** – maximální množství paliva, které je uživatel ochoten zaplatit za transakci (včetně baseFeePerGas a maxPriorityFeePerGas) + +Tyto konkrétní informace můžeme dotazovat pro transakce na veřejnou adresu Nadace Ethereum: + +```sql +SELECT + "to", + "from", + value / 1e18 AS ether, + data, + gas_limit, + gas_price / 1e9 AS gas_price_gwei, + gas_used, + ROUND(((gas_used / gas_limit) * 100),2) AS gas_used_pct +FROM ethereum."transactions" +WHERE "to" = '\xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe' +ORDER BY block_time DESC +``` + +### Bloky {#blocks} + +Každá transakce změní stav Ethereum Virtual Machine ([EVM](/developers/docs/evm/)) ([zdroj](/developers/docs/transactions/)). Transakce jsou vysílány do sítě, aby byly ověřeny a zahrnuty do bloku. Každá transakce je spojena s číslem bloku. Abychom viděli data, mohli bychom se dotázat na konkrétní číslo bloku: 12396854 (nejnovější blok mezi transakcemi Nadace Ethereum v době psaní tohoto článku, 11/5/21). + +Když se navíc dotážeme na další dva bloky, uvidíme, že každý blok obsahuje haš předchozího bloku (tj. rodičovský haš), což ukazuje, jak se blockchain tvoří. + +Každý blok obsahuje odkaz na svůj rodičovský blok. To je ukázáno níže mezi sloupci `hash` a `parent_hash` ([zdroj](/developers/docs/blocks/)): + +![parent_hash](./parent_hash.png) + +Zde je [dotaz](https://dune.com/queries/44856/88292) na Dune Analytics: + +```sql +SELECT + time, + number, + hash, + parent_hash, + nonce +FROM ethereum."blocks" +WHERE "number" = 12396854 OR "number" = 12396855 OR "number" = 12396856 +LIMIT 10 +``` + +Blok můžeme prozkoumat dotazováním na čas, číslo bloku, obtížnost, haš, rodičovský haš a nonce. + +Jediná věc, kterou tento dotaz nepokrývá, je _seznam transakcí_, který vyžaduje samostatný dotaz níže, a _kořen stavu_. Plný nebo archivní uzel ukládá všechny transakce a přechody stavů, což klientům umožňuje kdykoli dotazovat stav řetězce. Protože to vyžaduje velký úložný prostor, můžeme oddělit data řetězce od dat stavu: + +- Data řetězce (seznam bloků, transakcí) +- Data stavu (výsledek přechodu stavu každé transakce) + +Kořen stavu spadá do druhé kategorie a je _implicitními_ daty (není uložen onchain), zatímco data řetězce jsou explicitní a uložena na samotném řetězci ([zdroj](https://ethereum.stackexchange.com/questions/359/where-is-the-state-data-stored)). + +V tomto tutoriálu se zaměříme na onchain data, na která _se lze_ dotazovat pomocí SQL přes Dune Analytics. + +Jak bylo uvedeno výše, každý blok obsahuje seznam transakcí, které můžeme dotazovat filtrováním pro konkrétní blok. Zkusíme nejnovější blok, 12396854: + +```sql +SELECT * FROM ethereum."transactions" +WHERE block_number = 12396854 +ORDER BY block_time DESC` +``` + +Zde je výstup SQL na Dune: + +![](./list_of_txn.png) + +Tento jediný blok přidaný do řetězce mění stav Ethereum Virtual Machine ([EVM](/developers/docs/evm/)). Najednou se ověřují desítky, někdy i stovky transakcí. V tomto konkrétním případě bylo zahrnuto 222 transakcí. + +Abychom viděli, kolik jich bylo skutečně úspěšných, přidáme další filtr pro počítání úspěšných transakcí: + +```sql +WITH temp_table AS ( + SELECT * FROM ethereum."transactions" + WHERE block_number = 12396854 AND success = true + ORDER BY block_time DESC +) +SELECT + COUNT(success) AS num_successful_txn +FROM temp_table +``` + +Pro blok 12396854 bylo z celkových 222 transakcí úspěšně ověřeno 204: + +![](./successful_txn.png) + +Požadavky na transakce se objevují desítkykrát za sekundu, ale bloky jsou zapisovány přibližně jednou za 15 sekund ([zdroj](/developers/docs/blocks/)). + +Abychom viděli, že se jeden blok vyprodukuje přibližně každých 15 sekund, můžeme vzít počet sekund za den (86400) a vydělit jej 15, abychom získali odhadovaný průměrný počet bloků za den (~ 5760). + +Graf pro vyprodukované bloky Etherea za den (2016 - současnost) je: + +![](./daily_blocks.png) + +Průměrný počet denně vyprodukovaných bloků za toto období je ~5 874: + +![](./avg_daily_blocks.png) + +Dotazy jsou: + +```sql +# dotaz pro vizualizaci počtu denně vytvořených bloků od roku 2016 + +SELECT + DATE_TRUNC('day', time) AS dt, + COUNT(*) AS block_count +FROM ethereum."blocks" +GROUP BY dt +OFFSET 1 + +# průměrný počet denně vytvořených bloků + +WITH temp_table AS ( +SELECT + DATE_TRUNC('day', time) AS dt, + COUNT(*) AS block_count +FROM ethereum."blocks" +GROUP BY dt +OFFSET 1 +) +SELECT + AVG(block_count) AS avg_block_count +FROM temp_table +``` + +Průměrný počet denně vytvořených bloků od roku 2016 je mírně nad tímto číslem, a to 5 874. Alternativně, vydělením 86 400 sekund 5 874 průměrnými bloky získáme 14,7 sekundy, tedy přibližně jeden blok každých 15 sekund. + +### Gas {#gas} + +Bloky mají omezenou velikost. Maximální velikost bloku je dynamická a liší se podle poptávky v síti mezi 12 500 000 a 25 000 000 jednotkami. Limity jsou nutné k tomu, aby se zabránilo libovolně velkým blokům, které by zatěžovaly plné uzly z hlediska požadavků na diskový prostor a rychlost ([zdroj](/developers/docs/blocks/)). + +Jeden ze způsobů, jak si představit palivový limit bloku, je vnímat ho jako **nabídku** dostupného prostoru v bloku, do kterého se dávkují transakce. Palivový limit bloku lze dotazovat a vizualizovat od roku 2016 do současnosti: + +![](./avg_gas_limit.png) + +```sql +SELECT + DATE_TRUNC('day', time) AS dt, + AVG(gas_limit) AS avg_block_gas_limit +FROM ethereum."blocks" +GROUP BY dt +OFFSET 1 +``` + +Dále je tu skutečné množství paliva použitého denně k placení za výpočty prováděné v řetězci Ethereum (tj. odeslání transakce, volání chytrého kontraktu, ražba NFT). Toto je **poptávka** po dostupném prostoru v bloku Etherea: + +![](./daily_gas_used.png) + +```sql +SELECT + DATE_TRUNC('day', time) AS dt, + AVG(gas_used) AS avg_block_gas_used +FROM ethereum."blocks" +GROUP BY dt +OFFSET 1 +``` + +Můžeme také postavit tyto dva grafy vedle sebe, abychom viděli, jak se shoduje **poptávka a nabídka**: + +![gas_demand_supply](./gas_demand_supply.png) + +Proto můžeme ceny paliva chápat jako funkci poptávky po prostoru v bloku Etherea při dané dostupné nabídce. + +Nakonec bychom se mohli chtít dotázat na průměrné denní ceny paliva pro řetězec Ethereum, nicméně to by vedlo k obzvláště dlouhé době dotazu, takže náš dotaz omezíme na průměrné množství paliva zaplaceného za transakci Nadací Ethereum. + +![](./ef_daily_gas.png) + +Můžeme vidět ceny paliva zaplacené za všechny transakce provedené na adresu Nadace Ethereum v průběhu let. Zde je dotaz: + +```sql +SELECT + block_time, + gas_price / 1e9 AS gas_price_gwei, + value / 1e18 AS eth_sent +FROM ethereum."transactions" +WHERE "to" = '\xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe' +ORDER BY block_time DESC +``` + +### Shrnutí {#summary} + +Díky tomuto tutoriálu rozumíme základním konceptům Etherea a tomu, jak funguje blockchain Etherea, a to díky dotazování a získávání přehledu o onchain datech. + +Řídicí panel, který obsahuje veškerý kód použitý v tomto tutoriálu, naleznete [zde](https://dune.com/paulapivat/Learn-Ethereum). + +Pro další využití dat k prozkoumání web3 mě [najdete na Twitteru](https://twitter.com/paulapivat). diff --git a/public/content/translations/cs/developers/tutorials/logging-events-smart-contracts/index.md b/public/content/translations/cs/developers/tutorials/logging-events-smart-contracts/index.md new file mode 100644 index 00000000000..205e1b65218 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/logging-events-smart-contracts/index.md @@ -0,0 +1,48 @@ +--- +title: "Zaznamenávání dat z chytrých kontraktů pomocí událostí" +description: "Úvod do událostí chytrých kontraktů a jak je můžete použít k zaznamenávání dat." +author: "jdourlens" +tags: + [ + "smart kontrakt účty", + "remix", + "solidity", + "události" + ] +skill: intermediate +lang: cs +published: 2020-04-03 +source: EthereumDev +sourceUrl: https://ethereumdev.io/logging-data-with-events/ +address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +--- + +V Solidity jsou [události](/developers/docs/smart-contracts/anatomy/#events-and-logs) odesílané signály, které mohou chytré kontrakty spouštět. Dapps nebo cokoliv připojené k Ethereum JSON-RPC API může těmto událostem naslouchat a podle toho jednat. Událost může být také indexována, aby byla historie událostí později prohledávatelná. + +## Události {#events} + +Nejběžnější událostí na blockchainu Etherea v době psaní tohoto článku je událost Transfer, kterou emitují tokeny ERC20, když někdo převádí tokeny. + +```solidity +event Transfer(address indexed from, address indexed to, uint256 value); +``` + +Podpis události je deklarován uvnitř kódu kontraktu a může být emitován pomocí klíčového slova emit. Například událost převodu zaznamenává, kdo převod odeslal (_from_), komu (_to_) a kolik tokenů bylo převedeno (_value_). + +Pokud se vrátíme k našemu chytrému kontraktu Counter a rozhodneme se zaznamenávat každou změnu hodnoty. Protože tento kontrakt nemá být nasazen, ale má sloužit jako základ pro vytvoření jiného kontraktu jeho rozšířením, nazývá se abstraktní kontrakt. V případě našeho příkladu s čítačem by to vypadalo takto: + +```solidity +pragma solidity 0.5.17;\n\ncontract Counter {\n\n event ValueChanged(uint oldValue, uint256 newValue);\n\n // Soukromá proměnná typu unsigned int pro uchování počtu\n uint256 private count = 0;\n\n // Funkce, která zvyšuje náš čítač\n function increment() public {\n count += 1;\n emit ValueChanged(count - 1, count);\n }\n\n // Getter pro získání hodnoty čítače\n function getCount() public view returns (uint256) {\n return count;\n }\n\n} +``` + +Všimněte si, že: + +- **Řádek 5**: deklarujeme naši událost a co obsahuje, starou a novou hodnotu. + +- **Řádek 13**: Když zvýšíme hodnotu naší proměnné count, emitujeme událost. + +Pokud nyní kontrakt nasadíme a zavoláme funkci increment, uvidíme, že Remix ji automaticky zobrazí, pokud kliknete na novou transakci v poli s názvem logs. + +![Snímek obrazovky Remixu](./remix-screenshot.png) + +Záznamy jsou opravdu užitečné pro ladění vašich chytrých kontraktů, ale jsou také důležité, pokud vytváříte aplikace používané různými lidmi, a usnadňují analytiku pro sledování a pochopení toho, jak je váš chytrý kontrakt používán. Záznamy generované transakcemi se zobrazují v populárních prohlížečích bloků a můžete je například použít k vytvoření offchainových skriptů, které naslouchají specifickým událostem a provádějí akce, když k nim dojde. diff --git a/public/content/translations/cs/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md b/public/content/translations/cs/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md new file mode 100644 index 00000000000..0f097321ee4 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md @@ -0,0 +1,250 @@ +--- +title: "Merkleho důkazy pro integritu dat mimo blockchain" +description: "Zajištění integrity dat na blockchainu pro data, která jsou většinou uložena mimo blockchain" +author: Ori Pomerantz +tags: [ "úložiště" ] +skill: advanced +lang: cs +published: 2021-12-30 +--- + +## Úvod {#introduction} + +V ideálním případě bychom chtěli vše ukládat do úložiště Etherea, které je uloženo na tisících počítačů a má +extrémně vysokou dostupnost (data nelze cenzurovat) a integritu (data nelze neoprávněně upravovat), ale uložení 32bajtového slova obvykle stojí 20 000 gas. V době psaní tohoto článku se tato cena rovná 6,60 USD. Při 21 centech za bajt je to pro mnoho využití příliš drahé. + +K vyřešení tohoto problému vyvinul ekosystém Etherea mnoho alternativních způsobů, jak ukládat data decentralizovaným +způsobem. Obvykle zahrnují kompromis mezi dostupností +a cenou. Integrita je však obvykle zajištěna. + +V tomto článku se dozvíte, **jak** zajistit integritu dat bez jejich ukládání na blockchain pomocí +[Merkleho důkazů](https://computersciencewiki.org/index.php/Merkle_proof). + +## Jak to funguje? {#how-does-it-work} + +Teoreticky bychom mohli pouze uložit haš dat na blockchainu a odeslat všechna data v transakcích, které je vyžadují. To je však stále příliš drahé. Jeden bajt dat v transakci stojí asi 16 gas, což je v současnosti asi půl centu, neboli asi 5 USD za kilobajt. Při ceně 5 000 USD za megabajt je to pro mnoho použití stále příliš drahé, a to i bez dodatečných nákladů na hašování dat. + +Řešením je opakovaně hašovat různé podmnožiny dat, takže pro data, která nemusíte odesílat, můžete odeslat pouze haš. To se provádí pomocí Merkleho stromu, stromové datové struktury, kde každý uzel je hašem uzlů pod ním: + +![Merkleho strom](tree.png) + +Kořenový haš je jedinou částí, která musí být uložena na blockchainu. K prokázání určité hodnoty poskytnete všechny haše, které je třeba s ní zkombinovat, abyste získali kořen. Například k prokázání `C` poskytnete `D`, `H(A-B)` a `H(E-H)`. + +![Důkaz hodnoty C](proof-c.png) + +## Implementace {#implementation} + +[Ukázkový kód je k dispozici zde](https://github.com/qbzzt/merkle-proofs-for-offline-data-integrity). + +### Kód mimo blockchain {#offchain-code} + +V tomto článku používáme pro výpočty mimo blockchain JavaScript. Většina decentralizovaných aplikací má svou komponentu mimo blockchain v JavaScriptu. + +#### Vytvoření Merkleho kořene {#creating-the-merkle-root} + +Nejprve musíme poskytnout Merkleho kořen do řetězce. + +```javascript +const ethers = require("ethers") +``` + +[Používáme hašovací funkci z balíčku ethers](https://docs.ethers.io/v5/api/utils/hashing/#utils-keccak256). + +```javascript +// Hrubá data, jejichž integritu musíme ověřit. První dva bajty +// jsou identifikátor uživatele a poslední dva bajty jsou množství tokenů, které +// uživatel v současnosti vlastní. +const dataArray = [ + 0x0bad0010, 0x60a70020, 0xbeef0030, 0xdead0040, 0xca110050, 0x0e660060, + 0xface0070, 0xbad00080, 0x060d0091, +] +``` + +Kódování každé položky do jediného 256bitového celého čísla vede k méně čitelnému kódu než například použití JSON. To však znamená výrazně menší zpracování pro načtení dat ve smart kontraktu, tedy mnohem nižší náklady na gas. [Můžete číst JSON na blockchainu](https://github.com/chrisdotn/jsmnSol), ale je to špatný nápad, pokud je to možné se mu vyhnout. + +```javascript +// Pole hašovacích hodnot jako BigInts +const hashArray = dataArray +``` + +V tomto případě jsou naše data na začátku 256bitové hodnoty, takže není nutné žádné zpracování. Pokud použijeme složitější datovou strukturu, například řetězce, musíme se ujistit, že data nejprve hašujeme, abychom získali pole hašů. Všimněte si, že je to také proto, že nám nevadí, když uživatelé znají informace ostatních uživatelů. Jinak bychom museli hašovat, aby uživatel 1 neznal hodnotu pro uživatele 0, uživatel 2 neznal hodnotu pro uživatele 3 atd. + +```javascript +// Převod mezi řetězcem, který očekává hašovací funkce, a +// BigInt, který používáme všude jinde. +const hash = (x) => + BigInt(ethers.utils.keccak256("0x" + x.toString(16).padStart(64, 0))) +``` + +Hašovací funkce ethers očekává, že dostane řetězec JavaScriptu s hexadecimálním číslem, například `0x60A7`, a odpoví jiným řetězcem se stejnou strukturou. Pro zbytek kódu je však snazší použít `BigInt`, takže převádíme na hexadecimální řetězec a zase zpět. + +```javascript +// Symetrický haš páru, takže nám nebude vadit, pokud bude pořadí obrácené. +const pairHash = (a, b) => hash(hash(a) ^ hash(b)) +``` + +Tato funkce je symetrická (haš a [xor](https://en.wikipedia.org/wiki/Exclusive_or) b). To znamená, že při kontrole Merkleho důkazu se nemusíme starat o to, zda hodnotu z důkazu umístit před nebo za vypočtenou hodnotu. Kontrola Merkleho důkazu se provádí na blockchainu, takže čím méně toho tam musíme dělat, tím lépe. + +Upozornění: +Kryptografie je složitější, než se zdá. +Původní verze tohoto článku měla hašovací funkci `hash(a^b)`. +To byl **špatný** nápad, protože to znamenalo, že pokud jste znali legitimní hodnoty `a` a `b`, mohli jste použít `b' = a^b^a'` k prokázání jakékoli požadované hodnoty `a'`. +S touto funkcí byste museli vypočítat `b'` tak, aby `haš(a') ^ haš(b')` byl roven známé hodnotě (další větev na cestě ke kořeni), což je mnohem těžší. + +```javascript +// Hodnota pro označení, že určitá větev je prázdná, nemá +// žádnou hodnotu +const empty = 0n +``` + +Když počet hodnot není celočíselnou mocninou dvou, musíme řešit prázdné větve. Tento program to řeší tak, že jako zástupný symbol vloží nulu. + +![Merkleho strom s chybějícími větvemi](merkle-empty-hash.png) + +```javascript +// Vypočítá o jednu úroveň výše strom hašovacího pole tím, že vezme haš +// každého páru v pořadí. +const oneLevelUp = (inputArray) => { + var result = [] + var inp = [...inputArray] // Aby se zabránilo přepsání vstupních dat // V případě potřeby přidejte prázdnou hodnotu (potřebujeme, aby všechny listy byly spárovány) + + if (inp.length % 2 === 1) inp.push(empty) + + for (var i = 0; i < inp.length; i += 2) + result.push(pairHash(inp[i], inp[i + 1])) + + return result +} // oneLevelUp +``` + +Tato funkce „vystoupá“ o jednu úroveň v Merkleho stromu hašováním párů hodnot na aktuální vrstvě. Všimněte si, že se nejedná o nejefektivnější implementaci, mohli jsme se vyhnout kopírování vstupu a pouze přidat `hashEmpty` v příslušném místě cyklu, ale tento kód je optimalizován pro čitelnost. + +```javascript +const getMerkleRoot = (inputArray) => { + var result + + result = [...inputArray] // Stoupejte stromem, dokud nezbude jen jedna hodnota, což je // kořen. // // Pokud má vrstva lichý počet položek, // kód v oneLevelUp přidá prázdnou hodnotu, takže pokud máme například // 10 listů, budeme mít 5 větví ve druhé vrstvě, 3 // větve ve třetí, 2 ve čtvrté a kořen je pátý + + while (result.length > 1) result = oneLevelUp(result) + + return result[0] +} +``` + +Chcete-li získat kořen, stoupejte, dokud nezbude pouze jedna hodnota. + +#### Vytvoření Merkleho důkazu {#creating-a-merkle-proof} + +Merkleho důkaz jsou hodnoty, které se mají hašovat spolu s prokazovanou hodnotou, aby se vrátil Merkleho kořen. Hodnota, která se má prokázat, je často k dispozici z jiných dat, takže ji raději poskytuji samostatně než jako součást kódu. + +```javascript +// Merkleho důkaz se skládá z hodnoty seznamu položek, se kterými +// se má hašovat. Protože používáme symetrickou hašovací funkci, nepotřebujeme +// polohu položky k ověření důkazu, pouze k jeho vytvoření. +const getMerkleProof = (inputArray, n) => { +    var result = [], currentLayer = [...inputArray], currentN = n + +    // Dokud nedosáhneme vrcholu +    while (currentLayer.length > 1) { +        // Žádné vrstvy s lichou délkou +        if (currentLayer.length % 2) +            currentLayer.push(empty) + +        result.push(currentN % 2 +               // Pokud je currentN liché, přidejte k důkazu hodnotu před ním +            ? currentLayer[currentN-1] +               // Pokud je sudé, přidejte hodnotu za ním +            : currentLayer[currentN+1]) + +``` + +Hašujeme `(v[0],v[1])`, `(v[2],v[3])` atd. Takže pro sudé hodnoty potřebujeme následující, pro liché hodnoty předchozí. + +```javascript +        // Přesun na další vyšší vrstvu +        currentN = Math.floor(currentN/2) +        currentLayer = oneLevelUp(currentLayer) +    }   // while currentLayer.length > 1 + +    return result +}   // getMerkleProof +``` + +### Kód na blockchainu {#onchain-code} + +Nakonec máme kód, který kontroluje důkaz. Kód na blockchainu je napsán v [jazyce Solidity](https://docs.soliditylang.org/en/v0.8.11/). Optimalizace je zde mnohem důležitější, protože gas je relativně drahý. + +```solidity +//SPDX-License-Identifier: Public Domain +pragma solidity ^0.8.0; + +import "hardhat/console.sol"; +``` + +Napsal jsem to pomocí [vývojového prostředí Hardhat](https://hardhat.org/), které nám umožňuje mít [výstup z konzole ze Solidity](https://hardhat.org/docs/cookbook/debug-logs) během vývoje. + +```solidity + +contract MerkleProof { +    uint merkleRoot; + +    function getRoot() public view returns (uint) { +      return merkleRoot; +    } + +    // Extrémně nezabezpečené, v produkčním kódu MUSÍ BÝT přístup k +    // této funkci přísně omezen, pravděpodobně pouze na +    // vlastníka +    function setRoot(uint _merkleRoot) external { +      merkleRoot = _merkleRoot; +    }   // setRoot +``` + +Funkce Set a get pro Merkleho kořen. Povolit každému aktualizovat Merkleho kořen je v produkčním systému _extrémně špatný nápad_. Dělám to zde pro zjednodušení ukázkového kódu. **Nedělejte to v systému, kde na integritě dat skutečně záleží**. + +```solidity +    function hash(uint _a) internal pure returns(uint) { +      return uint(keccak256(abi.encode(_a))); +    } + +    function pairHash(uint _a, uint _b) internal pure returns(uint) { +      return hash(hash(_a) ^ hash(_b)); +    } +``` + +Tato funkce generuje párový haš. Je to jen překlad JavaScriptového kódu pro `hash` a `pairHash` do jazyka Solidity. + +**Poznámka:** Toto je další případ optimalizace pro čitelnost. Na základě [definice funkce](https://www.tutorialspoint.com/solidity/solidity_cryptographic_functions.htm) by bylo možné ukládat data jako hodnotu [`bytes32`](https://docs.soliditylang.org/en/v0.5.3/types.html#fixed-size-byte-arrays) a vyhnout se převodům. + +```solidity +    // Ověřit Merkleho důkaz +    function verifyProof(uint _value, uint[] calldata _proof) +        public view returns (bool) { +      uint temp = _value; +      uint i; + +      for(i=0; i<_proof.length; i++) { +        temp = pairHash(temp, _proof[i]); +      } + +      return temp == merkleRoot; +    } + +}  // MarkleProof +``` + +V matematické notaci vypadá ověření Merkleho důkazu takto: `H(proof_n, H(proof_n-1, H(proof_n-2, ...` H(proof_1, H(proof_0, value))...)))`. Tento kód to implementuje. + +## Merkleho důkazy a rollupy se nemíchají {#merkle-proofs-and-rollups} + +Merkleho důkazy nefungují dobře s [rollupy](/developers/docs/scaling/#rollups). Důvodem je, že rollupy zapisují všechna transakční data na L1, ale zpracovávají je na L2. Náklady na odeslání Merkleho důkazu s transakcí činí v průměru 638 gas za vrstvu (v současnosti stojí bajt v datech volání 16 gas, pokud není nulový, a 4, pokud je nulový). Pokud máme 1024 slov dat, Merkleho důkaz vyžaduje deset vrstev, neboli celkem 6380 gas. + +Když se podíváme například na [Optimism](https://public-grafana.optimism.io/d/9hkhMxn7z/public-dashboard?orgId=1&refresh=5m), zápis gas na L1 stojí asi 100 gwei a gas na L2 stojí 0,001 gwei (to je normální cena, při přetížení se může zvýšit). Takže za cenu jednoho L1 gas můžeme utratit sto tisíc gas za zpracování na L2. Za předpokladu, že nepřepisujeme úložiště, znamená to, že za cenu jednoho L1 gas můžeme na L2 zapsat do úložiště asi pět slov. Pro jediný Merkleho důkaz můžeme zapsat celých 1024 slov do úložiště (za předpokladu, že mohou být vypočtena na blockchainu, nikoli poskytnuta v transakci) a stále nám zbude většina gas. + +## Závěr {#conclusion} + +V reálném životě možná nikdy nebudete sami implementovat Merkleho stromy. Existují známé a auditované knihovny, které můžete použít, a obecně platí, že je nejlepší neimplementovat kryptografické primitivy sami. Ale doufám, že nyní lépe rozumíte Merkleho důkazům a můžete se rozhodnout, kdy se vyplatí je použít. + +Všimněte si, že zatímco Merkleho důkazy zachovávají _integritu_, nezachovávají _dostupnost_. Vědět, že nikdo jiný nemůže vzít vaše aktiva, je malou útěchou, pokud se úložiště dat rozhodne zakázat přístup a vy nemůžete sestavit Merkleho strom, abyste k nim měli přístup. Merkleho stromy se tedy nejlépe používají s nějakým druhem decentralizovaného úložiště, jako je IPFS. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). diff --git a/public/content/translations/cs/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md b/public/content/translations/cs/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md new file mode 100644 index 00000000000..30e02f29886 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md @@ -0,0 +1,151 @@ +--- +title: "Monitorování Gethu s InfluxDB a Grafanou" +description: "Nastavte si monitorování pro svůj Geth uzel pomocí InfluxDB a Grafany, abyste mohli sledovat jeho výkon a identifikovat problémy." +author: "Mario Havel" +tags: [ "klienti", "uzly" ] +skill: intermediate +lang: cs +published: 2021-01-13 +--- + +Tento tutoriál vám pomůže nastavit monitorování pro váš Geth uzel, abyste mohli lépe porozumět jeho výkonu a identifikovat případné problémy. + +## Předpoklady {#prerequisites} + +- Měli byste již mít spuštěnou instanci Gethu. +- Většina kroků a příkladů je určena pro prostředí Linux, takže se bude hodit základní znalost terminálu. +- Podívejte se na tento videopřehled sady metrik Gethu: [Monitorování infrastruktury Ethereum od Pétera Szilágyiho](https://www.youtube.com/watch?v=cOBab8IJMYI). + +## Sada pro monitorování {#monitoring-stack} + +Klient Etherea shromažďuje spoustu dat, která lze číst ve formě chronologické databáze. Pro usnadnění monitorování je můžete vložit do softwaru pro vizualizaci dat. K dispozici je více možností: + +- [Prometheus](https://prometheus.io/) (pull model) +- [InfluxDB](https://www.influxdata.com/get-influxdb/) (push model) +- [Telegraf](https://www.influxdata.com/get-influxdb/) +- [Grafana](https://www.grafana.com/) +- [Datadog](https://www.datadoghq.com/) +- [Chronograf](https://www.influxdata.com/time-series-platform/chronograf/) + +Existuje také [Geth Prometheus Exporter](https://github.com/hunterlong/gethexporter), možnost předkonfigurovaná s InfluxDB a Grafanou. + +V tomto tutoriálu nastavíme vašeho klienta Geth tak, aby odesílal data do InfluxDB k vytvoření databáze a Grafanu k vytvoření grafické vizualizace dat. Ruční provedení vám pomůže lépe porozumět procesu, měnit ho a nasazovat v různých prostředích. + +## Nastavení InfluxDB {#setting-up-influxdb} + +Nejprve si stáhněme a nainstalujme InfluxDB. Různé možnosti stažení naleznete na [stránce s verzemi Influxdata](https://portal.influxdata.com/downloads/). Vyberte si tu, která vyhovuje vašemu prostředí. +Můžete ji také nainstalovat z [úložiště](https://repos.influxdata.com/). Například v distribuci založené na Debianu: + +``` +curl -tlsv1.3 --proto =https -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add +source /etc/lsb-release +echo "deb https://repos.influxdata.com/${DISTRIB_ID,,} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list +sudo apt update +sudo apt install influxdb -y +sudo systemctl enable influxdb +sudo systemctl start influxdb +sudo apt install influxdb-client +``` + +Po úspěšné instalaci InfluxDB se ujistěte, že běží na pozadí. Ve výchozím nastavení je dostupná na `localhost:8086`. +Před použitím klienta `influx` musíte vytvořit nového uživatele s oprávněními správce. Tento uživatel bude sloužit pro správu na vysoké úrovni, vytváření databází a uživatelů. + +``` +curl -XPOST "http://localhost:8086/query" --data-urlencode "q=CREATE USER username WITH PASSWORD 'password' WITH ALL PRIVILEGES" +``` + +Nyní můžete použít klienta influx pro vstup do [InfluxDB shellu](https://docs.influxdata.com/influxdb/v1.8/tools/shell/) s tímto uživatelem. + +``` +influx -username 'username' -password 'password' +``` + +Přímou komunikací s InfluxDB v jeho shellu můžete vytvořit databázi a uživatele pro metriky Geth. + +``` +create database geth +create user geth with password choosepassword +``` + +Ověřte vytvořené položky pomocí: + +``` +show databases +show users +``` + +Opusťte InfluxDB shell. + +``` +exit +``` + +InfluxDB běží a je nakonfigurována pro ukládání metrik z Gethu. + +## Příprava Gethu {#preparing-geth} + +Po nastavení databáze musíme v Gethu povolit sběr metrik. Věnujte pozornost `METRICS AND STATS OPTIONS` v `geth --help`. Naleznete zde několik možností, v tomto případě chceme, aby Geth odesílal data do InfluxDB. +Základní nastavení specifikuje koncový bod, kde je InfluxDB dostupná, a ověření pro databázi. + +``` +geth --metrics --metrics.influxdb --metrics.influxdb.endpoint "http://0.0.0.0:8086" --metrics.influxdb.username "geth" --metrics.influxdb.password "chosenpassword" +``` + +Tyto příznaky mohou být připojeny k příkazu spouštějícímu klienta nebo uloženy do konfiguračního souboru. + +Můžete ověřit, že Geth úspěšně odesílá data, například výpisem metrik v databázi. V InfluxDB shellu: + +``` +use geth +show measurements +``` + +## Nastavení Grafany {#setting-up-grafana} + +Dalším krokem je instalace Grafany, která bude interpretovat data graficky. Postupujte podle instalačního procesu pro vaše prostředí v dokumentaci Grafany. Ujistěte se, že instalujete verzi OSS, pokud nechcete jinak. +Příklad instalačních kroků pro distribuce Debian s použitím úložiště: + +``` +curl -tlsv1.3 --proto =https -sL https://packages.grafana.com/gpg.key | sudo apt-key add - +echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee -a /etc/apt/sources.list.d/grafana.list +sudo apt update +sudo apt install grafana +sudo systemctl enable grafana-server +sudo systemctl start grafana-server +``` + +Když máte Grafanu spuštěnou, měla by být dostupná na `localhost:3000`. +Použijte preferovaný prohlížeč pro přístup k této cestě, poté se přihlaste s výchozími přihlašovacími údaji (uživatel: `admin` a heslo: `admin`). Po zobrazení výzvy změňte výchozí heslo a uložte. + +![](./grafana1.png) + +Budete přesměrováni na domovskou stránku Grafany. Nejprve nastavte svá zdrojová data. Klikněte na ikonu konfigurace v levém panelu a vyberte „Zdroje dat“. + +![](./grafana2.png) + +Zatím nejsou vytvořeny žádné zdroje dat, klikněte na „Přidat zdroj dat“ pro definování jednoho. + +![](./grafana3.png) + +Pro toto nastavení vyberte „InfluxDB“ a pokračujte. + +![](./grafana4.png) + +Konfigurace zdroje dat je poměrně jednoduchá, pokud spouštíte nástroje na stejném stroji. Musíte nastavit adresu InfluxDB a podrobnosti pro přístup k databázi. Viz obrázek níže. + +![](./grafana5.png) + +Pokud je vše kompletní a InfluxDB je dostupná, klikněte na „Uložit a testovat“ a počkejte, až se zobrazí potvrzení. + +![](./grafana6.png) + +Grafana je nyní nastavena ke čtení dat z InfluxDB. Nyní musíte vytvořit panel, který je bude interpretovat a zobrazovat. Vlastnosti panelů jsou kódovány v souborech JSON, které může kdokoli vytvořit a snadno importovat. V levém panelu klikněte na „Vytvořit a importovat“. + +![](./grafana7.png) + +Pro monitorovací panel Geth zkopírujte ID [tohoto panelu](https://grafana.com/grafana/dashboards/13877/) a vložte jej na stránku „Importovat“ v Grafaně. Po uložení panelu by měl vypadat takto: + +![](./grafana8.png) + +Své panely můžete upravovat. Každý panel lze upravovat, přesouvat, odstraňovat nebo přidávat. Můžete měnit své konfigurace. Je to na vás! Chcete-li se dozvědět více o tom, jak panely fungují, podívejte se do [dokumentace Grafany](https://grafana.com/docs/grafana/latest/dashboards/). +Mohlo by vás také zajímat [Upozorňování](https://grafana.com/docs/grafana/latest/alerting/). To vám umožní nastavit upozornění pro případy, kdy metriky dosáhnou určitých hodnot. Jsou podporovány různé komunikační kanály. diff --git a/public/content/translations/cs/developers/tutorials/nft-minter/index.md b/public/content/translations/cs/developers/tutorials/nft-minter/index.md new file mode 100644 index 00000000000..687c0937d96 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/nft-minter/index.md @@ -0,0 +1,874 @@ +--- +title: "Návod na minter pro NFT" +description: "V tomto návodu si vytvoříte minter pro NFT a naučíte se, jak vytvořit full-stack dapp propojením svého chytrého kontraktu s React frontendem pomocí nástrojů MetaMask a Web3." +author: "smudgil" +tags: + [ + "solidity", + "NFT", + "alchemy", + "chytré kontrakty", + "frontend", + "Pinata" + ] +skill: intermediate +lang: cs +published: 2021-10-06 +--- + +Jednou z největších výzev pro vývojáře přicházející z prostředí Web2 je přijít na to, jak propojit chytrý kontrakt s frontendovým projektem a pracovat s ním. + +Vytvořením minteru pro NFT – jednoduchého uživatelského rozhraní, do kterého můžete zadat odkaz na své digitální aktivum, název a popis – se naučíte: + +- Připojit se k MetaMasku prostřednictvím vašeho frontendového projektu +- Volat metody chytrého kontraktu z vašeho frontendu +- Podepisovat transakce pomocí MetaMasku + +V tomto návodu budeme jako náš frontendový framework používat [React](https://react.dev/). Protože se tento návod zaměřuje především na vývoj pro Web3, nebudeme trávit mnoho času rozebíráním základů Reactu. Místo toho se zaměříme na to, abychom našemu projektu dodali funkcionalitu. + +Předpokladem je, že byste měli mít základní znalosti Reactu – vědět, jak fungují komponenty, props, useState/useEffect a základní volání funkcí. Pokud jste o žádném z těchto pojmů nikdy neslyšeli, možná se budete chtít podívat na tento [návod Úvod do Reactu](https://react.dev/learn/tutorial-tic-tac-toe). Těm, kteří se učí spíše vizuálně, vřele doporučujeme tuto vynikající sérii videí [Kompletní moderní návod na React](https://www.youtube.com/playlist?list=PL4cUxeGkcC9gZD-Tvwfod2gaISzfRiP9d) od Net Ninja. + +A pokud ho ještě nemáte, budete k dokončení tohoto návodu a k vytváření čehokoli na blockchainu určitě potřebovat účet Alchemy. Zaregistrujte si bezplatný účet [zde](https://alchemy.com/). + +Bez dalších okolků se do toho pusťme! + +## Tvorba NFT 101 {#making-nfts-101} + +Než se vůbec začneme dívat na jakýkoli kód, je důležité pochopit, jak funguje tvorba NFT. Zahrnuje to dva kroky: + +### Zveřejnění chytrého kontraktu NFT na blockchainu Ethereum {#publish-nft} + +Největší rozdíl mezi dvěma standardy chytrých kontraktů pro NFT je ten, že ERC-1155 je standard pro více tokenů a zahrnuje dávkovou funkcionalitu, zatímco ERC-721 je standard pro jeden token, a proto podporuje pouze přenos jednoho tokenu najednou. + +### Volání funkce mintování {#minting-function} + +Obvykle tato funkce mintování vyžaduje, abyste jako parametry předali dvě proměnné: zaprvé `recipient`, která určuje adresu, jež obdrží vaše nově namintované NFT, a zadruhé `tokenURI` NFT, což je řetězec, který odkazuje na dokument JSON popisující metadata NFT. + +Metadata NFT jsou to, co ho skutečně oživuje a umožňuje mu mít vlastnosti, jako je název, popis, obrázek (nebo jiné digitální aktivum) a další atributy. [Zde je příklad tokenURI](https://gateway.pinata.cloud/ipfs/QmSvBcb4tjdFpajGJhbFAWeK3JAxCdNQLQtr6ZdiSi42V2), které obsahuje metadata NFT. + +V tomto návodu se zaměříme na část 2, volání funkce mintování existujícího chytrého kontraktu NFT pomocí našeho uživatelského rozhraní React. + +[Zde je odkaz](https://ropsten.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE) na chytrý kontrakt ERC-721 NFT, který budeme v tomto návodu volat. Pokud byste se chtěli dozvědět, jak jsme ho vytvořili, vřele doporučujeme podívat se na náš další návod, ["Jak vytvořit NFT"](https://www.alchemy.com/docs/how-to-create-an-nft). + +Super, teď když chápeme, jak funguje tvorba NFT, naklonujme si naše startovací soubory! + +## Naklonujte si startovací soubory {#clone-the-starter-files} + +Nejprve přejděte do [GitHub repozitáře nft-minter-tutorial](https://github.com/alchemyplatform/nft-minter-tutorial), abyste získali startovací soubory pro tento projekt. Naklonujte tento repozitář do svého lokálního prostředí. + +Když otevřete tento naklonovaný repozitář `nft-minter-tutorial`, všimnete si, že obsahuje dvě složky: `minter-starter-files` a `nft-minter`. + +- `minter-starter-files` obsahuje startovací soubory (v podstatě uživatelské rozhraní React) pro tento projekt. V tomto návodu **budeme pracovat v tomto adresáři**, kde se naučíte, jak toto uživatelské rozhraní oživit připojením k vaší peněžence Ethereum a chytrému kontraktu NFT. +- `nft-minter` obsahuje celý dokončený návod a je vám k dispozici jako **reference**, **pokud se zaseknete.** + +Dále otevřete svou kopii `minter-starter-files` v 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 `Minter.js` a psát další javascriptové soubory, abychom našemu projektu dodali funkcionalitu Web3. + +## Krok 2: Prohlédněte si naše startovací soubory {#step-2-check-out-our-starter-files} + +Než začneme kódovat, je důležité si prohlédnout, co je pro nás již připraveno ve startovacích souborech. + +### 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. + +Chcete-li projekt spustit, přejděte do kořenového adresáře složky `minter-starter-files` a spusťte v terminálu příkaz `npm install` pro instalaci závislostí projektu: + +```bash +cd minter-starter-files +npm install +``` + +Jakmile se dokončí instalace, spusťte v terminálu `npm start`: + +```bash +npm start +``` + +Tím by se vám měl v prohlížeči otevřít http://localhost:3000/, kde uvidíte frontend našeho projektu. Měl by se skládat ze 3 polí: místo pro zadání odkazu na aktivum vašeho NFT, zadání názvu vašeho NFT a poskytnutí popisu. + +Pokud se pokusíte kliknout na tlačítka „Připojit peněženku“ nebo „Mintovat NFT“, zjistíte, že nefungují – to proto, že jejich funkcionalitu musíme teprve naprogramovat! :\) + +### Komponenta Minter.js {#minter-js} + +**POZNÁMKA:** Ujistěte se, že se nacházíte ve složce `minter-starter-files` a ne ve složce `nft-minter`! + +Vraťme se do složky `src` v našem editoru a otevřete soubor `Minter.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. + +V horní části tohoto souboru máme naše stavové proměnné, které budeme aktualizovat po konkrétních událostech. + +```javascript +//Stavové proměnné +const [walletAddress, setWallet] = useState("") +const [status, setStatus] = useState("") +const [name, setName] = useState("") +const [description, setDescription] = useState("") +const [url, setURL] = useState("") +``` + +Nikdy jste neslyšeli o stavových proměnných Reactu nebo stavových hácích (hooks)? Podívejte se na [tuto](https://legacy.reactjs.org/docs/hooks-state.html) dokumentaci. + +Zde je přehled toho, co jednotlivé proměnné představují: + +- `walletAddress` – řetězec, který ukládá adresu peněženky uživatele +- `status` – řetězec, který obsahuje zprávu k zobrazení v dolní části uživatelského rozhraní +- `name` – řetězec, který ukládá název NFT +- `description` – řetězec, který ukládá popis NFT +- `url` – řetězec, který je odkazem na digitální aktivum NFT + +Po stavových proměnných uvidíte tři neimplementované funkce: `useEffect`, `connectWalletPressed` a `onMintPressed`. Všimnete si, že všechny tyto funkce jsou `async`, protože v nich budeme provádět asynchronní volání API! Jejich názvy jsou synonymem jejich funkcí: + +```javascript +useEffect(async () => { + //TODO: implementovat +}, []) + +const connectWalletPressed = async () => { + //TODO: implementovat +} + +const onMintPressed = async () => { + //TODO: implementovat +} +``` + +- [`useEffect`](https://legacy.reactjs.org/docs/hooks-effect.html) – toto je React hook, který se volá po vykreslení vaší komponenty. Protože má předanou prázdnou rekvizitu pole `[]` (viz řádek 3), bude volána pouze při _prvním_ vykreslení komponenty. Zde zavoláme náš posluchač peněženky a další funkci peněženky, abychom aktualizovali naše uživatelské rozhraní tak, aby odráželo, zda je peněženka již připojena. +- `connectWalletPressed` – tato funkce bude volána pro připojení peněženky MetaMask uživatele k naší dapp. +- `onMintPressed` – tato funkce bude volána pro mintování NFT uživatele. + +Na konci tohoto souboru máme uživatelské rozhraní naší komponenty. Pokud si tento kód pečlivě projdete, všimnete si, že aktualizujeme naše stavové proměnné `url`, `name` a `description` při změně vstupu v jejich odpovídajících textových polích. + +Také uvidíte, že `connectWalletPressed` a `onMintPressed` se volají při kliknutí na tlačítka s ID `mintButton` a `walletButton`. + +```javascript +//uživatelské rozhraní naší komponenty +return ( +
+ + +

+

🧙‍♂️ Minter NFT od Alchemy

+

+ Jednoduše přidejte odkaz na své aktivum, název a popis a poté stiskněte „Mintovat“. +

+
+

🖼 Odkaz na aktivum:

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

🤔 Název:

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

✍️ Popis:

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

{status}

+
+) +``` + +Nakonec se podívejme, kam se tato komponenta Minter přidává. + +Pokud přejdete do souboru `App.js`, který je hlavní komponentou v Reactu a funguje jako kontejner pro všechny ostatní komponenty, uvidíte, že naše komponenta Minter je vložena na řádku 7. + +**V tomto návodu budeme upravovat pouze soubor `Minter.js` a přidávat soubory do naší složky `src`.** + +Nyní, když chápeme, s čím pracujeme, nastavme si naši peněženku Ethereum! + +## Nastavte si svou peněženku Ethereum {#set-up-your-ethereum-wallet} + +Aby uživatelé mohli interagovat s vaším chytrým kontraktem, budou muset ke své dapp připojit svou peněženku Ethereum. + +### Stáhněte si MetaMask {#download-metamask} + +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. Pokud chcete lépe porozumět tomu, jak fungují transakce na Ethereu, podívejte se na [tuto stránku](/developers/docs/transactions/). + +Úč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, se ujistěte, že jste vpravo nahoře přepnuli na „Ropsten Test Network“ (abychom nepracovali se skutečnými penězi). + +### Přidejte ether z Faucetu {#add-ether-from-faucet} + +Abychom mohli mintovat naše NFT (nebo podepisovat jakékoli transakce na blockchainu Ethereum), budeme potřebovat nějaké falešné Eth. Pro získání Eth můžete přejít na [Ropsten faucet](https://faucet.ropsten.be/), zadat adresu svého účtu Ropsten a kliknout na „Send Ropsten Eth“. 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! + +## Připojte MetaMask k vašemu UI {#connect-metamask-to-your-UI} + +Nyní, když je naše peněženka MetaMask nastavena, připojme k ní naši dapp! + +Protože se chceme držet paradigmatu [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller), vytvoříme si samostatný soubor, který bude obsahovat naše funkce pro správu logiky, dat a pravidel naší dapp, a poté tyto funkce předáme našemu frontendu (naší komponentě Minter.js). + +### Funkce `connectWallet` {#connect-wallet-function} + +K tomu si vytvoříme novou složku s názvem `utils` ve vašem adresáři `src` a do ní přidáme soubor s názvem `interact.js`, který bude obsahovat všechny naše funkce pro interakci s peněženkou a chytrým kontraktem. + +V našem souboru `interact.js` napíšeme funkci `connectWallet`, kterou pak naimportujeme a zavoláme v naší komponentě `Minter.js`. + +Do svého souboru `interact.js` přidejte následující + +```javascript +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: ( + +

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

+
+ ), + } + } +} +``` + +Pojďme si rozebrat, co tento kód dělá: + +Nejprve naše funkce 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. + +**Většina funkcí, které napíšeme, bude vracet objekty JSON, které můžeme použít k aktualizaci našich stavových proměnných a uživatelského rozhraní.** + +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, které obsahuje všechny adresy účtů uživatele připojené k dapp. 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. + +### Přidejte funkci connectWallet do své komponenty UI Minter.js {#add-connect-wallet} + +Nyní, když jsme napsali tuto funkci `connectWallet`, připojme ji k naší komponentě `Minter.js`. + +Nejprve budeme muset naimportovat naši funkci do našeho souboru `Minter.js` přidáním `import { connectWallet } from \"./utils/interact.js\";` na začátek souboru `Minter.js`. Vašich prvních 11 řádků souboru `Minter.js` by nyní mělo vypadat takto: + +```javascript +import { useEffect, useState } from "react"; +import { connectWallet } from "./utils/interact.js"; + +const Minter = (props) => { + + //Stavové proměnné + const [walletAddress, setWallet] = useState(""); + const [status, setStatus] = useState(""); + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const [url, setURL] = useState(""); +``` + +Poté uvnitř naší funkce `connectWalletPressed` zavoláme naši importovanou funkci `connectWallet` takto: + +```javascript +const connectWalletPressed = async () => { + const walletResponse = await connectWallet() + setStatus(walletResponse.status) + setWallet(walletResponse.address) +} +``` + +Všimněte si, jak je většina naší funkcionality abstrahována z naší komponenty `Minter.js` ze 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í uložme oba soubory `Minter.js` a `interact.js` a vyzkoušejme naše dosavadní uživatelské rozhraní. + +Otevřete svůj prohlížeč na adrese localhost:3000 a stiskněte tlačítko „Připojit peněženku“ vpravo nahoře na stránce. + +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. + +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 nebojte se! To můžeme snadno opravit implementací funkce nazvané `getCurrentWalletConnected`, která zkontroluje, zda je adresa již připojena k naší dapp, a podle toho aktualizuje naše uživatelské rozhraní! + +### Funkce getCurrentWalletConnected {#get-current-wallet} + +Do svého souboru `interact.js` přidejte následující funkci `getCurrentWalletConnected`: + +```javascript +export const getCurrentWalletConnected = async () => { + if (window.ethereum) { + try { + const addressArray = await window.ethereum.request({ + method: "eth_accounts", + }) + if (addressArray.length > 0) { + return { + address: addressArray[0], + status: "👆🏽 Napište zprávu do textového pole výše.", + } + } else { + return { + address: "", + status: "🦊 Připojte se k MetaMasku pomocí tlačítka vpravo nahoře.", + } + } + } catch (err) { + return { + address: "", + status: "😥 " + err.message, + } + } + } else { + return { + address: "", + status: ( + +

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

+
+ ), + } + } +} +``` + +Tento kód je _velmi_ podobný funkci `connectWallet`, kterou jsme napsali dříve. + +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 viděli tuto funkci v akci, zavoláme ji ve funkci `useEffect` naší komponenty `Minter.js`. + +Stejně jako u `connectWallet` musíme tuto funkci naimportovat z našeho souboru `interact.js` do našeho souboru `Minter.js` takto: + +```javascript +import { useEffect, useState } from "react" +import { + connectWallet, + getCurrentWalletConnected, //importovat zde +} from "./utils/interact.js" +``` + +Nyní ji jednoduše zavoláme v naší funkci `useEffect`: + +```javascript +useEffect(async () => { + 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`. + +Jakmile přidáte tento kód, zkuste obnovit okno prohlížeče. Tlačítko by mělo hlásit, že jste připojeni, a zobrazovat náhled adresy vaší připojené peněženky – i po obnovení! + +### Implementujte addWalletListener {#implement-add-wallet-listener} + +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. + +Do svého souboru `Minter.js` přidejte funkci `addWalletListener`, která vypadá následovně: + +```javascript +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 MetaMasku pomocí tlačítka vpravo nahoře.") + } + }) + } else { + setStatus( +

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

+ ) + } +} +``` + +Pojďme si rychle rozebrat, co se zde děje: + +- 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. + +Nakonec ji musíme zavolat v naší funkci `useEffect`: + +```javascript +useEffect(async () => { + const { address, status } = await getCurrentWalletConnected() + setWallet(address) + setStatus(status) + + addWalletListener() +}, []) +``` + +A je to! Dokončili jsme programování veškeré funkcionality naší peněženky! Nyní, když je naše peněženka nastavena, pojďme zjistit, jak mintovat naše NFT! + +## Metadata NFT 101 {#nft-metadata-101} + +Vzpomeňte si na metadata NFT, o kterých jsme mluvili v kroku 0 tohoto návodu – oživují NFT a umožňují mu mít vlastnosti, jako je digitální aktivum, název, popis a další atributy. + +Tato metadata budeme muset nakonfigurovat jako objekt JSON a uložit je, abychom je mohli předat jako parametr `tokenURI` při volání funkce `mintNFT` našeho chytrého kontraktu. + +Text v polích „Odkaz na aktivum“, „Název“ a „Popis“ bude tvořit různé vlastnosti metadat našeho NFT. Tato metadata naformátujeme jako objekt JSON, ale existuje několik možností, kde tento objekt JSON můžeme uložit: + +- Mohli bychom je uložit na blockchainu Ethereum, avšak to by bylo velmi drahé. +- Mohli bychom je uložit na centralizovaném serveru, jako je AWS nebo Firebase. To by ale bylo v rozporu s naším decentralizačním étosem. +- Mohli bychom použít IPFS, decentralizovaný protokol a peer-to-peer síť pro ukládání a sdílení dat v distribuovaném souborovém systému. Protože je tento protokol decentralizovaný a bezplatný, je to naše nejlepší volba! + +K uložení našich metadat na IPFS použijeme [Pinata](https://pinata.cloud/), pohodlné API a sadu nástrojů pro IPFS. V dalším kroku si přesně vysvětlíme, jak na to! + +## Použijte Pinata k připnutí vašich metadat na IPFS {#use-pinata-to-pin-your-metadata-to-IPFS} + +Pokud nemáte účet [Pinata](https://pinata.cloud/), zaregistrujte si bezplatný účet [zde](https://app.pinata.cloud/auth/signup) a dokončete kroky k ověření svého e-mailu a účtu. + +### Vytvořte si API klíč Pinata {#create-pinata-api-key} + +Přejděte na stránku [https://pinata.cloud/keys](https://pinata.cloud/keys), poté vyberte tlačítko „New Key“ nahoře, nastavte widget Admin jako povolený a pojmenujte svůj klíč. + +Poté se vám zobrazí vyskakovací okno s vašimi informacemi o API. Ujistěte se, že si je uložíte na bezpečné místo. + +Nyní, když máme náš klíč nastavený, přidejme ho do našeho projektu, abychom ho mohli používat. + +### Vytvořte soubor .env {#create-a-env} + +Náš klíč Pinata a tajný klíč můžeme bezpečně uložit do souboru s proměnnými prostředí. Nainstalujme si do adresáře projektu [balíček dotenv](https://www.npmjs.com/package/dotenv). + +Otevřete novou kartu v terminálu (jinou než tu, na které běží localhost), ujistěte se, že se nacházíte ve složce `minter-starter-files` a spusťte následující příkaz: + +```text +npm install dotenv --save +``` + +Dále vytvořte soubor `.env` v kořenovém adresáři `minter-starter-files` zadáním následujícího příkazu: + +```javascript +vim.env +``` + +Tím se vám otevře soubor `.env` ve vimu (textovém editoru). Pro uložení stiskněte na klávesnici v tomto pořadí „esc“ + „:“ + „q“. + +Dále v editoru VSCode přejděte do souboru `.env` a přidejte do něj svůj API klíč Pinata a API secret takto: + +```text +REACT_APP_PINATA_KEY = +REACT_APP_PINATA_SECRET = +``` + +Uložte soubor a pak jste připraveni začít psát funkci pro nahrání vašich metadat JSON na IPFS! + +### Implementujte pinJSONToIPFS {#pin-json-to-ipfs} + +Naštěstí pro nás má Pinata [API speciálně pro nahrávání dat JSON na IPFS](https://docs.pinata.cloud/api-reference/endpoint/ipfs/pin-json-to-ipfs#pin-json) a pohodlný příklad v JavaScriptu s axios, který můžeme s drobnými úpravami použít. + +Ve složce `utils` vytvořme další soubor s názvem `pinata.js` a poté naimportujme náš Pinata secret a klíč ze souboru .env takto: + +```javascript +require("dotenv").config() +const key = process.env.REACT_APP_PINATA_KEY +const secret = process.env.REACT_APP_PINATA_SECRET +``` + +Dále vložte další kód z níže uvedeného do svého souboru `pinata.js`. Nebojte se, rozebereme si, co všechno znamená! + +```javascript +require("dotenv").config() +const key = process.env.REACT_APP_PINATA_KEY +const secret = process.env.REACT_APP_PINATA_SECRET + +const axios = require("axios") + +export const pinJSONToIPFS = async (JSONBody) => { + const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS` + //odeslání POST požadavku pomocí axios na Pinata ⬇️ + return axios + .post(url, JSONBody, { + headers: { + pinata_api_key: key, + pinata_secret_api_key: secret, + }, + }) + .then(function (response) { + return { + success: true, + pinataUrl: + "https://gateway.pinata.cloud/ipfs/" + response.data.IpfsHash, + } + }) + .catch(function (error) { + console.log(error) + return { + success: false, + message: error.message, + } + }) +} +``` + +Co přesně tedy tento kód dělá? + +Nejprve importuje [axios](https://www.npmjs.com/package/axios), HTTP klienta založeného na promises pro prohlížeč a node.js, který použijeme k provedení požadavku na Pinata. + +Poté máme naši asynchronní funkci `pinJSONToIPFS`, která jako vstup přijímá `JSONBody` a v hlavičce API klíč a secret Pinata, a to vše pro provedení POST požadavku na jejich API `pinJSONToIPFS`. + +- Pokud je tento POST požadavek úspěšný, naše funkce vrátí objekt JSON s booleovskou hodnotou `success` jako true a `pinataUrl`, kde byla naše metadata připnuta. Tuto vrácenou `pinataUrl` použijeme jako vstup `tokenURI` do funkce mintování našeho chytrého kontraktu. +- Pokud tento post požadavek selže, naše funkce vrátí objekt JSON s booleovskou hodnotou `success` jako false a řetězcem `message`, který sděluje naši chybu. + +Stejně jako u návratových typů naší funkce `connectWallet` vracíme objekty JSON, abychom mohli jejich parametry použít k aktualizaci našich stavových proměnných a uživatelského rozhraní. + +## Načtěte svůj chytrý kontrakt {#load-your-smart-contract} + +Nyní, když máme způsob, jak nahrát naše metadata NFT na IPFS prostřednictvím naší funkce `pinJSONToIPFS`, budeme potřebovat způsob, jak načíst instanci našeho chytrého kontraktu, abychom mohli volat jeho funkci `mintNFT`. + +Jak jsme již zmínili, v tomto návodu budeme používat [tento existující chytrý kontrakt NFT](https://ropsten.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE); nicméně, pokud byste se chtěli dozvědět, jak jsme ho vytvořili, nebo si vytvořit vlastní, vřele doporučujeme podívat se na náš další návod ["Jak vytvořit NFT"](https://www.alchemy.com/docs/how-to-create-an-nft). + +### ABI kontraktu {#contract-abi} + +Pokud jste si naše soubory pozorně prohlédli, jistě jste si všimli, že v našem adresáři `src` se nachází soubor `contract-abi.json`. ABI je nezbytné pro specifikaci, kterou funkci kontrakt vyvolá, a také pro zajištění, že funkce vrátí data ve formátu, který očekáváte. + +Budeme také potřebovat API klíč Alchemy a Alchemy Web3 API, abychom se připojili k blockchainu Ethereum a načetli náš chytrý kontrakt. + +### Vytvořte si API klíč Alchemy {#create-alchemy-api} + +Pokud ještě nemáte účet Alchemy, [zaregistrujte se zdarma zde.](https://alchemy.com/?a=eth-org-nft-minter) + +Jakmile si vytvoříte účet na Alchemy, můžete si vygenerovat klíč API vytvořením aplikace. To nám umožní provádět požadavky na testovací síť Ropsten. + +Přejděte na stránku „Create App“ ve svém panelu Alchemy tak, že najedete myší na „Apps“ v navigační liště a kliknete na „Create App“. + +Pojmenujte svou aplikaci – my jsme zvolili „My First NFT!“, nabídněte krátký popis, vyberte „Staging“ pro prostředí používané pro účetnictví vaší aplikace a jako síť zvolte „Ropsten“. + +Klikněte na „Create app“ a to je vše! Vaše aplikace by se měla objevit v tabulce níže. + +Skvělé, takže teď, když jsme vytvořili naši HTTP Alchemy API URL, zkopírujte si ji do schránky... + +… a poté ji přidejme do našeho souboru `.env`. Celkově by váš soubor .env měl vypadat takto: + +```text +REACT_APP_PINATA_KEY = +REACT_APP_PINATA_SECRET = +REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/ +``` + +Nyní, když máme naše ABI kontraktu a náš API klíč Alchemy, jsme připraveni načíst náš chytrý kontrakt pomocí [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3). + +### Nastavte si svůj koncový bod Alchemy Web3 a kontrakt {#setup-alchemy-endpoint} + +Nejprve, pokud ho ještě nemáte, budete muset nainstalovat [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) tak, že přejdete do domovského adresáře: `nft-minter-tutorial` v terminálu: + +```text +cd .. +npm install @alch/alchemy-web3 +``` + +Dále se vraťme do našeho souboru `interact.js`. Na začátek souboru přidejte následující kód, abyste naimportovali svůj klíč Alchemy ze souboru .env a nastavili si svůj koncový bod Alchemy Web3: + +```javascript +require("dotenv").config() +const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(alchemyKey) +``` + +[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) 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ě! + +Dále přidejme do našeho souboru ABI a adresu kontraktu. + +```javascript +require("dotenv").config() +const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(alchemyKey) + +const contractABI = require("../contract-abi.json") +const contractAddress = "0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE" +``` + +Jakmile máme obojí, jsme připraveni začít kódovat naši funkci mintování! + +## Implementujte funkci mintNFT {#implement-the-mintnft-function} + +Uvnitř vašeho souboru `interact.js` definujme naši funkci `mintNFT`, která bude, jak název napovídá, mintovat naše NFT. + +Protože budeme provádět četná asynchronní volání (na Pinata pro připnutí našich metadat na IPFS, Alchemy Web3 pro načtení našeho chytrého kontraktu a MetaMask pro podepsání našich transakcí), bude naše funkce také asynchronní. + +Tři vstupy do naší funkce budou `url` našeho digitálního aktiva, `name` a `description`. Pod funkci `connectWallet` přidejte následující signaturu funkce: + +```javascript +export const mintNFT = async (url, name, description) => {} +``` + +### Zpracování chyb na vstupu {#input-error-handling} + +Je samozřejmě logické mít na začátku funkce nějaké zpracování chyb na vstupu, takže z této funkce odejdeme, pokud naše vstupní parametry nejsou správné. Do naší funkce přidejme následující kód: + +```javascript +export const mintNFT = async (url, name, description) => { + //zpracování chyb + if (url.trim() == "" || name.trim() == "" || description.trim() == "") { + return { + success: false, + status: "❗Před mintováním se prosím ujistěte, že jsou všechna pole vyplněna.", + } + } +} +``` + +V podstatě, pokud je některý ze vstupních parametrů prázdný řetězec, vrátíme objekt JSON, kde je booleovská hodnota `success` false a řetězec `status` sděluje, že všechna pole v našem uživatelském rozhraní musí být vyplněna. + +### Nahrajte metadata na IPFS {#upload-metadata-to-ipfs} + +Jakmile víme, že jsou naše metadata správně naformátována, dalším krokem je zabalit je do objektu JSON a nahrát je na IPFS prostřednictvím funkce `pinJSONToIPFS`, kterou jsme napsali! + +K tomu musíme nejprve naimportovat funkci `pinJSONToIPFS` do našeho souboru `interact.js`. Na samý začátek `interact.js` přidejme: + +```javascript +import { pinJSONToIPFS } from "./pinata.js" +``` + +Vzpomeňte si, že `pinJSONToIPFS` přijímá jako vstup tělo JSON. Takže než ji zavoláme, budeme muset naformátovat naše parametry `url`, `name` a `description` do objektu JSON. + +Aktualizujme náš kód tak, aby vytvořil objekt JSON nazvaný `metadata` a poté proveďme volání `pinJSONToIPFS` s tímto parametrem `metadata`: + +```javascript +export const mintNFT = async (url, name, description) => { + //zpracování chyb + if (url.trim() == "" || name.trim() == "" || description.trim() == "") { + return { + success: false, + status: "❗Před mintováním se prosím ujistěte, že jsou všechna pole vyplněna.", + } + } + + //vytvořit metadata + const metadata = new Object() + metadata.name = name + metadata.image = url + metadata.description = description + + //volání pinata + const pinataResponse = await pinJSONToIPFS(metadata) + if (!pinataResponse.success) { + return { + success: false, + status: "😢 Něco se pokazilo při nahrávání vašeho tokenURI.", + } + } + const tokenURI = pinataResponse.pinataUrl +} +``` + +Všimněte si, že odpověď na naše volání `pinJSONToIPFS(metadata)` ukládáme do objektu `pinataResponse`. Poté tento objekt analyzujeme na případné chyby. + +Pokud dojde k chybě, vrátíme objekt JSON, kde je booleovská hodnota `success` false a náš řetězec `status` sděluje, že naše volání selhalo. V opačném případě extrahujeme `pinataURL` z `pinataResponse` a uložíme ji jako naši proměnnou `tokenURI`. + +Nyní je čas načíst náš chytrý kontrakt pomocí Alchemy Web3 API, které jsme inicializovali na začátku našeho souboru. Na konec funkce `mintNFT` přidejte následující řádek kódu, který nastaví kontrakt na globální proměnnou `window.contract`: + +```javascript +window.contract = await new web3.eth.Contract(contractABI, contractAddress) +``` + +Poslední věc, kterou je třeba přidat do naší funkce `mintNFT`, je naše transakce Ethereum: + +```javascript +//nastavte si svou transakci Ethereum +const transactionParameters = { + to: contractAddress, // Povinné s výjimkou zveřejnění kontraktu. + from: window.ethereum.selectedAddress, // musí odpovídat aktivní adrese uživatele. + data: window.contract.methods + .mintNFT(window.ethereum.selectedAddress, tokenURI) + .encodeABI(), //provést volání chytrého kontraktu NFT +} + +//podepsat transakci přes MetaMask +try { + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [transactionParameters], + }) + return { + success: true, + status: + "✅ Podívejte se na svou transakci na Etherscanu: https://ropsten.etherscan.io/tx/" + + txHash, + } +} catch (error) { + return { + success: false, + status: "😥 Něco se pokazilo: " + error.message, + } +} +``` + +Pokud jste již obeznámeni s transakcemi na Ethereu, všimnete si, že struktura je docela podobná tomu, co jste již viděli. + +- Nejprve nastavíme naše parametry transakce. + - `to` určuje adresu příjemce (náš chytrý kontrakt) + - `from` určuje podepisujícího transakce (připojená adresa uživatele k MetaMasku: `window.ethereum.selectedAddress`) + - `data` obsahuje volání metody `mintNFT` našeho chytrého kontraktu, která jako vstup přijímá náš `tokenURI` a adresu peněženky uživatele, `window.ethereum.selectedAddress` +- Poté provedeme await volání `window.ethereum.request`, kde požádáme MetaMask o podepsání transakce. Všimněte si, že v tomto požadavku specifikujeme naši eth metodu (eth_SentTransaction) 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í objekt JSON, kde je booleovská hodnota `success` nastavena na true a řetězec `status` vyzve uživatele, aby se podíval na Etherscan pro více informací o své transakci. + - Pokud transakce selže, funkce vrátí objekt JSON, kde je booleovská hodnota `success` nastavena na false a řetězec `status` sděluje chybovou zprávu. + +Celkově by naše funkce `mintNFT` měla vypadat takto: + +```javascript +export const mintNFT = async (url, name, description) => { + //zpracování chyb + if (url.trim() == "" || name.trim() == "" || description.trim() == "") { + return { + success: false, + status: "❗Před mintováním se prosím ujistěte, že jsou všechna pole vyplněna.", + } + } + + //vytvořit metadata + const metadata = new Object() + metadata.name = name + metadata.image = url + metadata.description = description + + //žádost o připnutí pinata + const pinataResponse = await pinJSONToIPFS(metadata) + if (!pinataResponse.success) { + return { + success: false, + status: "😢 Něco se pokazilo při nahrávání vašeho tokenURI.", + } + } + const tokenURI = pinataResponse.pinataUrl + + //načíst chytrý kontrakt + window.contract = await new web3.eth.Contract(contractABI, contractAddress) //loadContract(); + + //nastavte si svou transakci Ethereum + const transactionParameters = { + to: contractAddress, // Povinné s výjimkou zveřejnění kontraktu. + from: window.ethereum.selectedAddress, // musí odpovídat aktivní adrese uživatele. + data: window.contract.methods + .mintNFT(window.ethereum.selectedAddress, tokenURI) + .encodeABI(), //provést volání chytrého kontraktu NFT + } + + //podepsat transakci přes MetaMask + try { + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [transactionParameters], + }) + return { + success: true, + status: + "✅ Podívejte se na svou transakci na Etherscanu: https://ropsten.etherscan.io/tx/" + + txHash, + } + } catch (error) { + return { + success: false, + status: "😥 Něco se pokazilo: " + error.message, + } + } +} +``` + +To je jedna obrovská funkce! Nyní už jen stačí připojit naši funkci `mintNFT` k naší komponentě `Minter.js`... + +## Připojte mintNFT k našemu frontendu Minter.js {#connect-our-frontend} + +Otevřete svůj soubor `Minter.js` a aktualizujte řádek `import { connectWallet, getCurrentWalletConnected } from \"./utils/interact.js\";` na začátku na: + +```javascript +import { + connectWallet, + getCurrentWalletConnected, + mintNFT, +} from "./utils/interact.js" +``` + +Nakonec implementujte funkci `onMintPressed`, abyste provedli await volání na vaši importovanou funkci `mintNFT` a aktualizovali stavovou proměnnou `status` tak, aby odrážela, zda naše transakce uspěla nebo selhala: + +```javascript +const onMintPressed = async () => { + const { status } = await mintNFT(url, name, description) + setStatus(status) +} +``` + +## Nasaďte své NFT na živou webovou stránku {#deploy-your-NFT} + +Jste připraveni uvést svůj projekt do provozu, aby s ním uživatelé mohli interagovat? Podívejte se na [tento návod](https://docs.alchemy.com/alchemy/tutorials/nft-minter/how-do-i-deploy-nfts-online) pro nasazení vašeho Minteru na živou webovou stránku. + +Ještě poslední krok... + +## Dobijte svět blockchainu {#take-the-blockchain-world-by-storm} + +Děláme si legraci, dostali jste se až na konec návodu! + +Abychom to shrnuli, vytvořením minteru NFT jste se úspěšně naučili, jak: + +- Připojit se k MetaMasku prostřednictvím vašeho frontendového projektu +- Volat metody chytrého kontraktu z vašeho frontendu +- Podepisovat transakce pomocí MetaMasku + +Pravděpodobně se budete chtít pochlubit NFT vyraženými prostřednictvím vaší dapp ve své peněžence – proto se určitě podívejte na náš rychlý návod [Jak si zobrazit NFT ve vaší peněžence](https://www.alchemy.com/docs/how-to-view-your-nft-in-your-mobile-wallet)! + +A jako vždy, pokud máte nějaké dotazy, jsme tu, abychom vám pomohli na [Alchemy Discordu](https://discord.gg/gWuC7zB). Nemůžeme se dočkat, až uvidíme, jak koncepty z tohoto návodu uplatníte ve svých budoucích projektech! diff --git a/public/content/translations/cs/developers/tutorials/optimism-std-bridge-annotated-code/index.md b/public/content/translations/cs/developers/tutorials/optimism-std-bridge-annotated-code/index.md new file mode 100644 index 00000000000..7d40bdc994e --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/optimism-std-bridge-annotated-code/index.md @@ -0,0 +1,1356 @@ +--- +title: "Procházení kontraktu standardního přemostění Optimism" +description: "Jak funguje standardní přemostění pro Optimism? Proč to funguje zrovna takhle?" +author: Ori Pomerantz +tags: [ "solidity", "přemostění", "vrstva 2" ] +skill: intermediate +published: 2022-03-30 +lang: cs +--- + +[Optimism](https://www.optimism.io/) je [optimistický rollup](/developers/docs/scaling/optimistic-rollups/). +Optimistické rollupy mohou zpracovávat transakce za mnohem nižší cenu než hlavní síť Ethereum (také známá jako první vrstva nebo L1), protože transakce zpracovává jen několik uzlů, nikoli každý uzel v síti. +Všechna data jsou přitom zapsána na L1, takže vše lze prokázat a rekonstruovat se všemi zárukami integrity a dostupnosti hlavní sítě. + +Aby bylo možné používat aktiva z L1 v síti Optimism (nebo v jakékoli jiné L2), je třeba je [přemostit](/bridges/#prerequisites). +Jedním ze způsobů, jak toho dosáhnout, je, že uživatelé uzamknou aktiva (nejčastěji se jedná o ETH a [tokeny ERC-20](/developers/docs/standards/tokens/erc-20/)) na L1 a obdrží ekvivalentní aktiva k použití na L2. +Nakonec je může chtít ten, kdo je získá, přemostit zpět na L1. +Přitom se aktiva na L2 spálí a poté se na L1 uvolní zpět uživateli. + +Takto funguje [standardní přemostění Optimism](https://docs.optimism.io/app-developers/bridging/standard-bridge). +V tomto článku si projdeme zdrojový kód tohoto přemostění, abychom viděli, jak funguje, a prostudujeme si jej jako příklad dobře napsaného kódu v Solidity. + +## Kontrolní toky {#control-flows} + +Přemostění má dva hlavní toky: + +- Vklad (z L1 na L2) +- Výběr (z L2 na L1) + +### Tok vkladů {#deposit-flow} + +#### Vrstva 1 {#deposit-flow-layer-1} + +1. Při vkladu tokenu ERC-20 udělí vkladatel přemostění povolení k útratě vkládané částky. +2. Vkladatel zavolá přemostění na L1 (`depositERC20`, `depositERC20To`, `depositETH` nebo `depositETHTo`). +3. Přemostění na L1 převezme přemostěné aktivum. + - ETH: Aktivum je převedeno vkladatelem jako součást volání. + - ERC-20: Aktivum je přemostěním převedeno samo sobě pomocí povolení poskytnutého vkladatelem. +4. Přemostění na L1 použije mezidoménový mechanismus zpráv k zavolání `finalizeDeposit` na přemostění na L2. + +#### Vrstva 2 {#deposit-flow-layer-2} + +5. Přemostění na L2 ověří, že volání `finalizeDeposit` je legitimní: + - Pochází z mezidoménového kontraktu zpráv. + - Původně pocházelo z přemostění na L1. +6. Přemostění na L2 zkontroluje, zda je kontrakt tokenu ERC-20 na L2 správný: + - Kontrakt na L2 hlásí, že jeho protějšek na L1 je stejný jako ten, ze kterého tokeny na L1 pocházejí. + - Kontrakt na L2 hlásí, že podporuje správné rozhraní ([pomocí ERC-165](https://eips.ethereum.org/EIPS/eip-165)). +7. Pokud je kontrakt L2 správný, zavolejte jej, aby vyrazil příslušný počet tokenů na příslušnou adresu. Pokud ne, zahajte proces výběru, který uživateli umožní nárokovat tokeny na L1. + +### Tok výběrů {#withdrawal-flow} + +#### Vrstva 2 {#withdrawal-flow-layer-2} + +1. Vybírající zavolá přemostění L2 (`withdraw` nebo `withdrawTo`). +2. Přemostění L2 spálí příslušný počet tokenů patřících `msg.sender`. +3. Přemostění L2 použije mezidoménový mechanismus zpráv k zavolání `finalizeETHWithdrawal` nebo `finalizeERC20Withdrawal` na přemostění na L1. + +#### Vrstva 1 {#withdrawal-flow-layer-1} + +4. Přemostění na L1 ověří, že volání `finalizeETHWithdrawal` nebo `finalizeERC20Withdrawal` je legitimní: + - Pochází z mezidoménového mechanismu zpráv. + - Původně pocházelo z přemostění na L2. +5. Přemostění na L1 převede příslušné aktivum (ETH nebo ERC-20) na příslušnou adresu. + +## Kód na vrstvě 1 {#layer-1-code} + +Toto je kód, který běží na L1, tedy hlavní síti Etherea. + +### IL1ERC20Bridge {#IL1ERC20Bridge} + +[Toto rozhraní je definováno zde](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/IL1ERC20Bridge.sol). +Obsahuje funkce a definice potřebné pro přemostění tokenů ERC-20. + +```solidity +// SPDX-License-Identifier: MIT +``` + +[Většina kódu Optimism je vydána pod licencí MIT](https://help.optimism.io/hc/en-us/articles/4411908707995-What-software-license-does-Optimism-use-). + +```solidity +pragma solidity >0.5.0 <0.9.0; +``` + +V době psaní je poslední verze Solidity 0.8.12. +Dokud nebude vydána verze 0.9.0, nevíme, zda s ní tento kód bude kompatibilní. + +```solidity +/** + * @title IL1ERC20Bridge + */ +interface IL1ERC20Bridge { + /********** + * Události * + **********/ + + event ERC20DepositInitiated( +``` + +V terminologii přemostění Optimism znamená _vklad_ převod z L1 na L2 a _výběr_ převod z L2 na L1. + +```solidity + address indexed _l1Token, + address indexed _l2Token, +``` + +Ve většině případů se adresa ERC-20 na L1 nerovná adrese ekvivalentního ERC-20 na L2. +[Seznam adres tokenů naleznete zde](https://static.optimism.io/optimism.tokenlist.json). +Adresa s `chainId` 1 je na L1 (hlavní síť) a adresa s `chainId` 10 je na L2 (Optimism). +Další dvě hodnoty `chainId` jsou pro testovací síť Kovan (42) a testovací síť Optimistic Kovan (69). + +```solidity + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); +``` + +K převodům je možné přidávat poznámky, které se v takovém případě přidají k událostem, jež je hlásí. + +```solidity + event ERC20WithdrawalFinalized( + address indexed _l1Token, + address indexed _l2Token, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); +``` + +Stejný kontrakt přemostění zpracovává převody v obou směrech. +V případě přemostění L1 to znamená inicializaci vkladů a finalizaci výběrů. + +```solidity + + /******************** + * Veřejné funkce * + ********************/ + + /** + * @dev získá adresu odpovídajícího kontraktu přemostění na L2. + * @return Adresa odpovídajícího kontraktu přemostění na L2. + */ + function l2TokenBridge() external returns (address); +``` + +Tato funkce není ve skutečnosti potřeba, protože na L2 se jedná o předem nasazený kontrakt, takže se vždy nachází na adrese `0x4200000000000000000000000000000000000010`. +Je zde z důvodu symetrie s přemostěním L2, protože adresa přemostění L1 _není_ triviálně zjistitelná. + +```solidity + /** + * @dev vloží částku ERC20 na zůstatek volajícího na L2. + * @param _l1Token Adresa ERC20 na L1, který vkládáme. + * @param _l2Token Adresa příslušného ERC20 na L2. + * @param _amount Částka ERC20 k vložení. + * @param _l2Gas Limit transakčních poplatků potřebný k dokončení vkladu na L2. + * @param _data Volitelná data k předání na L2. Tato data jsou poskytována + * pouze pro pohodlí externích kontraktů. Kromě vynucení maximální + * délky tyto kontrakty neposkytují žádné záruky ohledně jejich obsahu. + */ + function depositERC20( + address _l1Token, + address _l2Token, + uint256 _amount, + uint32 _l2Gas, + bytes calldata _data + ) external; +``` + +Parametr `_l2Gas` je množství transakčního poplatku na L2, které může transakce utratit. +[Až do určitého (vysokého) limitu je to zdarma](https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions-2), takže pokud kontrakt ERC-20 nedělá při ražbě něco opravdu zvláštního, neměl by to být problém. +Tato funkce se stará o běžný scénář, kdy uživatel přemosťuje aktiva na stejnou adresu na jiném blockchainu. + +```solidity + /** + * @dev vloží částku ERC20 na zůstatek příjemce na L2. + * @param _l1Token Adresa ERC20 na L1, který vkládáme. + * @param _l2Token Adresa příslušného ERC20 na L2. + * @param _to Adresa na L2, na kterou se připíše výběr. + * @param _amount Částka ERC20 k vložení. + * @param _l2Gas Limit transakčních poplatků potřebný k dokončení vkladu na L2. + * @param _data Volitelná data k předání na L2. Tato data jsou poskytována + * pouze pro pohodlí externích kontraktů. Kromě vynucení maximální + * délky tyto kontrakty neposkytují žádné záruky ohledně jejich obsahu. + */ + function depositERC20To( + address _l1Token, + address _l2Token, + address _to, + uint256 _amount, + uint32 _l2Gas, + bytes calldata _data + ) external; +``` + +Tato funkce je téměř identická s `depositERC20`, ale umožňuje poslat ERC-20 na jinou adresu. + +```solidity +} /************************* + * Meziřetězcové funkce * + *************************/ + + /** + * @dev Dokončí výběr z L2 na L1 a připíše prostředky na zůstatek příjemce + * tokenu ERC20 na L1. + * Toto volání selže, pokud inicializovaný výběr z L2 nebyl finalizován. + * + * @param _l1Token Adresa tokenu na L1, pro který se má dokončit výběr. + * @param _l2Token Adresa tokenu na L2, kde byl výběr iniciován. + * @param _from Adresa na L2, která iniciuje převod. + * @param _to Adresa na L1, na kterou se má výběr připsat. + * @param _amount Částka ERC20 k vložení. + * @param _data Data poskytnutá odesílatelem na L2. Tato data jsou poskytována + * pouze pro pohodlí externích kontraktů. Kromě vynucení maximální + * délky tyto kontrakty neposkytují žádné záruky ohledně jejich obsahu. + */ + function finalizeERC20Withdrawal( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external; +} +``` + +Výběry (a další zprávy z L2 do L1) v Optimism jsou dvoukrokový proces: + +1. Iniciační transakce na L2. +2. Finalizační nebo nárokovací transakce na L1. + Tato transakce se musí uskutečnit po skončení [období pro napadení chyb](https://community.optimism.io/docs/how-optimism-works/#fault-proofs) pro transakci na L2. + +### IL1StandardBridge {#il1standardbridge} + +[Toto rozhraní je definováno zde](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/IL1StandardBridge.sol). +Tento soubor obsahuje definice událostí a funkcí pro ETH. +Tyto definice jsou velmi podobné těm, které jsou definovány v `IL1ERC20Bridge` výše pro ERC-20. + +Rozhraní přemostění je rozděleno do dvou souborů, protože některé tokeny ERC-20 vyžadují vlastní zpracování a standardní přemostění je nemůže zpracovat. +Tímto způsobem může vlastní přemostění, které takový token zpracovává, implementovat `IL1ERC20Bridge` a nemusí přemosťovat také ETH. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity >0.5.0 <0.9.0; + +import "./IL1ERC20Bridge.sol"; + +/** + * @title IL1StandardBridge + */ +interface IL1StandardBridge is IL1ERC20Bridge { + /********** + * Události * + **********/ + event ETHDepositInitiated( + address indexed _from, + address indexed _to, + uint256 _amount, + bytes _data + ); +``` + +Tato událost je téměř totožná s verzí ERC-20 (`ERC20DepositInitiated`), pouze bez adres tokenů L1 a L2. +Totéž platí pro ostatní události a funkce. + +```solidity + event ETHWithdrawalFinalized( + . + . + . + ); + + /******************** + * Veřejné funkce * + ********************/ + + /** + * @dev Vloží částku ETH na zůstatek volajícího na L2. + . + . + . + */ + function depositETH(uint32 _l2Gas, bytes calldata _data) external payable; + + /** + * @dev Vloží částku ETH na zůstatek příjemce na L2. + . + . + . + */ + function depositETHTo( + address _to, + uint32 _l2Gas, + bytes calldata _data + ) external payable; + + /************************* + * Meziřetězcové funkce * + *************************/ + + /** + * @dev Dokončí výběr z L2 na L1 a připíše prostředky na zůstatek příjemce + * tokenu ETH na L1. Protože tuto funkci může volat pouze xDomainMessenger, nebude nikdy volána + * před dokončením výběru. + . + . + . + */ + function finalizeETHWithdrawal( + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external; +} +``` + +### CrossDomainEnabled {#crossdomainenabled} + +[Tento kontrakt](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/bridge/CrossDomainEnabled.sol) je zděděn oběma přemostěními ([L1](#the-l1-bridge-contract) a [L2](#the-l2-bridge-contract)) pro posílání zpráv na druhou vrstvu. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity >0.5.0 <0.9.0; + +/* Importy rozhraní */ +import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol"; +``` + +[Toto rozhraní](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/bridge/ICrossDomainMessenger.sol) říká kontraktu, jak posílat zprávy na druhou vrstvu pomocí mezidoménového posílače zpráv. +Tento mezidoménový posílač zpráv je zcela jiný systém a zaslouží si vlastní článek, který, doufám, v budoucnu napíšu. + +```solidity +/** + * @title CrossDomainEnabled + * @dev Pomocný kontrakt pro kontrakty provádějící mezidoménovou komunikaci + * + * Použitý kompilátor: definován dědícím kontraktem + */ +contract CrossDomainEnabled { + /************* + * Proměnné * + *************/ + + // Kontrakt Messengeru použitý k odesílání a přijímání zpráv z druhé domény. + address public messenger; + + /*************** + * Konstruktor * + ***************/ + + /** + * @param _messenger Adresa CrossDomainMessenger na aktuální vrstvě. + */ + constructor(address _messenger) { + messenger = _messenger; + } +``` + +Jediný parametr, který kontrakt potřebuje znát, je adresa mezidoménového posílače zpráv na této vrstvě. +Tento parametr je nastaven jednou, v konstruktoru, a nikdy se nemění. + +```solidity + + /********************** + * Modifikátory funkcí * + **********************/ + + /** + * Vynucuje, aby upravená funkce byla volána pouze z konkrétního mezidoménového účtu. + * @param _sourceDomainAccount Jediný účet na původní doméně, který je + * oprávněn tuto funkci volat. + */ + modifier onlyFromCrossDomainAccount(address _sourceDomainAccount) { +``` + +Mezidoménové zasílání zpráv je dostupné jakémukoli kontraktu na blockchainu, kde běží (buď na hlavní síti Etherea, nebo Optimism). +Potřebujeme ale, aby přemostění na každé straně důvěřovalo _pouze_ určitým zprávám, pokud pocházejí z přemostění na druhé straně. + +```solidity + require( + msg.sender == address(getCrossDomainMessenger()), + "OVM_XCHAIN: kontrakt posílače zpráv není ověřený" + ); +``` + +Důvěřovat lze pouze zprávám z příslušného mezidoménového posílače zpráv (`messenger`, jak uvidíte níže). + +```solidity + + require( + getCrossDomainMessenger().xDomainMessageSender() == _sourceDomainAccount, + "OVM_XCHAIN: nesprávný odesílatel mezidoménové zprávy" + ); +``` + +Způsob, jakým mezidoménový posílač zpráv poskytuje adresu, která odeslala zprávu z druhé vrstvy, je [funkce `.xDomainMessageSender()`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L122-L128). +Pokud je volána v transakci, která byla zprávou iniciována, může tyto informace poskytnout. + +Musíme se ujistit, že zpráva, kterou jsme obdrželi, pochází z druhého přemostění. + +```solidity + + _; + } + + /********************** + * Interní funkce * + **********************/ + + /** + * Získá posílače zpráv, obvykle z úložiště. Tato funkce je vystavena pro případ, že by ji + * dětský kontrakt potřeboval přepsat. + * @return Adresa kontraktu mezidoménového posílače zpráv, který by se měl použít. + */ + function getCrossDomainMessenger() internal virtual returns (ICrossDomainMessenger) { + return ICrossDomainMessenger(messenger); + } +``` + +Tato funkce vrací mezidoménového posílače zpráv. +Používáme funkci spíše než proměnnou `messenger`, aby kontrakty, které z ní dědí, mohly použít algoritmus k určení, který mezidoménový posílač zpráv použít. + +```solidity + + /** + * Odešle zprávu účtu v jiné doméně. + * @param _crossDomainTarget Zamýšlený příjemce v cílové doméně. + * @param _message Data k odeslání cíli (obvykle calldata pro funkci s + * `onlyFromCrossDomainAccount()`) + * @param _gasLimit Limit transakčních poplatků pro příjem zprávy v cílové doméně. + */ + function sendCrossDomainMessage( + address _crossDomainTarget, + uint32 _gasLimit, + bytes memory _message +``` + +A nakonec funkce, která posílá zprávu na druhou vrstvu. + +```solidity + ) internal { + // slither-disable-next-line reentrancy-events, reentrancy-benign +``` + +[Slither](https://github.com/crytic/slither) je statický analyzátor, který Optimism spouští na každém kontraktu, aby hledal zranitelnosti a další potenciální problémy. +V tomto případě následující řádek spouští dvě zranitelnosti: + +1. [Události opětovného vstupu (reentrancy)](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3) +2. [Nezávažné opětovné vstupy (reentrancy)](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2) + +```solidity + getCrossDomainMessenger().sendMessage(_crossDomainTarget, _message, _gasLimit); + } +} +``` + +V tomto případě se o opětovné vstupy (reentrancy) nestaráme, protože víme, že `getCrossDomainMessenger()` vrací důvěryhodnou adresu, i když Slither to nemá jak vědět. + +### Kontrakt přemostění L1 {#the-l1-bridge-contract} + +[Zdrojový kód tohoto kontraktu je zde](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol). + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; +``` + +Rozhraní mohou být součástí jiných kontraktů, takže musí podporovat širokou škálu verzí Solidity. +Ale samotné přemostění je náš kontrakt a můžeme být přísní ohledně toho, jakou verzi Solidity používá. + +```solidity +/* Importy rozhraní */ +import { IL1StandardBridge } from "./IL1StandardBridge.sol"; +import { IL1ERC20Bridge } from "./IL1ERC20Bridge.sol"; +``` + +[IL1ERC20Bridge](#IL1ERC20Bridge) a [IL1StandardBridge](#IL1StandardBridge) jsou vysvětleny výše. + +```solidity +import { IL2ERC20Bridge } from "../../L2/messaging/IL2ERC20Bridge.sol"; +``` + +[Toto rozhraní](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol) nám umožňuje vytvářet zprávy pro ovládání standardního přemostění na L2. + +```solidity +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +``` + +[Toto rozhraní](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) nám umožňuje ovládat kontrakty ERC-20. +[Více si o tom můžete přečíst zde](/developers/tutorials/erc20-annotated-code/#the-interface). + +```solidity +/* Importy knihoven */ +import { CrossDomainEnabled } from "../../libraries/bridge/CrossDomainEnabled.sol"; +``` + +[Jak je vysvětleno výše](#crossdomainenabled), tento kontrakt se používá pro mezivrstvové zasílání zpráv. + +```solidity +import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol"; +``` + +`Lib_PredeployAddresses` (https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/constants/Lib_PredeployAddresses.sol) má adresy kontraktů L2, které mají vždy stejnou adresu. To zahrnuje standardní přemostění na L2. + +```solidity +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; +``` + +[Nástroje pro adresy OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol). Používá se k rozlišení mezi adresami kontraktů a adresami patřícími externě vlastněným účtům (EOA). + +Všimněte si, že se nejedná o dokonalé řešení, protože neexistuje způsob, jak rozlišit mezi přímými voláními a voláními provedenými z konstruktoru kontraktu, ale alespoň nám to umožňuje identifikovat a zabránit některým běžným chybám uživatelů. + +```solidity +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +``` + +[Standard ERC-20](https://eips.ethereum.org/EIPS/eip-20) podporuje dva způsoby, jak může kontrakt nahlásit selhání: + +1. Zpětné vrácení (revert) +2. Vrátit `false` + +Zpracování obou případů by náš kód zkomplikovalo, takže místo toho používáme [`SafeERC20` od OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol), který zajišťuje, že [všechna selhání vedou k zpětnému vrácení (revert)](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol#L96). + +```solidity +/** + * @title L1StandardBridge + * @dev Přemostění L1 pro ETH a ERC20 je kontrakt, který ukládá vložené prostředky z L1 a standardní + * tokeny, které se používají na L2. Synchronizuje odpovídající přemostění L2, informuje ho o vkladech + * a naslouchá mu pro nově finalizované výběry. + * + */ +contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled { + using SafeERC20 for IERC20; +``` + +Tento řádek určuje, že se má použít obal `SafeERC20` pokaždé, když použijeme rozhraní `IERC20`. + +```solidity + + /******************************** + * Reference na externí kontrakty * + ********************************/ + + address public l2TokenBridge; +``` + +Adresa [L2StandardBridge](#the-l2-bridge-contract). + +```solidity + + // Mapuje token L1 na token L2 k zůstatku vloženého tokenu L1 + mapping(address => mapping(address => uint256)) public deposits; +``` + +Dvojité [mapování](https://www.tutorialspoint.com/solidity/solidity_mappings.htm) jako je toto, je způsob, jak definovat [dvojrozměrné řídké pole](https://en.wikipedia.org/wiki/Sparse_matrix). +Hodnoty v této datové struktuře jsou identifikovány jako `deposit[adresa tokenu L1][adresa tokenu L2]`. +Výchozí hodnota je nula. +Do úložiště jsou zapsány pouze buňky, které jsou nastaveny na jinou hodnotu. + +```solidity + + /*************** + * Konstruktor * + ***************/ + + // Tento kontrakt se nachází za proxy, takže parametry konstruktoru nebudou použity. + constructor() CrossDomainEnabled(address(0)) {} +``` + +Chceme mít možnost upgradovat tento kontrakt bez nutnosti kopírovat všechny proměnné v úložišti. +K tomu používáme [`Proxy`](https://docs.openzeppelin.com/contracts/3.x/api/proxy), kontrakt, který používá [`delegatecall`](https://solidity-by-example.org/delegatecall/) k přenosu volání na samostatný kontrakt, jehož adresa je uložena v proxy kontraktu (při upgradu řeknete proxy, aby změnila tuto adresu). +Při použití `delegatecall` zůstává úložiště úložištěm _volajícího_ kontraktu, takže hodnoty všech proměnných stavu kontraktu zůstávají nedotčeny. + +Jedním z důsledků tohoto vzoru je, že úložiště kontraktu, který je _volaný_ pomocí `delegatecall`, se nepoužívá, a proto hodnoty konstruktoru, které mu jsou předány, nejsou důležité. +To je důvod, proč můžeme konstruktoru `CrossDomainEnabled` poskytnout nesmyslnou hodnotu. +Je to také důvod, proč je níže uvedená inicializace oddělena od konstruktoru. + +```solidity + /****************** + * Inicializace * + ******************/ + + /** + * @param _l1messenger Adresa L1 Messengeru používaná pro meziřetězcovou komunikaci. + * @param _l2TokenBridge Adresa standardního přemostění na L2. + */ + // slither-disable-next-line external-function +``` + +Tento [test Slither](https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external) identifikuje funkce, které nejsou volány z kódu kontraktu a mohly by být proto deklarovány jako `external` místo `public`. +Náklady na transakční poplatky u funkcí `external` mohou být nižší, protože jim mohou být parametry poskytnuty v calldata. +Funkce deklarované jako `public` musí být přístupné zevnitř kontraktu. +Kontrakty nemohou měnit svá vlastní calldata, takže parametry musí být v paměti. +Když je taková funkce volána externě, je nutné zkopírovat calldata do paměti, což stojí transakční poplatky. +V tomto případě je funkce volána pouze jednou, takže neefektivita pro nás není důležitá. + +```solidity + function initialize(address _l1messenger, address _l2TokenBridge) public { + require(messenger == address(0), "Contract has already been initialized."); +``` + +Funkce `initialize` by měla být volána pouze jednou. +Pokud se adresa mezidoménového posílače zpráv na L1 nebo přemostění tokenu na L2 změní, vytvoříme nové proxy a nové přemostění, které ho volá. +Je nepravděpodobné, že by k tomu došlo, s výjimkou upgradu celého systému, což je velmi vzácná událost. + +Všimněte si, že tato funkce nemá žádný mechanismus, který by omezoval, _kdo_ ji může volat. +To znamená, že teoreticky by útočník mohl počkat, až nasadíme proxy a první verzi přemostění, a poté provést [front-running](https://solidity-by-example.org/hacks/front-running/) a dostat se k funkci `initialize` dříve, než to udělá legitimní uživatel. Existují však dvě metody, jak tomu zabránit: + +1. Pokud kontrakty nejsou nasazeny přímo pomocí EOA, ale [v transakci, která nechá vytvořit jiný kontrakt](https://medium.com/upstate-interactive/creating-a-contract-with-a-smart-contract-bdb67c5c8595), celý proces může být atomický a dokončen dříve, než je provedena jakákoli jiná transakce. +2. Pokud legitimní volání `initialize` selže, je vždy možné ignorovat nově vytvořené proxy a přemostění a vytvořit nové. + +```solidity + messenger = _l1messenger; + l2TokenBridge = _l2TokenBridge; + } +``` + +Toto jsou dva parametry, které přemostění potřebuje znát. + +```solidity + + /************** + * Vkládání * + **************/ + + /** @dev Modifikátor vyžadující, aby odesílatel byl EOA. Toto ověření by mohl obejít zákeřný + * kontrakt pomocí initcode, ale řeší to chybu uživatele, které se chceme vyhnout. + */ + modifier onlyEOA() { + // Používá se k zastavení vkladů z kontraktů (zabraňuje náhodné ztrátě tokenů) + require(!Address.isContract(msg.sender), "Account not EOA"); + _; + } +``` + +To je důvod, proč jsme potřebovali nástroje `Address` od OpenZeppelin. + +```solidity + /** + * @dev Tuto funkci lze volat bez dat + * pro vložení částky ETH na zůstatek volajícího na L2. + * Protože funkce receive nepřijímá data, konzervativní + * výchozí částka se předává na L2. + */ + receive() external payable onlyEOA { + _initiateETHDeposit(msg.sender, msg.sender, 200_000, bytes("")); + } +``` + +Tato funkce existuje pro testovací účely. +Všimněte si, že se neobjevuje v definicích rozhraní – není určena pro běžné použití. + +```solidity + /** + * @inheritdoc IL1StandardBridge + */ + function depositETH(uint32 _l2Gas, bytes calldata _data) external payable onlyEOA { + _initiateETHDeposit(msg.sender, msg.sender, _l2Gas, _data); + } + + /** + * @inheritdoc IL1StandardBridge + */ + function depositETHTo( + address _to, + uint32 _l2Gas, + bytes calldata _data + ) external payable { + _initiateETHDeposit(msg.sender, _to, _l2Gas, _data); + } +``` + +Tyto dvě funkce jsou obaly kolem `_initiateETHDeposit`, funkce, která zpracovává skutečný vklad ETH. + +```solidity + /** + * @dev Provádí logiku pro vklady uložením ETH a informováním L2 ETH Gateway o + * vkladu. + * @param _from Účet, ze kterého se má vklad na L1 stáhnout. + * @param _to Účet, na který se má vklad na L2 připsat. + * @param _l2Gas Limit transakčních poplatků potřebný k dokončení vkladu na L2. + * @param _data Volitelná data k předání na L2. Tato data jsou poskytována + * pouze pro pohodlí externích kontraktů. Kromě vynucení maximální + * délky tyto kontrakty neposkytují žádné záruky ohledně jejich obsahu. + */ + function _initiateETHDeposit( + address _from, + address _to, + uint32 _l2Gas, + bytes memory _data + ) internal { + // Sestavení calldata pro volání finalizeDeposit + bytes memory message = abi.encodeWithSelector( +``` + +Způsob, jakým fungují mezidoménové zprávy, je ten, že cílový kontrakt je volán se zprávou jako jeho calldata. +Kontrakty v Solidity vždy interpretují svá calldata v souladu s +[specifikacemi ABI](https://docs.soliditylang.org/en/v0.8.12/abi-spec.html). +Funkce Solidity [`abi.encodeWithSelector`](https://docs.soliditylang.org/en/v0.8.12/units-and-global-variables.html#abi-encoding-and-decoding-functions) vytváří tato calldata. + +```solidity + IL2ERC20Bridge.finalizeDeposit.selector, + address(0), + Lib_PredeployAddresses.OVM_ETH, + _from, + _to, + msg.value, + _data + ); +``` + +Zpráva zde znamená volání [funkce `finalizeDeposit`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol#L141-L148) s těmito parametry: + +| Parametr | Hodnota | Význam | +| ------------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| \_l1Token | address(0) | Speciální hodnota, která na L1 představuje ETH (který není tokenem ERC-20). | +| \_l2Token | Lib_PredeployAddresses.OVM_ETH | Kontrakt na L2, který spravuje ETH v síti Optimism, `0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000` (tento kontrakt je určen pouze pro interní použití v síti Optimism). | +| \_from | \_from | Adresa na L1, která odesílá ETH. | +| \_to | \_to | Adresa na L2, která přijímá ETH. | +| částka | msg.value | Odeslaná částka ve wei (která již byla odeslána do přemostění). | +| \_data | \_data | Další data, která se připojí k vkladu. | + +```solidity + // Odeslání calldata na L2 + // slither-disable-next-line reentrancy-events + sendCrossDomainMessage(l2TokenBridge, _l2Gas, message); +``` + +Odeslání zprávy prostřednictvím mezidoménového posílače zpráv. + +```solidity + // slither-disable-next-line reentrancy-events + emit ETHDepositInitiated(_from, _to, msg.value, _data); + } +``` + +Vyslat událost, aby se o tomto převodu informovala jakákoli decentralizovaná aplikace, která naslouchá. + +```solidity + /** + * @inheritdoc IL1ERC20Bridge + */ + function depositERC20( + . + . + . + ) external virtual onlyEOA { + _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, msg.sender, _amount, _l2Gas, _data); + } + + /** + * @inheritdoc IL1ERC20Bridge + */ + function depositERC20To( + . + . + . + ) external virtual { + _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _l2Gas, _data); + } +``` + +Tyto dvě funkce jsou obaly kolem `_initiateERC20Deposit`, funkce, která zpracovává skutečný vklad ERC-20. + +```solidity + /** + * @dev Provádí logiku pro vklady informováním kontraktu L2 Deposited Token + * o vkladu a voláním handleru pro uzamčení prostředků L1. (např. transferFrom) + * + * @param _l1Token Adresa ERC20 na L1, který vkládáme. + * @param _l2Token Adresa příslušného ERC20 na L2. + * @param _from Účet, ze kterého se má vklad na L1 stáhnout. + * @param _to Účet, na který se má vklad na L2 připsat. + * @param _amount Částka ERC20 k vložení. + * @param _l2Gas Limit transakčních poplatků potřebný k dokončení vkladu na L2. + * @param _data Volitelná data k předání na L2. Tato data jsou poskytována + * pouze pro pohodlí externích kontraktů. Kromě vynucení maximální + * délky tyto kontrakty neposkytují žádné záruky ohledně jejich obsahu. + */ + function _initiateERC20Deposit( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + uint32 _l2Gas, + bytes calldata _data + ) internal { +``` + +Tato funkce je podobná výše uvedené funkci `_initiateETHDeposit`, s několika důležitými rozdíly. +Prvním rozdílem je, že tato funkce přijímá adresy tokenů a částku k převodu jako parametry. +V případě ETH volání přemostění již zahrnuje převod aktiv na účet přemostění (`msg.value`). + +```solidity + // Když je vklad iniciován na L1, přemostění L1 převede prostředky na sebe pro budoucí + // výběry. safeTransferFrom také kontroluje, zda má kontrakt kód, takže toto selže, pokud + // _from je EOA nebo address(0). + // slither-disable-next-line reentrancy-events, reentrancy-benign + IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount); +``` + +Převody tokenů ERC-20 se řídí jiným procesem než ETH: + +1. Uživatel (`_from`) udělí přemostění povolení k převodu příslušných tokenů. +2. Uživatel zavolá přemostění s adresou kontraktu tokenu, částkou atd. +3. Přemostění převede tokeny (na sebe) jako součást procesu vkladu. + +První krok se může uskutečnit v samostatné transakci od posledních dvou. +Front-running však není problém, protože obě funkce, které volají `_initiateERC20Deposit` (`depositERC20` a `depositERC20To`), volají tuto funkci pouze s `msg.sender` jako parametrem `_from`. + +```solidity + // Sestavení calldata pro _l2Token.finalizeDeposit(_to, _amount) + bytes memory message = abi.encodeWithSelector( + IL2ERC20Bridge.finalizeDeposit.selector, + _l1Token, + _l2Token, + _from, + _to, + _amount, + _data + ); + + // Odeslání calldata na L2 + // slither-disable-next-line reentrancy-events, reentrancy-benign + sendCrossDomainMessage(l2TokenBridge, _l2Gas, message); + + // slither-disable-next-line reentrancy-benign + deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] + _amount; +``` + +Přidat vloženou částku tokenů do datové struktury `deposits`. +Může existovat více adres na L2, které odpovídají stejnému tokenu ERC-20 na L1, takže pro sledování vkladů nestačí použít zůstatek přemostění tokenu ERC-20 na L1. + +```solidity + + // slither-disable-next-line reentrancy-events + emit ERC20DepositInitiated(_l1Token, _l2Token, _from, _to, _amount, _data); + } + + /************************* + * Meziřetězcové funkce * + *************************/ + + /** + * @inheritdoc IL1StandardBridge + */ + function finalizeETHWithdrawal( + address _from, + address _to, + uint256 _amount, + bytes calldata _data +``` + +Přemostění L2 odešle zprávu mezidoménovému posílači zpráv L2, což způsobí, že mezidoménový posílač zpráv L1 zavolá tuto funkci (samozřejmě až po odeslání [transakce, která zprávu finalizuje](https://community.optimism.io/docs/developers/bridge/messaging/#fees-for-l2-%E2%87%92-l1-transactions) na L1). + +```solidity + ) external onlyFromCrossDomainAccount(l2TokenBridge) { +``` + +Ujistěte se, že se jedná o _legitimní_ zprávu, která pochází od mezidoménového posílače zpráv a pochází z přemostění tokenu L2. +Tato funkce se používá k výběru ETH z přemostění, takže se musíme ujistit, že ji volá pouze oprávněný volající. + +```solidity + // slither-disable-next-line reentrancy-events + (bool success, ) = _to.call{ value: _amount }(new bytes(0)); +``` + +Způsobem převodu ETH je zavolat příjemce s částkou wei v `msg.value`. + +```solidity + require(success, "TransferHelper::safeTransferETH: ETH transfer failed"); + + // slither-disable-next-line reentrancy-events + emit ETHWithdrawalFinalized(_from, _to, _amount, _data); +``` + +Vyslat událost o výběru. + +```solidity + } + + /** + * @inheritdoc IL1ERC20Bridge + */ + function finalizeERC20Withdrawal( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external onlyFromCrossDomainAccount(l2TokenBridge) { +``` + +Tato funkce je podobná výše uvedené funkci `finalizeETHWithdrawal` s nezbytnými změnami pro tokeny ERC-20. + +```solidity + deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] - _amount; +``` + +Aktualizovat datovou strukturu `deposits`. + +```solidity + + // Když je výběr finalizován na L1, přemostění L1 převede prostředky vybírajícímu + // slither-disable-next-line reentrancy-events + IERC20(_l1Token).safeTransfer(_to, _amount); + + // slither-disable-next-line reentrancy-events + emit ERC20WithdrawalFinalized(_l1Token, _l2Token, _from, _to, _amount, _data); + } + + + /***************************** + * Dočasné - migrace ETH * + *****************************/ + + /** + * @dev Přidá zůstatek ETH na účet. Toto je určeno k tomu, aby se ETH + * mohlo migrovat ze staré brány na novou bránu. + * POZNÁMKA: Toto je ponecháno pouze pro jeden upgrade, abychom mohli přijmout migrované ETH ze + * starého kontraktu + */ + function donateETH() external payable {} +} +``` + +Existovala starší implementace přemostění. +Když jsme přešli z této implementace na tuto, museli jsme přesunout všechna aktiva. +Tokeny ERC-20 se dají jednoduše přesunout. +Abyste však mohli převést ETH do kontraktu, potřebujete souhlas tohoto kontraktu, což nám poskytuje `donateETH`. + +## Tokeny ERC-20 na L2 {#erc-20-tokens-on-l2} + +Aby token ERC-20 vyhovoval standardnímu přemostění, musí umožnit standardnímu přemostění, a _pouze_ standardnímu přemostění, razit tokeny. +To je nutné, protože přemostění musí zajistit, aby se počet tokenů v oběhu v síti Optimism rovnal počtu tokenů uzamčených v kontraktu přemostění na L1. +Pokud by na L2 bylo příliš mnoho tokenů, někteří uživatelé by nemohli svá aktiva přemostit zpět na L1. +Místo důvěryhodného přemostění bychom v podstatě znovu vytvořili [bankovnictví s částečnými rezervami](https://www.investopedia.com/terms/f/fractionalreservebanking.asp). +Pokud je na L1 příliš mnoho tokenů, některé z nich by zůstaly navždy uzamčeny v kontraktu přemostění, protože neexistuje způsob, jak je uvolnit bez spálení tokenů na L2. + +### IL2StandardERC20 {#il2standarderc20} + +Každý token ERC-20 na L2, který používá standardní přemostění, musí poskytovat [toto rozhraní](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/standards/IL2StandardERC20.sol), které má funkce a události, jež standardní přemostění potřebuje. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +``` + +[Standardní rozhraní ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) nezahrnuje funkce `mint` a `burn`. +Tyto metody nejsou vyžadovány [standardem ERC-20](https://eips.ethereum.org/EIPS/eip-20), který ponechává mechanismy pro vytváření a ničení tokenů nespecifikované. + +```solidity +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +``` + +[Rozhraní ERC-165](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/IERC165.sol) se používá ke specifikaci funkcí, které kontrakt poskytuje. +[Standard si můžete přečíst zde](https://eips.ethereum.org/EIPS/eip-165). + +```solidity +interface IL2StandardERC20 is IERC20, IERC165 { + function l1Token() external returns (address); +``` + +Tato funkce poskytuje adresu tokenu L1, který je přemostěn do tohoto kontraktu. +Všimněte si, že podobnou funkci v opačném směru nemáme. +Musíme být schopni přemostit jakýkoli token na L1 bez ohledu na to, zda podpora na L2 byla plánována při jeho implementaci či nikoli. + +```solidity + + function mint(address _to, uint256 _amount) external; + + function burn(address _from, uint256 _amount) external; + + event Mint(address indexed _account, uint256 _amount); + event Burn(address indexed _account, uint256 _amount); +} +``` + +Funkce a události pro ražbu (vytvoření) a pálení (zničení) tokenů. +Přemostění by mělo být jedinou entitou, která může tyto funkce spouštět, aby se zajistil správný počet tokenů (rovný počtu tokenů uzamčených na L1). + +### L2StandardERC20 {#L2StandardERC20} + +[Toto je naše implementace rozhraní `IL2StandardERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/standards/L2StandardERC20.sol). +Pokud nepotřebujete žádnou vlastní logiku, měli byste použít tuto. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +``` + +[Kontrakt ERC-20 od OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol). +Optimism nevěří v znovuobjevování kola, zejména když je kolo dobře auditováno a musí být dostatečně důvěryhodné pro držení aktiv. + +```solidity +import "./IL2StandardERC20.sol"; + +contract L2StandardERC20 is IL2StandardERC20, ERC20 { + address public l1Token; + address public l2Bridge; +``` + +Toto jsou dva další konfigurační parametry, které vyžadujeme a které ERC-20 normálně nemá. + +```solidity + + /** + * @param _l2Bridge Adresa standardního přemostění na L2. + * @param _l1Token Adresa odpovídajícího tokenu na L1. + * @param _name Název ERC20. + * @param _symbol Symbol ERC20. + */ + constructor( + address _l2Bridge, + address _l1Token, + string memory _name, + string memory _symbol + ) ERC20(_name, _symbol) { + l1Token = _l1Token; + l2Bridge = _l2Bridge; + } +``` + +Nejprve zavoláme konstruktor kontraktu, ze kterého dědíme (`ERC20(_name, _symbol)`), a poté nastavíme vlastní proměnné. + +```solidity + + modifier onlyL2Bridge() { + require(msg.sender == l2Bridge, "Only L2 Bridge can mint and burn"); + _; + } + + + // slither-disable-next-line external-function + function supportsInterface(bytes4 _interfaceId) public pure returns (bool) { + bytes4 firstSupportedInterface = bytes4(keccak256("supportsInterface(bytes4)")); // ERC165 + bytes4 secondSupportedInterface = IL2StandardERC20.l1Token.selector ^ + IL2StandardERC20.mint.selector ^ + IL2StandardERC20.burn.selector; + return _interfaceId == firstSupportedInterface || _interfaceId == secondSupportedInterface; + } +``` + +Takto funguje [ERC-165](https://eips.ethereum.org/EIPS/eip-165). +Každé rozhraní je souborem podporovaných funkcí a je identifikováno jako [exkluzivní nebo](https://en.wikipedia.org/wiki/Exclusive_or) [selektorů funkcí ABI](https://docs.soliditylang.org/en/v0.8.12/abi-spec.html#function-selector) těchto funkcí. + +Přemostění na L2 používá ERC-165 jako kontrolu správnosti, aby se ujistilo, že kontrakt ERC-20, do kterého posílá aktiva, je `IL2StandardERC20`. + +**Poznámka:** Nic nebrání tomu, aby podvodný kontrakt poskytoval falešné odpovědi na `supportsInterface`, takže se jedná o mechanismus kontroly správnosti, _nikoli_ o bezpečnostní mechanismus. + +```solidity + // slither-disable-next-line external-function + function mint(address _to, uint256 _amount) public virtual onlyL2Bridge { + _mint(_to, _amount); + + emit Mint(_to, _amount); + } + + // slither-disable-next-line external-function + function burn(address _from, uint256 _amount) public virtual onlyL2Bridge { + _burn(_from, _amount); + + emit Burn(_from, _amount); + } +} +``` + +Pouze přemostění L2 smí razit a pálit aktiva. + +`_mint` a `_burn` jsou ve skutečnosti definovány v [kontraktu ERC-20 OpenZeppelin](/developers/tutorials/erc20-annotated-code/#the-_mint-and-_burn-functions-_mint-and-_burn). +Tento kontrakt je pouze nevystavuje externě, protože podmínky pro ražbu a pálení tokenů jsou tak rozmanité jako počet způsobů použití ERC-20. + +## Kód přemostění L2 {#l2-bridge-code} + +Toto je kód, který spouští přemostění v síti Optimism. +[Zdrojový kód tohoto kontraktu je zde](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol). + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +/* Importy rozhraní */ +import { IL1StandardBridge } from "../../L1/messaging/IL1StandardBridge.sol"; +import { IL1ERC20Bridge } from "../../L1/messaging/IL1ERC20Bridge.sol"; +import { IL2ERC20Bridge } from "./IL2ERC20Bridge.sol"; +``` + +Rozhraní [IL2ERC20Bridge](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol) je velmi podobné [ekvivalentu L1](#IL1ERC20Bridge), který jsme viděli výše. +Existují dva významné rozdíly: + +1. Na L1 iniciujete vklady a finalizujete výběry. + Zde iniciujete výběry a finalizujete vklady. +2. Na L1 je nutné rozlišovat mezi ETH a tokeny ERC-20. + Na L2 můžeme použít stejné funkce pro obojí, protože interně jsou zůstatky ETH v síti Optimism spravovány jako token ERC-20 s adresou [0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000](https://explorer.optimism.io/address/0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000). + +```solidity +/* Importy knihoven */ +import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; +import { CrossDomainEnabled } from "../../libraries/bridge/CrossDomainEnabled.sol"; +import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol"; + +/* Importy kontraktů */ +import { IL2StandardERC20 } from "../../standards/IL2StandardERC20.sol"; + +/** + * @title L2StandardBridge + * @dev Standardní přemostění L2 je kontrakt, který spolupracuje se standardním přemostěním L1, + * aby umožnil přechody ETH a ERC20 mezi L1 a L2. + * Tento kontrakt funguje jako razič nových tokenů, když se dozví o vkladech do standardního + * přemostění L1. + * Tento kontrakt také funguje jako palič tokenů určených k výběru a informuje přemostění L1 + * o uvolnění prostředků L1. + */ +contract L2StandardBridge is IL2ERC20Bridge, CrossDomainEnabled { + /******************************** + * Reference na externí kontrakty * + ********************************/ + + address public l1TokenBridge; +``` + +Sledovat adresu přemostění L1. +Všimněte si, že na rozdíl od ekvivalentu na L1 zde tuto proměnnou _potřebujeme_. +Adresa přemostění L1 není známa předem. + +```solidity + + /*************** + * Konstruktor * + ***************/ + + /** + * @param _l2CrossDomainMessenger Mezidoménový posílač zpráv používaný tímto kontraktem. + * @param _l1TokenBridge Adresa přemostění L1 nasazeného na hlavní řetězec. + */ + constructor(address _l2CrossDomainMessenger, address _l1TokenBridge) + CrossDomainEnabled(_l2CrossDomainMessenger) + { + l1TokenBridge = _l1TokenBridge; + } + + /*************** + * Vybírání * + ***************/ + + /** + * @inheritdoc IL2ERC20Bridge + */ + function withdraw( + address _l2Token, + uint256 _amount, + uint32 _l1Gas, + bytes calldata _data + ) external virtual { + _initiateWithdrawal(_l2Token, msg.sender, msg.sender, _amount, _l1Gas, _data); + } + + /** + * @inheritdoc IL2ERC20Bridge + */ + function withdrawTo( + address _l2Token, + address _to, + uint256 _amount, + uint32 _l1Gas, + bytes calldata _data + ) external virtual { + _initiateWithdrawal(_l2Token, msg.sender, _to, _amount, _l1Gas, _data); + } +``` + +Tyto dvě funkce iniciují výběry. +Všimněte si, že není třeba specifikovat adresu tokenu L1. +Očekává se, že tokeny na L2 nám sdělí adresu svého ekvivalentu na L1. + +```solidity + + /** + * @dev Provádí logiku pro výběry pálením tokenu a informováním + * brány tokenu L1 o výběru. + * @param _l2Token Adresa tokenu na L2, kde je výběr iniciován. + * @param _from Účet, ze kterého se má výběr na L2 stáhnout. + * @param _to Účet, na který se má výběr na L1 připsat. + * @param _amount Částka tokenu k výběru. + * @param _l1Gas Nepoužito, ale zahrnuto pro případné úvahy o budoucí kompatibilitě. + * @param _data Volitelná data k předání na L1. Tato data jsou poskytována + * pouze pro pohodlí externích kontraktů. Kromě vynucení maximální + * délky tyto kontrakty neposkytují žádné záruky ohledně jejich obsahu. + */ + function _initiateWithdrawal( + address _l2Token, + address _from, + address _to, + uint256 _amount, + uint32 _l1Gas, + bytes calldata _data + ) internal { + // Když je výběr iniciován, spálíme prostředky vybírajícího, abychom zabránili následnému použití na L2 + // slither-disable-next-line reentrancy-events + IL2StandardERC20(_l2Token).burn(msg.sender, _amount); +``` + +Všimněte si, že se _nespoléháme_ na parametr `_from`, ale na `msg.sender`, který je mnohem těžší zfalšovat (pokud vím, nemožné). + +```solidity + + // Sestavení calldata pro l1TokenBridge.finalizeERC20Withdrawal(_to, _amount) + // slither-disable-next-line reentrancy-events + address l1Token = IL2StandardERC20(_l2Token).l1Token(); + bytes memory message; + + if (_l2Token == Lib_PredeployAddresses.OVM_ETH) { +``` + +Na L1 je nutné rozlišovat mezi ETH a ERC-20. + +```solidity + message = abi.encodeWithSelector( + IL1StandardBridge.finalizeETHWithdrawal.selector, + _from, + _to, + _amount, + _data + ); + } else { + message = abi.encodeWithSelector( + IL1ERC20Bridge.finalizeERC20Withdrawal.selector, + l1Token, + _l2Token, + _from, + _to, + _amount, + _data + ); + } + + // Odeslání zprávy do přemostění na L1 + // slither-disable-next-line reentrancy-events + sendCrossDomainMessage(l1TokenBridge, _l1Gas, message); + + // slither-disable-next-line reentrancy-events + emit WithdrawalInitiated(l1Token, _l2Token, msg.sender, _to, _amount, _data); + } + + /************************************ + * Meziřetězcová funkce: Vkládání * + ************************************/ + + /** + * @inheritdoc IL2ERC20Bridge + */ + function finalizeDeposit( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data +``` + +Tuto funkci volá `L1StandardBridge`. + +```solidity + ) external virtual onlyFromCrossDomainAccount(l1TokenBridge) { +``` + +Ujistěte se, že zdroj zprávy je legitimní. +To je důležité, protože tato funkce volá `_mint` a mohla by být použita k poskytnutí tokenů, které nejsou kryty tokeny, jež přemostění vlastní na L1. + +```solidity + // Zkontrolujte, zda je cílový token vyhovující a + // ověřte, že vložený token na L1 odpovídá reprezentaci vloženého tokenu na L2 zde + if ( + // slither-disable-next-line reentrancy-events + ERC165Checker.supportsInterface(_l2Token, 0x1d1d8b63) && + _l1Token == IL2StandardERC20(_l2Token).l1Token() +``` + +Kontroly správnosti: + +1. Je podporováno správné rozhraní +2. Adresa L1 kontraktu ERC-20 na L2 odpovídá zdroji L1 tokenů. + +```solidity + ) { + // Když je vklad finalizován, připíšeme na účet na L2 stejnou částku + // tokenů. + // slither-disable-next-line reentrancy-events + IL2StandardERC20(_l2Token).mint(_to, _amount); + // slither-disable-next-line reentrancy-events + emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _data); +``` + +Pokud kontroly správnosti projdou, dokončete vklad: + +1. Razit tokeny +2. Vyslat příslušnou událost + +```solidity + } else { + // Buď token L2, do kterého se vkládá, nesouhlasí se správnou adresou + // svého tokenu L1, nebo nepodporuje správné rozhraní. + // Toto by se mělo stát pouze v případě zákeřného tokenu L2, nebo pokud uživatel nějakým způsobem + // zadal špatnou adresu tokenu L2 pro vklad. + // V obou případech zde proces zastavíme a sestavíme zprávu o výběru, + // aby si uživatelé mohli v některých případech své prostředky vybrat. + // Neexistuje žádný způsob, jak zcela zabránit zákeřným kontraktům tokenů, ale toto omezuje + // chyby uživatelů a zmírňuje některé formy zákeřného chování kontraktů. +``` + +Pokud uživatel udělal zjistitelnou chybu použitím špatné adresy tokenu na L2, chceme vklad zrušit a vrátit tokeny na L1. +Jediný způsob, jak to můžeme udělat z L2, je poslat zprávu, která bude muset počkat na období pro napadení chyby, ale to je pro uživatele mnohem lepší než trvalá ztráta tokenů. + +```solidity + bytes memory message = abi.encodeWithSelector( + IL1ERC20Bridge.finalizeERC20Withdrawal.selector, + _l1Token, + _l2Token, + _to, // zde jsme prohodili _to a _from, abychom vklad vrátili odesílateli + _from, + _amount, + _data + ); + + // Odeslání zprávy do přemostění na L1 + // slither-disable-next-line reentrancy-events + sendCrossDomainMessage(l1TokenBridge, 0, message); + // slither-disable-next-line reentrancy-events + emit DepositFailed(_l1Token, _l2Token, _from, _to, _amount, _data); + } + } +} +``` + +## Závěr {#conclusion} + +Standardní přemostění je nejflexibilnějším mechanismem pro převody aktiv. +Protože je však tak obecný, není vždy nejjednodušším mechanismem k použití. +Zejména pro výběry většina uživatelů dává přednost použití [přemostění třetích stran](https://optimism.io/apps#bridge), která nečekají na období pro napadení a nevyžadují Merkleho důkaz k dokončení výběru. + +Tato přemostění obvykle fungují tak, že mají aktiva na L1, která okamžitě poskytnou za malý poplatek (často nižší než náklady na transakční poplatky za výběr standardním přemostěním). +Když přemostění (nebo lidé, kteří ho provozují) předpokládá, že bude mít málo aktiv na L1, převede dostatečná aktiva z L2. Protože se jedná o velmi velké výběry, náklady na výběr se amortizují na velkou částku a představují mnohem menší procento. + +Doufejme, že vám tento článek pomohl lépe pochopit, jak funguje druhá vrstva a jak psát kód v Solidity, který je jasný a bezpečný. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). diff --git a/public/content/translations/cs/developers/tutorials/reverse-engineering-a-contract/index.md b/public/content/translations/cs/developers/tutorials/reverse-engineering-a-contract/index.md new file mode 100644 index 00000000000..c2005f1cdff --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/reverse-engineering-a-contract/index.md @@ -0,0 +1,744 @@ +--- +title: "Reverzní inženýrství kontraktu" +description: "Jak porozumět kontraktu, když nemáte zdrojový kód" +author: Ori Pomerantz +lang: cs +tags: [ "evm", "opkódy" ] +skill: advanced +published: 2021-12-30 +--- + +## Úvod {#introduction} + +_Na blockchainu neexistují žádná tajemství_, vše, co se stane, je konzistentní, ověřitelné a veřejně dostupné. V ideálním případě by [kontrakty měly mít svůj zdrojový kód zveřejněný a ověřený na Etherscanu](https://etherscan.io/address/0xb8901acb165ed027e32754e0ffe830802919727f#code). Nicméně [to tak není vždy](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f#code). V tomto článku se dozvíte, jak reverzně analyzovat kontrakty na příkladu kontraktu bez zdrojového kódu, [`0x2510c039cc3b061d79e564b38836da87e31b342f`](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f). + +Existují reverzní kompilátory, ale ne vždy produkují [použitelné výsledky](https://etherscan.io/bytecode-decompiler?a=0x2510c039cc3b061d79e564b38836da87e31b342f). V tomto článku se dozvíte, jak manuálně provést reverzní inženýrství a porozumět kontraktu z [opkódů](https://github.com/wolflo/evm-opcodes) a také jak interpretovat výsledky dekompilátoru. + +Abyste mohli porozumět tomuto článku, měli byste již znát základy EVM a být alespoň trochu obeznámeni s EVM assemblerem. [O těchto tématech si můžete přečíst zde](https://medium.com/mycrypto/the-ethereum-virtual-machine-how-does-it-work-9abac2b7c9e). + +## Příprava spustitelného kódu {#prepare-the-executable-code} + +Opkódy získáte tak, že na Etherscanu přejdete na kontrakt, kliknete na záložku **Contract** a poté na **Switch to Opcodes View**. Zobrazí se vám pohled s jedním opkódem na řádek. + +![Pohled na opkódy z Etherscanu](opcode-view.png) + +Abyste však porozuměli skokům, musíte vědět, kde v kódu se který opkód nachází. Jedním ze způsobů, jak to udělat, je otevřít tabulku Google a vložit opkódy do sloupce C. [Následující kroky můžete přeskočit tak, že si vytvoříte kopii této již připravené tabulky](https://docs.google.com/spreadsheets/d/1tKmTJiNjUwHbW64wCKOSJxHjmh0bAUapt6btUYE7kDA/edit?usp=sharing). + +Dalším krokem je získání správných umístění v kódu, abychom mohli pochopit skoky. Velikost opkódu vložíme do sloupce B a umístění (v hexadecimálním tvaru) do sloupce A. Zadejte tuto funkci do buňky `B1` a poté ji zkopírujte a vložte do zbytku sloupce B, až na konec kódu. Poté můžete sloupec B skrýt. + +``` +=1+IF(REGEXMATCH(C1,"PUSH"),REGEXEXTRACT(C1,"PUSH(\d+)"),0) +``` + +Tato funkce nejprve přidá jeden bajt pro samotný opkód a poté hledá `PUSH`. Opkódy PUSH jsou speciální, protože potřebují další bajty pro vkládanou hodnotu. Pokud je opkód `PUSH`, extrahujeme počet bajtů a přičteme ho. + +Do buňky `A1` vložte první offset, nulu. Poté do buňky `A2` vložte tuto funkci a opět ji zkopírujte a vložte do zbytku sloupce A: + +``` +=dec2hex(hex2dec(A1)+B1) +``` + +Tuto funkci potřebujeme, aby nám poskytla hexadecimální hodnotu, protože hodnoty, které jsou vkládány před skoky (`JUMP` a `JUMPI`), jsou nám dány v hexadecimálním tvaru. + +## Vstupní bod (0x00) {#the-entry-point-0x00} + +Kontrakty se vždy spouštějí od prvního bajtu. Toto je počáteční část kódu: + +| Offset | Opkód | Zásobník (po opkódu) | +| -----: | ------------ | ---------------------------------------------- | +| 0 | PUSH1 0x80 | 0x80 | +| 2 | PUSH1 0x40 | 0x40, 0x80 | +| 4 | MSTORE | Prázdné | +| 5 | PUSH1 0x04 | 0x04 | +| 7 | CALLDATASIZE | CALLDATASIZE 0x04 | +| 8 | LT | CALLDATASIZE\<4 | +| 9 | PUSH2 0x005e | 0x5E CALLDATASIZE\<4 | +| C | JUMPI | Prázdné | + +Tento kód dělá dvě věci: + +1. Zapište 0x80 jako 32bajtovou hodnotu do paměťových míst 0x40-0x5F (0x80 se uloží do 0x5F a 0x40-0x5E jsou všechny nuly). +2. Přečtěte velikost calldata. Normálně se data volání (calldata) pro kontrakt na Ethereu řídí [ABI (application binary interface)](https://docs.soliditylang.org/en/v0.8.10/abi-spec.html), které vyžaduje minimálně čtyři bajty pro selektor funkce. Pokud je velikost calldata menší než čtyři, skočí se na 0x5E. + +![Vývojový diagram pro tuto část](flowchart-entry.png) + +### Handler na 0x5E (pro data volání bez ABI) {#the-handler-at-0x5e-for-non-abi-call-data} + +| Offset | Opkód | +| -----: | ------------ | +| 5E | JUMPDEST | +| 5F | CALLDATASIZE | +| 60 | PUSH2 0x007c | +| 63 | JUMPI | + +Tento úryvek začíná `JUMPDEST`. Programy EVM (Ethereum Virtual Machine) vyvolají výjimku, pokud skočíte na opkód, který není `JUMPDEST`. Poté se podívá na CALLDATASIZE, a pokud je „true“ (tedy nenulová), skočí na 0x7C. K tomu se dostaneme níže. + +| Offset | Opkód | Zásobník (po opkódu) | +| -----: | ---------- | ------------------------------------------------------------------------------------------ | +| 64 | CALLVALUE | [Wei](/glossary/#wei) poskytnuté voláním. V Solidity se nazývá `msg.value` | +| 65 | PUSH1 0x06 | 6 CALLVALUE | +| 67 | PUSH1 0x00 | 0 6 CALLVALUE | +| 69 | DUP3 | CALLVALUE 0 6 CALLVALUE | +| 6A | DUP3 | 6 CALLVALUE 0 6 CALLVALUE | +| 6B | SLOAD | Storage[6] CALLVALUE 0 6 CALLVALUE | + +Takže když nejsou žádná calldata, přečteme hodnotu Storage[6]. Zatím nevíme, co tato hodnota znamená, ale můžeme se podívat na transakce, které kontrakt přijal bez calldata. Transakce, které pouze převádějí ETH bez jakýchkoli calldata (a tedy bez metody), mají v Etherscanu metodu `Transfer`. Ve skutečnosti [úplně první transakce, kterou kontrakt obdržel](https://etherscan.io/tx/0xeec75287a583c36bcc7ca87685ab41603494516a0f5986d18de96c8e630762e7), je převod. + +Když se podíváme na tuto transakci a klikneme na **Click to see More**, uvidíme, že data volání (calldata), zde nazvaná vstupní data, jsou skutečně prázdná (`0x`). Všimněte si také, že hodnota je 1,559 ETH, což bude relevantní později. + +![Calldata jsou prázdná](calldata-empty.png) + +Dále klikněte na záložku **State** a rozbalte kontrakt, který reverzně analyzujeme (0x2510...). Můžete vidět, že se `Storage[6]` během transakce změnilo, a pokud změníte Hex na **Number**, uvidíte, že se stalo 1 559 000 000 000 000 000, což je převedená hodnota ve wei (pro přehlednost jsem přidal čárky), odpovídající další hodnotě kontraktu. + +![Změna v Storage[6]](storage6.png) + +Pokud se podíváme na změny stavu způsobené [jinými `Transfer` transakcemi ze stejného období](https://etherscan.io/tx/0xf708d306de39c422472f43cb975d97b66fd5d6a6863db627067167cbf93d84d1#statechange), vidíme, že `Storage[6]` po nějakou dobu sledovalo hodnotu kontraktu. Prozatím to budeme nazývat `Hodnota*`. Hvězdička (`*`) nám připomíná, že ještě _nevíme_, co tato proměnná dělá, ale nemůže sloužit pouze ke sledování hodnoty kontraktu, protože není třeba používat úložiště, které je velmi drahé, když můžete zůstatek svého účtu získat pomocí `ADDRESS BALANCE`. První opkód vloží na zásobník vlastní adresu kontraktu. Druhý opkód přečte adresu na vrcholu zásobníku a nahradí ji zůstatkem této adresy. + +| Offset | Opkód | Zásobník | +| -----: | ------------ | --------------------------------------------- | +| 6C | PUSH2 0x0075 | 0x75 Hodnota\* CALLVALUE 0 6 CALLVALUE | +| 6F | SWAP2 | CALLVALUE Hodnota\* 0x75 0 6 CALLVALUE | +| 70 | SWAP1 | Hodnota\* CALLVALUE 0x75 0 6 CALLVALUE | +| 71 | PUSH2 0x01a7 | 0x01A7 Hodnota\* CALLVALUE 0x75 0 6 CALLVALUE | +| 74 | JUMP | | + +Budeme pokračovat ve sledování tohoto kódu v cíli skoku. + +| Offset | Opkód | Zásobník | +| -----: | ---------- | ------------------------------------------------------------- | +| 1A7 | JUMPDEST | Hodnota\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1A8 | PUSH1 0x00 | 0x00 Hodnota\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AA | DUP3 | CALLVALUE 0x00 Hodnota\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AB | NOT | 2^256-CALLVALUE-1 0x00 Hodnota\* CALLVALUE 0x75 0 6 CALLVALUE | + +`NOT` je bitová operace, takže obrátí hodnotu každého bitu v hodnotě volání. + +| Offset | Opkód | Zásobník | +| -----: | ------------ | ---------------------------------------------------------------------------------------------------------- | +| 1AC | DUP3 | Hodnota\* 2^256-CALLVALUE-1 0x00 Hodnota\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AD | GT | Hodnota\*>2^256-CALLVALUE-1 0x00 Hodnota\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AE | ISZERO | Hodnota\*\<=2^256-CALLVALUE-1 0x00 Hodnota\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AF | PUSH2 0x01df | 0x01DF Hodnota\*\<=2^256-CALLVALUE-1 0x00 Hodnota\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1B2 | JUMPI | | + +Skočíme, pokud je `Hodnota*` menší než 2^256-CALLVALUE-1 nebo se jí rovná. Vypadá to jako logika k zabránění přetečení. A skutečně, vidíme, že po několika nesmyslných operacích (například zápis do paměti, která bude brzy smazána) na offsetu 0x01DE kontrakt vrátí transakci zpět (revert), pokud je detekováno přetečení, což je normální chování. + +Všimněte si, že takové přetečení je extrémně nepravděpodobné, protože by vyžadovalo, aby hodnota volání plus `Hodnota*` byla srovnatelná s 2^256 wei, což je asi 10^59 ETH. [Celková zásoba ETH je v době psaní tohoto článku menší než dvě stě milionů](https://etherscan.io/stat/supply). + +| Offset | Opkód | Zásobník | +| -----: | -------- | ------------------------------------------- | +| 1DF | JUMPDEST | 0x00 Hodnota\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1E0 | POP | Hodnota\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1E1 | ADD | Hodnota\*+CALLVALUE 0x75 0 6 CALLVALUE | +| 1E2 | SWAP1 | 0x75 Hodnota\*+CALLVALUE 0 6 CALLVALUE | +| 1E3 | JUMP | | + +Pokud jsme se dostali sem, získáme `Hodnota* + CALLVALUE` a skočíme na offset 0x75. + +| Offset | Opkód | Zásobník | +| -----: | -------- | --------------------------------- | +| 75 | JUMPDEST | Hodnota\*+CALLVALUE 0 6 CALLVALUE | +| 76 | SWAP1 | 0 Hodnota\*+CALLVALUE 6 CALLVALUE | +| 77 | SWAP2 | 6 Hodnota\*+CALLVALUE 0 CALLVALUE | +| 78 | SSTORE | 0 CALLVALUE | + +Pokud se dostaneme sem (což vyžaduje, aby data volání byla prázdná), přičteme k `Hodnota*` hodnotu volání. To je v souladu s tím, co dělají transakce `Transfer`. + +| Offset | Opkód | +| -----: | ----- | +| 79 | POP | +| 7A | POP | +| 7B | STOP | + +Nakonec vyčistěte zásobník (což není nutné) a signalizujte úspěšné ukončení transakce. + +Abychom to shrnuli, zde je vývojový diagram počátečního kódu. + +![Vývojový diagram vstupního bodu](flowchart-entry.png) + +## Handler na adrese 0x7C {#the-handler-at-0x7c} + +Záměrně jsem do nadpisu neuvedl, co tento handler dělá. Cílem není naučit vás, jak funguje tento konkrétní kontrakt, ale jak provádět reverzní inženýrství kontraktů. Dozvíte se, co dělá, stejně jako já: sledováním kódu. + +Dostaneme se sem z několika míst: + +- Pokud existují calldata o velikosti 1, 2 nebo 3 bajtů (z offsetu 0x63) +- Pokud je podpis metody neznámý (z offsetů 0x42 a 0x5D) + +| Offset | Opkód | Zásobník | +| -----: | ------------ | ------------------------------------------------------------------------ | +| 7C | JUMPDEST | | +| 7D | PUSH1 0x00 | 0x00 | +| 7F | PUSH2 0x009d | 0x9D 0x00 | +| 82 | PUSH1 0x03 | 0x03 0x9D 0x00 | +| 84 | SLOAD | Storage[3] 0x9D 0x00 | + +Toto je další buňka úložiště, kterou jsem v žádné transakci nenašel, takže je těžší zjistit, co znamená. Níže uvedený kód to objasní. + +| Offset | Opkód | Zásobník | +| -----: | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| 85 | PUSH20 0xffffffffffffffffffffffffffffffffffffffff | 0xff....ff Storage[3] 0x9D 0x00 | +| 9A | AND | Storage[3]-jako-adresa 0x9D 0x00 | + +Tyto opkódy zkrátí hodnotu, kterou čteme z Storage[3], na 160 bitů, což je délka ethereové adresy. + +| Offset | Opkód | Zásobník | +| -----: | ----- | ------------------------------------------------------------------------------------ | +| 9B | SWAP1 | 0x9D Storage[3]-jako-adresa 0x00 | +| 9C | JUMP | Storage[3]-jako-adresa 0x00 | + +Tento skok je nadbytečný, protože přecházíme na další opkód. Tento kód není zdaleka tak efektivní z hlediska poplatků (gas), jak by mohl být. + +| Offset | Opkód | Zásobník | +| -----: | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| 9D | JUMPDEST | Storage[3]-jako-adresa 0x00 | +| 9E | SWAP1 | 0x00 Storage[3]-jako-adresa | +| 9F | POP | Storage[3]-jako-adresa | +| A0 | PUSH1 0x40 | 0x40 Storage[3]-jako-adresa | +| A2 | MLOAD | Mem[0x40] Storage[3]-jako-adresa | + +Na samém začátku kódu jsme nastavili Mem[0x40] na 0x80. Pokud se podíváme na 0x40 později, vidíme, že se nemění – takže můžeme předpokládat, že je to 0x80. + +| Offset | Opkód | Zásobník | +| -----: | ------------ | ------------------------------------------------------------------------------------------------------ | +| A3 | CALLDATASIZE | CALLDATASIZE 0x80 Storage[3]-jako-adresa | +| A4 | PUSH1 0x00 | 0x00 CALLDATASIZE 0x80 Storage[3]-jako-adresa | +| A6 | DUP3 | 0x80 0x00 CALLDATASIZE 0x80 Storage[3]-jako-adresa | +| A7 | CALLDATACOPY | 0x80 Storage[3]-jako-adresa | + +Zkopírujte všechna calldata do paměti, počínaje adresou 0x80. + +| Offset | Opkód | Zásobník | +| -----: | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| A8 | PUSH1 0x00 | 0x00 0x80 Storage[3]-jako-adresa | +| AA | DUP1 | 0x00 0x00 0x80 Storage[3]-jako-adresa | +| AB | CALLDATASIZE | CALLDATASIZE 0x00 0x00 0x80 Storage[3]-jako-adresa | +| AC | DUP4 | 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-jako-adresa | +| AD | DUP6 | Storage[3]-jako-adresa 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-jako-adresa | +| AE | GAS | GAS Storage[3]-jako-adresa 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-jako-adresa | +| AF | DELEGATE_CALL | | + +Teď je to mnohem jasnější. Tento kontrakt může fungovat jako [proxy](https://blog.openzeppelin.com/proxy-patterns/) a volat adresu v Storage[3], aby odvedla skutečnou práci. `DELEGATE_CALL` volá samostatný kontrakt, ale zůstává ve stejném úložišti. To znamená, že delegovaný kontrakt, pro který jsme proxy, přistupuje ke stejnému úložnému prostoru. Parametry pro volání jsou: + +- _Gas_: Veškerý zbývající gas +- _Volaná adresa_: Storage[3]-jako-adresa +- _Data volání_: CALLDATASIZE bajtů začínajících na adrese 0x80, což je místo, kam jsme vložili původní data volání +- _Návratová data_: Žádná (0x00 - 0x00) Návratová data získáme jiným způsobem (viz níže) + +| Offset | Opkód | Zásobník | +| -----: | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| B0 | RETURNDATASIZE | RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| B1 | DUP1 | RETURNDATASIZE RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| B2 | PUSH1 0x00 | 0x00 RETURNDATASIZE RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| B4 | DUP5 | 0x80 0x00 RETURNDATASIZE RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| B5 | RETURNDATACOPY | RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | + +Zde zkopírujeme všechna návratová data do paměťové vyrovnávací paměti začínající na adrese 0x80. + +| Offset | Opkód | Zásobník | +| -----: | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| B6 | DUP2 | (((úspěch/selhání volání))) RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| B7 | DUP1 | (((úspěch/selhání volání))) (((úspěch/selhání volání))) RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| B8 | ISZERO | (((selhalo volání))) (((úspěch/selhání volání))) RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| B9 | PUSH2 0x00c0 | 0xC0 (((selhalo volání))) (((úspěch/selhání volání))) RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| BC | JUMPI | (((úspěch/selhání volání))) RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| BD | DUP2 | RETURNDATASIZE (((úspěch/selhání volání))) RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| BE | DUP5 | 0x80 RETURNDATASIZE (((úspěch/selhání volání))) RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| BF | RETURN | | + +Takže po volání zkopírujeme návratová data do vyrovnávací paměti 0x80 - 0x80+RETURNDATASIZE a pokud je volání úspěšné, pak `RETURN` s přesně touto vyrovnávací pamětí. + +### DELEGATECALL selhal {#delegatecall-failed} + +Pokud se dostaneme sem, na 0xC0, znamená to, že volaný kontrakt vrátil transakci zpět (revert). Jelikož jsme pouze proxy pro tento kontrakt, chceme vrátit stejná data a také provést revert. + +| Offset | Opkód | Zásobník | +| -----: | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| C0 | JUMPDEST | (((úspěch/selhání volání))) RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| C1 | DUP2 | RETURNDATASIZE (((úspěch/selhání volání))) RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| C2 | DUP5 | 0x80 RETURNDATASIZE (((úspěch/selhání volání))) RETURNDATASIZE (((úspěch/selhání volání))) 0x80 Storage[3]-jako-adresa | +| C3 | REVERT | | + +Takže provedeme `REVERT` se stejnou vyrovnávací pamětí, kterou jsme použili pro `RETURN` dříve: 0x80 - 0x80+RETURNDATASIZE + +![Vývojový diagram volání proxy](flowchart-proxy.png) + +## Volání ABI {#abi-calls} + +Pokud je velikost calldata čtyři bajty nebo více, může se jednat o platné volání ABI. + +| Offset | Opkód | Zásobník | +| -----: | ------------ | ------------------------------------------------------------------------------------------------------------------------ | +| D | PUSH1 0x00 | 0x00 | +| F | CALLDATALOAD | (((První slovo (256 bitů) z calldata))) | +| 10 | PUSH1 0xe0 | 0xE0 (((První slovo (256 bitů) z calldata))) | +| 12 | SHR | (((prvních 32 bitů (4 bajty) z calldata))) | + +Etherscan nám říká, že `1C` je neznámý opkód, protože [byl přidán poté, co Etherscan napsal tuto funkci](https://eips.ethereum.org/EIPS/eip-145) a ještě ji neaktualizovali. [Aktuální tabulka opkódů](https://github.com/wolflo/evm-opcodes) nám ukazuje, že se jedná o posun doprava + +| Offset | Opkód | Zásobník | +| -----: | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 13 | DUP1 | (((prvních 32 bitů (4 bajty) z calldata))) (((prvních 32 bitů (4 bajty) z calldata))) | +| 14 | PUSH4 0x3cd8045e | 0x3CD8045E (((prvních 32 bitů (4 bajty) z calldata))) (((prvních 32 bitů (4 bajty) z calldata))) | +| 19 | GT | 0x3CD8045E>prvních-32-bitů-calldata (((prvních 32 bitů (4 bajty) z calldata))) | +| 1A | PUSH2 0x0043 | 0x43 0x3CD8045E>prvních-32-bitů-calldata (((prvních 32 bitů (4 bajty) z calldata))) | +| 1D | JUMPI | (((prvních 32 bitů (4 bajty) z calldata))) | + +Rozdělením testů shody podpisu metody na dvě části se v průměru ušetří polovina testů. Kód, který bezprostředně následuje, a kód na 0x43 se řídí stejným vzorem: `DUP1` prvních 32 bitů calldata, `PUSH4 (((podpis metody>`, spustit `EQ` pro kontrolu rovnosti a poté `JUMPI`, pokud se podpis metody shoduje. Zde jsou podpisy metod, jejich adresy a pokud je známa [odpovídající definice metody](https://www.4byte.directory/): + +| Metoda | Podpis metody | Offset pro skok | +| --------------------------------------------------------------------------------------------------------- | ------------- | --------------- | +| [splitter()](https://www.4byte.directory/signatures/?bytes4_signature=0x3cd8045e) | 0x3cd8045e | 0x0103 | +| ??? | 0x81e580d3 | 0x0138 | +| [currentWindow()](https://www.4byte.directory/signatures/?bytes4_signature=0xba0bafb4) | 0xba0bafb4 | 0x0158 | +| ??? | 0x1f135823 | 0x00C4 | +| [merkleRoot()](https://www.4byte.directory/signatures/?bytes4_signature=0x2eb4a7ab) | 0x2eb4a7ab | 0x00ED | + +Pokud se nenajde žádná shoda, kód skočí na [proxy handler na adrese 0x7C](#the-handler-at-0x7c) v naději, že kontrakt, pro který jsme proxy, shodu mít bude. + +![Vývojový diagram volání ABI](flowchart-abi.png) + +## splitter() {#splitter} + +| Offset | Opkód | Zásobník | +| -----: | ------------ | ----------------------------- | +| 103 | JUMPDEST | | +| 104 | CALLVALUE | CALLVALUE | +| 105 | DUP1 | CALLVALUE CALLVALUE | +| 106 | ISZERO | CALLVALUE==0 CALLVALUE | +| 107 | PUSH2 0x010f | 0x010F CALLVALUE==0 CALLVALUE | +| 10A | JUMPI | CALLVALUE | +| 10B | PUSH1 0x00 | 0x00 CALLVALUE | +| 10D | DUP1 | 0x00 0x00 CALLVALUE | +| 10E | REVERT | | + +První věc, kterou tato funkce dělá, je kontrola, zda volání neposlalo žádné ETH. Tato funkce není [`payable`](https://solidity-by-example.org/payable/). Pokud nám někdo poslal ETH, musí to být omyl a chceme provést `REVERT`, abychom se vyhnuli tomu, že by se tyto ETH dostaly tam, odkud je nemohou dostat zpět. + +| Offset | Opkód | Zásobník | +| -----: | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 10F | JUMPDEST | | +| 110 | POP | | +| 111 | PUSH1 0x03 | 0x03 | +| 113 | SLOAD | (((Storage[3] a.k.a kontrakt, pro který jsme proxy))) | +| 114 | PUSH1 0x40 | 0x40 (((Storage[3] a.k.a kontrakt, pro který jsme proxy))) | +| 116 | MLOAD | 0x80 (((Storage[3] a.k.a kontrakt, pro který jsme proxy))) | +| 117 | PUSH20 0xffffffffffffffffffffffffffffffffffffffff | 0xFF...FF 0x80 (((Storage[3] a.k.a kontrakt, pro který jsme proxy))) | +| 12C | SWAP1 | 0x80 0xFF...FF (((Storage[3] a.k.a kontrakt, pro který jsme proxy))) | +| 12D | SWAP2 | (((Storage[3] a.k.a kontrakt, pro který jsme proxy))) 0xFF...FF 0x80 | +| 12E | AND | ProxyAddr 0x80 | +| 12F | DUP2 | 0x80 ProxyAddr 0x80 | +| 130 | MSTORE | 0x80 | + +A 0x80 nyní obsahuje adresu proxy + +| Offset | Opkód | Zásobník | +| -----: | ------------ | --------- | +| 131 | PUSH1 0x20 | 0x20 0x80 | +| 133 | ADD | 0xA0 | +| 134 | PUSH2 0x00e4 | 0xE4 0xA0 | +| 137 | JUMP | 0xA0 | + +### Kód E4 {#the-e4-code} + +Toto je poprvé, co vidíme tyto řádky, ale jsou sdíleny s dalšími metodami (viz níže). Hodnotu v zásobníku tedy nazveme X a budeme si pamatovat, že v `splitter()` je hodnota tohoto X 0xA0. + +| Offset | Opkód | Zásobník | +| -----: | ---------- | ----------- | +| E4 | JUMPDEST | X | +| E5 | PUSH1 0x40 | 0x40 X | +| E7 | MLOAD | 0x80 X | +| E8 | DUP1 | 0x80 0x80 X | +| E9 | SWAP2 | X 0x80 0x80 | +| EA | SUB | X-0x80 0x80 | +| EB | SWAP1 | 0x80 X-0x80 | +| EC | RETURN | | + +Tento kód tedy obdrží ukazatel na paměť v zásobníku (X) a způsobí, že kontrakt `RETURN` s vyrovnávací pamětí, která je 0x80 - X. + +V případě `splitter()` vrátí adresu, pro kterou jsme proxy. `RETURN` vrátí vyrovnávací paměť v 0x80-0x9F, což je místo, kam jsme zapsali tato data (offset 0x130 výše). + +## currentWindow() {#currentwindow} + +Kód v offsetech 0x158-0x163 je identický s tím, co jsme viděli v 0x103-0x10E v `splitter()` (kromě cíle `JUMPI`), takže víme, že `currentWindow()` také není `payable`. + +| Offset | Opkód | Zásobník | +| -----: | ------------ | ------------------------------------------------------------------------ | +| 164 | JUMPDEST | | +| 165 | POP | | +| 166 | PUSH2 0x00da | 0xDA | +| 169 | PUSH1 0x01 | 0x01 0xDA | +| 16B | SLOAD | Storage[1] 0xDA | +| 16C | DUP2 | 0xDA Storage[1] 0xDA | +| 16D | JUMP | Storage[1] 0xDA | + +### Kód DA {#the-da-code} + +Tento kód je také sdílen s dalšími metodami. Hodnotu v zásobníku tedy nazveme Y a budeme si pamatovat, že v `currentWindow()` je hodnota tohoto Y Storage[1]. + +| Offset | Opkód | Zásobník | +| -----: | ---------- | ---------------- | +| DA | JUMPDEST | Y 0xDA | +| DB | PUSH1 0x40 | 0x40 Y 0xDA | +| DD | MLOAD | 0x80 Y 0xDA | +| DE | SWAP1 | Y 0x80 0xDA | +| DF | DUP2 | 0x80 Y 0x80 0xDA | +| E0 | MSTORE | 0x80 0xDA | + +Zapište Y do 0x80-0x9F. + +| Offset | Opkód | Zásobník | +| -----: | ---------- | -------------- | +| E1 | PUSH1 0x20 | 0x20 0x80 0xDA | +| E3 | ADD | 0xA0 0xDA | + +A zbytek je již vysvětlen [výše](#the-e4-code). Takže skoky na 0xDA zapíší vrchol zásobníku (Y) do 0x80-0x9F a vrátí tuto hodnotu. V případě `currentWindow()` vrátí Storage[1]. + +## merkleRoot() {#merkleroot} + +Kód v offsetech 0xED-0xF8 je identický s tím, co jsme viděli v 0x103-0x10E v `splitter()` (kromě cíle `JUMPI`), takže víme, že `merkleRoot()` také není `payable`. + +| Offset | Opkód | Zásobník | +| -----: | ------------ | ------------------------------------------------------------------------ | +| F9 | JUMPDEST | | +| FA | POP | | +| FB | PUSH2 0x00da | 0xDA | +| FE | PUSH1 0x00 | 0x00 0xDA | +| 100 | SLOAD | Storage[0] 0xDA | +| 101 | DUP2 | 0xDA Storage[0] 0xDA | +| 102 | JUMP | Storage[0] 0xDA | + +Co se stane po skoku [jsme již zjistili](#the-da-code). Takže `merkleRoot()` vrátí Storage[0]. + +## 0x81e580d3 {#0x81e580d3} + +Kód v offsetech 0x138-0x143 je identický s tím, co jsme viděli v 0x103-0x10E v `splitter()` (kromě cíle `JUMPI`), takže víme, že tato funkce také není `payable`. + +| Offset | Opkód | Zásobník | +| -----: | ------------ | ------------------------------------------------------------------------------- | +| 144 | JUMPDEST | | +| 145 | POP | | +| 146 | PUSH2 0x00da | 0xDA | +| 149 | PUSH2 0x0153 | 0x0153 0xDA | +| 14C | CALLDATASIZE | CALLDATASIZE 0x0153 0xDA | +| 14D | PUSH1 0x04 | 0x04 CALLDATASIZE 0x0153 0xDA | +| 14F | PUSH2 0x018f | 0x018F 0x04 CALLDATASIZE 0x0153 0xDA | +| 152 | JUMP | 0x04 CALLDATASIZE 0x0153 0xDA | +| 18F | JUMPDEST | 0x04 CALLDATASIZE 0x0153 0xDA | +| 190 | PUSH1 0x00 | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 192 | PUSH1 0x20 | 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 194 | DUP3 | 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 195 | DUP5 | CALLDATASIZE 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 196 | SUB | CALLDATASIZE-4 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 197 | SLT | CALLDATASIZE-4\<32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 198 | ISZERO | CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 199 | PUSH2 0x01a0 | 0x01A0 CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 19C | JUMPI | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | + +Vypadá to, že tato funkce bere alespoň 32 bajtů (jedno slovo) calldata. + +| Offset | Opkód | Zásobník | +| -----: | ------ | -------------------------------------------- | +| 19D | DUP1 | 0x00 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 19E | DUP2 | 0x00 0x00 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 19F | REVERT | | + +Pokud neobdrží calldata, transakce je vrácena zpět bez jakýchkoli návratových dat. + +Podívejme se, co se stane, když funkce _dostane_ potřebná calldata. + +| Offset | Opkód | Zásobník | +| -----: | ------------ | ----------------------------------------------------------- | +| 1A0 | JUMPDEST | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 1A1 | POP | 0x04 CALLDATASIZE 0x0153 0xDA | +| 1A2 | CALLDATALOAD | calldataload(4) CALLDATASIZE 0x0153 0xDA | + +`calldataload(4)` je první slovo calldata _po_ podpisu metody + +| Offset | Opkód | Zásobník | +| -----: | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1A3 | SWAP2 | 0x0153 CALLDATASIZE calldataload(4) 0xDA | +| 1A4 | SWAP1 | CALLDATASIZE 0x0153 calldataload(4) 0xDA | +| 1A5 | POP | 0x0153 calldataload(4) 0xDA | +| 1A6 | JUMP | calldataload(4) 0xDA | +| 153 | JUMPDEST | calldataload(4) 0xDA | +| 154 | PUSH2 0x016e | 0x016E calldataload(4) 0xDA | +| 157 | JUMP | calldataload(4) 0xDA | +| 16E | JUMPDEST | calldataload(4) 0xDA | +| 16F | PUSH1 0x04 | 0x04 calldataload(4) 0xDA | +| 171 | DUP2 | calldataload(4) 0x04 calldataload(4) 0xDA | +| 172 | DUP2 | 0x04 calldataload(4) 0x04 calldataload(4) 0xDA | +| 173 | SLOAD | Storage[4] calldataload(4) 0x04 calldataload(4) 0xDA | +| 174 | DUP2 | calldataload(4) Storage[4] calldataload(4) 0x04 calldataload(4) 0xDA | +| 175 | LT | calldataload(4)\)` a další je `isClaimed()`, takže to vypadá na airdrop kontrakt. Místo toho, abychom procházeli zbytek opkód po opkódu, můžeme [vyzkoušet dekompilátor](https://etherscan.io/bytecode-decompiler?a=0x2f81e57ff4f4d83b40a9f719fd892d8e806e0761), který pro tři funkce z tohoto kontraktu vytváří použitelné výsledky. Reverzní inženýrství ostatních je ponecháno jako cvičení pro čtenáře. + +### scaleAmountByPercentage {#scaleamountbypercentage} + +Toto nám dekompilátor dává pro tuto funkci: + +```python +def unknown8ffb5c97(uint256 _param1, uint256 _param2) payable: + require calldata.size - 4 >=′ 64 + if _param1 and _param2 > -1 / _param1: + revert with 0, 17 + return (_param1 * _param2 / 100 * 10^6) +``` + +První `require` testuje, zda calldata mají kromě čtyř bajtů signatury funkce alespoň 64 bajtů, což stačí pro dva parametry. Pokud ne, je zjevně něco špatně. + +Příkaz `if` se zdá, že kontroluje, zda `_param1` není nula a zda `_param1 * _param2` není záporné. Pravděpodobně se tak předchází případům přetečení. + +Nakonec funkce vrátí škálovanou hodnotu. + +### claim {#claim} + +Kód, který dekompilátor vytváří, je složitý a ne všechen je pro nás relevantní. Chystám se přeskočit některé jeho části, abych se zaměřil na řádky, které podle mě poskytují užitečné informace + +```python +def unknown2e7ba6ef(uint256 _param1, uint256 _param2, uint256 _param3, array _param4) payable: + ... + require _param2 == addr(_param2) + ... + if currentWindow <= _param1: + revert with 0, 'cannot claim for a future window' +``` + +Vidíme zde dvě důležité věci: + +- `_param2`, i když je deklarován jako `uint256`, je ve skutečnosti adresa +- `_param1` je nárokované okno, které musí být `currentWindow` nebo dřívější. + +```python + ... + if stor5[_claimWindow][addr(_claimFor)]: + revert with 0, 'Account already claimed the given window' +``` + +Takže teď víme, že Storage[5] je pole oken a adres a zda adresa nárokovala odměnu za dané okno. + +```python + ... + idx = 0 + s = 0 + while idx < _param4.length: + ... + if s + sha3(mem[(32 * _param4.length) + 328 len mem[(32 * _param4.length) + 296]]) > mem[(32 * idx) + 296]: + mem[mem[64] + 32] = mem[(32 * idx) + 296] + ... + s = sha3(mem[_62 + 32 len mem[_62]]) + continue + ... + s = sha3(mem[_66 + 32 len mem[_66]]) + continue + if unknown2eb4a7ab != s: + revert with 0, 'Invalid proof' +``` + +Víme, že `unknown2eb4a7ab` je ve skutečnosti funkce `merkleRoot()`, takže tento kód vypadá, jako by ověřoval [Merkleho důkaz](https://medium.com/crypto-0-nite/merkle-proofs-explained-6dd429623dc5). To znamená, že `_param4` je Merkleho důkaz. + +```python + call addr(_param2) with: + value unknown81e580d3[_param1] * _param3 / 100 * 10^6 wei + gas 30000 wei +``` + +Takto kontrakt převádí své vlastní ETH na jinou adresu (kontrakt nebo externě vlastněný účet). Volá ji s hodnotou, která je částkou, jež má být převedena. Vypadá to tedy, že se jedná o airdrop ETH. + +```python + if not return_data.size: + if not ext_call.success: + require ext_code.size(stor2) + call stor2.deposit() with: + value unknown81e580d3[_param1] * _param3 / 100 * 10^6 wei +``` + +Spodní dva řádky nám říkají, že Storage[2] je také kontrakt, který voláme. Pokud se [podíváme na transakci konstruktoru](https://etherscan.io/tx/0xa1ea0549fb349eb7d3aff90e1d6ce7469fdfdcd59a2fd9b8d1f5e420c0d05b58#statechange), vidíme, že tento kontrakt je [0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2), kontrakt Wrapped Ether [jehož zdrojový kód byl nahrán na Etherscan](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code). + +Vypadá to tedy, že se kontrakty pokoušejí poslat ETH na `_param2`. Pokud to dokáže, skvělé. Pokud ne, pokusí se odeslat [WETH](https://weth.tkn.eth.limo/). Pokud je `_param2` externě vlastněný účet (EOA), může vždy přijímat ETH, ale kontrakty mohou odmítnout přijímat ETH. WETH je však ERC-20 a kontrakty to nemohou odmítnout. + +```python + ... + log 0xdbd5389f: addr(_param2), unknown81e580d3[_param1] * _param3 / 100 * 10^6, bool(ext_call.success) +``` + +Na konci funkce vidíme generovaný záznam protokolu. [Podívejte se na vygenerované záznamy protokolu](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f#events) a filtrujte podle tématu, které začíná `0xdbd5...`. Pokud [klikneme na jednu z transakcí, která takový záznam vygenerovala](https://etherscan.io/tx/0xe7d3b7e00f645af17dfbbd010478ef4af235896c65b6548def1fe95b3b7d2274), uvidíme, že to skutečně vypadá jako nárok – účet odeslal zprávu kontraktu, který reverzně inženýrujeme, a na oplátku dostal ETH. + +![Transakce nároku](claim-tx.png) + +### 1e7df9d3 {#1e7df9d3} + +Tato funkce je velmi podobná [`claim`](#claim) výše. Také kontroluje Merkleho důkaz, pokouší se převést ETH na první a vytváří stejný typ záznamu do protokolu. + +```python +def unknown1e7df9d3(uint256 _param1, uint256 _param2, array _param3) payable: + ... + idx = 0 + s = 0 + while idx < _param3.length: + if idx >= mem[96]: + revert with 0, 50 + _55 = mem[(32 * idx) + 128] + if s + sha3(mem[(32 * _param3.length) + 160 len mem[(32 * _param3.length) + 128]]) > mem[(32 * idx) + 128]: + ... + s = sha3(mem[_58 + 32 len mem[_58]]) + continue + mem[mem[64] + 32] = s + sha3(mem[(32 * _param3.length) + 160 len mem[(32 * _param3.length) + 128]]) + ... + if unknown2eb4a7ab != s: + revert with 0, 'Invalid proof' + ... + call addr(_param1) with: + value s wei + gas 30000 wei + if not return_data.size: + if not ext_call.success: + require ext_code.size(stor2) + call stor2.deposit() with: + value s wei + gas gas_remaining wei + ... + log 0xdbd5389f: addr(_param1), s, bool(ext_call.success) +``` + +Hlavní rozdíl je v tom, že první parametr, okno pro výběr, zde není. Místo toho existuje smyčka přes všechna okna, která by mohla být nárokována. + +```python + idx = 0 + s = 0 + while idx < currentWindow: + ... + if stor5[mem[0]]: + if idx == -1: + revert with 0, 17 + idx = idx + 1 + s = s + continue + ... + stor5[idx][addr(_param1)] = 1 + if idx >= unknown81e580d3.length: + revert with 0, 50 + mem[0] = 4 + if unknown81e580d3[idx] and _param2 > -1 / unknown81e580d3[idx]: + revert with 0, 17 + if s > !(unknown81e580d3[idx] * _param2 / 100 * 10^6): + revert with 0, 17 + if idx == -1: + revert with 0, 17 + idx = idx + 1 + s = s + (unknown81e580d3[idx] * _param2 / 100 * 10^6) + continue +``` + +Vypadá to tedy na variantu `claim`, která nárokuje všechna okna. + +## Závěr {#conclusion} + +Nyní byste měli vědět, jak porozumět kontraktům, jejichž zdrojový kód není k dispozici, a to buď pomocí opkódů, nebo (pokud to funguje) pomocí dekompilátoru. Jak je zřejmé z délky tohoto článku, reverzní inženýrství kontraktu není triviální, ale v systému, kde je bezpečnost zásadní, je důležitou dovedností umět ověřit, že kontrakty fungují, jak slibují. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). diff --git a/public/content/translations/cs/developers/tutorials/run-node-raspberry-pi/index.md b/public/content/translations/cs/developers/tutorials/run-node-raspberry-pi/index.md new file mode 100644 index 00000000000..549ab2e1220 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/run-node-raspberry-pi/index.md @@ -0,0 +1,190 @@ +--- +title: "Spuštění uzlu Etherea na Raspberry Pi 4" +description: "Flashněte své Raspberry Pi 4, zapojte ethernetový kabel, připojte disk SSD a zapněte zařízení, abyste z Raspberry Pi 4 udělali plnohodnotný uzel Etherea + validátor." +author: "EthereumOnArm" +tags: + [ + "klienti", + "exekuční vrstva", + "konsensuální vrstva", + "uzly" + ] +lang: cs +skill: intermediate +published: 2022-06-10 +source: Ethereum on ARM +sourceUrl: https://ethereum-on-arm-documentation.readthedocs.io/en/latest/ +--- + +**Ethereum on Arm je vlastní obraz systému Linux, který může z Raspberry Pi udělat uzel Etherea.** + +Pro použití Etherea na Arm a přeměnu Raspberry Pi na uzel Etherea se doporučuje následující hardware: + +- Deska Raspberry 4 (model B 8GB), Odroid M1 nebo Rock 5B (8GB/16GB RAM) +- MicroSD karta (minimálně 16 GB, třída 10) +- Minimálně 2TB disk SSD USB 3.0 nebo SSD s pouzdrem USB na SATA. +- Zdroj napájení +- Ethernetový kabel +- Přesměrování portů (další informace najdete u klientů) +- Pouzdro s chladičem a ventilátorem +- USB klávesnice, monitor a kabel HDMI (micro-HDMI) (volitelně) + +## Proč provozovat Ethereum na ARM? {#why-run-ethereum-on-arm} + +Desky ARM jsou velmi cenově dostupné, flexibilní a malé počítače. Jsou dobrou volbou pro provozování uzlů Etherea, protože je lze levně koupit, nakonfigurovat tak, aby se všechny jejich zdroje soustředily pouze na uzel, což je činí efektivními, spotřebovávají málo energie a jsou fyzicky malé, takže se nenápadně vejdou do každé domácnosti. Je také velmi snadné zprovoznit uzly, protože MicroSD kartu Raspberry Pi lze jednoduše flashnout předpřipraveným obrazem, aniž by bylo nutné stahovat nebo sestavovat software. + +## Jak to funguje? {#how-does-it-work} + +Paměťová karta Raspberry Pi se flashne předpřipraveným obrazem. Tento obraz obsahuje vše potřebné pro spuštění uzlu Etherea. S flashnutou kartou stačí uživateli pouze zapnout Raspberry Pi. Všechny procesy potřebné ke spuštění uzlu se spustí automaticky. Funguje to proto, že paměťová karta obsahuje operační systém (OS) na bázi Linuxu, na kterém se automaticky spouštějí procesy na úrovni systému, které z jednotky udělají uzel Etherea. + +Ethereum nelze provozovat pomocí oblíbeného OS pro Raspberry Pi „Raspbian“, protože Raspbian stále používá 32bitovou architekturu, což způsobuje uživatelům Etherea problémy s pamětí, a konsensuální klienti nepodporují 32bitové binární soubory. Aby se to překonalo, tým Ethereum on Arm přešel na nativní 64bitový OS s názvem „Armbian“. + +**Obrazy se postarají o všechny nezbytné kroky**, od nastavení prostředí a formátování disku SSD až po instalaci a spuštění softwaru Etherea a zahájení synchronizace blockchainu. + +## Poznámka k exekučním a konsensuálním klientům {#note-on-execution-and-consensus-clients} + +Obraz Ethereum on Arm zahrnuje předpřipravené exekuční a konsensuální klienty jako služby. Uzel Etherea vyžaduje, aby byli oba klienti synchronizováni a spuštěni. Stačí si pouze stáhnout a flashnout obraz a poté spustit služby. V obrazu jsou předinstalováni následující exekuční klienti: + +- Geth +- Nethermind +- Besu + +a následující konsensuální klienti: + +- Lighthouse +- Nimbus +- Prysm +- Teku + +Měli byste si vybrat po jednom z každého typu – všichni exekuční klienti jsou kompatibilní se všemi konsensuálními klienty. Pokud si klienta explicitně nevyberete, uzel se vrátí ke svým výchozím hodnotám – Geth a Lighthouse – a spustí je automaticky po zapnutí desky. Na routeru musíte otevřít port 30303, aby Geth mohl najít peery a připojit se k nim. + +## Stažení obrazu {#downloading-the-image} + +Obraz Ethereum pro Raspberry Pi 4 je obraz typu „plug and play“, který automaticky nainstaluje a nastaví exekuční i konsensuální klienty, nakonfiguruje je pro vzájemnou komunikaci a připojení k síti Ethereum. Uživatel musí pouze spustit jejich procesy pomocí jednoduchého příkazu. + +Stáhněte si obraz Raspberry Pi z [Ethereum on Arm](https://ethereumonarm-my.sharepoint.com/:u:/p/dlosada/Ec_VmUvr80VFjf3RYSU-NzkBmj2JOteDECj8Bibde929Gw?download=1) a ověřte haš SHA256: + +```sh +# Z adresáře obsahujícího stažený obraz +shasum -a 256 ethonarm_22.04.00.img.zip +# Haš by měl mít výstup: fb497e8f8a7388b62d6e1efbc406b9558bee7ef46ec7e53083630029c117444f +``` + +Upozorňujeme, že obrazy pro desky Rock 5B a Odroid M1 jsou k dispozici na [stránce pro stažení](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/quick-guide/download-and-install.html) Ethereum-on-Arm. + +## Flashování MicroSD {#flashing-the-microsd} + +MicroSD karta, která bude použita pro Raspberry Pi, by měla být nejprve vložena do stolního počítače nebo notebooku, aby mohla být flashnuta. Poté následující příkazy v terminálu flashnou stažený obraz na SD kartu: + +```shell +# zkontrolujte název MicroSD karty +sudo fdisk -l + +>> sdxxx +``` + +Je opravdu důležité zadat správný název, protože další příkaz obsahuje `dd`, který před nahráním obrazu na kartu zcela vymaže její stávající obsah. Chcete-li pokračovat, přejděte do adresáře obsahujícího zazipovaný obraz: + +```shell +# rozbalte a flashněte obraz +unzip ethonarm_22.04.00.img.zip +sudo dd bs=1M if=ethonarm_22.04.00.img of=/dev/ conv=fdatasync status=progress +``` + +Karta je nyní flashnutá, takže ji lze vložit do Raspberry Pi. + +## Spuštění uzlu {#start-the-node} + +S SD kartou vloženou do Raspberry Pi připojte ethernetový kabel a SSD a poté zapněte napájení. OS se spustí a automaticky začne provádět předkonfigurované úlohy, které z Raspberry Pi udělají uzel Etherea, včetně instalace a sestavení klientského softwaru. To bude pravděpodobně trvat 10-15 minut. + +Jakmile je vše nainstalováno a nakonfigurováno, přihlaste se k zařízení přes ssh připojení nebo přímo pomocí terminálu, pokud je k desce připojen monitor a klávesnice. Pro přihlášení použijte účet `ethereum`, protože má oprávnění potřebná ke spuštění uzlu. + +```shell +Uživatel: ethereum +Heslo: ethereum +``` + +Výchozí exekuční klient, Geth, se spustí automaticky. To můžete potvrdit kontrolou protokolů pomocí následujícího příkazu v terminálu: + +```sh +sudo journalctl -u geth -f +``` + +Konsensuální klient je třeba spustit explicitně. Chcete-li to provést, nejprve na routeru otevřete port 9000, aby Lighthouse mohl najít peery a připojit se k nim. Poté povolte a spusťte službu lighthouse: + +```sh +sudo systemctl enable lighthouse-beacon +sudo systemctl start lighthouse-beacon +``` + +Zkontrolujte klienta pomocí protokolů: + +```sh +sudo journalctl -u lighthouse-beacon +``` + +Všimněte si, že konsensuální klient se synchronizuje během několika minut, protože používá synchronizaci z kontrolního bodu. Exekučnímu klientovi to bude trvat déle – potenciálně několik hodin, a nespustí se, dokud se konsensuální klient nedosynchronizuje (je to proto, že exekuční klient potřebuje cíl, se kterým se synchronizuje, a ten mu poskytne synchronizovaný konsensuální klient). + +Se spuštěnými a synchronizovanými službami Geth a Lighthouse je vaše Raspberry Pi nyní uzlem Etherea! Nejběžnější způsob interakce se sítí Ethereum je pomocí javascriptové konzole Geth, kterou lze připojit ke klientovi Geth na portu 8545. Je také možné odesílat příkazy formátované jako objekty JSON pomocí nástroje pro požadavky, jako je Curl. Více informací naleznete v [dokumentaci Geth](https://geth.ethereum.org/). + +Geth je předkonfigurován tak, aby hlásil metriky na řídicí panel Grafana, který lze zobrazit v prohlížeči. Pokročilejší uživatelé mohou chtít tuto funkci využít ke sledování stavu svého uzlu přechodem na `ipaddress:3000` a zadáním `user: admin` a `passwd: ethereum`. + +## Validátoři {#validators} + +K konsensuálnímu klientovi lze volitelně přidat také validátor. Software validátoru umožňuje vašemu uzlu aktivně se podílet na konsensu a poskytuje síti kryptoekonomickou bezpečnost. Za tuto práci dostanete odměnu v ETH. Abyste mohli spustit validátor, musíte mít nejprve 32 ETH, které musí být vloženy do vkladové smlouvy. Vklad lze provést podle podrobného průvodce na [Launchpadu](https://launchpad.ethereum.org/). Udělejte to na stolním počítači/notebooku, ale negenerujte klíče — to lze provést přímo na Raspberry Pi. + +Otevřete terminál na Raspberry Pi a spuštěním následujícího příkazu vygenerujte vkladové klíče: + +``` +sudo apt-get update +sudo apt-get install staking-deposit-cli +cd && deposit new-mnemonic --num_validators 1 +``` + +(Nebo si stáhněte [staking-deposit-cli](https://github.com/ethereum/staking-deposit-cli) pro spuštění na air-gapped zařízení a spusťte příkaz `deposit new-mnemnonic`) + +Uchovejte mnemotechnickou frázi v bezpečí! Výše uvedený příkaz vygeneroval v úložišti klíčů uzlu dva soubory: klíče validátoru a soubor s daty vkladu. Data vkladu je třeba nahrát do Launchpadu, takže je nutné je zkopírovat z Raspberry Pi do stolního počítače/notebooku. To lze provést pomocí ssh připojení nebo jakoukoli jinou metodou kopírování a vkládání. + +Jakmile je soubor s daty vkladu k dispozici na počítači, na kterém běží Launchpad, lze jej přetáhnout na `+` na obrazovce Launchpadu. Podle pokynů na obrazovce odešlete transakci do vkladové smlouvy. + +Zpět na Raspberry Pi lze spustit validátor. To vyžaduje import klíčů validátoru, nastavení adresy pro sběr odměn a následné spuštění předkonfigurovaného procesu validátoru. Níže uvedený příklad je pro Lighthouse – pokyny pro ostatní konsensuální klienti jsou k dispozici v [dokumentaci Ethereum on Arm](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/): + +```shell +# importujte klíče validátoru +lighthouse account validator import --directory=/home/ethereum/validator_keys + +# nastavte adresu pro odměny +sudo sed -i 's/' /etc/ethereum/lighthouse-validator.conf + +# spusťte validátor +sudo systemctl start lighthouse-validator +``` + +Gratulujeme, nyní máte na Raspberry Pi spuštěný plnohodnotný uzel Etherea a validátor! + +## Další podrobnosti {#more-details} + +Tato stránka poskytla přehled o tom, jak nastavit uzel a validátor Geth-Lighthouse pomocí Raspberry Pi. Podrobnější pokyny jsou k dispozici na [webu Ethereum-on-Arm](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/index.html). + +## Zpětná vazba je vítána {#feedback-appreciated} + +Víme, že Raspberry Pi má obrovskou uživatelskou základnu, která by mohla mít velmi pozitivní dopad na zdraví sítě Ethereum. +Projděte si prosím podrobnosti v tomto návodu, vyzkoušejte spuštění na testnetech, podívejte se na GitHubu na Ethereum on Arm, poskytněte zpětnou vazbu, nahlašujte problémy a posílejte pull requesty a pomozte tak posunout technologii a dokumentaci! + +## Odkazy {#references} + +1. https://ubuntu.com/download/raspberry-pi +2. https://wikipedia.org/wiki/Port_forwarding +3. https://prometheus.io +4. https://grafana.com +5. https://forum.armbian.com/topic/5565-zram-vs-swap/ +6. https://geth.ethereum.org +7. https://nethermind.io +8. https://www.hyperledger.org/projects/besu +9. https://github.com/prysmaticlabs/prysm +10. https://lighthouse.sigmaprime.io +11. https://ethersphere.github.io/swarm-home +12. https://raiden.network +13. https://ipfs.io +14. https://status.im +15. https://vipnode.org diff --git a/public/content/translations/cs/developers/tutorials/scam-token-tricks/index.md b/public/content/translations/cs/developers/tutorials/scam-token-tricks/index.md new file mode 100644 index 00000000000..713b73064f9 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/scam-token-tricks/index.md @@ -0,0 +1,470 @@ +--- +title: "Některé triky používané podvodnými tokeny a jak je odhalit" +description: "V tomto tutoriálu rozebereme podvodný token, abychom se podívali na některé triky, které podvodníci používají, jak je implementují a jak je můžeme odhalit." +author: Ori Pomerantz +tags: + [ + "podvod", + "solidity", + "erc-20", + "javascript", + "typescript" + ] +skill: intermediate +published: 2023-09-15 +lang: cs +--- + +V tomto tutoriálu rozebereme [podvodný token](https://etherscan.io/token/0xb047c8032b99841713b8e3872f06cf32beb27b82#code), abychom se podívali na některé triky, které podvodníci používají a jak je implementují. Na konci tohoto tutoriálu získáte komplexnější pohled na smlouvy o tokenech ERC-20, jejich možnosti a důvody, proč je nutná skepse. Poté se podíváme na události emitované tímto podvodným tokenem a zjistíme, jak můžeme automaticky identifikovat, že není legitimní. + +## Podvodné tokeny – co jsou, proč je lidé vytvářejí a jak se jim vyhnout {#scam-tokens} + +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. Kdekoliv, kde existují legitimní případy použití, které přinášejí hodnotu, se také objevují i zločinci, kteří se snaží tuto hodnotu ukrást pro sebe. + +Více si o tomto tématu můžete přečíst [jinde na ethereum.org](/guides/how-to-id-scam-tokens/) z pohledu uživatele. Tento tutoriál se zaměřuje na rozebrání podvodného tokenu, abychom zjistili, jak je vytvořen a jak jej lze odhalit. + +### Jak poznám, že wARB je podvod? {#warb-scam} + +Token, který rozebíráme, je [wARB](https://etherscan.io/token/0xb047c8032b99841713b8e3872f06cf32beb27b82#code), který se tváří jako ekvivalent legitimního [tokenu ARB](https://etherscan.io/token/0xb50721bcf8d664c30412cfbc6cf7a15145234ad1). + +Nejjednodušší způsob, jak zjistit, který token je legitimní, je podívat se na původní organizaci, [Arbitrum](https://arbitrum.foundation/). Legitimní adresy jsou uvedeny [v jejich dokumentaci](https://docs.arbitrum.foundation/deployment-addresses#token). + +### Proč je zdrojový kód dostupný? {#why-source} + +Obvykle bychom očekávali, že lidé, kteří se snaží ostatní podvést, budou tajnůstkářští, a skutečně mnoho podvodných tokenů nemá svůj kód k dispozici (například [tento](https://optimistic.etherscan.io/token/0x15992f382d8c46d667b10dc8456dc36651af1452#code) a [tento](https://optimistic.etherscan.io/token/0x026b623eb4aada7de37ef25256854f9235207178#code)). + +Legitimní tokeny však obvykle svůj zdrojový kód zveřejňují, takže aby působili legitimně, autoři podvodných tokenů někdy dělají totéž. [wARB](https://etherscan.io/token/0xb047c8032b99841713b8e3872f06cf32beb27b82#code) je jedním z těch tokenů s dostupným zdrojovým kódem, což usnadňuje jeho pochopení. + +I když si tvůrci smluv mohou vybrat, zda zdrojový kód zveřejní, či nikoli, _nemohou_ zveřejnit nesprávný zdrojový kód. Průzkumník bloků nezávisle zkompiluje poskytnutý zdrojový kód, a pokud neobdrží přesně stejný bajtkód, tento zdrojový kód odmítne. [Více si o tom můžete přečíst na stránkách Etherscanu](https://etherscan.io/verifyContract). + +## Srovnání s legitimními tokeny ERC-20 {#compare-legit-erc20} + +Tento token porovnáme s legitimními tokeny ERC-20. Pokud nevíte, jak se obvykle píší legitimní tokeny ERC-20, [podívejte se na tento tutoriál](/developers/tutorials/erc20-annotated-code/). + +### Konstanty pro privilegované adresy {#constants-for-privileged-addresses} + +Smlouvy někdy potřebují privilegované adresy. Smlouvy, které jsou navrženy pro dlouhodobé použití, umožňují některé privilegované adrese změnit tyto adresy, například aby bylo možné použít novou smlouvu multisig. Existuje několik způsobů, jak to udělat. + +[Smlouva tokenu `HOP`](https://etherscan.io/address/0xc5102fe9359fd9a28f877a67e36b0f050d81a3cc#code) používá vzor [`Ownable`](https://docs.openzeppelin.com/contracts/2.x/access-control#ownership-and-ownable). Privilegovaná adresa je uložena v úložišti, v poli nazvaném `_owner` (viz třetí soubor, `Ownable.sol`). + +```solidity +abstract contract Ownable is Context { + address private _owner; + . + . + . +} +``` + +Smlouva o [tokenu `ARB`](https://etherscan.io/address/0xad0c361ef902a7d9851ca7dcc85535da2d3c6fc7#code) nemá přímo privilegovanou adresu. Nepotřebuje ji však. Nachází se za [`proxy`](https://docs.openzeppelin.com/contracts/5.x/api/proxy) na [adrese `0xb50721bcf8d664c30412cfbc6cf7a15145234ad1`](https://etherscan.io/address/0xb50721bcf8d664c30412cfbc6cf7a15145234ad1#code). Tato smlouva má privilegovanou adresu (viz čtvrtý soubor, `ERC1967Upgrade.sol`), kterou lze použít pro upgrady. + +```solidity + /** + * @dev Uloží novou adresu do slotu správce EIP1967. + */ + function _setAdmin(address newAdmin) private { + require(newAdmin != address(0), "ERC1967: new admin is the zero address"); + StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; + } +``` + +Naproti tomu smlouva `wARB` má napevno zakódovaného `contract_owner`. + +```solidity +contract WrappedArbitrum is Context, IERC20 { + . + . + . + address deployer = 0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1; + address public contract_owner = 0xb40dE7b1beE84Ff2dc22B70a049A07A13a411A33; + . + . + . +} +``` + +[Tento vlastník smlouvy](https://etherscan.io/address/0xb40dE7b1beE84Ff2dc22B70a049A07A13a411A33) není smlouva, kterou by mohly v různých časech ovládat různé účty, ale [externě vlastněný účet](/developers/docs/accounts/#externally-owned-accounts-and-key-pairs). To znamená, že je pravděpodobně navržen pro krátkodobé použití jednotlivcem, spíše než jako dlouhodobé řešení pro ovládání ERC-20, které si udrží hodnotu. + +A skutečně, když se podíváme na Etherscan, vidíme, že podvodník tuto smlouvu používal pouze 12 hodin ([první transakce](https://etherscan.io/tx/0xf49136198c3f925fcb401870a669d43cecb537bde36eb8b41df77f06d5f6fbc2) po [poslední transakci](https://etherscan.io/tx/0xdfd6e717157354e64bbd5d6adf16761e5a5b3f914b1948d3545d39633244d47b)) dne 19. května 2023. + +### Falešná funkce `_transfer` {#the-fake-transfer-function} + +Je standardní, že skutečné převody probíhají pomocí [interní funkce `_transfer`](/developers/tutorials/erc20-annotated-code/#the-_transfer-function-_transfer). + +Ve `wARB` tato funkce vypadá téměř legitimně: + +```solidity + function _transfer(address sender, address recipient, uint256 amount) internal virtual{ + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: částka převodu překračuje zůstatek"); + _balances[recipient] = _balances[recipient].add(amount); + if (sender == contract_owner){ + sender = deployer; + } + emit Transfer(sender, recipient, amount); + } +``` + +Podezřelá část je: + +```solidity + if (sender == contract_owner){ + sender = deployer; + } + emit Transfer(sender, recipient, amount); +``` + +Pokud vlastník smlouvy posílá tokeny, proč událost `Transfer` ukazuje, že pocházejí od `deployer`? + +Je zde však důležitější problém. Kdo volá tuto funkci `_transfer`? Nelze ji volat zvenčí, je označena jako `internal`. A kód, který máme, neobsahuje žádná volání `_transfer`. Je zřejmé, že je zde jako návnada. + +```solidity + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _f_(_msgSender(), recipient, amount); + return true; + } + + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _f_(sender, recipient, amount); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: částka převodu překračuje povolenou částku")); + return true; + } +``` + +Když se podíváme na funkce, které se volají pro převod tokenů, `transfer` a `transferFrom`, vidíme, že volají úplně jinou funkci, `_f_`. + +### Skutečná funkce `_f_` {#the-real-f-function} + +```solidity + function _f_(address sender, address recipient, uint256 amount) internal _mod_(sender,recipient,amount) virtual { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: částka převodu překračuje zůstatek"); + _balances[recipient] = _balances[recipient].add(amount); + if (sender == contract_owner){ + + sender = deployer; + } + emit Transfer(sender, recipient, amount); + } +``` + +V této funkci jsou dva potenciální varovné signály. + +- Použití [modifikátoru funkce](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm) `_mod_`. Když se však podíváme do zdrojového kódu, vidíme, že `_mod_` je ve skutečnosti neškodný. + + ```solidity + modifier _mod_(address sender, address recipient, uint256 amount){ + _; + } + ``` + +- Stejný problém, který jsme viděli v `_transfer`, což je, když `contract_owner` posílá tokeny, které se zdají pocházet od `deployer`. + +### Falešná funkce událostí `dropNewTokens` {#the-fake-events-function-dropNewTokens} + +Nyní se dostáváme k něčemu, co vypadá jako skutečný podvod. Funkci jsem pro lepší čitelnost trochu upravil, ale je funkčně ekvivalentní. + +```solidity +function dropNewTokens(address uPool, + address[] memory eReceiver, + uint256[] memory eAmounts) public auth() +``` + +Tato funkce má modifikátor `auth()`, což znamená, že ji může volat pouze vlastník smlouvy. + +```solidity +modifier auth() { + require(msg.sender == contract_owner, "Not allowed to interact"); + _; +} +``` + +Toto omezení dává dokonalý smysl, protože bychom nechtěli, aby náhodné účty distribuovaly tokeny. Zbytek funkce je však podezřelý. + +```solidity +{ + for (uint256 i = 0; i < eReceiver.length; i++) { + emit Transfer(uPool, eReceiver[i], eAmounts[i]); + } +} +``` + +Funkce pro převod z účtu fondu na pole příjemců pole částek dává dokonalý smysl. Existuje mnoho případů použití, kdy budete chtít distribuovat tokeny z jednoho zdroje do více cílů, jako jsou výplaty, airdropy atd. Je levnější (z hlediska paliva) provést to v jedné transakci namísto vydávání více transakcí, nebo dokonce volat ERC-20 vícekrát z jiné smlouvy jako součást stejné transakce. + +`dropNewTokens` to však nedělá. Emituje [události `Transfer`](https://eips.ethereum.org/EIPS/eip-20#transfer-1), ale ve skutečnosti žádné tokeny nepřevádí. Neexistuje žádný legitimní důvod k matení offchainových aplikací tím, že jim řeknete o převodu, který se ve skutečnosti nestal. + +### Pálící funkce `Approve` {#the-burning-approve-function} + +Smlouvy ERC-20 mají mít [funkci `approve`](/developers/tutorials/erc20-annotated-code/#approve) pro povolené částky, a náš podvodný token skutečně takovou funkci má, a je dokonce správná. Protože však Solidity vychází z jazyka C, rozlišuje velikost písmen. "Approve" a "approve" jsou různé řetězce. + +Funkčnost také nesouvisí s `approve`. + +```solidity + function Approve( + address[] memory holders) +``` + +Tato funkce je volána s polem adres držitelů tokenu. + +```solidity + public approver() { +``` + +Modifikátor `approver()` zajišťuje, že tuto funkci může volat pouze `contract_owner` (viz níže). + +```solidity + for (uint256 i = 0; i < holders.length; i++) { + uint256 amount = _balances[holders[i]]; + _beforeTokenTransfer(holders[i], 0x0000000000000000000000000000000000000001, amount); + _balances[holders[i]] = _balances[holders[i]].sub(amount, + "ERC20: pálená částka překračuje zůstatek"); + _balances[0x0000000000000000000000000000000000000001] = + _balances[0x0000000000000000000000000000000000000001].add(amount); + } + } + +``` + +Pro každou adresu držitele funkce přesune celý zůstatek držitele na adresu `0x00...01` a tím ho efektivně spálí (skutečné `pálení` ve standardu také mění celkovou zásobu a převádí tokeny na `0x00...00`). To znamená, že `contract_owner` může odebrat majetek jakéhokoli uživatele. To se nezdá jako funkce, kterou byste chtěli ve správcovském tokenu. + +### Problémy s kvalitou kódu {#code-quality-issues} + +Tyto problémy s kvalitou kódu _nedokazují_, že tento kód je podvod, ale způsobují, že působí podezřele. Organizované společnosti jako Arbitrum obvykle takto špatný kód nevydávají. + +#### Funkce `mount` {#the-mount-function} + +Ačkoli to není uvedeno ve [standardu](https://eips.ethereum.org/EIPS/eip-20), obecně se funkce, která vytváří nové tokeny, nazývá [`mint`](https://ethereum.org/el/developers/tutorials/erc20-annotated-code/#the-_mint-and-_burn-functions-_mint-and-_burn). + +Když se podíváme do konstruktoru `wARB`, vidíme, že funkce mint byla z nějakého důvodu přejmenována na `mount` a je volána pětkrát s pětinou počáteční zásoby, místo jednou pro celou částku z důvodu efektivity. + +```solidity + constructor () public { + + _name = "Wrapped Arbitrum"; + _symbol = "wARB"; + _decimals = 18; + uint256 initialSupply = 1000000000000; + + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + } +``` + +Samotná funkce `mount` je také podezřelá. + +```solidity + function mount(address account, uint256 amount) public { + require(msg.sender == contract_owner, "ERC20: mint to the zero address"); +``` + +Při pohledu na `require` vidíme, že pouze vlastník smlouvy má povoleno razit. To je legitimní. Ale chybová zpráva by měla být _pouze vlastník má povoleno razit_ nebo něco podobného. Místo toho je to irelevantní _ERC20: ražba na nulovou adresu_. Správný test pro ražbu na nulovou adresu je `require(account != address(0), "")`, což se smlouva nikdy neobtěžuje zkontrolovat. + +```solidity + _totalSupply = _totalSupply.add(amount); + _balances[contract_owner] = _balances[contract_owner].add(amount); + emit Transfer(address(0), account, amount); + } +``` + +Existují dvě další podezřelé skutečnosti, přímo související s ražbou: + +- Existuje parametr `account`, což je pravděpodobně účet, který by měl obdržet vyraženou částku. Ale zůstatek, který se zvyšuje, je ve skutečnosti `contract_owner`a. + +- Zatímco zvýšený zůstatek patří `contract_owner`ovi, emitovaná událost ukazuje převod na `account`. + +### Proč `auth` i `approver`? Proč `mod`, který nic nedělá? {#why-both-autho-and-approver-why-the-mod-that-does-nothing} + +Tato smlouva obsahuje tři modifikátory: `_mod_`, `auth` a `approver`. + +```solidity + modifier _mod_(address sender, address recipient, uint256 amount){ + _; + } +``` + +`_mod_` přijímá tři parametry a nic s nimi nedělá. Proč ho mít? + +```solidity + modifier auth() { + require(msg.sender == contract_owner, "Not allowed to interact"); + _; + } + + modifier approver() { + require(msg.sender == contract_owner, "Not allowed to interact"); + _; + } +``` + +`auth` a `approver` dávají větší smysl, protože kontrolují, že smlouva byla volána `contract_owner`. Očekávali bychom, že určité privilegované akce, jako je ražba, budou omezeny na tento účet. Jaký je však smysl mít dvě samostatné funkce, které dělají _přesně to samé_? + +## Co můžeme detekovat automaticky? {#what-can-we-detect-automatically} + +Při pohledu na Etherscan vidíme, že `wARB` je podvodný token. Jedná se však o centralizované řešení. Teoreticky by mohl být Etherscan podvržen nebo napaden. Je lepší být schopen nezávisle zjistit, zda je token legitimní, nebo ne. + +Existují některé triky, které můžeme použít k identifikaci, že token ERC-20 je podezřelý (buď podvod, nebo velmi špatně napsaný), a to pohledem na události, které emitují. + +## Podezřelé události `Approval` {#suspicious-approval-events} + +[Události `Approval`](https://eips.ethereum.org/EIPS/eip-20#approval) by se měly dít pouze na přímou žádost (na rozdíl od [událostí `Transfer`](https://eips.ethereum.org/EIPS/eip-20#transfer-1), které se mohou stát v důsledku povolené částky). [Viz dokumentaci Solidity](https://docs.soliditylang.org/en/v0.8.20/security-considerations.html#tx-origin) pro podrobné vysvětlení tohoto problému a proč musí být žádosti přímé, nikoli zprostředkované smlouvou. + +To znamená, že události `Approval`, které schvalují útratu z [externě vlastněného účtu](/developers/docs/accounts/#types-of-account), musí pocházet z transakcí, které vznikly na tomto účtu a jejichž cílem je smlouva ERC-20. Jakýkoli jiný druh schválení z externě vlastněného účtu je podezřelý. + +Zde je [program, který identifikuje tento druh události](https://github.com/qbzzt/20230915-scam-token-detection), pomocí [viem](https://viem.sh/) a [TypeScript](https://www.typescriptlang.org/docs/), varianty JavaScriptu s typovou bezpečností. Spustíte ho takto: + +1. Zkopírujte `.env.example` do `.env`. +2. Upravte `.env` a zadejte URL k uzlu hlavní sítě Ethereum. +3. Spusťte `pnpm install` pro instalaci potřebných balíčků. +4. Spusťte `pnpm susApproval` pro vyhledání podezřelých schválení. + +Zde je vysvětlení řádek po řádku: + +```typescript +import { + Address, + TransactionReceipt, + createPublicClient, + http, + parseAbiItem, +} from "viem" +import { mainnet } from "viem/chains" +``` + +Importujte definice typů, funkce a definici řetězce z `viem`. + +```typescript +import { config } from "dotenv" +config() +``` + +Přečtěte si `.env` pro získání URL. + +```typescript +const client = createPublicClient({ + chain: mainnet, + transport: http(process.env.URL), +}) +``` + +Vytvořte klienta Viem. Potřebujeme pouze číst z blockchainu, takže tento klient nepotřebuje soukromý klíč. + +```typescript +const testedAddress = "0xb047c8032b99841713b8e3872f06cf32beb27b82" +const fromBlock = 16859812n +const toBlock = 16873372n +``` + +Adresa podezřelé smlouvy ERC-20 a bloky, ve kterých budeme hledat události. Poskytovatelé uzlů obvykle omezují naši schopnost číst události, protože šířka pásma může být drahá. Naštěstí `wARB` nebyl používán po dobu osmnácti hodin, takže se můžeme podívat na všechny události (bylo jich celkem jen 13). + +```typescript +const approvalEvents = await client.getLogs({ + address: testedAddress, + fromBlock, + toBlock, + event: parseAbiItem( + "event Approval(address indexed _owner, address indexed _spender, uint256 _value)" + ), +}) +``` + +Tímto způsobem požádáte Viem o informace o události. Když mu poskytneme přesný podpis události, včetně názvů polí, událost pro nás analyzuje. + +```typescript +const isContract = async (addr: Address): boolean => + await client.getBytecode({ address: addr }) +``` + +Náš algoritmus je použitelný pouze na externě vlastněné účty. Pokud `client.getBytecode` vrátí jakýkoli bajtkód, znamená to, že se jedná o smlouvu a měli bychom ji přeskočit. + +Pokud jste TypeScript ještě nepoužívali, definice funkce může vypadat trochu divně. Neříkáme mu jen, že se první (a jediný) parametr jmenuje `addr`, ale také, že je typu `Address`. Podobně část `: boolean` říká TypeScriptu, že návratová hodnota funkce je boolean. + +```typescript +const getEventTxn = async (ev: Event): TransactionReceipt => + await client.getTransactionReceipt({ hash: ev.transactionHash }) +``` + +Tato funkce získá potvrzení o transakci z události. Potvrzení potřebujeme, abychom se ujistili, že známe cíl transakce. + +```typescript +const suspiciousApprovalEvent = async (ev : Event) : (Event | null) => { +``` + +Toto je nejdůležitější funkce, která skutečně rozhoduje, zda je událost podezřelá, nebo ne. Návratový typ, `(Event | null)`, říká TypeScriptu, že tato funkce může vrátit buď `Event`, nebo `null`. Vrátíme `null`, pokud událost není podezřelá. + +```typescript +const owner = ev.args._owner +``` + +Viem má názvy polí, takže pro nás analyzoval událost. `_owner` je vlastníkem tokenů, které mají být utraceny. + +```typescript +// Schválení smlouvami nejsou podezřelá +if (await isContract(owner)) return null +``` + +Pokud je vlastníkem smlouva, předpokládejme, že toto schválení není podezřelé. Abychom zkontrolovali, zda je schválení smlouvy podezřelé, nebo ne, budeme muset sledovat celé provedení transakce, abychom zjistili, zda se někdy dostala k vlastnické smlouvě a zda tato smlouva volala smlouvu ERC-20 přímo. To je mnohem náročnější na zdroje, než bychom chtěli dělat. + +```typescript +const txn = await getEventTxn(ev) +``` + +Pokud schválení pochází z externě vlastněného účtu, získejte transakci, která ho způsobila. + +```typescript +// Schválení je podezřelé, pokud pochází od vlastníka EOA, který není `from` transakce +if (owner.toLowerCase() != txn.from.toLowerCase()) return ev +``` + +Nemůžeme jen zkontrolovat rovnost řetězců, protože adresy jsou hexadecimální, takže obsahují písmena. Někdy, například v `txn.from`, jsou všechna tato písmena malá. V jiných případech, jako je `ev.args._owner`, je adresa v [různých velikostech písmen pro identifikaci chyby](https://eips.ethereum.org/EIPS/eip-55). + +Ale pokud transakce nepochází od vlastníka a tento vlastník je externě vlastněn, pak máme podezřelou transakci. + +```typescript +// Je také podezřelé, pokud cíl transakce není smlouva ERC-20, kterou +// zkoumáme +if (txn.to.toLowerCase() != testedAddress) return ev +``` + +Podobně, pokud adresa `to` transakce, první volaná smlouva, není zkoumaná smlouva ERC-20, pak je to podezřelé. + +```typescript + // Pokud není důvod k podezření, vrátíme null. + return null +} +``` + +Pokud žádná z podmínek není pravdivá, pak událost `Approval` není podezřelá. + +```typescript +const testPromises = approvalEvents.map((ev) => suspiciousApprovalEvent(ev)) +const testResults = (await Promise.all(testPromises)).filter((x) => x != null) + +console.log(testResults) +``` + +[Funkce `async`](https://www.w3schools.com/js/js_async.asp) vrací objekt `Promise`. S běžnou syntaxí `await x()` čekáme, až se tento `Promise` splní, než budeme pokračovat ve zpracování. To se jednoduše programuje a sleduje, ale je to také neefektivní. Zatímco čekáme na splnění `Promise` pro konkrétní událost, můžeme již začít pracovat na další události. + +Zde používáme [`map`](https://www.w3schools.com/jsref/jsref_map.asp) k vytvoření pole objektů `Promise`. Poté použijeme [`Promise.all`](https://www.javascripttutorial.net/es6/javascript-promise-all/) k čekání, až se všechny tyto sliby vyřeší. Poté tyto výsledky [`filtrujeme`](https://www.w3schools.com/jsref/jsref_filter.asp), abychom odstranili nepodezřelé události. + +### Podezřelé události `Transfer` {#suspicious-transfer-events} + +Dalším možným způsobem, jak identifikovat podvodné tokeny, je zjistit, zda mají nějaké podezřelé převody. Například převody z účtů, které nemají tolik tokenů. Můžete se podívat, [jak implementovat tento test](https://github.com/qbzzt/20230915-scam-token-detection/blob/main/susTransfer.ts), ale `wARB` tento problém nemá. + +## Závěr {#conclusion} + +Automatická detekce podvodů ERC-20 trpí [falešně negativními výsledky](https://en.wikipedia.org/wiki/False_positives_and_false_negatives#False_negative_error), protože podvod může použít naprosto normální smlouvu o tokenu ERC-20, která jen nepředstavuje nic skutečného. Proto byste se měli vždy pokusit _získat adresu tokenu z důvěryhodného zdroje_. + +Automatická detekce může pomoci v určitých případech, jako jsou součásti DeFi, kde je mnoho tokenů a je třeba s nimi zacházet automaticky. Ale jako vždy [caveat emptor](https://www.investopedia.com/terms/c/caveatemptor.asp), proveďte si vlastní průzkum a povzbuďte své uživatele, aby dělali totéž. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). diff --git a/public/content/translations/cs/developers/tutorials/secret-state/index.md b/public/content/translations/cs/developers/tutorials/secret-state/index.md new file mode 100644 index 00000000000..1e18be82af1 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/secret-state/index.md @@ -0,0 +1,741 @@ +--- +title: "Použití nulové znalosti pro tajný stav" +description: "Onchain hry jsou omezené, protože nemohou uchovávat žádné skryté informace. Po přečtení tohoto tutoriálu bude čtenář schopen kombinovat důkazy s nulovou znalostí a serverové komponenty k vytvoření ověřitelných her s tajnou stavovou offchain komponentou. Technika, jak toho dosáhnout, bude demonstrována vytvořením hry hledání min (minesweeper)." +author: Ori Pomerantz +tags: + [ + "server", + "offchain", + "centralizované", + "nulová znalost", + "zokrates", + "mud" + ] +skill: advanced +lang: cs +published: 2025-03-15 +--- + +_Na blockchainu neexistují žádná tajemství_. Vše, co je zveřejněno na blockchainu, si může kdokoli přečíst. To je nutné, protože blockchain je založen na tom, že ho kdokoli může ověřit. Hry však často spoléhají na tajný stav. Například hra [hledání min](https://en.wikipedia.org/wiki/Minesweeper_\(video_game\)) nedává absolutně žádný smysl, pokud se můžete jen podívat na průzkumník bloků a vidět mapu. + +Nejjednodušším řešením je použít [serverovou komponentu](/developers/tutorials/server-components/) k uchování tajného stavu. Důvodem, proč používáme blockchain, je však zabránit podvádění ze strany vývojáře hry. Musíme zajistit poctivost serverové komponenty. Server může poskytnout haš stavu a použít [důkazy s nulovou znalostí](/zero-knowledge-proofs/#why-zero-knowledge-proofs-are-important) k prokázání, že stav použitý k výpočtu výsledku tahu je správný. + +Po přečtení tohoto článku budete vědět, jak vytvořit tento druh serveru pro uchovávání tajného stavu, klienta pro zobrazení stavu a onchain komponentu pro komunikaci mezi nimi. Hlavní nástroje, které použijeme, budou: + +| Nástroj | Účel | Ověřeno na verzi | +| --------------------------------------------- | ------------------------------------------ | --------------------------------------: | +| [Zokrates](https://zokrates.github.io/) | Důkazy s nulovou znalostí a jejich ověření | 1.1.9 | +| [Typescript](https://www.typescriptlang.org/) | Programovací jazyk pro server i klienta | 5.4.2 | +| [Node](https://nodejs.org/en) | Spuštění serveru | 20.18.2 | +| [Viem](https://viem.sh/) | Komunikace s blockchainem | 2.9.20 | +| [MUD](https://mud.dev/) | Onchain správa dat | 2.0.12 | +| [React](https://react.dev/) | Uživatelské rozhraní klienta | 18.2.0 | +| [Vite](https://vitejs.dev/) | Poskytování klientského kódu | 4.2.1 | + +## Příklad hry Hledání min (Minesweeper) {#minesweeper} + +[Hledání min](https://en.wikipedia.org/wiki/Minesweeper_\(video_game\)) je hra, která obsahuje tajnou mapu s minovým polem. Hráč si vybere, kde bude kopat. Pokud je na daném místě mina, hra končí. V opačném případě hráč získá počet min v osmi polích obklopujících dané místo. + +Tato aplikace je napsána pomocí [MUD](https://mud.dev/), což je framework, který nám umožňuje ukládat data na blockchainu pomocí [databáze klíč-hodnota](https://aws.amazon.com/nosql/key-value/) a automaticky synchronizovat tato data s offchain komponentami. Kromě synchronizace MUD usnadňuje poskytování řízení přístupu a umožňuje ostatním uživatelům bez oprávnění [rozšířit](https://mud.dev/guides/extending-a-world) naši aplikaci. + +### Spuštění příkladu Hledání min {#running-minesweeper-example} + +Chcete-li spustit příklad Hledání min: + +1. Ujistěte se, že [máte nainstalované všechny předpoklady](https://mud.dev/quickstart#prerequisites): [Node](https://mud.dev/quickstart#prerequisites), [Foundry](https://book.getfoundry.sh/getting-started/installation), [`git`](https://git-scm.com/downloads), [`pnpm`](https://git-scm.com/downloads) a [`mprocs`](https://github.com/pvolok/mprocs). + +2. Naklonujte repozitář. + + ```sh copy + git clone https://github.com/qbzzt/20240901-secret-state.git + ``` + +3. Nainstalujte balíčky. + + ```sh copy + cd 20240901-secret-state/ + pnpm install + npm install -g mprocs + ``` + + Pokud byl Foundry nainstalován jako součást `pnpm install`, musíte restartovat příkazový řádek. + +4. Zkompilujte kontrakty + + ```sh copy + cd packages/contracts + forge build + cd ../.. + ``` + +5. Spusťte program (včetně [anvil](https://book.getfoundry.sh/anvil/) blockchainu) a počkejte. + + ```sh copy + mprocs + ``` + + Upozorňujeme, že spuštění trvá dlouho. Chcete-li vidět postup, nejprve pomocí šipky dolů přejděte na záložku _contracts_, abyste viděli, jak se nasazují MUD kontrakty. Když se zobrazí zpráva _Waiting for file changes…_, kontrakty jsou nasazeny a další postup se bude odehrávat na záložce _server_. Tam počkáte, dokud neobdržíte zprávu _Verifier address: 0x...._. + + Pokud je tento krok úspěšný, zobrazí se obrazovka `mprocs` s různými procesy vlevo a výstupem konzole pro aktuálně vybraný proces vpravo. + + ![Obrazovka mprocs](./mprocs.png) + + Pokud nastane problém s `mprocs`, můžete čtyři procesy spustit ručně, každý v samostatném okně příkazového řádku: + + - **Anvil** + + ```sh + cd packages/contracts + anvil --base-fee 0 --block-time 2 + ``` + + - **Kontrakty** + + ```sh + cd packages/contracts + pnpm mud dev-contracts --rpc http://127.0.0.1:8545 + ``` + + - **Server** + + ```sh + cd packages/server + pnpm start + ``` + + - **Klient** + + ```sh + cd packages/client + pnpm run dev + ``` + +6. Nyní můžete přejít na [klienta](http://localhost:3000), kliknout na **New Game** (Nová hra) a začít hrát. + +### Tabulky {#tables} + +Potřebujeme [několik tabulek](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/mud.config.ts) na blockchainu. + +- `Configuration`: Tato tabulka je singleton, nemá žádný klíč a jeden záznam. Slouží k uchovávání informací o konfiguraci hry: + - `height`: Výška minového pole + - `width`: Šířka minového pole + - `numberOfBombs`: Počet bomb v každém minovém poli + +- `VerifierAddress`: Tato tabulka je také singleton. Slouží k uchování jedné části konfigurace, adresy verifikačního kontraktu (`verifier`). Tuto informaci jsme mohli umístit do tabulky `Configuration`, ale je nastavována jinou komponentou, serverem, takže je jednodušší ji umístit do samostatné tabulky. + +- `PlayerGame`: Klíčem je adresa hráče. Data jsou: + + - `gameId`: 32bajtová hodnota, která je hašem mapy, na které hráč hraje (identifikátor hry). + - `win`: booleovská hodnota, která udává, zda hráč hru vyhrál. + - `lose`: booleovská hodnota, která udává, zda hráč hru prohrál. + - `digNumber`: počet úspěšných odkrytí políček ve hře. + +- `GamePlayer`: Tato tabulka obsahuje reverzní mapování z `gameId` na adresu hráče. + +- `Map`: Klíč je n-tice tří hodnot: + + - `gameId`: 32bajtová hodnota, která je hašem mapy, na které hráč hraje (identifikátor hry). + - `x` souřadnice + - `y` souřadnice + + Hodnota je jediné číslo. Je to 255, pokud byla detekována bomba. Jinak je to počet bomb v okolí daného místa plus jedna. Nemůžeme použít jen počet bomb, protože ve výchozím nastavení jsou všechna úložiště v EVM a všechny hodnoty řádků v MUD nulové. Musíme rozlišovat mezi „hráč zde ještě nekopal“ a „hráč zde kopal a zjistil, že v okolí nejsou žádné bomby“. + +Kromě toho komunikace mezi klientem a serverem probíhá prostřednictvím onchain komponenty. To je také implementováno pomocí tabulek. + +- `PendingGame`: Nevyřízené žádosti o spuštění nové hry. +- `PendingDig`: Nevyřízené požadavky na kopání na konkrétním místě v konkrétní hře. Toto je [offchain tabulka](https://mud.dev/store/tables#types-of-tables), což znamená, že se nezapisuje do úložiště EVM, je čitelná pouze mimo blockchain pomocí událostí. + +### Provádění a datové toky {#execution-data-flows} + +Tyto toky koordinují provádění mezi klientem, onchain komponentou a serverem. + +#### Inicializace {#initialization-flow} + +Když spustíte `mprocs`, dojde k těmto krokům: + +1. [`mprocs`](https://github.com/pvolok/mprocs) spouští čtyři komponenty: + + - [Anvil](https://book.getfoundry.sh/anvil/), který spouští místní blockchain + - [Contracts](https://github.com/qbzzt/20240901-secret-state/tree/main/packages/contracts), který kompiluje (v případě potřeby) a nasazuje kontrakty pro MUD + - [Client](https://github.com/qbzzt/20240901-secret-state/tree/main/packages/client), který spouští [Vite](https://vitejs.dev/) k poskytování uživatelského rozhraní a klientského kódu webovým prohlížečům. + - [Server](https://github.com/qbzzt/20240901-secret-state/tree/main/packages/server), který provádí akce serveru + +2. Balíček `contracts` nasadí kontrakty MUD a poté spustí [skript `PostDeploy.s.sol`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/script/PostDeploy.s.sol). Tento skript nastaví konfiguraci. Kód z GitHubu specifikuje [minové pole 10x5 s osmi minami](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/script/PostDeploy.s.sol#L23). + +3. [Server](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts) se spouští [nastavením MUD](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L6). Mimo jiné se tím aktivuje synchronizace dat, takže kopie příslušných tabulek existuje v paměti serveru. + +4. Server zaregistruje funkci k provedení [při změně tabulky `Configuration`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L23). [Tato funkce](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L24-L168) se volá po spuštění `PostDeploy.s.sol` a úpravě tabulky. + +5. Když má inicializační funkce serveru konfiguraci, [volá `zkFunctions`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L34-L35) pro inicializaci [části serveru s nulovou znalostí](#using-zokrates-from-typescript). To se nemůže stát, dokud nezískáme konfiguraci, protože funkce s nulovou znalostí musí mít šířku a výšku minového pole jako konstanty. + +6. Po inicializaci části serveru s nulovou znalostí je dalším krokem [nasazení verifikačního kontraktu s nulovou znalostí na blockchain](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L42-L53) a nastavení adresy ověřovatele v MUD. + +7. Nakonec se přihlásíme k odběru aktualizací, abychom viděli, když hráč požádá buď [o spuštění nové hry](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L55-L71), nebo [o odkrytí pole v existující hře](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L73-L108). + +#### Nová hra {#new-game-flow} + +Toto se stane, když hráč požádá o novou hru. + +1. Pokud pro tohoto hráče neprobíhá žádná hra nebo probíhá, ale s nulovým gameId, klient zobrazí [tlačítko nové hry](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L175). Když uživatel stiskne toto tlačítko, [React spustí funkci `newGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L96). + +2. [`newGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/mud/createSystemCalls.ts#L43-L46) je volání `System`. V MUD jsou všechna volání směrována přes kontrakt `World` a ve většině případů voláte `__`. V tomto případě se volá `app__newGame`, které MUD následně přesměruje na [`newGame` v `GameSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L16-L22). + +3. Onchain funkce zkontroluje, že hráč nemá žádnou rozehranou hru, a pokud ne, [přidá požadavek do tabulky `PendingGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L21). + +4. Server detekuje změnu v `PendingGame` a [spustí registrovanou funkci](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L55-L71). Tato funkce volá [`newGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L110-L114), která zase volá [`createGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L116-L144). + +5. První věc, kterou `createGame` udělá, je [vytvoření náhodné mapy s příslušným počtem min](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L120-L135). Poté volá [`makeMapBorders`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L147-L166), aby vytvořil mapu s prázdnými okraji, což je nutné pro Zokrates. Nakonec `createGame` volá [`calculateMapHash`](#calculateMapHash), aby získal haš mapy, který se používá jako ID hry. + +6. Funkce `newGame` přidá novou hru do `gamesInProgress`. + +7. Poslední věc, kterou server udělá, je zavolání [`app__newGameResponse`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L38-L43), které je na blockchainu. Tato funkce se nachází v jiném `System`, [`ServerSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol), aby umožnila řízení přístupu. Řízení přístupu je definováno v [konfiguračním souboru MUD](https://mud.dev/config), [`mud.config.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/mud.config.ts#L67-L72). + + Seznam přístupů umožňuje volat `System` pouze jediné adrese. To omezuje přístup k funkcím serveru na jedinou adresu, takže se nikdo nemůže vydávat za server. + +8. Onchain komponenta aktualizuje příslušné tabulky: + + - Vytvoří hru v `PlayerGame`. + - Nastaví reverzní mapování v `GamePlayer`. + - Odebere požadavek z `PendingGame`. + +9. Server identifikuje změnu v `PendingGame`, ale nic nedělá, protože [`wantsGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L58-L60) je nepravdivé. + +10. Na klientu je [`gameRecord`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L143-L148) nastaven na položku `PlayerGame` pro adresu hráče. Když se změní `PlayerGame`, změní se i `gameRecord`. + +11. Pokud je v `gameRecord` hodnota a hra ještě nebyla vyhrána ani prohrána, klient [zobrazí mapu](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L175-L190). + +#### Odkrytí pole {#dig-flow} + +1. Hráč [klikne na tlačítko buňky mapy](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L188), což zavolá [funkci `dig`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/mud/createSystemCalls.ts#L33-L36). Tato funkce volá [`dig` na blockchainu](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L24-L32). + +2. Onchain komponenta [provádí řadu kontrol správnosti](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L25-L30) a pokud je úspěšná, přidá požadavek na odkrytí pole do [`PendingDig`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L31). + +3. Server [detekuje změnu v `PendingDig`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L73). [Pokud je platný](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L75-L84), [zavolá kód s nulovou znalostí](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L86-L95) (vysvětleno níže), aby vygeneroval jak výsledek, tak důkaz, že je platný. + +4. [Server](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L97-L107) volá [`digResponse`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L45-L64) na blockchainu. + +5. `digResponse` dělá dvě věci. Nejprve zkontroluje [důkaz s nulovou znalostí](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L47-L61). Poté, pokud je důkaz v pořádku, volá [`processDigResult`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L67-L86) pro skutečné zpracování výsledku. + +6. `processDigResult` zkontroluje, zda byla hra [prohrána](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L76-L78) nebo [vyhrána](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L83-L86), a [aktualizuje `Map`, onchain mapu](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L80). + +7. Klient automaticky zachytí aktualizace a [aktualizuje mapu zobrazenou hráči](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L175-L190) a případně hráči sdělí, zda se jedná o výhru, nebo prohru. + +## Použití Zokrates {#using-zokrates} + +Ve výše vysvětlených tocích jsme přeskočili části s nulovou znalostí a považovali je za černou skříňku. Teď ji otevřeme a podíváme se, jak je ten kód napsán. + +### Hašování mapy {#hashing-map} + +Můžeme použít [tento kód v JavaScriptu](https://github.com/ZK-Plus/ICBC24_Tutorial_Compute-Offchain-Verify-onchain/tree/solutions/exercise) k implementaci [Poseidon](https://www.poseidon-hash.info), hašovací funkce Zokrates, kterou používáme. Ačkoli by to však bylo rychlejší, bylo by to také složitější, než jen použít k tomu hašovací funkci Zokrates. Toto je tutoriál, takže kód je optimalizován pro jednoduchost, nikoli pro výkon. Proto potřebujeme dva různé programy Zokrates, jeden pro výpočet haše mapy (`hash`) a druhý pro vytvoření důkazu s nulovou znalostí o výsledku odkrytí pole na mapě (`dig`). + +### Hašovací funkce {#hash-function} + +Toto je funkce, která počítá haš mapy. Tento kód si projdeme řádek po řádku. + +``` +import "hashes/poseidon/poseidon.zok" as poseidon; +import "utils/pack/bool/pack128.zok" as pack128; +``` + +Tyto dva řádky importují dvě funkce ze [standardní knihovny Zokrates](https://zokrates.github.io/toolbox/stdlib.html). [První funkce](https://github.com/Zokrates/ZoKrates/blob/latest/zokrates_stdlib/stdlib/hashes/poseidon/poseidon.zok) je [haš Poseidon](https://www.poseidon-hash.info/). Přijímá pole prvků [`field`](https://zokrates.github.io/language/types.html#field) a vrací `field`. + +Prvek pole (field) v Zokrates je obvykle kratší než 256 bitů, ale ne o moc. Pro zjednodušení kódu omezujeme mapu na maximálně 512 bitů a hašujeme pole čtyř polí a v každém poli používáme pouze 128 bitů. [Funkce `pack128`](https://github.com/Zokrates/ZoKrates/blob/latest/zokrates_stdlib/stdlib/utils/pack/bool/pack128.zok) pro tento účel změní pole 128 bitů na `field`. + +``` + def hashMap(bool[${width+2}][${height+2}] map) -> field { +``` + +Tento řádek zahajuje definici funkce. `hashMap` dostane jediný parametr nazvaný `map`, dvourozměrné pole typu `bool`(ean). Velikost mapy je `width+2` krát `height+2` z důvodů, které jsou [vysvětleny níže](#why-map-border). + +Můžeme použít `${width+2}` a `${height+2}`, protože programy Zokrates jsou v této aplikaci uloženy jako [šablonové řetězce](https://www.w3schools.com/js/js_string_templates.asp). Kód mezi `${` a `}` je vyhodnocen JavaScriptem, a tímto způsobem lze program použít pro různé velikosti mapy. Parametr mapy má kolem sebe okraj o šířce jedné pozice bez bomb, což je důvod, proč musíme k šířce a výšce přidat dva. + +Návratová hodnota je `field`, který obsahuje haš. + +``` + bool[512] mut map1d = [false; 512]; +``` + +Mapa je dvourozměrná. Funkce `pack128` však nefunguje s dvourozměrnými poli. Takže nejprve zploštíme mapu do 512bajtového pole pomocí `map1d`. Ve výchozím nastavení jsou proměnné Zokrates konstanty, ale potřebujeme přiřadit hodnoty tomuto poli ve smyčce, takže ho definujeme jako [`mut`](https://zokrates.github.io/language/variables.html#mutability). + +Musíme pole inicializovat, protože Zokrates nemá `undefined`. Výraz `[false; 512]` znamená [pole 512 hodnot `false`](https://zokrates.github.io/language/types.html#declaration-and-initialization). + +``` + u32 mut counter = 0; +``` + +Potřebujeme také počítadlo k rozlišení mezi bity, které jsme již v `map1d` vyplnili, a těmi, které ještě ne. + +``` + for u32 x in 0..${width+2} { +``` + +Takto se v Zokrates deklaruje [smyčka `for`](https://zokrates.github.io/language/control_flow.html#for-loops). Smyčka `for` v Zokrates musí mít pevné meze, protože i když se jeví jako smyčka, kompilátor ji ve skutečnosti „rozbalí“. Výraz `${width+2}` je konstanta v době kompilace, protože `width` je nastaven kódem TypeScript předtím, než zavolá kompilátor. + +``` + for u32 y in 0..${height+2} { + map1d[counter] = map[x][y]; + counter = counter+1; + } + } +``` + +Pro každé místo na mapě vložte tuto hodnotu do pole `map1d` a zvyšte počítadlo. + +``` + field[4] hashMe = [ + pack128(map1d[0..128]), + pack128(map1d[128..256]), + pack128(map1d[256..384]), + pack128(map1d[384..512]) + ]; +``` + +`pack128` vytvoří z `map1d` pole čtyř hodnot typu `field`. V Zokrates znamená `array[a..b]` výsek pole, který začíná na `a` a končí na `b-1`. + +``` + return poseidon(hashMe); +} +``` + +Použijte `poseidon` k převedení tohoto pole na haš. + +### Hašovací program {#hash-program} + +Server musí volat `hashMap` přímo k vytvoření identifikátorů hry. Zokrates však může pro spuštění volat pouze funkci `main` v programu, takže vytvoříme program s `main`, který volá hašovací funkci. + +``` +${hashFragment} + +def main(bool[${width+2}][${height+2}] map) -> field { + return hashMap(map); +} +``` + +### Program pro odkrytí pole {#dig-program} + +Toto je srdce části aplikace s nulovou znalostí, kde vytváříme důkazy, které se používají k ověření výsledků odkrytí polí. + +``` +${hashFragment} + +// Počet min na pozici (x,y) +def map2mineCount(bool[${width+2}][${height+2}] map, u32 x, u32 y) -> u8 { + return if map[x+1][y+1] { 1 } else { 0 }; +} +``` + +#### Proč okraj mapy {#why-map-border} + +Důkazy s nulovou znalostí používají [aritmetické obvody](https://medium.com/web3studio/simple-explanations-of-arithmetic-circuits-and-zero-knowledge-proofs-806e59a79785), které nemají jednoduchý ekvivalent příkazu `if`. Místo toho používají ekvivalent [podmíněného operátoru](https://en.wikipedia.org/wiki/Ternary_conditional_operator). Pokud `a` může být buď nula, nebo jedna, můžete vypočítat `if a { b } else { c }` jako `ab+(1-a)c`. + +Z tohoto důvodu příkaz `if` v Zokrates vždy vyhodnocuje obě větve. Například, pokud máte tento kód: + +``` +bool[5] arr = [false; 5]; +u32 index=10; +return if index>4 { 0 } else { arr[index] } +``` + +Dojde k chybě, protože potřebuje vypočítat `arr[10]`, i když tato hodnota bude později vynásobena nulou. + +To je důvod, proč potřebujeme okraj o šířce jednoho pole kolem celé mapy. Potřebujeme vypočítat celkový počet min v okolí pole, a to znamená, že musíme vidět pole o jeden řádek nad a pod, vlevo a vpravo od pole, které odkrýváme. Což znamená, že tato pole musí existovat v poli mapy, které je Zokrates poskytnuto. + +``` +def main(private bool[${width+2}][${height+2}] map, u32 x, u32 y) -> (field, u8) { +``` + +Ve výchozím nastavení důkazy Zokrates obsahují své vstupy. Není k ničemu vědět, že kolem pole je pět min, pokud nevíte, o které pole se jedná (a nemůžete to jen porovnat s vaším požadavkem, protože pak by mohl dokazovatel použít jiné hodnoty a neříct vám o tom). Musíme však udržet mapu v tajnosti a zároveň ji poskytnout Zokrates. Řešením je použít `private` parametr, který _není_ odhalen důkazem. + +To otevírá další možnost zneužití. Dokazovatel by mohl použít správné souřadnice, ale vytvořit mapu s libovolným počtem min kolem daného pole, a možná i na samotném poli. Abychom zabránili tomuto zneužití, zajistíme, aby důkaz s nulovou znalostí obsahoval haš mapy, který je identifikátorem hry. + +``` + return (hashMap(map), +``` + +Návratová hodnota je zde n-tice, která obsahuje pole hašů mapy a výsledek odkrytí pole. + +``` + if map2mineCount(map, x, y) > 0 { 0xFF } else { +``` + +Používáme 255 jako speciální hodnotu pro případ, že na samotném poli je bomba. + +``` + map2mineCount(map, x-1, y-1) + map2mineCount(map, x, y-1) + map2mineCount(map, x+1, y-1) + + map2mineCount(map, x-1, y) + map2mineCount(map, x+1, y) + + map2mineCount(map, x-1, y+1) + map2mineCount(map, x, y+1) + map2mineCount(map, x+1, y+1) + } + ); +} +``` + +Pokud hráč nenašel minu, sečtěte počty min v okolí pole a vraťte je. + +### Použití Zokrates z TypeScriptu {#using-zokrates-from-typescript} + +Zokrates má rozhraní příkazového řádku, ale v tomto programu ho používáme v [kódu TypeScript](https://zokrates.github.io/toolbox/zokrates_js.html). + +Knihovna, která obsahuje definice Zokrates, se jmenuje [`zero-knowledge.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts). + +```typescript +import { initialize as zokratesInitialize } from "zokrates-js" +``` + +Importujte [JavaScriptové vazby Zokrates](https://zokrates.github.io/toolbox/zokrates_js.html). Potřebujeme pouze funkci [`initialize`](https://zokrates.github.io/toolbox/zokrates_js.html#initialize), protože vrací promise, který se vyřeší na všechny definice Zokrates. + +```typescript +export const zkFunctions = async (width: number, height: number) : Promise => { +``` + +Podobně jako u samotného Zokrates, exportujeme také pouze jednu funkci, která je také [asynchronní](https://www.w3schools.com/js/js_async.asp). Když se nakonec vrátí, poskytne několik funkcí, jak uvidíme níže. + +```typescript +const zokrates = await zokratesInitialize() +``` + +Inicializujte Zokrates, získejte vše, co potřebujeme z knihovny. + +```typescript +const hashFragment = ` + import "utils/pack/bool/pack128.zok" as pack128; + import "hashes/poseidon/poseidon.zok" as poseidon; + . + . + . + } + ` + +const hashProgram = ` + ${hashFragment} + . + . + . + ` + +const digProgram = ` + ${hashFragment} + . + . + . + ` +``` + +Dále máme hašovací funkci a dva programy Zokrates, které jsme viděli výše. + +```typescript +const digCompiled = zokrates.compile(digProgram) +const hashCompiled = zokrates.compile(hashProgram) +``` + +Zde tyto programy kompilujeme. + +```typescript +// Vytvoří klíče pro ověření nulové znalosti. +// V produkčním systému byste chtěli použít setup ceremonii. +// (https://zokrates.github.io/toolbox/trusted_setup.html#initializing-a-phase-2-ceremony). +const keySetupResults = zokrates.setup(digCompiled.program, "") +const verifierKey = keySetupResults.vk +const proverKey = keySetupResults.pk +``` + +V produkčním systému bychom mohli použít složitější [setup ceremonii](https://zokrates.github.io/toolbox/trusted_setup.html#initializing-a-phase-2-ceremony), ale toto je pro demonstraci dostačující. Není problém, že uživatelé mohou znát klíč dokazovatele – stále ho nemohou použít k prokázání věcí, pokud nejsou pravdivé. Protože specifikujeme entropii (druhý parametr, `""`), výsledky budou vždy stejné. + +**Poznámka:** Kompilace programů Zokrates a tvorba klíčů jsou pomalé procesy. Není třeba je opakovat pokaždé, pouze když se změní velikost mapy. V produkčním systému byste je provedli jednou a pak uložili výstup. Jediný důvod, proč to zde nedělám, je pro jednoduchost. + +#### `calculateMapHash` {#calculateMapHash} + +```typescript +const calculateMapHash = function (hashMe: boolean[][]): string { + return ( + "0x" + + BigInt(zokrates.computeWitness(hashCompiled, [hashMe]).output.slice(1, -1)) + .toString(16) + .padStart(64, "0") + ) +} +``` + +Funkce [`computeWitness`](https://zokrates.github.io/toolbox/zokrates_js.html#computewitnessartifacts-args-options) skutečně spouští program Zokrates. Vrací strukturu se dvěma poli: `output`, což je výstup programu jako řetězec JSON, a `witness`, což je informace potřebná k vytvoření důkazu s nulovou znalostí výsledku. Zde potřebujeme pouze výstup. + +Výstupem je řetězec ve formátu `"31337"`, desetinné číslo uzavřené v uvozovkách. Ale výstup, který potřebujeme pro `viem`, je hexadecimální číslo ve formátu `0x60A7`. Takže použijeme `.slice(1,-1)` k odstranění uvozovek a poté `BigInt` k převedení zbývajícího řetězce, což je desetinné číslo, na [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt). `.toString(16)` převede tento `BigInt` na hexadecimální řetězec a `"0x"+` přidá značku pro hexadecimální čísla. + +```typescript +// Odkryjte pole a vraťte důkaz s nulovou znalostí o výsledku +// (kód na straně serveru) +``` + +Důkaz s nulovou znalostí zahrnuje veřejné vstupy (`x` a `y`) a výsledky (haš mapy a počet bomb). + +```typescript + const zkDig = function(map: boolean[][], x: number, y: number) : any { + if (x<0 || x>=width || y<0 || y>=height) + throw new Error("Pokus o odkrytí pole mimo mapu") +``` + +Je problém kontrolovat, zda je index mimo rozsah v Zokrates, takže to děláme zde. + +```typescript +const runResults = zokrates.computeWitness(digCompiled, [map, `${x}`, `${y}`]) +``` + +Spusťte program pro odkrytí pole. + +```typescript + const proof = zokrates.generateProof( + digCompiled.program, + runResults.witness, + proverKey) + + return proof + } +``` + +Použijte [`generateProof`](https://zokrates.github.io/toolbox/zokrates_js.html#generateproofprogram-witness-provingkey-entropy) a vraťte důkaz. + +```typescript +const solidityVerifier = ` + // Velikost mapy: ${width} x ${height} + \n${zokrates.exportSolidityVerifier(verifierKey)} + ` +``` + +Ověřovatel Solidity, chytrý kontrakt, který můžeme nasadit na blockchain a použít k ověření důkazů generovaných `digCompiled.program`. + +```typescript + return { + zkDig, + calculateMapHash, + solidityVerifier, + } +} +``` + +Nakonec vraťte vše, co by mohl jiný kód potřebovat. + +## Bezpečnostní testy {#security-tests} + +Bezpečnostní testy jsou důležité, protože chyba funkčnosti se nakonec projeví. Ale pokud je aplikace nezabezpečená, pravděpodobně to zůstane skryto po dlouhou dobu, než to odhalí někdo, kdo podvádí a získá zdroje, které patří ostatním. + +### Oprávnění {#permissions} + +V této hře je jedna privilegovaná entita, server. Je to jediný uživatel, který smí volat funkce v [`ServerSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol). Můžeme použít [`cast`](https://book.getfoundry.sh/cast/) k ověření, že volání funkcí s oprávněním jsou povolena pouze jako účet serveru. + +[Soukromý klíč serveru je v `setupNetwork.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/mud/setupNetwork.ts#L52). + +1. Na počítači, který spouští `anvil` (blockchain), nastavte tyto proměnné prostředí. + + ```sh copy + WORLD_ADDRESS=0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b + UNAUTHORIZED_KEY=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a + AUTHORIZED_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d + ``` + +2. Použijte `cast` k pokusu o nastavení adresy ověřovatele jako neoprávněné adresy. + + ```sh copy + cast send $WORLD_ADDRESS 'app__setVerifier(address)' `cast address-zero` --private-key $UNAUTHORIZED_KEY + ``` + + Nejenže `cast` hlásí selhání, ale můžete otevřít **MUD Dev Tools** ve hře v prohlížeči, kliknout na **Tables** a vybrat **app\_\_VerifierAddress**. Podívejte se, že adresa není nulová. + +3. Nastavte adresu ověřovatele jako adresu serveru. + + ```sh copy + cast send $WORLD_ADDRESS 'app__setVerifier(address)' `cast address-zero` --private-key $AUTHORIZED_KEY + ``` + + Adresa v **app\_\_VerifiedAddress** by nyní měla být nulová. + +Všechny funkce MUD ve stejném `System` procházejí stejným řízením přístupu, takže tento test považuji za dostatečný. Pokud ne, můžete zkontrolovat ostatní funkce v [`ServerSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol). + +### Zneužití nulové znalosti {#zero-knowledge-abuses} + +Matematika k ověření Zokrates je nad rámec tohoto tutoriálu (a mých schopností). Můžeme však spustit různé kontroly na kódu s nulovou znalostí, abychom ověřili, že pokud není proveden správně, selže. Všechny tyto testy budou vyžadovat, abychom změnili [`zero-knowledge.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts) a restartovali celou aplikaci. Nestačí restartovat proces serveru, protože to uvede aplikaci do nemožného stavu (hráč má rozehranou hru, ale hra již není pro server dostupná). + +#### Špatná odpověď {#wrong-answer} + +Nejjednodušší možností je poskytnout špatnou odpověď v důkazu s nulovou znalostí. Chcete-li to provést, přejděte do `zkDig` a [upravte řádek 91](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L91): + +```ts +proof.inputs[3] = "0x" + "1".padStart(64, "0") +``` + +To znamená, že budeme vždy tvrdit, že je tam jedna bomba, bez ohledu na správnou odpověď. Zkuste si zahrát s touto verzí a na kartě **server** na obrazovce `pnpm dev` uvidíte tuto chybu: + +``` + cause: { + code: 3, + message: 'execution reverted: revert: Zero knowledge verification fail', + data: '0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000 +000000000000000000000000000000000000000000000000205a65726f206b6e6f776c6564676520766572696669636174696f6 +e206661696c' + }, +``` + +Takže tento druh podvodu selže. + +#### Špatný důkaz {#wrong-proof} + +Co se stane, když poskytneme správné informace, ale máme jen špatná data důkazu? Nyní nahraďte řádek 91: + +```ts +proof.proof = { + a: ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], + b: [ + ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], + ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], + ], + c: ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], +} +``` + +Stále selhává, ale nyní selhává bez udání důvodu, protože k tomu dochází během volání ověřovatele. + +### Jak může uživatel ověřit kód s nulovou znalostí? {#user-verify-zero-trust} + +Chytré kontrakty je poměrně snadné ověřit. Vývojář obvykle zveřejní zdrojový kód v průzkumníku bloků a průzkumník bloků ověří, že se zdrojový kód zkompiluje do kódu v [transakci nasazení kontraktu](/developers/docs/smart-contracts/deploying/). V případě MUD `System`s je to [trochu složitější](https://mud.dev/cli/verify), ale ne o moc. + +S nulovou znalostí je to těžší. Ověřovatel obsahuje některé konstanty a provádí s nimi některé výpočty. To vám neřekne, co se dokazuje. + +```solidity + function verifyingKey() pure internal returns (VerifyingKey memory vk) { + vk.alpha = Pairing.G1Point(uint256(0x0f43f4fe7b5c2326fed4ac6ed2f4003ab9ab4ea6f667c2bdd77afb068617ee16), uint256(0x25a77832283f9726935219b5f4678842cda465631e72dbb24708a97ba5d0ce6f)); + vk.beta = Pairing.G2Point([uint256(0x2cebd0fbd21aca01910581537b21ae4fed46bc0e524c055059aa164ba0a6b62b), uint256(0x18fd4a7bc386cf03a95af7163d5359165acc4e7961cb46519e6d9ee4a1e2b7e9)], [uint256(0x11449dee0199ef6d8eebfe43b548e875c69e7ce37705ee9a00c81fe52f11a009), uint256(0x066d0c83b32800d3f335bb9e8ed5e2924cf00e77e6ec28178592eac9898e1a00)]); +``` + +Řešením, alespoň do doby, než průzkumníci bloků přidají ověřování Zokrates do svých uživatelských rozhraní, je, aby vývojáři aplikací zpřístupnili programy Zokrates a aby si alespoň někteří uživatelé sami zkompilovali s příslušným ověřovacím klíčem. + +Postupujte takto: + +1. [Nainstalujte Zokrates](https://zokrates.github.io/gettingstarted.html). + +2. Vytvořte soubor `dig.zok` s programem Zokrates. Níže uvedený kód předpokládá, že jste ponechali původní velikost mapy, 10x5. + + ```zokrates + import "utils/pack/bool/pack128.zok" as pack128; + import "hashes/poseidon/poseidon.zok" as poseidon; + + def hashMap(bool[12][7] map) -> field { + bool[512] mut map1d = [false; 512]; + u32 mut counter = 0; + + for u32 x in 0..12 { + for u32 y in 0..7 { + map1d[counter] = map[x][y]; + counter = counter+1; + } + } + + field[4] hashMe = [ + pack128(map1d[0..128]), + pack128(map1d[128..256]), + pack128(map1d[256..384]), + pack128(map1d[384..512]) + ]; + + return poseidon(hashMe); + } + + + // Počet min na pozici (x,y) + def map2mineCount(bool[12][7] map, u32 x, u32 y) -> u8 { + return if map[x+1][y+1] { 1 } else { 0 }; + } + + def main(private bool[12][7] map, u32 x, u32 y) -> (field, u8) { + return (hashMap(map) , + if map2mineCount(map, x, y) > 0 { 0xFF } else { + map2mineCount(map, x-1, y-1) + map2mineCount(map, x, y-1) + map2mineCount(map, x+1, y-1) + + map2mineCount(map, x-1, y) + map2mineCount(map, x+1, y) + + map2mineCount(map, x-1, y+1) + map2mineCount(map, x, y+1) + map2mineCount(map, x+1, y+1) + } + ); + } + ``` + +3. Zkompilujte kód Zokrates a vytvořte ověřovací klíč. Ověřovací klíč musí být vytvořen se stejnou entropií, jaká byla použita v původním serveru, [v tomto případě prázdný řetězec](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L67). + + ```sh copy + zokrates compile --input dig.zok + zokrates setup -e "" + ``` + +4. Vytvořte si vlastní ověřovač Solidity a ověřte, že je funkčně shodný s tím na blockchainu (server přidává komentář, ale to není důležité). + + ```sh copy + zokrates export-verifier + diff verifier.sol ~/20240901-secret-state/packages/contracts/src/verifier.sol + ``` + +## Rozhodnutí o návrhu {#design} + +V každé dostatečně složité aplikaci existují konkurenční cíle návrhu, které vyžadují kompromisy. Podívejme se na některé kompromisy a proč je současné řešení vhodnější než jiné možnosti. + +### Proč nulová znalost {#why-zero-knowledge} + +Pro Hledání min ve skutečnosti nepotřebujete nulovou znalost. Server může vždy držet mapu a pak ji jednoduše odhalit, když hra skončí. Poté na konci hry může chytrý kontrakt vypočítat haš mapy, ověřit, že se shoduje, a pokud ne, penalizovat server nebo hru zcela ignorovat. + +Nepoužil jsem toto jednodušší řešení, protože funguje pouze pro krátké hry s dobře definovaným koncovým stavem. Když je hra potenciálně nekonečná (jako v případě [autonomních světů](https://0xparc.org/blog/autonomous-worlds)), potřebujete řešení, které prokáže stav, _aniž_ by ho odhalilo. + +Jako tutoriál tento článek potřeboval krátkou hru, která je snadno pochopitelná, ale tato technika je nejužitečnější pro delší hry. + +### Proč Zokrates? {#why-zokrates} + +[Zokrates](https://zokrates.github.io/) není jedinou dostupnou knihovnou s nulovou znalostí, ale je podobný normálnímu, [imperativnímu](https://en.wikipedia.org/wiki/Imperative_programming) programovacímu jazyku a podporuje booleovské proměnné. + +Pro vaši aplikaci s odlišnými požadavky můžete raději použít [Circum](https://docs.circom.io/getting-started/installation/) nebo [Cairo](https://www.cairo-lang.org/tutorials/getting-started-with-cairo/). + +### Kdy kompilovat Zokrates {#when-compile-zokrates} + +V tomto programu kompilujeme programy Zokrates [pokaždé, když se server spustí](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L60-L61). Je to zjevné plýtvání zdroji, ale toto je tutoriál optimalizovaný pro jednoduchost. + +Kdybych psal aplikaci na produkční úrovni, zkontroloval bych, zda mám soubor s kompilovanými programy Zokrates pro tuto velikost minového pole, a pokud ano, použil bych ho. Totéž platí pro nasazení ověřovacího kontraktu na blockchainu. + +### Vytvoření klíčů ověřovatele a dokazovatele {#key-creation} + +[Vytváření klíčů](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L63-L69) je další čistý výpočet, který není nutné provádět více než jednou pro danou velikost minového pole. Opět se to dělá pouze jednou pro zjednodušení. + +Kromě toho bychom mohli použít [setup ceremonii](https://zokrates.github.io/toolbox/trusted_setup.html#initializing-a-phase-2-ceremony). Výhodou setup ceremonie je, že k podvádění důkazu s nulovou znalostí potřebujete buď entropii, nebo nějaký mezivýsledek od každého účastníka. Pokud je alespoň jeden účastník ceremonie poctivý a smaže tyto informace, jsou důkazy s nulovou znalostí v bezpečí před určitými útoky. Neexistuje však _žádný mechanismus_, který by ověřil, že informace byly smazány všude. Pokud jsou důkazy s nulovou znalostí kriticky důležité, chcete se zúčastnit setup ceremonie. + +Zde se spoléháme na [perpetual powers of tau](https://github.com/privacy-scaling-explorations/perpetualpowersoftau), kterých se zúčastnily desítky účastníků. Je to pravděpodobně dostatečně bezpečné a mnohem jednodušší. Během vytváření klíčů také nepřidáváme entropii, což uživatelům usnadňuje [ověření konfigurace s nulovou znalostí](#user-verify-zero-trust). + +### Kde ověřovat {#where-verification} + +Důkazy s nulovou znalostí můžeme ověřit buď na blockchainu (což stojí gas), nebo v klientovi (pomocí [`verify`](https://zokrates.github.io/toolbox/zokrates_js.html#verifyverificationkey-proof)). Zvolil jsem první možnost, protože to umožňuje [ověřit ověřovatele](#user-verify-zero-trust) jednou a pak důvěřovat, že se nezmění, dokud adresa kontraktu zůstane stejná. Pokud by se ověření provádělo na klientovi, museli byste ověřit kód, který obdržíte při každém stažení klienta. + +Ačkoli je tato hra pro jednoho hráče, mnoho blockchainových her je pro více hráčů. Onchain ověření znamená, že důkaz s nulovou znalostí ověříte pouze jednou. Provedení v klientovi by vyžadovalo, aby každý klient ověřoval nezávisle. + +### Zploštit mapu v TypeScriptu nebo Zokrates? {#where-flatten} + +Obecně platí, že když lze zpracování provést buď v TypeScriptu, nebo v Zokrates, je lepší to udělat v TypeScriptu, který je mnohem rychlejší a nevyžaduje důkazy s nulovou znalostí. To je například důvod, proč neposkytujeme Zokrates haš a nenutíme ho ověřovat, že je správný. Hašování musí být provedeno uvnitř Zokrates, ale shoda mezi vráceným hašem a hašem na blockchainu může proběhnout mimo něj. + +Přesto stále [zplošťujeme mapu v Zokrates](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L15-L20), i když jsme to mohli udělat v TypeScriptu. Důvodem je, že ostatní možnosti jsou podle mého názoru horší. + +- Poskytněte jednorozměrné pole booleovských hodnot kódu Zokrates a použijte výraz jako `x*(height+2) + +y` k získání dvourozměrné mapy. To by [kód](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L44-L47) poněkud zkomplikovalo, takže jsem se rozhodl, že zvýšení výkonu za to pro tutoriál nestojí. + +- Pošlete Zokrates jak jednorozměrné pole, tak dvourozměrné pole. Toto řešení nám však nic nepřináší. Kód Zokrates by musel ověřit, že poskytnuté jednorozměrné pole je skutečně správnou reprezentací dvourozměrného pole. Takže by nedošlo k žádnému zvýšení výkonu. + +- Zploštit dvourozměrné pole v Zokrates. Toto je nejjednodušší možnost, takže jsem si ji vybral. + +### Kde ukládat mapy {#where-store-maps} + +V této aplikaci je [`gamesInProgress`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L20) jednoduše proměnná v paměti. To znamená, že pokud váš server selže a je třeba ho restartovat, všechny uložené informace jsou ztraceny. Nejenže hráči nemohou pokračovat ve hře, nemohou ani začít novou hru, protože onchain komponenta si myslí, že stále mají rozehranou hru. + +Toto je zjevně špatný návrh pro produkční systém, ve kterém byste tyto informace ukládali do databáze. Jediný důvod, proč jsem zde použil proměnnou, je, že se jedná o tutoriál a hlavní úvahou je jednoduchost. + +## Závěr: Za jakých podmínek je toto vhodná technika? {#conclusion} + +Takže teď víte, jak napsat hru se serverem, který ukládá tajný stav, který nepatří na blockchain. Ale v jakých případech byste to měli dělat? Jsou zde dvě hlavní úvahy. + +- _Dlouhotrvající hra_: [Jak bylo zmíněno výše](#why-zero-knowledge), v krátké hře můžete jednoduše zveřejnit stav, jakmile hra skončí, a nechat vše ověřit. To ale není možnost, když hra trvá dlouhou nebo neurčitou dobu a stav musí zůstat tajný. + +- _Určitá centralizace je přijatelná_: Důkazy s nulovou znalostí mohou ověřit integritu, že entita nefalšuje výsledky. Co nemohou udělat, je zajistit, že entita bude stále dostupná a bude odpovídat na zprávy. V situacích, kdy musí být dostupnost také decentralizovaná, nejsou důkazy s nulovou znalostí dostatečným řešením a potřebujete [výpočet více stran](https://en.wikipedia.org/wiki/Secure_multi-party_computation). + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). + +### Poděkování {#acknowledgements} + +- Alvaro Alonso si přečetl návrh tohoto článku a vyjasnil některé mé nejasnosti ohledně Zokrates. + +Za zbývající chyby jsem zodpovědný já. diff --git a/public/content/translations/cs/developers/tutorials/secure-development-workflow/index.md b/public/content/translations/cs/developers/tutorials/secure-development-workflow/index.md new file mode 100644 index 00000000000..181d7443b61 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/secure-development-workflow/index.md @@ -0,0 +1,52 @@ +--- +title: "Kontrolní seznam zabezpečení chytrých kontraktů" +description: "Doporučený pracovní postup pro psaní bezpečných chytrých kontraktů" +author: "Trailofbits" +tags: [ "chytré kontrakty", "bezpečnost", "solidity" ] +skill: intermediate +lang: cs +published: 2020-09-07 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/blob/master/development-guidelines/workflow.md +--- + +## Kontrolní seznam pro vývoj chytrých kontraktů {#smart-contract-development-checklist} + +Zde je obecný postup, který doporučujeme dodržovat při psaní vašich chytrých kontraktů. + +Zkontrolujte, zda se nevyskytují známé bezpečnostní problémy: + +- Zkontrolujte své kontrakty pomocí nástroje [Slither](https://github.com/crytic/slither). Obsahuje více než 40 vestavěných detektorů pro běžné zranitelnosti. Spouštějte jej při každém odevzdání nového kódu a zajistěte, aby měl čistý výstup (nebo použijte režim triage k potlačení určitých problémů). +- Zkontrolujte své kontrakty pomocí nástroje [Crytic](https://crytic.io/). Kontroluje 50 problémů, které Slither nekontroluje. Crytic může také pomoci vašemu týmu udržet si vzájemný přehled, protože snadno odhaluje bezpečnostní problémy v pull requestech na GitHubu. + +Zvažte speciální vlastnosti svého kontraktu: + +- Jsou vaše kontrakty aktualizovatelné? Zkontrolujte kód pro aktualizovatelnost na přítomnost chyb pomocí [`slither-check-upgradeability`](https://github.com/crytic/slither/wiki/Upgradeability-Checks) nebo [Crytic](https://blog.trailofbits.com/2020/06/12/upgradeable-contracts-made-safer-with-crytic/). Zdokumentovali jsme 17 způsobů, jak se mohou aktualizace pokazit. +- Jsou vaše kontrakty v souladu se standardy ERC? Zkontrolujte je pomocí [`slither-check-erc`](https://github.com/crytic/slither/wiki/ERC-Conformance). Tento nástroj okamžitě identifikuje odchylky od šesti běžných specifikací. +- Integrujete se s tokeny třetích stran? Než se spolehnete na externí kontrakty, projděte si náš [kontrolní seznam pro integraci tokenů](/developers/tutorials/token-integration-checklist/). + +Vizuálně zkontrolujte kritické bezpečnostní prvky vašeho kódu: + +- Zkontrolujte tiskový výstup [inheritance-graph](https://github.com/trailofbits/slither/wiki/Printer-documentation#inheritance-graph) nástroje Slither. Vyhněte se nechtěnému zastínění (shadowing) a problémům s C3 linearizací. +- Zkontrolujte tiskový výstup [function-summary](https://github.com/trailofbits/slither/wiki/Printer-documentation#function-summary) nástroje Slither. Reportuje viditelnost funkcí a řízení přístupu. +- Zkontrolujte tiskový výstup [vars-and-auth](https://github.com/trailofbits/slither/wiki/Printer-documentation#variables-written-and-authorization) nástroje Slither. Reportuje řízení přístupu ke stavovým proměnným. + +Zdokumentujte kritické bezpečnostní vlastnosti a použijte automatizované generátory testů k jejich vyhodnocení: + +- Naučte se, jak [dokumentovat bezpečnostní vlastnosti svého kódu](/developers/tutorials/guide-to-smart-contract-security-tools/). Zpočátku to není snadné, ale je to nejdůležitější činnost pro dosažení dobrého výsledku. Je to také předpokladem pro použití jakýchkoli pokročilých technik v tomto návodu. +- Definujte bezpečnostní vlastnosti v jazyce Solidity pro použití s nástroji [Echidna](https://github.com/crytic/echidna) a [Manticore](https://manticore.readthedocs.io/en/latest/verifier.html). Zaměřte se na váš stavový automat, řízení přístupu, aritmetické operace, externí interakce a soulad se standardy. +- Definujte bezpečnostní vlastnosti pomocí [Python API nástroje Slither](/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/). Zaměřte se na dědičnost, závislosti proměnných, řízení přístupu a další strukturální problémy. +- Spouštějte své testy vlastností při každém commitu pomocí nástroje [Crytic](https://crytic.io). Crytic dokáže zpracovat a vyhodnotit testy bezpečnostních vlastností, takže všichni ve vašem týmu mohou na GitHubu snadno vidět, že procházejí. Neúspěšné testy mohou zablokovat commity. + +Nakonec si dejte pozor na problémy, které automatizované nástroje nedokážou snadno najít: + +- Nedostatek soukromí: všichni ostatní mohou vidět vaše transakce, zatímco jsou zařazeny v poolu +- Front-running transakce +- Kryptografické operace +- Rizikové interakce s externími komponenty DeFi + +## Požádejte o pomoc {#ask-for-help} + +[Konzultační hodiny k Ethereu](https://calendly.com/dan-trailofbits/office-hours) se konají každé úterý odpoledne. Tato hodinová, individuální sezení jsou příležitostí zeptat se nás na jakékoli otázky, které máte ohledně bezpečnosti, řešit problémy s používáním našich nástrojů a získat zpětnou vazbu od odborníků na váš současný přístup. Pomůžeme vám projít tímto průvodcem. + +Připojte se na náš Slack: [Empire Hacking](https://join.slack.com/t/empirehacking/shared_invite/zt-h97bbrj8-1jwuiU33nnzg67JcvIciUw). Jsme vždy k dispozici na kanálech #crytic a #ethereum, pokud máte jakékoli dotazy. diff --git a/public/content/translations/cs/developers/tutorials/send-token-ethersjs/index.md b/public/content/translations/cs/developers/tutorials/send-token-ethersjs/index.md new file mode 100644 index 00000000000..52119d1dc04 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/send-token-ethersjs/index.md @@ -0,0 +1,210 @@ +--- +title: "Posílání tokenů pomocí ethers.js" +description: "Návod pro začátečníky k posílání tokenů pomocí ethers.js." +author: Kim YongJun +tags: [ "ETHERS.JS", "ERC-20", "TOKENY" ] +skill: beginner +lang: cs +published: 2021-04-06 +--- + +## Odeslání tokenu pomocí ethers.js(5.0) {#send-token} + +### V tomto tutoriálu se naučíte {#you-learn-about} + +- Importovat ethers.js +- Převést token +- Nastavit cenu transakčních poplatků podle situace v síti + +### Než začnete {#to-get-started} + +Abyste mohli začít, musíme nejprve importovat knihovnu ethers.js do našeho javascriptu +Zahrňte ethers.js(5.0) + +### Instalace {#install-ethersjs} + +```shell +/home/ricmoo> npm install --save ethers +``` + +ES6 v prohlížeči + +```html + +``` + +ES3(UMD) v prohlížeči + +```html + +``` + +### Parametry {#param} + +1. **`contract_address`**: Adresa kontraktu tokenu (adresa kontraktu je potřeba, když token, který chcete převést, není ether) +2. **`send_token_amount`**: Částka, kterou chcete poslat příjemci +3. **`to_address`**: Adresa příjemce +4. **`send_account`**: Adresa odesílatele +5. **`private_key`**: Soukromý klíč odesílatele k podepsání transakce a skutečnému převodu tokenů + +## Upozornění {#notice} + +`signTransaction(tx)` je odstraněno, protože `sendTransaction()` to dělá interně. + +## Postupy odesílání {#procedure} + +### 1. Připojit se k síti (testnet) {#connect-to-network} + +#### Nastavit poskytovatele (Infura) {#set-provider} + +Připojit se k Ropsten testnetu + +```javascript +window.ethersProvider = new ethers.providers.InfuraProvider("ropsten") +``` + +### 2. Vytvořit peněženku {#create-wallet} + +```javascript +let wallet = new ethers.Wallet(private_key) +``` + +### 3. Připojit peněženku k síti {#connect-wallet-to-net} + +```javascript +let walletSigner = wallet.connect(window.ethersProvider) +``` + +### 4. Získat aktuální cenu transakčních poplatků {#get-gas} + +```javascript +window.ethersProvider.getGasPrice() // cena transakčních poplatků +``` + +### 5. Definovat transakci {#define-transaction} + +Níže definované proměnné jsou závislé na `send_token()` + +### Parametry transakce {#transaction-params} + +1. **`send_account`**: adresa odesílatele tokenu +2. **`to_address`**: adresa příjemce tokenu +3. **`send_token_amount`**: počet tokenů k odeslání +4. **`gas_limit`**: limit transakčních poplatků +5. **`gas_price`**: cena transakčních poplatků + +[Jak používat, viz níže](#how-to-use) + +```javascript +const tx = { + from: send_account, + to: to_address, + value: ethers.utils.parseEther(send_token_amount), + nonce: window.ethersProvider.getTransactionCount(send_account, "latest"), + gasLimit: ethers.utils.hexlify(gas_limit), // 100000 + gasPrice: gas_price, +} +``` + +### 6. Převod {#transfer} + +```javascript +walletSigner.sendTransaction(tx).then((transaction) => { + console.dir(transaction) + alert("Send finished!") +}) +``` + +## Jak to použít {#how-to-use} + +```javascript +let private_key = + "41559d28e936dc92104ff30691519693fc753ffbee6251a611b9aa1878f12a4d" +let send_token_amount = "1" +let to_address = "0x4c10D2734Fb76D3236E522509181CC3Ba8DE0e80" +let send_address = "0xda27a282B5B6c5229699891CfA6b900A716539E6" +let gas_limit = "0x100000" +let wallet = new ethers.Wallet(private_key) +let walletSigner = wallet.connect(window.ethersProvider) +let contract_address = "" +window.ethersProvider = new ethers.providers.InfuraProvider("ropsten") + +send_token( + contract_address, + send_token_amount, + to_address, + send_address, + private_key +) +``` + +### Úspěch! {#success} + +![obrázek úspěšně dokončené transakce](./successful-transaction.png) + +## send_token() {#send-token-method} + +```javascript +function send_token( + contract_address, + send_token_amount, + to_address, + send_account, + private_key +) { + let wallet = new ethers.Wallet(private_key) + let walletSigner = wallet.connect(window.ethersProvider) + + window.ethersProvider.getGasPrice().then((currentGasPrice) => { + let gas_price = ethers.utils.hexlify(parseInt(currentGasPrice)) + console.log(`gas_price: ${gas_price}`) + + if (contract_address) { + // obecné odeslání tokenu + let contract = new ethers.Contract( + contract_address, + send_abi, + walletSigner + ) + + // Kolik tokenů? + let numberOfTokens = ethers.utils.parseUnits(send_token_amount, 18) + console.log(`numberOfTokens: ${numberOfTokens}`) + + // Odeslat tokeny + contract.transfer(to_address, numberOfTokens).then((transferResult) => { + console.dir(transferResult) + alert("sent token") + }) + } // odeslání etheru + else { + const tx = { + from: send_account, + to: to_address, + value: ethers.utils.parseEther(send_token_amount), + nonce: window.ethersProvider.getTransactionCount( + send_account, + "latest" + ), + gasLimit: ethers.utils.hexlify(gas_limit), // 100000 + gasPrice: gas_price, + } + console.dir(tx) + try { + walletSigner.sendTransaction(tx).then((transaction) => { + console.dir(transaction) + alert("Send finished!") + }) + } catch (error) { + alert("failed to send!!") + } + } + }) +} +``` diff --git a/public/content/translations/cs/developers/tutorials/sending-transactions-using-web3-and-alchemy/index.md b/public/content/translations/cs/developers/tutorials/sending-transactions-using-web3-and-alchemy/index.md new file mode 100644 index 00000000000..6e8e4982984 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/sending-transactions-using-web3-and-alchemy/index.md @@ -0,0 +1,208 @@ +--- +title: "Odesílání transakcí pomocí Web3" +description: "Toto je průvodce pro začátečníky, jak odesílat transakce v síti Ethereum pomocí Web3. Existují tři hlavní kroky pro odeslání transakce do blockchainu Etherea: vytvoření, podepsání a vysílání. Všechny tři si projdeme." +author: "Elan Halpern" +tags: [ "transakce", "web3.js", "alchemy" ] +skill: beginner +lang: cs +published: 2020-11-04 +source: Alchemy docs +sourceUrl: https://www.alchemy.com/docs/how-to-send-transactions-on-ethereum +--- + +Toto je průvodce pro začátečníky, jak odesílat transakce v síti Ethereum pomocí Web3. Existují tři hlavní kroky pro odeslání transakce do blockchainu Etherea: vytvoření, podepsání a vysílání. Všechny tři si projdeme a doufejme, že odpovíme na všechny vaše případné otázky! V tomto návodu budeme k odesílání našich transakcí do řetězce Ethereum používat [Alchemy](https://www.alchemy.com/). Můžete si [zde vytvořit bezplatný účet Alchemy](https://auth.alchemyapi.io/signup). + +**POZNÁMKA:** Tento průvodce je určen k podepisování transakcí na _backendu_ vaší aplikace. Pokud chcete integrovat podepisování transakcí na frontendu, podívejte se na integraci [Web3 s poskytovatelem prohlížeče](https://docs.alchemy.com/reference/api-overview#with-a-browser-provider). + +## Základy {#the-basics} + +Stejně jako většina blockchainových vývojářů, když začínali, jste si možná udělali průzkum, jak poslat transakci (něco, co by mělo být docela jednoduché), a narazili jste na spoustu průvodců, z nichž každý říkal něco jiného a nechal vás trochu zahlcené a zmatené. Pokud se v tom poznáváte, nedělejte si starosti; v určitém okamžiku jsme si tím prošli všichni! Než tedy začneme, ujasněme si několik věcí: + +### 1. Alchemy neukládá vaše soukromé klíče {#alchemy-does-not-store-your-private-keys} + +- To znamená, že Alchemy nemůže vaším jménem podepisovat a odesílat transakce. Důvodem jsou bezpečnostní účely. Alchemy vás nikdy nepožádá o sdílení vašeho soukromého klíče a vy byste nikdy neměli sdílet svůj soukromý klíč s hostovaným uzlem (nebo s kýmkoli jiným). +- Pomocí základního API od Alchemy můžete číst z blockchainu, ale abyste do něj mohli zapisovat, budete muset použít něco jiného k podepsání transakcí před jejich odesláním přes Alchemy (to samé platí pro jakoukoli jinou [službu uzlů](/developers/docs/nodes-and-clients/nodes-as-a-service/)). + +### 2. Co je to „podepisovatel“? {#what-is-a-signer} + +- Podepisovatelé za vás podepíší transakce pomocí vašeho soukromého klíče. V tomto návodu budeme k podepsání naší transakce používat [Alchemy web3](https://docs.alchemyapi.io/alchemy/documentation/alchemy-web3), ale můžete použít i jakoukoliv jinou web3 knihovnu. +- Na frontendu je dobrým příkladem podepisovatele [MetaMask](https://metamask.io/), který za vás bude podepisovat a odesílat transakce. + +### 3. Proč musím podepisovat své transakce? {#why-do-i-need-to-sign-my-transactions} + +- Každý uživatel, který chce odeslat transakci v síti Ethereum, musí transakci podepsat (pomocí svého soukromého klíče), aby se ověřilo, že původce transakce je ten, za koho se vydává. +- Je velmi důležité tento soukromý klíč chránit, protože přístup k němu poskytuje plnou kontrolu nad vaším účtem Ethereum, což vám (nebo komukoli s přístupem) umožňuje provádět transakce vaším jménem. + +### 4. Jak ochráním svůj soukromý klíč? {#how-do-i-protect-my-private-key} + +- Existuje mnoho způsobů, jak chránit svůj soukromý klíč a používat ho k odesílání transakcí. V tomto návodu budeme používat soubor `.env`. Můžete však také použít samostatného poskytovatele, který ukládá soukromé klíče, použít soubor keystore nebo jiné možnosti. + +### 5. Jaký je rozdíl mezi `eth_sendTransaction` a `eth_sendRawTransaction`? {#difference-between-send-and-send-raw} + +`eth_sendTransaction` a `eth_sendRawTransaction` jsou obě funkce API Etherea, které vysílají transakci do sítě Ethereum, aby byla přidána do budoucího bloku. Liší se v tom, jak nakládají s podepisováním transakcí. + +- [`eth_sendTransaction`](https://docs.web3js.org/api/web3-eth/function/sendTransaction) se používá k odesílání _nepodepsaných_ transakcí, což znamená, že uzel, na který odesíláte, musí spravovat váš soukromý klíč, aby mohl transakci podepsat před jejím vysíláním do řetězce. Jelikož Alchemy neuchovává soukromé klíče uživatelů, tuto metodu nepodporuje. +- [`eth_sendRawTransaction`](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction) se používá k vysílání transakcí, které již byly podepsány. To znamená, že nejprve musíte použít [`signTransaction(tx, private_key)`](https://docs.web3js.org/api/web3-eth-accounts/function/signTransaction) a poté výsledek předat do `eth_sendRawTransaction`. + +Při použití web3 se k `eth_sendRawTransaction` přistupuje voláním funkce [web3.eth.sendSignedTransaction](https://docs.web3js.org/api/web3-eth/function/sendSignedTransaction). + +To je to, co budeme používat v tomto návodu. + +### 6. Co je to knihovna web3? {#what-is-the-web3-library} + +- Web3.js je obalová knihovna pro standardní volání JSON-RPC, která se ve vývoji pro Ethereum běžně používá. +- Existuje mnoho web3 knihoven pro různé jazyky. V tomto návodu budeme používat [Alchemy Web3](https://docs.alchemy.com/reference/api-overview), který je napsán v JavaScriptu. Můžete se podívat na další možnosti [zde](https://docs.alchemyapi.io/guides/getting-started#other-web3-libraries), jako je například [ethers.js](https://docs.ethers.org/v5/). + +Dobře, teď, když máme několik těchto otázek z cesty, přejděme k návodu. Neváhejte se kdykoli na cokoli zeptat na [Discordu](https://discord.gg/gWuC7zB) společnosti Alchemy! + +### 7. Jak posílat bezpečné, soukromé transakce a transakce s optimalizovanými poplatky? {#how-to-send-secure-gas-optimized-and-private-transactions} + +- [Alchemy má sadu Transact API](https://docs.alchemy.com/reference/transact-api-quickstart). Můžete je použít k odesílání posílených transakcí, simulaci transakcí před jejich uskutečněním, odesílání soukromých transakcí a odesílání transakcí optimalizovaných z hlediska poplatků +- Můžete také použít [Notify API](https://docs.alchemy.com/docs/alchemy-notify), abyste byli upozorněni, když je vaše transakce vytažena z mempoolu a přidána do řetězce. + +**POZNÁMKA:** Tento průvodce vyžaduje účet Alchemy, adresu Ethereum nebo peněženku MetaMask, nainstalovaný NodeJs a npm. Pokud ne, postupujte podle následujících kroků: + +1. [Vytvořte si bezplatný účet Alchemy](https://auth.alchemyapi.io/signup) +2. [Vytvořte si účet MetaMask](https://metamask.io/) (nebo získejte adresu Ethereum) +3. [Postupujte podle těchto kroků k instalaci NodeJs a NPM](https://docs.alchemy.com/alchemy/guides/alchemy-for-macs) + +## Kroky k odeslání vaší transakce {#steps-to-sending-your-transaction} + +### 1. Vytvořte aplikaci Alchemy na testnetu Sepolia {#create-an-alchemy-app-on-the-sepolia-testnet} + +Přejděte na svůj [Alchemy Dashboard](https://dashboard.alchemyapi.io/) a vytvořte novou aplikaci a pro svou síť zvolte Sepolia (nebo jakýkoli jiný testnet). + +### 2. Požádejte o ETH z faucetu Sepolia {#request-eth-from-sepolia-faucet} + +Chcete-li obdržet ETH, postupujte podle pokynů na [faucetu Sepolia od Alchemy](https://www.sepoliafaucet.com/). Ujistěte se, že jste zadali svou adresu Ethereum na síti **Sepolia** (z MetaMasku) a ne jinou síť. Po provedení pokynů si zkontrolujte, že jste ETH obdrželi do své peněženky. + +### 3. Vytvořte nový adresář projektu a přejděte do něj pomocí `cd` {#create-a-new-project-direction} + +Vytvořte nový adresář projektu z příkazového řádku (terminál pro Mac) a přejděte do něj: + +``` +mkdir sendtx-example +cd sendtx-example +``` + +### 4. Nainstalujte si Alchemy Web3 (nebo jakoukoli web3 knihovnu) {#install-alchemy-web3} + +Spusťte následující příkaz ve svém projektovém adresáři, abyste si nainstalovali [Alchemy Web3](https://docs.alchemy.com/reference/api-overview): + +Poznámka: Pokud byste chtěli použít knihovnu ethers.js, [postupujte podle pokynů zde](https://docs.alchemy.com/docs/how-to-send-transactions-on-ethereum). + +``` +npm install @alch/alchemy-web3 +``` + +### 5. Nainstalujte si dotenv {#install-dotenv} + +Použijeme soubor `.env` k bezpečnému uložení našeho klíče API a soukromého klíče. + +``` +npm install dotenv --save +``` + +### 6. Vytvořte soubor `.env` {#create-the-dotenv-file} + +Vytvořte soubor `.env` ve svém projektovém adresáři a přidejte následující (nahraďte "`your-api-url`" a "`your-private-key`") + +- Chcete-li najít adresu URL vašeho Alchemy API, přejděte na stránku s podrobnostmi aplikace, kterou jste právě vytvořili na svém ovládacím panelu, klikněte na „View Key“ v pravém horním rohu a zkopírujte URL adresu HTTP. +- Chcete-li najít svůj soukromý klíč pomocí MetaMasku, podívejte se na tohoto [průvodce](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key). + +``` +API_URL = "your-api-url" +PRIVATE_KEY = "your-private-key" +``` + + + + +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. + + + + +### 7. Vytvořte soubor `sendTx.js` {#create-sendtx-js} + +Skvělé, teď, když máme svá citlivá data chráněná v souboru `.env`, pojďme začít kódovat. Jako příklad odeslání transakce budeme posílat ETH zpět do faucetu Sepolia. + +Vytvořte soubor `sendTx.js`, ve kterém nakonfigurujeme a odešleme naši ukázkovou transakci, a přidejte do něj následující řádky kódu: + +``` +async function main() { + require('dotenv').config(); + const { API_URL, PRIVATE_KEY } = process.env; + const { createAlchemyWeb3 } = require("@alch/alchemy-web3"); + const web3 = createAlchemyWeb3(API_URL); + const myAddress = '0x610Ae88399fc1687FA7530Aac28eC2539c7d6d63' //TODO: nahraďte tuto adresu vaší veřejnou adresou + + const nonce = await web3.eth.getTransactionCount(myAddress, 'latest'); // nonce se začíná počítat od 0 + + const transaction = { + 'to': '0x31B98D14007bDEe637298086988A0bBd31184523', // adresa faucetu pro vrácení ETH + 'value': 1000000000000000000, // 1 ETH + 'gas': 30000, + 'nonce': nonce, + // volitelné datové pole pro odeslání zprávy nebo spuštění chytrého kontraktu + }; + + const signedTx = await web3.eth.accounts.signTransaction(transaction, PRIVATE_KEY); + + web3.eth.sendSignedTransaction(signedTx.rawTransaction, function(error, hash) { + if (!error) { + console.log("🎉 The hash of your transaction is: ", hash, "\n Check Alchemy's Mempool to view the status of your transaction!"); + } else { + console.log("❗Something went wrong while submitting your transaction:", error) + } + }); +} + +main(); +``` + +Nezapomeňte nahradit adresu na **řádku 6** vaší vlastní veřejnou adresou. + +Než se pustíme do spouštění tohoto kódu, promluvme si o některých jeho součástech. + +- `nonce` : Specifikace nonce se používá ke sledování počtu transakcí odeslaných z vaší adresy. Potřebujeme to z bezpečnostních důvodů a k zabránění [útokům typu replay](https://docs.alchemyapi.io/resources/blockchain-glossary#account-nonce). K získání počtu transakcí odeslaných z vaší adresy používáme [getTransactionCount](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc#eth_gettransactioncount). +- `transaction`: Objekt transakce má několik aspektů, které musíme specifikovat + - `to`: Toto je adresa, na kterou chceme poslat ETH. V tomto případě posíláme ETH zpět do [faucetu Sepolia](https://sepoliafaucet.com/), od kterého jsme je původně požadovali. + - `value`: Toto je částka, kterou si přejeme poslat, specifikovaná ve Wei, kde 10^18 Wei = 1 ETH + - `gas`: Existuje mnoho způsobů, jak určit správné množství gasu, které má vaše transakce obsahovat. Alchemy má dokonce [webhook pro cenu gasu](https://docs.alchemyapi.io/guides/alchemy-notify#address-activity-1), který vás upozorní, když cena gasu klesne pod určitou hranici. U transakcí na mainnetu je dobrým zvykem zkontrolovat odhadce poplatků za gas, jako je [ETH Gas Station](https://ethgasstation.info/), abyste určili správné množství gasu, které má transakce obsahovat. 21000 je minimální množství gasu, které operace na Ethereu spotřebuje, takže abychom zajistili, že se naše transakce provede, zadáme zde 30000. + - `nonce`: viz definice nonce výše. Nonce se začíná počítat od nuly. + - [VOLITELNÉ] data: Používá se k odeslání dodatečných informací s vaším převodem nebo k volání chytrého kontraktu, není vyžadováno pro převody zůstatku, podívejte se na poznámku níže. +- `signedTx`: K podepsání našeho objektu transakce použijeme metodu `signTransaction` s naším `PRIVATE_KEY` +- `sendSignedTransaction`: Jakmile máme podepsanou transakci, můžeme ji odeslat k zahrnutí do následujícího bloku pomocí `sendSignedTransaction` + +**Poznámka k datům** +Existují dva hlavní typy transakcí, které lze v Ethereu odeslat. + +- Převod zůstatku: Odešlete ETH z jedné adresy na druhou. Není vyžadováno žádné datové pole, avšak pokud byste chtěli spolu s transakcí poslat další informace, můžete je v tomto poli uvést ve formátu HEX. + - Řekněme například, že bychom chtěli zapsat haš dokumentu IPFS do řetězce Ethereum, abychom mu dali neměnné časové razítko. Naše datové pole by pak mělo vypadat takto: data: `web3.utils.toHex('haš IPFS')`. A teď se kdokoli může dotázat řetězce a zjistit, kdy byl daný dokument přidán. +- Transakce chytrého kontraktu: Spusťte nějaký kód chytrého kontraktu na řetězci. V tomto případě by datové pole mělo obsahovat chytrou funkci, kterou si přejete spustit, spolu s jakýmikoli parametry. + - Praktický příklad najdete v kroku 8 tohoto [návodu Hello World](https://docs.alchemyapi.io/alchemy/tutorials/hello-world-smart-contract#step-8-create-the-transaction). + +### 8. Spusťte kód pomocí `node sendTx.js` {#run-the-code-using-node-sendtx-js} + +Vraťte se do svého terminálu nebo příkazového řádku a spusťte: + +``` +node sendTx.js +``` + +### 9. Zobrazte svou transakci v Mempoolu {#see-your-transaction-in-the-mempool} + +Otevřete stránku [Mempool](https://dashboard.alchemyapi.io/mempool) na svém ovládacím panelu Alchemy a filtrujte podle aplikace, kterou jste vytvořili, abyste našli svou transakci. Zde můžeme sledovat přechod naší transakce z čekajícího stavu do stavu vytěženého (v případě úspěchu) nebo zahozeného stavu (v případě neúspěchu). Ujistěte se, že je nastaveno na „All“, abyste zachytili „vytěžené“, „čekající“ a „zahozené“ transakce. Svou transakci můžete také vyhledat tak, že budete hledat transakce odeslané na adresu `0x31b98d14007bdee637298086988a0bbd31184523`. + +Chcete-li zobrazit podrobnosti své transakce, jakmile ji najdete, vyberte haš transakce, který by vás měl přenést do zobrazení, které vypadá takto: + +![Snímek obrazovky Mempool watcher](./mempool.png) + +Odtud si můžete prohlédnout svou transakci na Etherscanu kliknutím na červeně zakroužkovanou ikonu! + +**Jupííí! Právě jste odeslali svou první transakci na Ethereu pomocí Alchemy 🎉** + +_Pro zpětnou vazbu a návrhy k tomuto průvodci prosím napište Elanovi na [Discordu](https://discord.gg/A39JVCM) společnosti Alchemy!_ + +_Původně publikováno na [https://docs.alchemyapi.io/tutorials/sending-transactions-using-web3-and-alchemy](https://docs.alchemyapi.io/tutorials/sending-transactions-using-web3-and-alchemy)_ diff --git a/public/content/translations/cs/developers/tutorials/server-components/index.md b/public/content/translations/cs/developers/tutorials/server-components/index.md new file mode 100644 index 00000000000..4c182cdf0e5 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/server-components/index.md @@ -0,0 +1,295 @@ +--- +title: "Serverové komponenty a agenti pro web3 aplikace" +description: "Po přečtení tohoto tutoriálu budete schopni psát TypeScript servery, které naslouchají událostem na blockchainu a odpovídajícím způsobem reagují vlastními transakcemi. To vám umožní psát centralizované aplikace (protože server je bodem selhání), které ale mohou interagovat s web3 entitami. Stejné techniky lze také použít k napsání agenta, který reaguje na události na blockchainu bez zásahu člověka." + +author: Ori Pomerantz +lang: cs +tags: [ "agent", "server", "offchain" ] +skill: beginner +published: 2024-07-15 +--- + +## Úvod {#introduction} + +Ve většině případů decentralizovaná aplikace používá server k distribuci softwaru, ale veškerá skutečná interakce probíhá mezi klientem (obvykle webovým prohlížečem) a blockchainem. + +![Normální interakce mezi webovým serverem, klientem a blockchainem](./fig-1.svg) + +Existují však případy, kdy by aplikaci prospěla serverová komponenta, která běží nezávisle. Takový server by byl schopen reagovat na události a na požadavky, které přicházejí z jiných zdrojů, jako je API, vydáváním transakcí. + +![Interakce s přidaným serverem](./fig-2.svg) + +Existuje několik možných úkolů, které by takový server mohl plnit. + +- Držitel tajného stavu. Při hraní je často užitečné, aby hráči neměli k dispozici všechny informace, které hra zná. Nicméně, _na blockchainu neexistují žádná tajemství_, jakoukoli informaci, která je v blockchainu, může kdokoli snadno zjistit. Proto, pokud má být část stavu hry utajena, musí být uložena jinde (a případně nechat účinky tohoto stavu ověřit pomocí [důkazů s nulovou znalostí](/zero-knowledge-proofs)). + +- Centralizované orákulum. Pokud jsou sázky dostatečně nízké, externí server, který čte některé informace online a poté je zveřejňuje na řetězci, může být dostatečně dobrý na to, aby byl použit jako [orákulum](/developers/docs/oracles/). + +- Agent. Na blockchainu se nic nestane bez transakce, která by to aktivovala. Server může jednat jménem uživatele a provádět akce, jako je [arbitráž](/developers/docs/mev/#mev-examples-dex-arbitrage), když se naskytne příležitost. + +## Ukázkový program {#sample-program} + +Ukázkový server si můžete prohlédnout [na GitHubu](https://github.com/qbzzt/20240715-server-component). Tento server naslouchá událostem pocházejícím z [tohoto kontraktu](https://eth-holesky.blockscout.com/address/0xB8f6460Dc30c44401Be26B0d6eD250873d8a50A6?tab=contract_code), upravené verze Hardhat Greeteru. Když se pozdrav změní, změní ho zpět. + +Spustíte ho takto: + +1. Naklonujte repozitář. + + ```sh copy + git clone https://github.com/qbzzt/20240715-server-component.git + cd 20240715-server-component + ``` + +2. Nainstalujte potřebné balíčky. Pokud jej ještě nemáte, [nainstalujte nejprve Node](https://nodejs.org/en/download/package-manager). + + ```sh copy + npm install + ``` + +3. Upravte soubor `.env` a zadejte soukromý klíč účtu, který má ETH na testnetu Holesky. Pokud nemáte ETH na Holesky, můžete [použít tento faucet](https://holesky-faucet.pk910.de/). + + ```sh filename=".env" copy + PRIVATE_KEY=0x + ``` + +4. Spusťte server. + + ```sh copy + npm start + ``` + +5. Přejděte do [prohlížeče bloků](https://eth-holesky.blockscout.com/address/0xB8f6460Dc30c44401Be26B0d6eD250873d8a50A6?tab=write_contract) a pomocí jiné adresy, než je ta, ke které máte soukromý klíč, upravte pozdrav. Uvidíte, že pozdrav je automaticky změněn zpět. + +### Jak to funguje? Jak to funguje {#how-it-works} + +Nejjednodušší způsob, jak pochopit, jak napsat serverovou komponentu, je projít si ukázku řádek po řádku. + +#### `src/app.ts` {#src-app-ts} + +Drtivá většina programu je obsažena v [`src/app.ts`](https://github.com/qbzzt/20240715-server-component/blob/main/src/app.ts). + +##### Vytvoření nezbytných objektů + +```typescript +import { + createPublicClient, + createWalletClient, + getContract, + http, + Address, +} from "viem" +``` + +Toto jsou entity [Viem](https://viem.sh/), které potřebujeme, funkce a [typ `Address`](https://viem.sh/docs/glossary/types#address). Tento server je napsán v [TypeScriptu](https://www.typescriptlang.org/), což je rozšíření JavaScriptu, které ho činí [silně typovaným](https://en.wikipedia.org/wiki/Strong_and_weak_typing). + +```typescript +import { privateKeyToAccount } from "viem/accounts" +``` + +[Tato funkce](https://viem.sh/docs/accounts/privateKey) nám umožňuje generovat informace o peněžence, včetně adresy, odpovídající soukromému klíči. + +```typescript +import { holesky } from "viem/chains" +``` + +Abyste mohli v Viem používat blockchain, musíte importovat jeho definici. V tomto případě se chceme připojit k testovacímu blockchainu [Holesky](https://github.com/eth-clients/holesky). + +```typescript +// Takto přidáváme definice z .env do process.env. +import * as dotenv from "dotenv" +dotenv.config() +``` + +Takto načítáme `.env` do prostředí. Potřebujeme to pro soukromý klíč (viz dále). + +```typescript +const greeterAddress : Address = "0xB8f6460Dc30c44401Be26B0d6eD250873d8a50A6" +const greeterABI = [ + { + "inputs": [ + { + "internalType": "string", + "name": "_greeting", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + . + . + . + { + "inputs": [ + { + "internalType": "string", + "name": "_greeting", + "type": "string" + } + ], + "name": "setGreeting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] as const +``` + +Pro použití kontraktu potřebujeme jeho adresu a [ABI](/glossary/#abi). Obojí zde uvádíme. + +V JavaScriptu (a tedy i v TypeScriptu) nemůžete konstantě přiřadit novou hodnotu, ale _můžete_ upravit objekt, který je v ní uložen. Použitím přípony `as const` říkáme TypeScriptu, že seznam samotný je konstantní a nesmí být změněn. + +```typescript +const publicClient = createPublicClient({ + chain: holesky, + transport: http(), +}) +``` + +Vytvořte Viem [veřejného klienta](https://viem.sh/docs/clients/public.html). Veřejní klienti nemají připojený soukromý klíč, a proto nemohou odesílat transakce. Mohou volat [`view` funkce](https://www.tutorialspoint.com/solidity/solidity_view_functions.htm), číst zůstatky na účtech atd. + +```typescript +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) +``` + +Proměnné prostředí jsou k dispozici v [`process.env`](https://www.totaltypescript.com/how-to-strongly-type-process-env). TypeScript je však silně typovaný. Proměnná prostředí může být jakýkoli řetězec, nebo prázdná, takže typem pro proměnnou prostředí je `string | undefined`. Klíč je však v Viem definován jako `0x${string}` (`0x` následované řetězcem). Zde říkáme TypeScriptu, že proměnná prostředí `PRIVATE_KEY` bude tohoto typu. Pokud ne, dojde k chybě za běhu. + +Funkce [`privateKeyToAccount`](https://viem.sh/docs/accounts/privateKey) pak použije tento soukromý klíč k vytvoření úplného objektu účtu. + +```typescript +const walletClient = createWalletClient({ + account, + chain: holesky, + transport: http(), +}) +``` + +Dále použijeme objekt účtu k vytvoření [klienta peněženky](https://viem.sh/docs/clients/wallet). Tento klient má soukromý klíč a adresu, takže jej lze použít k odesílání transakcí. + +```typescript +const greeter = getContract({ + address: greeterAddress, + abi: greeterABI, + client: { public: publicClient, wallet: walletClient }, +}) +``` + +Nyní, když máme všechny předpoklady, můžeme konečně vytvořit [instanci kontraktu](https://viem.sh/docs/contract/getContract). Tuto instanci kontraktu použijeme ke komunikaci s on-chain kontraktem. + +##### Čtení z blockchainu + +```typescript +console.log(`Current greeting:`, await greeter.read.greet()) +``` + +Funkce kontraktu, které jsou pouze pro čtení ([`view`](https://www.tutorialspoint.com/solidity/solidity_view_functions.htm) a [`pure`](https://www.tutorialspoint.com/solidity/solidity_pure_functions.htm)), jsou dostupné pod `read`. V tomto případě ji používáme pro přístup k funkci [`greet`](https://eth-holesky.blockscout.com/address/0xB8f6460Dc30c44401Be26B0d6eD250873d8a50A6?tab=read_contract#cfae3217), která vrací pozdrav. + +JavaScript je jednovláknový, takže když spouštíme dlouhotrvající proces, musíme [specifikovat, že to děláme asynchronně](https://eloquentjavascript.net/11_async.html#h-XvLsfAhtsE). Volání blockchainu, i pro operaci pouze pro čtení, vyžaduje obousměrnou komunikaci mezi počítačem a uzlem blockchainu. To je důvod, proč zde specifikujeme, že kód musí na výsledek `await` (počkat). + +Pokud vás zajímá, jak to funguje, můžete si [o tom přečíst zde](https://www.w3schools.com/js/js_promise.asp), ale v praxi vše, co potřebujete vědět, je, že `await` (čekáte na) výsledky, pokud spustíte operaci, která trvá dlouho, a že jakákoli funkce, která to dělá, musí být deklarována jako `async`. + +##### Vydávání transakcí + +```typescript +const setGreeting = async (greeting: string): Promise => { +``` + +Toto je funkce, kterou voláte pro vydání transakce, která mění pozdrav. Jelikož se jedná o dlouhou operaci, funkce je deklarována jako `async`. Kvůli interní implementaci musí každá `async` funkce vracet objekt `Promise`. V tomto případě `Promise` znamená, že nespecifikujeme, co přesně bude v `Promise` vráceno. + +```typescript +const txHash = await greeter.write.setGreeting([greeting]) +``` + +Pole `write` instance kontraktu obsahuje všechny funkce, které zapisují do stavu blockchainu (ty, které vyžadují odeslání transakce), jako je [`setGreeting`](https://eth-holesky.blockscout.com/address/0xB8f6460Dc30c44401Be26B0d6eD250873d8a50A6?tab=write_contract#a4136862). Parametry, pokud nějaké jsou, jsou poskytnuty jako seznam a funkce vrací haš transakce. + +```typescript + console.log(`Working on a fix, see https://eth-holesky.blockscout.com/tx/${txHash}`) + + return txHash +} +``` + +Nahlaste haš transakce (jako součást adresy URL do prohlížeče bloků pro její zobrazení) a vraťte jej. + +##### Reakce na události + +```typescript +greeter.watchEvent.SetGreeting({ +``` + +[Funkce `watchEvent`](https://viem.sh/docs/actions/public/watchEvent) umožňuje specifikovat, že se má funkce spustit, když je emitována událost. Pokud vás zajímá pouze jeden typ události (v tomto případě `SetGreeting`), můžete použít tuto syntaxi k omezení se na tento typ události. + +```typescript + onLogs: logs => { +``` + +Funkce `onLogs` je volána, když existují záznamy protokolu (logy). V Ethereu jsou pojmy „log“ a „událost“ obvykle zaměnitelné. + +```typescript +console.log( + `Adresa ${logs[0].args.sender} změnila pozdrav na ${logs[0].args.greeting}` +) +``` + +Mohlo by zde být více událostí, ale pro jednoduchost nás zajímá pouze ta první. `logs[0].args` jsou argumenty události, v tomto případě `sender` a `greeting`. + +```typescript + if (logs[0].args.sender != account.address) + setGreeting(`${account.address} trvá na tom, aby to bylo Hello!`) + } +}) +``` + +Pokud odesílatel _není_ tento server, použijte `setGreeting` ke změně pozdravu. + +#### `package.json` {#package-json} + +[Tento soubor](https://github.com/qbzzt/20240715-server-component/blob/main/package.json) řídí konfiguraci [Node.js](https://nodejs.org/en). Tento článek vysvětluje pouze důležité definice. + +```json +{ + "main": "dist/index.js", +``` + +Tato definice určuje, který soubor JavaScriptu se má spustit. + +```json + "scripts": { + "start": "tsc && node dist/app.js", + }, +``` + +Skripty jsou různé akce aplikace. V tomto případě máme pouze jeden, `start`, který kompiluje a poté spouští server. Příkaz `tsc` je součástí balíčku `typescript` a kompiluje TypeScript do JavaScriptu. Pokud jej chcete spustit ručně, nachází se v `node_modules/.bin`. Druhý příkaz spouští server. + +```json + "type": "module", +``` + +Existuje více typů JavaScriptových node aplikací. Typ `module` nám umožňuje mít `await` v kódu nejvyšší úrovně, což je důležité, když provádíte pomalé (a tedy asynchronní) operace. + +```json + "devDependencies": { + "@types/node": "^20.14.2", + "typescript": "^5.4.5" + }, +``` + +Toto jsou balíčky, které jsou vyžadovány pouze pro vývoj. Zde potřebujeme `typescript` a protože ho používáme s Node.js, získáváme také typy pro node proměnné a objekty, jako je `process`. [Zápis `^`](https://github.com/npm/node-semver?tab=readme-ov-file#caret-ranges-123-025-004) znamená danou verzi nebo vyšší verzi, která neobsahuje zásadní změny. Více informací o významu čísel verzí naleznete [zde](https://semver.org). + +```json + "dependencies": { + "dotenv": "^16.4.5", + "viem": "2.14.1" + } +} +``` + +Toto jsou balíčky, které jsou vyžadovány za běhu, při spouštění `dist/app.js`. + +## Závěr {#conclusion} + +Centralizovaný server, který jsme zde vytvořili, plní svůj úkol, kterým je jednat jako agent pro uživatele. Kdokoli jiný, kdo chce, aby dapp nadále fungoval a je ochoten utratit palivo, může spustit novou instanci serveru s vlastní adresou. + +To však funguje pouze tehdy, když lze akce centralizovaného serveru snadno ověřit. Pokud má centralizovaný server nějaké tajné stavové informace nebo provádí složité výpočty, je to centralizovaná entita, které musíte důvěřovat, abyste mohli aplikaci používat, což je přesně to, čemu se blockchainy snaží vyhnout. V budoucím článku plánuji ukázat, jak tento problém obejít pomocí [důkazů s nulovou znalostí](/zero-knowledge-proofs). + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). diff --git a/public/content/translations/cs/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/index.md b/public/content/translations/cs/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/index.md new file mode 100644 index 00000000000..0d7fdbcfe02 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/index.md @@ -0,0 +1,92 @@ +--- +title: "Nastavení web3.js pro použití blockchainu Ethereum v JavaScriptu" +description: "Naučte se, jak nastavit a nakonfigurovat knihovnu web3.js pro interakci s blockchainem Etherea z javascriptových aplikací." +author: "jdourlens" +tags: [ "web3.js", "javascript" ] +skill: beginner +lang: cs +published: 2020-04-11 +source: EthereumDev +sourceUrl: https://ethereumdev.io/setup-web3js-to-use-the-ethereum-blockchain-in-javascript/ +address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +--- + +V tomto tutoriálu se podíváme, jak začít s [web3.js](https://web3js.readthedocs.io/) pro interakci s blockchainem Etherea. Web3.js lze použít jak na frontendu, tak na backendu ke čtení dat z blockchainu, provádění transakcí, a dokonce i k nasazení chytrých kontraktů. + +Prvním krokem je zahrnout web3.js do vašeho projektu. Chcete-li jej použít na webové stránce, můžete knihovnu importovat přímo pomocí CDN, jako je JSDeliver. + +```html + +``` + +Pokud dáváte přednost instalaci knihovny pro použití ve vašem backendu nebo front-endovém projektu, který používá sestavení, můžete ji nainstalovat pomocí npm: + +```bash +npm install web3 --save +``` + +Chcete-li poté importovat Web3.js do skriptu Node.js nebo front-endového projektu Browserify, můžete použít následující řádek JavaScriptu: + +```js +const Web3 = require("web3") +``` + +Nyní, když jsme knihovnu zahrnuli do projektu, ji musíme inicializovat. Váš projekt musí být schopen komunikovat s blockchainem. Většina knihoven pro Ethereum komunikuje s [uzlem](/developers/docs/nodes-and-clients/) prostřednictvím volání RPC. Pro inicializaci poskytovatele Web3 vytvoříme instanci Web3 a jako konstruktor předáme URL adresu poskytovatele. Pokud máte na svém počítači spuštěný uzel nebo [instanci ganache](https://ethereumdev.io/testing-your-smart-contract-with-existing-protocols-ganache-fork/), bude to vypadat takto: + +```js +const web3 = new Web3("http://localhost:8545") +``` + +Pokud chcete přistupovat přímo k hostovanému uzlu, můžete najít možnosti na stránce [uzly jako služba](/developers/docs/nodes-and-clients/nodes-as-a-service). + +```js +const web3 = new Web3("https://cloudflare-eth.com") +``` + +Abychom otestovali, že jsme správně nakonfigurovali naši instanci Web3, zkusíme načíst číslo posledního bloku pomocí funkce `getBlockNumber`. Tato funkce přijímá jako parametr zpětné volání (callback) a vrací číslo bloku jako celé číslo. + +```js +var Web3 = require("web3") +const web3 = new Web3("https://cloudflare-eth.com") + +web3.eth.getBlockNumber(function (error, result) { + console.log(result) +}) +``` + +Pokud tento program spustíte, jednoduše vypíše číslo posledního bloku: vrchol blockchainu. Můžete také použít volání funkcí `await/async`, abyste se vyhnuli vnořování zpětných volání (callbacks) ve vašem kódu: + +```js +async function getBlockNumber() { + const latestBlockNumber = await web3.eth.getBlockNumber() + console.log(latestBlockNumber) + return latestBlockNumber +} + +getBlockNumber() +``` + +Všechny dostupné funkce v instanci Web3 si můžete prohlédnout v [oficiální dokumentaci web3.js](https://docs.web3js.org/). + +Většina knihoven Web3 je asynchronních, protože na pozadí knihovna provádí JSON-RPC volání na uzel, který posílá zpět výsledek. + + + +Pokud pracujete v prohlížeči, některé peněženky přímo vkládají instanci Web3 a měli byste se ji pokusit použít, kdykoli je to možné, zejména pokud plánujete interagovat s ethereovou adresou uživatele za účelem provádění transakcí. + +Zde je úryvek kódu pro zjištění, zda je k dispozici peněženka MetaMask, a pokud ano, pokus o její povolení. To vám později umožní číst zůstatek uživatele a umožní mu ověřovat transakce, které po něm chcete, aby na blockchainu Etherea provedl: + +```js +if (window.ethereum != null) { + state.web3 = new Web3(window.ethereum) + try { + // V případě potřeby si vyžádejte přístup k účtu + await window.ethereum.enable() + // Účty jsou nyní odhaleny + } catch (error) { + // Uživatel odepřel přístup k účtu... + } +} +``` + +Existují alternativy k web3.js, jako je [Ethers.js](https://docs.ethers.io/), které se také běžně používají. V dalším tutoriálu se podíváme, [jak snadno naslouchat novým příchozím blokům na blockchainu a vidět, co obsahují](https://ethereumdev.io/listening-to-new-transactions-happening-on-the-blockchain/). diff --git a/public/content/translations/cs/developers/tutorials/short-abi/index.md b/public/content/translations/cs/developers/tutorials/short-abi/index.md new file mode 100644 index 00000000000..fb1db52cf37 --- /dev/null +++ b/public/content/translations/cs/developers/tutorials/short-abi/index.md @@ -0,0 +1,585 @@ +--- +title: "Zkrácené ABI pro optimalizaci calldata" +description: "Optimalizace chytrých kontraktů pro optimistické rollupy" +author: Ori Pomerantz +lang: cs +tags: [ "vrstva 2" ] +skill: intermediate +published: 2022-04-01 +--- + +## Úvod {#introduction} + +V tomto článku se dozvíte o [optimistických rollupech](/developers/docs/scaling/optimistic-rollups), nákladech na transakce na nich a o tom, jak tato odlišná struktura nákladů vyžaduje, abychom optimalizovali jiné věci než na hlavní síti Etherea. +Také se dozvíte, jak tuto optimalizaci implementovat. + +### Úplné zveřejnění {#full-disclosure} + +Jsem zaměstnancem společnosti [Optimism](https://www.optimism.io/) na plný úvazek, takže příklady v tomto článku poběží na Optimismu. +Zde vysvětlená technika by však měla fungovat stejně dobře i pro ostatní rollupy. + +### Terminologie {#terminology} + +Při diskusi o rollupech se termín „vrstva 1“ (L1) používá pro hlavní síť (Mainnet), produkční síť Etherea. +Termín „vrstva 2“ (L2) se používá pro rollup nebo jakýkoli jiný systém, který se spoléhá na L1 kvůli bezpečnosti, ale většinu zpracování provádí mimo řetězec (offchain). + +## Jak můžeme dále snížit náklady na transakce L2? {#how-can-we-further-reduce-the-cost-of-L2-transactions} + +[Optimistické rollupy](/developers/docs/scaling/optimistic-rollups) musí uchovávat záznam o každé historické transakci, aby si je kdokoli mohl projít a ověřit, že aktuální stav je správný. +Nejlevnější způsob, jak dostat data do hlavní sítě Etherea, je zapsat je jako calldata. +Toto řešení si zvolily jak [Optimism](https://help.optimism.io/hc/en-us/articles/4413163242779-What-is-a-rollup-), tak [Arbitrum](https://developer.offchainlabs.com/docs/rollup_basics#intro-to-rollups). + +### Náklady na transakce L2 {#cost-of-l2-transactions} + +Náklady na transakce L2 se skládají ze dvou složek: + +1. Zpracování na L2, které je obvykle extrémně levné +2. Úložiště na L1, které je vázáno na náklady na palivo na hlavní síti + +V době, kdy toto píšu, je na Optimismu cena paliva L2 0,001 [Gwei](/developers/docs/gas/#pre-london). +Cena paliva L1 je naopak přibližně 40 gwei. +[Aktuální ceny si můžete prohlédnout zde](https://public-grafana.optimism.io/d/9hkhMxn7z/public-dashboard?orgId=1&refresh=5m). + +Jeden bajt calldata stojí buď 4 jednotky paliva (pokud je nulový), nebo 16 jednotek paliva (pokud má jakoukoli jinou hodnotu). +Jednou z nejdražších operací na EVM je zápis do úložiště. +Maximální cena zápisu 32bajtového slova do úložiště na L2 je 22 100 jednotek paliva. V současnosti je to 22,1 gwei. +Takže pokud ušetříme jediný nulový bajt calldata, budeme moci zapsat do úložiště asi 200 bajtů a stále na tom vyděláme. + +### ABI {#the-abi} + +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 podle [aplikačního binárního rozhraní (ABI)](https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding). + +ABI však bylo navrženo pro L1, kde bajt calldata stojí přibližně stejně jako čtyři aritmetické operace, nikoliv pro L2, kde bajt calldata stojí více než tisíc aritmetických operací. +Calldata jsou rozdělena takto: + +| Sekce | Délka | Bajty | Zbytečné bajty | Zbytečné palivo | Nezbytné bajty | Nezbytné palivo | +| --------------- | ----: | ----: | -------------: | --------------: | -------------: | --------------: | +| Selektor funkce | 4 | 0-3 | 3 | 48 | 1 | 16 | +| Nuly | 12 | 4-15 | 12 | 48 | 0 | 0 | +| Cílová adresa | 20 | 16-35 | 0 | 0 | 20 | 320 | +| Částka | 32 | 36-67 | 17 | 64 | 15 | 240 | +| Celkem | 68 | | | 160 | | 576 | + +Vysvětlení: + +- **Selektor funkce**: Kontrakt má méně než 256 funkcí, takže je můžeme rozlišit jediným bajtem. + Tyto bajty jsou obvykle nenulové, a proto [stojí šestnáct jednotek paliva](https://eips.ethereum.org/EIPS/eip-2028). +- **Nuly**: Tyto bajty jsou vždy nulové, protože dvacetibajtová adresa nevyžaduje k uložení třicetidvoubajtové slovo. + Bajty, které obsahují nulu, stojí čtyři jednotky paliva ([viz Yellow paper](https://ethereum.github.io/yellowpaper/paper.pdf), Dodatek G, + str. 27, hodnota pro `G``txdatazero`). +- **Částka**: Pokud předpokládáme, že v tomto kontraktu je `decimals` osmnáct (normální hodnota) a maximální množství tokenů, které převedeme, bude 1018, dostaneme maximální částku 1036. + 25615 > 1036, takže patnáct bajtů stačí. + +Ztráta 160 jednotek paliva na L1 je obvykle zanedbatelná. Transakce stojí nejméně [21 000 jednotek paliva](https://yakkomajuri.medium.com/blockchain-definition-of-the-week-ethereum-gas-2f976af774ed), takže na 0,8 % navíc nezáleží. +Na L2 je to však jinak. Téměř celé náklady na transakci tvoří její zápis na L1. +Kromě calldata transakce existuje 109 bajtů záhlaví transakce (cílová adresa, podpis atd.). +Celkové náklady jsou tedy `109*16+576+160=2480` a my z toho plýtváme asi 6,5 %. + +## Snížení nákladů, když nemáte kontrolu nad cílem {#reducing-costs-when-you-dont-control-the-destination} + +Za předpokladu, že nemáte kontrolu nad cílovým kontraktem, můžete stále použít řešení podobné [tomuto](https://github.com/qbzzt/ethereum.org-20220330-shortABI). +Pojďme si projít příslušné soubory. + +### Token.sol {#token-sol} + +[Toto je cílový kontrakt](https://github.com/qbzzt/ethereum.org-20220330-shortABI/blob/master/contracts/Token.sol). +Jedná se o standardní kontrakt ERC-20 s jednou další funkcí. +Tato funkce `faucet` umožňuje každému uživateli získat tokeny k použití. +U produkčního kontraktu ERC-20 by byla k ničemu, ale usnadňuje život, když kontrakt ERC-20 existuje pouze pro usnadnění testování. + +```solidity + /** + * @dev Dává volajícímu 1000 tokenů na hraní + */ + function faucet() external { + _mint(msg.sender, 1000); + } // function faucet +``` + +### CalldataInterpreter.sol {#calldatainterpreter-sol} + +[Toto je kontrakt, který mají transakce volat s kratšími calldata](https://github.com/qbzzt/ethereum.org-20220330-shortABI/blob/master/contracts/CalldataInterpreter.sol). +Pojďme si ho projít řádek po řádku. + +```solidity +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + + +import { OrisUselessToken } from "./Token.sol"; +``` + +Potřebujeme funkci tokenu, abychom věděli, jak ji volat. + +```solidity +contract CalldataInterpreter { + + OrisUselessToken public immutable token; +``` + +Adresa tokenu, pro který jsme proxy. + +```solidity + + /** + * @dev Určete adresu tokenu + * @param tokenAddr_ Adresa kontraktu ERC-20 + */ + constructor( + address tokenAddr_ + ) { + token = OrisUselessToken(tokenAddr_); + } // constructor +``` + +Adresa tokenu je jediný parametr, který musíme zadat. + +```solidity + function calldataVal(uint startByte, uint length) + private pure returns (uint) { +``` + +Přečtěte hodnotu z calldata. + +```solidity + uint _retVal; + + require(length < 0x21, + "calldataVal length limit is 32 bytes"); + + require(length + startByte <= msg.data.length, + "calldataVal trying to read beyond calldatasize"); +``` + +Chystáme se načíst jedno 32bajtové (256bitové) slovo do paměti a odstranit bajty, které nejsou součástí požadovaného pole. +Tento algoritmus nefunguje pro hodnoty delší než 32 bajtů a samozřejmě nemůžeme číst za koncem calldata. +Na L1 může být nutné tyto testy přeskočit, abychom ušetřili palivo, ale na L2 je palivo extrémně levné, což umožňuje jakékoli kontroly správnosti, na které si vzpomeneme. + +```solidity + assembly { + _retVal := calldataload(startByte) + } +``` + +Mohli jsme zkopírovat data z volání do `fallback()` (viz níže), ale je jednodušší použít [Yul](https://docs.soliditylang.org/en/v0.8.12/yul.html), assembler EVM. + +Zde používáme [operační kód CALLDATALOAD](https://www.evm.codes/#35) ke čtení bajtů `startByte` až `startByte+31` do zásobníku. +Obecně je syntaxe operačního kódu v Yul `(, ...).` + +```solidity + + _retVal = _retVal >> (256-length*8); +``` + +Součástí pole jsou pouze nejvýznamnější bajty `length`, takže provedeme [posun doprava](https://en.wikipedia.org/wiki/Logical_shift), abychom se zbavili ostatních hodnot. +To má další výhodu v tom, že se hodnota přesune napravo od pole, takže se jedná o hodnotu samotnou, nikoli o hodnotu vynásobenou 256něčím. + +```solidity + + return _retVal; + } + + + fallback() external { +``` + +Když volání kontraktu v Solidity neodpovídá žádnému z podpisů funkcí, zavolá se [funkce `fallback()`](https://docs.soliditylang.org/en/v0.8.12/contracts.html#fallback-function) (pokud existuje). +V případě `CalldataInterpreter` se sem dostane _jakékoli_ volání, protože neexistují žádné jiné `externí` nebo `veřejné` funkce. + +```solidity + uint _func; + + _func = calldataVal(0, 1); +``` + +Přečtěte první bajt calldata, který nám sdělí funkci. +Existují dva důvody, proč by zde funkce nebyla dostupná: + +1. Funkce, které jsou `pure` nebo `view`, nemění stav a nestojí palivo (při volání mimo řetězec). + Nemá smysl se snažit snižovat jejich náklady na palivo. +2. Funkce, které se spoléhají na [`msg.sender`](https://docs.soliditylang.org/en/v0.8.12/units-and-global-variables.html#block-and-transaction-properties). + Hodnota `msg.sender` bude adresa kontraktu `CalldataInterpreter`, nikoli volajícího. + +Bohužel [při pohledu na specifikace ERC-20](https://eips.ethereum.org/EIPS/eip-20), zbývá pouze jedna funkce, `transfer`. +To nám ponechává pouze dvě funkce: `transfer` (protože můžeme volat `transferFrom`) a `faucet` (protože můžeme převést tokeny zpět tomu, kdo nás volal). + +```solidity + + // Volání metod tokenu měnících stav pomocí + // informací z calldata + + // faucet + if (_func == 1) { +``` + +Volání funkce `faucet()`, která nemá žádné parametry. + +```solidity + token.faucet(); + token.transfer(msg.sender, + token.balanceOf(address(this))); + } +``` + +Po zavolání `token.faucet()` získáme tokeny. Jako proxy kontrakt však tokeny **nepotřebujeme**. +Potřebuje je EOA (externě vlastněný účet) nebo kontrakt, který nás volal. +Takže převedeme všechny naše tokeny tomu, kdo nás volal. + +```solidity + // transfer (předpokládejme, že na něj máme povolenku) + if (_func == 2) { +``` + +Převod tokenů vyžaduje dva parametry: cílovou adresu a částku. + +```solidity + token.transferFrom( + msg.sender, +``` + +Volajícím povolujeme převádět pouze tokeny, které vlastní + +```solidity + address(uint160(calldataVal(1, 20))), +``` + +Cílová adresa začíná na bajtu č. 1 (bajt č. 0 je funkce). +Jako adresa je dlouhá 20 bajtů. + +```solidity + calldataVal(21, 2) +``` + +U tohoto konkrétního kontraktu předpokládáme, že maximální počet tokenů, které by kdokoli chtěl převést, se vejde do dvou bajtů (méně než 65 536). + +```solidity + ); + } +``` + +Celkově převod zabere 35 bajtů calldata: + +| Sekce | Délka | Bajty | +| --------------- | ----: | ----: | +| Selektor funkce | 1 | 0 | +| Cílová adresa | 32 | 1-32 | +| Částka | 2 | 33-34 | + +```solidity + } // fallback + +} // contract CalldataInterpreter +``` + +### test.js {#test-js} + +[Tento jednotkový test v JavaScriptu](https://github.com/qbzzt/ethereum.org-20220330-shortABI/blob/master/test/test.js) nám ukazuje, jak tento mechanismus používat (a jak ověřit, že funguje správně). +Budu předpokládat, že rozumíte [chai](https://www.chaijs.com/) a [ethers](https://docs.ethers.io/v5/) a vysvětlím pouze části, které se týkají konkrétně tohoto kontraktu. + +```js +const { expect } = require("chai"); + +describe("CalldataInterpreter", function () { + it("Should let us use tokens", async function () { + const Token = await ethers.getContractFactory("OrisUselessToken") + const token = await Token.deploy() + await token.deployed() + console.log("Token addr:", token.address) + + const Cdi = await ethers.getContractFactory("CalldataInterpreter") + const cdi = await Cdi.deploy(token.address) + await cdi.deployed() + console.log("CalldataInterpreter addr:", cdi.address) + + const signer = await ethers.getSigner() +``` + +Začneme nasazením obou kontraktů. + +```javascript + // Získat tokeny na hraní + const faucetTx = { +``` + +Nemůžeme k vytváření transakcí použít funkce na vysoké úrovni, které bychom normálně používali (například `token.faucet()`), protože nedodržujeme ABI. +Místo toho musíme transakci sestavit sami a poté ji odeslat. + +```javascript + to: cdi.address, + data: "0x01" +``` + +Pro transakci musíme zadat dva parametry: + +1. `to`, cílová adresa. + Toto je kontrakt interpretu calldata. +2. `data`, calldata k odeslání. + V případě volání faucet jsou data tvořena jediným bajtem, `0x01`. + +```javascript + + } + await (await signer.sendTransaction(faucetTx)).wait() +``` + +Voláme [metodu `sendTransaction` podepisujícího](https://docs.ethers.io/v5/api/signer/#Signer-sendTransaction), protože jsme již zadali cíl (`faucetTx.to`) a potřebujeme, aby byla transakce podepsána. + +```javascript +// Zkontrolujte, zda faucet poskytuje tokeny správně +expect(await token.balanceOf(signer.address)).to.equal(1000) +``` + +Zde ověříme zůstatek. +Není třeba šetřit palivo na funkcích `view`, takže je prostě spouštíme normálně. + +```javascript +// Dejte CDI povolenku (schválení nelze provést přes proxy) +const approveTX = await token.approve(cdi.address, 10000) +await approveTX.wait() +expect(await token.allowance(signer.address, cdi.address)).to.equal(10000) +``` + +Dejte interpretu calldata povolenku, aby mohl provádět převody. + +```javascript +// Převod tokenů +const destAddr = "0xf5a6ead936fb47f342bb63e676479bddf26ebe1d" +const transferTx = { + to: cdi.address, + data: "0x02" + destAddr.slice(2, 42) + "0100", +} +``` + +Vytvořte transakci převodu. První bajt je „0x02“, následuje cílová adresa a nakonec částka (0x0100, což je 256 v desítkové soustavě). + +```javascript + await (await signer.sendTransaction(transferTx)).wait() + + // Zkontrolujte, že máme o 256 tokenů méně + expect (await token.balanceOf(signer.address)).to.equal(1000-256) + + // A že je náš cíl dostal + expect (await token.balanceOf(destAddr)).to.equal(256) + }) // it +}) // describe +``` + +## Snížení nákladů, když máte kontrolu nad cílovým kontraktem {#reducing-the-cost-when-you-do-control-the-destination-contract} + +Pokud máte kontrolu nad cílovým kontraktem, můžete vytvořit funkce, které obcházejí kontroly `msg.sender`, protože důvěřují interpretu calldata. +[Příklad, jak to funguje, si můžete prohlédnout zde, ve větvi `control-contract`](https://github.com/qbzzt/ethereum.org-20220330-shortABI/tree/control-contract). + +Pokud by kontrakt reagoval pouze na externí transakce, vystačili bychom si s jedním kontraktem. +To by však narušilo [složitelnost](/developers/docs/smart-contracts/composability/). +Je mnohem lepší mít kontrakt, který reaguje na běžná volání ERC-20, a další kontrakt, který reaguje na transakce s krátkými calldata. + +### Token.sol {#token-sol-2} + +V tomto příkladu můžeme upravit `Token.sol`. +To nám umožňuje mít řadu funkcí, které může volat pouze proxy. +Zde jsou nové části: + +```solidity + // Jediná adresa, která může zadat adresu CalldataInterpreter + address owner; + + // Adresa CalldataInterpreter + address proxy = address(0); +``` + +Kontrakt ERC-20 musí znát identitu autorizovaného proxy. +Tuto proměnnou však nemůžeme nastavit v konstruktoru, protože její hodnotu ještě neznáme. +Tento kontrakt je instanciován jako první, protože proxy očekává adresu tokenu ve svém konstruktoru. + +```solidity + /** + * @dev Volá konstruktor ERC20. + */ + constructor( + ) ERC20("Oris useless token-2", "OUT-2") { + owner = msg.sender; + } +``` + +Adresa tvůrce (nazývaná `owner`) je zde uložena, protože je to jediná adresa, která smí nastavit proxy. + +```solidity + /** + * @dev nastaví adresu pro proxy (CalldataInterpreter). + * Může být zavolána pouze jednou vlastníkem + */ + function setProxy(address _proxy) external { + require(msg.sender == owner, "Can only be called by owner"); + require(proxy == address(0), "Proxy is already set"); + + proxy = _proxy; + } // function setProxy +``` + +Proxy má privilegovaný přístup, protože může obcházet bezpečnostní kontroly. +Abychom se ujistili, že můžeme proxy důvěřovat, necháme tuto funkci volat pouze `vlastníka`, a to pouze jednou. +Jakmile má `proxy` skutečnou hodnotu (nenulovou), nelze tuto hodnotu změnit, takže i kdyby se vlastník rozhodl stát se nepoctivým, nebo by byla odhalena jeho mnemotechnická pomůcka, jsme stále v bezpečí. + +```solidity + /** + * @dev Některé funkce mohou být volány pouze proxy. + */ + modifier onlyProxy { +``` + +Toto je [`modifikátorová` funkce](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm), která upravuje způsob fungování ostatních funkcí. + +```solidity + require(msg.sender == proxy); +``` + +Nejprve ověřte, že nás volal proxy a nikdo jiný. +Pokud ne, `revert`. + +```solidity + _; + } +``` + +Pokud ano, spusťte funkci, kterou upravujeme. + +```solidity + /* Funkce, které umožňují proxy skutečně zastupovat účty */ + + function transferProxy(address from, address to, uint256 amount) + public virtual onlyProxy() returns (bool) + { + _transfer(from, to, amount); + return true; + } + + function approveProxy(address from, address spender, uint256 amount) + public virtual onlyProxy() returns (bool) + { + _approve(from, spender, amount); + return true; + } + + function transferFromProxy( + address spender, + address from, + address to, + uint256 amount + ) public virtual onlyProxy() returns (bool) + { + _spendAllowance(from, spender, amount); + _transfer(from, to, amount); + return true; + } +``` + +Jedná se o tři operace, které obvykle vyžadují, aby zpráva přišla přímo od subjektu, který převádí tokeny nebo schvaluje povolenku. +Zde máme proxy verzi těchto operací, která: + +1. Je upravena `onlyProxy()`, takže je nikdo jiný nesmí ovládat. +2. Získá adresu, která by normálně byla `msg.sender` jako další parametr. + +### CalldataInterpreter.sol {#calldatainterpreter-sol-2} + +Interpret calldata je téměř totožný s výše uvedeným, s výjimkou toho, že funkce proxy dostávají parametr `msg.sender` a není potřeba povolenka pro `transfer`. + +```solidity + // transfer (není potřeba povolenka) + if (_func == 2) { + token.transferProxy( + msg.sender, + address(uint160(calldataVal(1, 20))), + calldataVal(21, 2) + ); + } + + // schválit + if (_func == 3) { + token.approveProxy( + msg.sender, + address(uint160(calldataVal(1, 20))), + calldataVal(21, 2) + ); + } + + // transferFrom + if (_func == 4) { + token.transferFromProxy( + msg.sender, + address(uint160(calldataVal( 1, 20))), + address(uint160(calldataVal(21, 20))), + calldataVal(41, 2) + ); + } +``` + +### Test.js {#test-js-2} + +Mezi předchozím testovacím kódem a tímto je několik změn. + +```js +const Cdi = await ethers.getContractFactory("CalldataInterpreter") +const cdi = await Cdi.deploy(token.address) +await cdi.deployed() +await token.setProxy(cdi.address) +``` + +Musíme kontraktu ERC-20 sdělit, kterému proxy má důvěřovat. + +```js +console.log("CalldataInterpreter addr:", cdi.address) + +// K ověření povolenek potřebujeme dva podepisující +const signers = await ethers.getSigners() +const signer = signers[0] +const poorSigner = signers[1] +``` + +Ke kontrole `approve()` a `transferFrom()` potřebujeme druhého podepisujícího. +Říkáme mu `poorSigner` (chudý podepisující), protože nedostane žádný z našich tokenů (samozřejmě ale musí mít ETH). + +```js +// Převod tokenů +const destAddr = "0xf5a6ead936fb47f342bb63e676479bddf26ebe1d" +const transferTx = { + to: cdi.address, + data: "0x02" + destAddr.slice(2, 42) + "0100", +} +await (await signer.sendTransaction(transferTx)).wait() +``` + +Protože kontrakt ERC-20 důvěřuje proxy (`cdi`), nepotřebujeme povolenku k předávání převodů. + +```js +// schválení a transferFrom +const approveTx = { + to: cdi.address, + data: "0x03" + poorSigner.address.slice(2, 42) + "00FF", +} +await (await signer.sendTransaction(approveTx)).wait() + +const destAddr2 = "0xE1165C689C0c3e9642cA7606F5287e708d846206" + +const transferFromTx = { + to: cdi.address, + data: "0x04" + signer.address.slice(2, 42) + destAddr2.slice(2, 42) + "00FF", +} +await (await poorSigner.sendTransaction(transferFromTx)).wait() + +// Zkontrolujte, zda byla kombinace approve / transferFrom provedena správně +expect(await token.balanceOf(destAddr2)).to.equal(255) +``` + +Otestujte dvě nové funkce. +Všimněte si, že `transferFromTx` vyžaduje dva parametry adresy: dárce povolenky a příjemce. + +## Závěr {#conclusion} + +Jak [Optimism](https://medium.com/ethereum-optimism/the-road-to-sub-dollar-transactions-part-2-compression-edition-6bb2890e3e92), tak [Arbitrum](https://developer.offchainlabs.com/docs/special_features) hledají způsoby, jak zmenšit velikost calldata zapisovaných na L1, a tím i náklady na transakce. +Jako poskytovatelé infrastruktury, kteří hledají obecná řešení, jsou však naše schopnosti omezené. +Jako vývojář dapp máte znalosti specifické pro danou aplikaci, což vám umožňuje optimalizovat calldata mnohem lépe, než bychom mohli v obecném řešení. +Doufejme, že vám tento článek pomůže najít ideální řešení pro vaše potřeby. + +[Více z mé práce najdete zde](https://cryptodocguy.pro/). +