From b3cff2c811c2994f1dfa8cc16df7ebdf1403c576 Mon Sep 17 00:00:00 2001 From: Joshua <62268199+minimalsm@users.noreply.github.com> Date: Sat, 14 Feb 2026 00:08:10 +0000 Subject: [PATCH] i18n(pl): translation import part 09 of 13 (23 files) --- .../index.md | 208 ++ .../tutorials/server-components/index.md | 295 +++ .../index.md | 92 + .../developers/tutorials/short-abi/index.md | 585 +++++ .../index.md | 77 +- .../tutorials/stealth-addr/index.md | 443 ++++ .../index.md | 142 +- .../token-integration-checklist/index.md | 92 +- .../index.md | 174 +- .../index.md | 79 +- .../uniswap-v2-annotated-code/index.md | 1970 +++++++++++++++++ .../tutorials/using-websockets/index.md | 80 +- .../index.md | 141 +- .../index.md | 204 ++ .../index.md | 199 ++ .../tutorials/yellow-paper-evm/index.md | 278 +++ public/content/translations/pl/eips/index.md | 37 +- .../pl/energy-consumption/index.md | 59 +- .../translations/pl/eth/supply/index.md | 80 + .../translations/pl/ethereum-forks/index.md | 514 ++++- .../translations/pl/foundation/index.md | 37 +- .../content/translations/pl/gaming/index.md | 70 + .../content/translations/pl/glossary/index.md | 755 ++----- 23 files changed, 5473 insertions(+), 1138 deletions(-) create mode 100644 public/content/translations/pl/developers/tutorials/sending-transactions-using-web3-and-alchemy/index.md create mode 100644 public/content/translations/pl/developers/tutorials/server-components/index.md create mode 100644 public/content/translations/pl/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/index.md create mode 100644 public/content/translations/pl/developers/tutorials/short-abi/index.md create mode 100644 public/content/translations/pl/developers/tutorials/stealth-addr/index.md create mode 100644 public/content/translations/pl/developers/tutorials/uniswap-v2-annotated-code/index.md create mode 100644 public/content/translations/pl/developers/tutorials/waffle-say-hello-world-with-hardhat-and-ethers/index.md create mode 100644 public/content/translations/pl/developers/tutorials/waffle-test-simple-smart-contract/index.md create mode 100644 public/content/translations/pl/developers/tutorials/yellow-paper-evm/index.md create mode 100644 public/content/translations/pl/eth/supply/index.md create mode 100644 public/content/translations/pl/gaming/index.md diff --git a/public/content/translations/pl/developers/tutorials/sending-transactions-using-web3-and-alchemy/index.md b/public/content/translations/pl/developers/tutorials/sending-transactions-using-web3-and-alchemy/index.md new file mode 100644 index 00000000000..937eca077e6 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/sending-transactions-using-web3-and-alchemy/index.md @@ -0,0 +1,208 @@ +--- +title: "Wysyłanie transakcji za pomocą Web3" +description: "To jest przyjazny dla początkujących przewodnik po wysyłaniu transakcji Ethereum za pomocą Web3. Istnieją trzy główne kroki, aby wysłać transakcję do blockchaina Ethereum: tworzenie, podpisywanie i rozgłaszanie. Omówimy wszystkie trzy." +author: "Elan Halpern" +tags: [ "transakcje", "web3.js", "alchemy" ] +skill: beginner +lang: pl +published: 2020-11-04 +source: Alchemy docs +sourceUrl: https://www.alchemy.com/docs/how-to-send-transactions-on-ethereum +--- + +To jest przyjazny dla początkujących przewodnik po wysyłaniu transakcji Ethereum za pomocą Web3. Istnieją trzy główne kroki, aby wysłać transakcję do blockchaina Ethereum: tworzenie, podpisywanie i rozgłaszanie. Omówimy wszystkie trzy, mając nadzieję, że odpowiemy na wszelkie pytania, jakie możesz mieć! W tym samouczku będziemy używać [Alchemy](https://www.alchemy.com/), aby wysyłać nasze transakcje do łańcucha Ethereum. Możesz [utworzyć darmowe konto Alchemy tutaj](https://auth.alchemyapi.io/signup). + +**UWAGA:** Ten przewodnik dotyczy podpisywania transakcji w _backendzie_ aplikacji. Jeśli chcesz zintegrować podpisywanie transakcji we frontendzie, sprawdź integrację [Web3 z dostawcą przeglądarki](https://docs.alchemy.com/reference/api-overview#with-a-browser-provider). + +## Podstawy {#the-basics} + +Podobnie jak większość deweloperów blockchain na początku swojej drogi, być może szukałeś informacji o tym, jak wysłać transakcję (coś, co powinno być całkiem proste) i natknąłeś się na mnóstwo przewodników, z których każdy mówił co innego, co sprawiło, że czułeś się nieco przytłoczony i zdezorientowany. Jeśli jesteś w takiej sytuacji, nie martw się; wszyscy kiedyś byliśmy! Zanim więc zaczniemy, wyjaśnijmy sobie kilka kwestii: + +### 1. Alchemy nie przechowuje Twoich kluczy prywatnych {#alchemy-does-not-store-your-private-keys} + +- Oznacza to, że Alchemy nie może podpisywać i wysyłać transakcji w Twoim imieniu. Dzieje się tak ze względów bezpieczeństwa. Alchemy nigdy nie poprosi Cię o udostępnienie Twojego klucza prywatnego i nigdy nie powinieneś go udostępniać hostowanemu węzłowi (ani nikomu innemu). +- Możesz odczytywać dane z blockchaina za pomocą podstawowego API Alchemy, ale aby do niego zapisywać, musisz użyć czegoś innego do podpisania swoich transakcji przed wysłaniem ich przez Alchemy (to samo dotyczy każdej innej [usługi węzłów](/developers/docs/nodes-and-clients/nodes-as-a-service/)). + +### 2. Czym jest „signer”? {#what-is-a-signer} + +- Signerzy podpisują dla Ciebie transakcje, używając Twojego klucza prywatnego. W tym samouczku do podpisania naszej transakcji użyjemy [Alchemy web3](https://docs.alchemyapi.io/alchemy/documentation/alchemy-web3), ale można również użyć dowolnej innej biblioteki web3. +- We frontendzie dobrym przykładem signera jest [MetaMask](https://metamask.io/), który podpisuje i wysyła transakcje w Twoim imieniu. + +### 3. Dlaczego muszę podpisywać swoje transakcje? {#why-do-i-need-to-sign-my-transactions} + +- Każdy użytkownik, który chce wysłać transakcję w sieci Ethereum, musi ją podpisać (używając swojego klucza prywatnego), aby zweryfikować, że pochodzenie transakcji jest zgodne z deklarowanym. +- Niezwykle ważne jest, aby chronić ten klucz prywatny, ponieważ posiadanie do niego dostępu daje pełną kontrolę nad kontem Ethereum, pozwalając Tobie (lub każdemu, kto ma dostęp) na wykonywanie transakcji w Twoim imieniu. + +### 4. Jak chronić mój klucz prywatny? {#how-do-i-protect-my-private-key} + +- Istnieje wiele sposobów na ochronę klucza prywatnego i używanie go do wysyłania transakcji. W tym samouczku będziemy używać pliku `.env`. Można jednak również skorzystać z osobnego dostawcy, który przechowuje klucze prywatne, użyć pliku keystore lub innych opcji. + +### 5. Jaka jest różnica między `eth_sendTransaction` a `eth_sendRawTransaction`? {#difference-between-send-and-send-raw} + +`eth_sendTransaction` i `eth_sendRawTransaction` to funkcje API Ethereum, które rozgłaszają transakcję w sieci Ethereum, aby została ona dodana do przyszłego bloku. Różnią się one sposobem obsługi podpisywania transakcji. + +- [`eth_sendTransaction`](https://docs.web3js.org/api/web3-eth/function/sendTransaction) służy do wysyłania _niepodpisanych_ transakcji, co oznacza, że węzeł, do którego je wysyłasz, musi zarządzać Twoim kluczem prywatnym, aby mógł podpisać transakcję przed jej rozgłoszeniem w łańcuchu. Ponieważ Alchemy nie przechowuje kluczy prywatnych użytkowników, nie obsługuje tej metody. +- [`eth_sendRawTransaction`](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction) służy do rozgłaszania transakcji, które zostały już podpisane. Oznacza to, że najpierw musisz użyć [`signTransaction(tx, private_key)`](https://docs.web3js.org/api/web3-eth-accounts/function/signTransaction), a następnie przekazać wynik do `eth_sendRawTransaction`. + +Podczas korzystania z web3 dostęp do `eth_sendRawTransaction` uzyskuje się poprzez wywołanie funkcji [web3.eth.sendSignedTransaction](https://docs.web3js.org/api/web3-eth/function/sendSignedTransaction). + +Tego właśnie będziemy używać w tym samouczku. + +### 6. Czym jest biblioteka web3? {#what-is-the-web3-library} + +- Web3.js jest biblioteką opakowującą standardowe wywołania JSON-RPC, która jest dość często używana w deweloperce Ethereum. +- Istnieje wiele bibliotek web3 dla różnych języków. W tym samouczku będziemy używać [Alchemy Web3](https://docs.alchemy.com/reference/api-overview), który jest napisany w języku JavaScript. Możesz sprawdzić inne opcje [tutaj](https://docs.alchemyapi.io/guides/getting-started#other-web3-libraries), takie jak [ethers.js](https://docs.ethers.org/v5/). + +OK, teraz, gdy odpowiedzieliśmy na kilka pytań, przejdźmy do samouczka. W każdej chwili możesz zadawać pytania na [discordzie](https://discord.gg/gWuC7zB) Alchemy! + +### 7. Jak wysyłać bezpieczne, zoptymalizowane pod kątem opłat za gaz i prywatne transakcje? {#how-to-send-secure-gas-optimized-and-private-transactions} + +- [Alchemy ma zestaw interfejsów API Transact](https://docs.alchemy.com/reference/transact-api-quickstart). Możesz ich używać do wysyłania wzmocnionych transakcji, symulowania transakcji przed ich wykonaniem, wysyłania transakcji prywatnych oraz wysyłania transakcji zoptymalizowanych pod względem opłat za gaz. +- Możesz również użyć [API Notify](https://docs.alchemy.com/docs/alchemy-notify), aby otrzymywać powiadomienia, gdy Twoja transakcja zostanie pobrana z mempoola i dodana do łańcucha. + +**UWAGA:** Ten przewodnik wymaga posiadania konta Alchemy, adresu Ethereum lub portfela MetaMask oraz zainstalowanych NodeJs i npm. Jeśli nie, wykonaj następujące kroki: + +1. [Utwórz darmowe konto Alchemy](https://auth.alchemyapi.io/signup) +2. [Utwórz konto MetaMask](https://metamask.io/) (lub uzyskaj adres Ethereum) +3. [Wykonaj te kroki, aby zainstalować NodeJs i NPM](https://docs.alchemy.com/alchemy/guides/alchemy-for-macs) + +## Kroki wysyłania transakcji {#steps-to-sending-your-transaction} + +### 1. Utwórz aplikację Alchemy w sieci testowej Sepolia {#create-an-alchemy-app-on-the-sepolia-testnet} + +Przejdź do swojego [Panelu Alchemy](https://dashboard.alchemyapi.io/) i utwórz nową aplikację, wybierając Sepolia (lub dowolną inną sieć testową) jako swoją sieć. + +### 2. Poproś o ETH z kranu Sepolia {#request-eth-from-sepolia-faucet} + +Postępuj zgodnie z instrukcjami na stronie [kranu Sepolia od Alchemy](https://www.sepoliafaucet.com/), aby otrzymać ETH. Upewnij się, że podajesz swój adres Ethereum **Sepolia** (z MetaMask), a nie z innej sieci. Po wykonaniu instrukcji sprawdź, czy otrzymałeś ETH w swoim portfelu. + +### 3. Utwórz nowy katalog projektu i przejdź do niego za pomocą `cd` {#create-a-new-project-direction} + +Utwórz nowy katalog projektu z wiersza poleceń (terminal na komputerach Mac) i przejdź do niego: + +``` +mkdir sendtx-example +cd sendtx-example +``` + +### 4. Zainstaluj Alchemy Web3 (lub dowolną bibliotekę web3) {#install-alchemy-web3} + +Uruchom następujące polecenie w katalogu projektu, aby zainstalować [Alchemy Web3](https://docs.alchemy.com/reference/api-overview): + +Uwaga, jeśli chcesz użyć biblioteki ethers.js, [postępuj zgodnie z instrukcjami podanymi tutaj](https://docs.alchemy.com/docs/how-to-send-transactions-on-ethereum). + +``` +npm install @alch/alchemy-web3 +``` + +### 5. Zainstaluj dotenv {#install-dotenv} + +Użyjemy pliku `.env` do bezpiecznego przechowywania naszego klucza API i klucza prywatnego. + +``` +npm install dotenv --save +``` + +### 6. Utwórz plik `.env` {#create-the-dotenv-file} + +Utwórz plik `.env` w katalogu projektu i dodaj następującą treść (zastępując „`your-api-url`” i „`your-private-key`”): + +- Aby znaleźć adres URL interfejsu API Alchemy, przejdź do strony szczegółów aplikacji, którą właśnie utworzyłeś w panelu, kliknij „View Key” w prawym górnym rogu i pobierz adres URL HTTP. +- Aby znaleźć swój klucz prywatny za pomocą MetaMask, zapoznaj się z tym [przewodnikiem](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" +``` + + + + +Nie commituj pliku .env! Upewnij się, że nigdy nie udostępniasz ani nie ujawniasz nikomu swojego pliku .env, ponieważ w ten sposób narażasz swoje poufne dane. Jeśli używasz kontroli wersji, dodaj swój plik .env do pliku gitignore. + + + + +### 7. Utwórz plik `sendTx.js` {#create-sendtx-js} + +Świetnie, teraz, gdy nasze poufne dane są chronione w pliku `.env`, zacznijmy kodować. W naszym przykładzie wysyłania transakcji będziemy odsyłać ETH z powrotem do kranu Sepolia. + +Utwórz plik `sendTx.js`, w którym skonfigurujemy i wyślemy naszą przykładową transakcję, i dodaj do niego następujące linie kodu: + +``` +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: zastąp ten adres swoim własnym adresem publicznym + + const nonce = await web3.eth.getTransactionCount(myAddress, 'latest'); // nonce zaczyna liczyć od 0 + + const transaction = { + 'to': '0x31B98D14007bDEe637298086988A0bBd31184523', // adres kranu, na który ma być zwrócony eth + 'value': 1000000000000000000, // 1 ETH + 'gas': 30000, + 'nonce': nonce, + // opcjonalne pole danych do wysłania wiadomości lub wykonania smart kontraktu + }; + + const signedTx = await web3.eth.accounts.signTransaction(transaction, PRIVATE_KEY); + + web3.eth.sendSignedTransaction(signedTx.rawTransaction, function(error, hash) { + if (!error) { + console.log("🎉 Hasz Twojej transakcji to: ", hash, "\n Sprawdź Mempool Alchemy, aby zobaczyć status swojej transakcji!"); + } else { + console.log("❗Coś poszło nie tak podczas przesyłania transakcji:", error) + } + }); +} + +main(); +``` + +Pamiętaj, aby zastąpić adres w **wierszu 6** swoim własnym adresem publicznym. + +Zanim przejdziemy do uruchomienia tego kodu, omówmy kilka jego komponentów. + +- `nonce`: Specyfikacja nonce służy do śledzenia liczby transakcji wysłanych z Twojego adresu. Potrzebujemy tego ze względów bezpieczeństwa i aby zapobiegać [atakom typu replay](https://docs.alchemyapi.io/resources/blockchain-glossary#account-nonce). Aby uzyskać liczbę transakcji wysłanych z Twojego adresu, używamy [getTransactionCount](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc#eth_gettransactioncount). +- `transaction`: Obiekt transakcji ma kilka aspektów, które musimy określić. + - `to`: Jest to adres, na który chcemy wysłać ETH. W tym przypadku odsyłamy ETH z powrotem do [kranu Sepolia](https://sepoliafaucet.com/), z którego pierwotnie je pozyskaliśmy. + - `value`: Jest to kwota, którą chcemy wysłać, określona w Wei, gdzie 10^18 Wei = 1 ETH. + - `gas`: Istnieje wiele sposobów na określenie odpowiedniej ilości gazu do uwzględnienia w transakcji. Alchemy ma nawet [webhook ceny gazu](https://docs.alchemyapi.io/guides/alchemy-notify#address-activity-1), który powiadamia, gdy cena gazu spadnie poniżej określonego progu. W przypadku transakcji w sieci głównej Mainnet dobrą praktyką jest sprawdzanie estymatora gazu, takiego jak [ETH Gas Station](https://ethgasstation.info/), w celu określenia właściwej ilości gazu do uwzględnienia. 21000 to minimalna ilość gazu, jaką zużyje operacja na Ethereum, więc aby zapewnić, że nasza transakcja zostanie wykonana, wpisujemy tutaj 30000. + - `nonce`: patrz definicja nonce powyżej. Nonce zaczyna liczyć od zera. + - [OPCJONALNE] dane: Służy do wysyłania dodatkowych informacji wraz z transferem lub wywoływania smart kontraktu. Nie jest wymagane w przypadku transferów salda. Sprawdź uwagę poniżej. +- `signedTx`: Aby podpisać nasz obiekt transakcji, użyjemy metody `signTransaction` z naszym `PRIVATE_KEY`. +- `sendSignedTransaction`: Gdy mamy już podpisaną transakcję, możemy ją wysłać, aby została uwzględniona w kolejnym bloku, używając `sendSignedTransaction`. + +**Uwaga na temat danych** +Istnieją dwa główne typy transakcji, które można wysyłać w Ethereum. + +- Transfer salda: Wyślij ETH z jednego adresu na drugi. Pole danych nie jest wymagane, jednak jeśli chcesz wysłać dodatkowe informacje wraz z transakcją, możesz je umieścić w tym polu w formacie HEX. + - Załóżmy na przykład, że chcemy zapisać hasz dokumentu IPFS w łańcuchu Ethereum, aby nadać mu niezmienny znacznik czasu. Nasze pole danych powinno wtedy wyglądać następująco: `web3.utils.toHex(‘hasz IPFS‘)`. I teraz każdy może odpytać łańcuch i zobaczyć, kiedy ten dokument został dodany. +- Transakcja smart kontraktu: wykonanie kodu smart kontraktu w łańcuchu. W tym przypadku pole danych powinno zawierać inteligentną funkcję, którą chcesz wykonać, wraz z wszelkimi parametrami. + - Praktyczny przykład znajdziesz w kroku 8 tego [samouczka „Witaj, świecie”](https://docs.alchemyapi.io/alchemy/tutorials/hello-world-smart-contract#step-8-create-the-transaction). + +### 8. Uruchom kod za pomocą `node sendTx.js` {#run-the-code-using-node-sendtx-js} + +Wróć do terminala lub wiersza poleceń i uruchom: + +``` +node sendTx.js +``` + +### 9. Zobacz swoją transakcję w Mempoolu {#see-your-transaction-in-the-mempool} + +Otwórz [stronę Mempool](https://dashboard.alchemyapi.io/mempool) w panelu Alchemy i filtruj według utworzonej aplikacji, aby znaleźć swoją transakcję. Tutaj możemy obserwować przejście naszej transakcji ze stanu oczekującego do stanu wydobycia (jeśli się powiedzie) lub do stanu porzucenia, jeśli się nie powiedzie. Upewnij się, że opcja jest ustawiona na „Wszystkie”, aby przechwycić transakcje „wydobyte”, „oczekujące” i „porzucone”. Możesz również wyszukać swoją transakcję, szukając transakcji wysłanych na adres `0x31b98d14007bdee637298086988a0bbd31184523`. + +Aby wyświetlić szczegóły transakcji po jej znalezieniu, wybierz jej hasz, co powinno przenieść Cię do widoku, który wygląda następująco: + +![Zrzut ekranu obserwatora Mempool](./mempool.png) + +Stamtąd możesz wyświetlić swoją transakcję w Etherscan, klikając ikonę zaznaczoną na czerwono! + +**Huraaa! Właśnie wysłałeś swoją pierwszą transakcję Ethereum za pomocą Alchemy 🎉** + +_Aby uzyskać opinie i sugestie dotyczące tego przewodnika, wyślij wiadomość do Elan na [Discordzie](https://discord.gg/A39JVCM) Alchemy!_ + +_Oryginalnie opublikowano na stronie [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/pl/developers/tutorials/server-components/index.md b/public/content/translations/pl/developers/tutorials/server-components/index.md new file mode 100644 index 00000000000..04495a4d439 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/server-components/index.md @@ -0,0 +1,295 @@ +--- +title: "Komponenty serwera i agenty dla aplikacji web3" +description: "Po przeczytaniu tego samouczka będziesz w stanie pisać serwery TypeScript, które nasłuchują zdarzeń w łańcuchu bloków i odpowiednio na nie reagują własnymi transakcjami. Umożliwi to pisanie scentralizowanych aplikacji (ponieważ serwer jest pojedynczym punktem awarii), które mogą wchodzić w interakcje z jednostkami web3. Te same techniki mogą być również użyte do napisania agenta, który reaguje na zdarzenia onchain bez udziału człowieka." + +author: Ori Pomerantz +lang: pl +tags: [ "agent", "serwer", "offchain" ] +skill: beginner +published: 2024-07-15 +--- + +## Wprowadzenie {#introduction} + +W większości przypadków zdecentralizowana aplikacja wykorzystuje serwer do dystrybucji oprogramowania, ale cała faktyczna interakcja zachodzi między klientem (zazwyczaj przeglądarką internetową) a łańcuchem bloków. + +![Normalna interakcja między serwerem internetowym, klientem a łańcuchem bloków](./fig-1.svg) + +Istnieją jednak przypadki, w których aplikacja odniosłaby korzyść z posiadania niezależnie działającego komponentu serwera. Taki serwer byłby w stanie reagować na zdarzenia i żądania pochodzące z innych źródeł, takich jak API, emitując transakcje. + +![Interakcja z dodanym serwerem](./fig-2.svg) + +Istnieje kilka możliwych zadań, które taki serwer mógłby spełniać. + +- Posiadacz tajnego stanu. W grach często przydatne jest, aby nie wszystkie informacje znane grze były dostępne dla graczy. Jednakże _w łańcuchu bloków nie ma żadnych tajemnic_, każdą informację, która się w nim znajduje, każdy może łatwo poznać. Dlatego też, jeśli część stanu gry ma pozostać tajna, musi być przechowywana gdzie indziej (a ewentualne skutki tego stanu zweryfikowane za pomocą [dowodów o wiedzy zerowej](/zero-knowledge-proofs)). + +- Scentralizowana wyrocznia. Jeśli stawki są wystarczająco niskie, zewnętrzny serwer, który odczytuje pewne informacje online, a następnie publikuje je w łańcuchu, może być wystarczająco dobry, aby użyć go jako [wyroczni](/developers/docs/oracles/). + +- Agent. W łańcuchu bloków nic się nie dzieje bez transakcji, która to aktywuje. Serwer może działać w imieniu użytkownika, wykonując działania takie jak [arbitraż](/developers/docs/mev/#mev-examples-dex-arbitrage), gdy nadarzy się okazja. + +## Przykładowy program {#sample-program} + +Przykładowy serwer można zobaczyć [na GitHubie](https://github.com/qbzzt/20240715-server-component). Ten serwer nasłuchuje zdarzeń pochodzących z [tego kontraktu](https://eth-holesky.blockscout.com/address/0xB8f6460Dc30c44401Be26B0d6eD250873d8a50A6?tab=contract_code), zmodyfikowanej wersji Greeter od Hardhat. Gdy powitanie zostanie zmienione, zmienia je z powrotem. + +Aby go uruchomić: + +1. Sklonuj repozytorium. + + ```sh copy + git clone https://github.com/qbzzt/20240715-server-component.git + cd 20240715-server-component + ``` + +2. Zainstaluj niezbędne pakiety. Jeśli jeszcze go nie masz, [najpierw zainstaluj Node](https://nodejs.org/en/download/package-manager). + + ```sh copy + npm install + ``` + +3. Edytuj plik `.env`, aby określić klucz prywatny konta, które posiada ETH w sieci testowej Holesky. Jeśli nie masz ETH na Holesky, możesz [użyć tego kraniku](https://holesky-faucet.pk910.de/). + + ```sh filename=".env" copy + PRIVATE_KEY=0x + ``` + +4. Uruchom serwer. + + ```sh copy + npm start + ``` + +5. Przejdź do [eksploratora bloków](https://eth-holesky.blockscout.com/address/0xB8f6460Dc30c44401Be26B0d6eD250873d8a50A6?tab=write_contract) i używając innego adresu niż ten, który posiada klucz prywatny, zmodyfikuj powitanie. Zobacz, że powitanie jest automatycznie przywracane. + +### Jak to działa? {#how-it-works} + +Najłatwiejszym sposobem na zrozumienie, jak napisać komponent serwera, jest przeanalizowanie przykładu linijka po linijce. + +#### `src/app.ts` {#src-app-ts} + +Zdecydowana większość programu znajduje się w [`src/app.ts`](https://github.com/qbzzt/20240715-server-component/blob/main/src/app.ts). + +##### Tworzenie wymaganych obiektów + +```typescript +import { + createPublicClient, + createWalletClient, + getContract, + http, + Address, +} from "viem" +``` + +Są to potrzebne nam encje [Viem](https://viem.sh/), funkcje i [typ `Address`](https://viem.sh/docs/glossary/types#address). Ten serwer jest napisany w języku [TypeScript](https://www.typescriptlang.org/), który jest rozszerzeniem języka JavaScript, czyniącym go [silnie typowanym](https://en.wikipedia.org/wiki/Strong_and_weak_typing). + +```typescript +import { privateKeyToAccount } from "viem/accounts" +``` + +[Ta funkcja](https://viem.sh/docs/accounts/privateKey) pozwala nam wygenerować informacje o portfelu, w tym adres, odpowiadające kluczowi prywatnemu. + +```typescript +import { holesky } from "viem/chains" +``` + +Aby używać blockchaina w Viem, musisz zaimportować jego definicję. W tym przypadku chcemy połączyć się z testowym łańcuchem bloków [Holesky](https://github.com/eth-clients/holesky). + +```typescript +// W ten sposób dodajemy definicje z .env do process.env. +import * as dotenv from "dotenv" +dotenv.config() +``` + +W ten sposób wczytujemy plik `.env` do środowiska. Potrzebujemy go do klucza prywatnego (zobacz później). + +```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 +``` + +Aby użyć kontraktu, potrzebujemy jego adresu i [ABI](/glossary/#abi). Podajemy oba tutaj. + +W języku JavaScript (a więc i w TypeScript) nie można przypisać nowej wartości do stałej, ale _można_ zmodyfikować obiekt, który jest w niej przechowywany. Używając sufiksu `as const`, informujemy TypeScript, że sama lista jest stała i nie może być zmieniana. + +```typescript +const publicClient = createPublicClient({ + chain: holesky, + transport: http(), +}) +``` + +Utwórz [klienta publicznego](https://viem.sh/docs/clients/public.html) Viem. Klienci publiczni nie mają dołączonego klucza prywatnego, a zatem nie mogą wysyłać transakcji. Mogą wywoływać [`funkcje widoku`](https://www.tutorialspoint.com/solidity/solidity_view_functions.htm), odczytywać salda kont itp. + +```typescript +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) +``` + +Zmienne środowiskowe są dostępne w [`process.env`](https://www.totaltypescript.com/how-to-strongly-type-process-env). Jednak TypeScript jest silnie typowany. Zmienna środowiskowa może być dowolnym ciągiem znaków lub być pusta, więc typem zmiennej środowiskowej jest `string | undefined`. Jednak klucz jest zdefiniowany w Viem jako `0x${string}` (`0x` po którym następuje ciąg znaków). W tym miejscu informujemy TypeScript, że zmienna środowiskowa `PRIVATE_KEY` będzie tego typu. Jeśli tak nie będzie, otrzymamy błąd wykonania. + +Funkcja [`privateKeyToAccount`](https://viem.sh/docs/accounts/privateKey) następnie używa tego klucza prywatnego do utworzenia pełnego obiektu konta. + +```typescript +const walletClient = createWalletClient({ + account, + chain: holesky, + transport: http(), +}) +``` + +Następnie używamy obiektu konta do utworzenia [klienta portfela](https://viem.sh/docs/clients/wallet). Ten klient ma klucz prywatny i adres, więc może być używany do wysyłania transakcji. + +```typescript +const greeter = getContract({ + address: greeterAddress, + abi: greeterABI, + client: { public: publicClient, wallet: walletClient }, +}) +``` + +Teraz, gdy mamy już wszystkie wymagania wstępne, możemy wreszcie utworzyć [instancję kontraktu](https://viem.sh/docs/contract/getContract). Będziemy używać tej instancji kontraktu do komunikacji z kontraktem onchain. + +##### Odczytywanie z łańcucha bloków + +```typescript +console.log(`Current greeting:`, await greeter.read.greet()) +``` + +Funkcje kontraktu, które są tylko do odczytu ([`view`](https://www.tutorialspoint.com/solidity/solidity_view_functions.htm) i [`pure`](https://www.tutorialspoint.com/solidity/solidity_pure_functions.htm)), są dostępne w `read`. W tym przypadku używamy jej do uzyskania dostępu do funkcji [`greet`](https://eth-holesky.blockscout.com/address/0xB8f6460Dc30c44401Be26B0d6eD250873d8a50A6?tab=read_contract#cfae3217), która zwraca powitanie. + +JavaScript jest jednowątkowy, więc gdy uruchamiamy długo działający proces, musimy [określić, że robimy to asynchronicznie](https://eloquentjavascript.net/11_async.html#h-XvLsfAhtsE). Wywołanie łańcucha bloków, nawet w przypadku operacji tylko do odczytu, wymaga komunikacji w obie strony między komputerem a węzłem łańcucha bloków. Dlatego w tym miejscu określamy, że kod musi `await` (oczekiwać) na wynik. + +Jeśli interesuje Cię, jak to działa, możesz [przeczytać o tym tutaj](https://www.w3schools.com/js/js_promise.asp), ale w praktyce wystarczy wiedzieć, że należy `await` (oczekiwać) na wyniki, jeśli rozpoczynasz operację, która trwa długo, a każda funkcja, która to robi, musi być zadeklarowana jako `async`. + +##### Emitowanie transakcji + +```typescript +const setGreeting = async (greeting: string): Promise => { +``` + +Jest to funkcja, którą wywołujesz, aby wysłać transakcję zmieniającą powitanie. Ponieważ jest to długa operacja, funkcja jest zadeklarowana jako `async`. Ze względu na wewnętrzną implementację, każda funkcja `async` musi zwracać obiekt `Promise`. W tym przypadku `Promise` oznacza, że nie określamy, co dokładnie zostanie zwrócone w `Promise`. + +```typescript +const txHash = await greeter.write.setGreeting([greeting]) +``` + +Pole `write` instancji kontraktu zawiera wszystkie funkcje, które zapisują do stanu łańcucha bloków (te, które wymagają wysłania transakcji), takie jak [`setGreeting`](https://eth-holesky.blockscout.com/address/0xB8f6460Dc30c44401Be26B0d6eD250873d8a50A6?tab=write_contract#a4136862). Parametry, jeśli istnieją, są podawane w postaci listy, a funkcja zwraca hasz transakcji. + +```typescript + console.log(`Pracuję nad poprawką, zobacz https://eth-holesky.blockscout.com/tx/${txHash}`) + + return txHash +} +``` + +Zgłoś hasz transakcji (jako część adresu URL do eksploratora bloków, aby go wyświetlić) i zwróć go. + +##### Reagowanie na zdarzenia + +```typescript +greeter.watchEvent.SetGreeting({ +``` + +[Funkcja `watchEvent`](https://viem.sh/docs/actions/public/watchEvent) pozwala określić, że funkcja ma być uruchamiana po wyemitowaniu zdarzenia. Jeśli interesuje Cię tylko jeden typ zdarzenia (w tym przypadku `SetGreeting`), możesz użyć tej składni, aby ograniczyć się do tego typu zdarzenia. + +```typescript + onLogs: logs => { +``` + +Funkcja `onLogs` jest wywoływana, gdy pojawiają się wpisy w dzienniku. W Ethereum „log” i „zdarzenie” są zwykle używane zamiennie. + +```typescript +console.log( + `Adres ${logs[0].args.sender} zmienił powitanie na ${logs[0].args.greeting}` +) +``` + +Może być wiele zdarzeń, ale dla uproszczenia interesuje nas tylko pierwsze z nich. `logs[0].args` to argumenty zdarzenia, w tym przypadku `sender` i `greeting`. + +```typescript + if (logs[0].args.sender != account.address) + setGreeting(`${account.address} nalega, aby było to Hello!`) + } +}) +``` + +Jeśli nadawcą _nie jest_ ten serwer, użyj `setGreeting`, aby zmienić powitanie. + +#### `package.json` {#package-json} + +[Ten plik](https://github.com/qbzzt/20240715-server-component/blob/main/package.json) kontroluje konfigurację [Node.js](https://nodejs.org/en). W tym artykule wyjaśniono tylko ważne definicje. + +```json +{ + "main": "dist/index.js", +``` + +Ta definicja określa, który plik JavaScript ma być uruchomiony. + +```json + "scripts": { + "start": "tsc && node dist/app.js", + }, +``` + +Skrypty to różne działania aplikacji. W tym przypadku jedynym, jaki mamy, jest `start`, który kompiluje, a następnie uruchamia serwer. Polecenie `tsc` jest częścią pakietu `typescript` i kompiluje TypeScript do JavaScript. Jeśli chcesz uruchomić go ręcznie, znajduje się on w `node_modules/.bin`. Drugie polecenie uruchamia serwer. + +```json + "type": "module", +``` + +Istnieje wiele typów aplikacji węzła JavaScript. Typ `module` pozwala nam na użycie `await` w kodzie najwyższego poziomu, co jest ważne, gdy wykonujesz wolne (i asynchroniczne) operacje. + +```json + "devDependencies": { + "@types/node": "^20.14.2", + "typescript": "^5.4.5" + }, +``` + +Są to pakiety, które są wymagane tylko do programowania. W tym miejscu potrzebujemy `typescript`, a ponieważ używamy go z Node.js, otrzymujemy również typy dla zmiennych i obiektów węzła, takich jak `process`. [Notacja `^`](https://github.com/npm/node-semver?tab=readme-ov-file#caret-ranges-123-025-004) oznacza tę wersję lub wyższą wersję, która nie zawiera przełomowych zmian. Więcej informacji na temat znaczenia numerów wersji można znaleźć [tutaj](https://semver.org). + +```json + "dependencies": { + "dotenv": "^16.4.5", + "viem": "2.14.1" + } +} +``` + +Są to pakiety wymagane w czasie wykonywania, podczas uruchamiania `dist/app.js`. + +## Wnioski {#conclusion} + +Scentralizowany serwer, który tu stworzyliśmy, wykonuje swoje zadanie, którym jest działanie jako agent dla użytkownika. Każdy, kto chce, aby dapka nadal funkcjonowała i jest gotów wydać gaz, może uruchomić nową instancję serwera z własnym adresem. + +Działa to jednak tylko wtedy, gdy działania scentralizowanego serwera można łatwo zweryfikować. Jeśli scentralizowany serwer ma jakiekolwiek informacje o tajnym stanie lub wykonuje trudne obliczenia, jest to scentralizowany podmiot, któremu trzeba zaufać, aby korzystać z aplikacji, a tego właśnie starają się unikać łańcuchy bloków. W przyszłym artykule planuję pokazać, jak używać [dowodów o wiedzy zerowej](/zero-knowledge-proofs), aby obejść ten problem. + +[Zobacz więcej mojej pracy tutaj](https://cryptodocguy.pro/). diff --git a/public/content/translations/pl/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/index.md b/public/content/translations/pl/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/index.md new file mode 100644 index 00000000000..59f07059bd1 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/index.md @@ -0,0 +1,92 @@ +--- +title: "Skonfiguruj web3.js do używania blockchainu Ethereum w JavaScript" +description: "Dowiedz się, jak skonfigurować bibliotekę web3.js do interakcji z blockchainem Ethereum z aplikacji JavaScript." +author: "jdourlens" +tags: [ "web3.js", "JavaScript" ] +skill: beginner +lang: pl +published: 2020-04-11 +source: EthereumDev +sourceUrl: https://ethereumdev.io/setup-web3js-to-use-the-ethereum-blockchain-in-javascript/ +address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +--- + +W tym samouczku zobaczymy, jak zacząć z [web3.js](https://web3js.readthedocs.io/), aby wejść w interakcję z blockchainem Ethereum. Web3.js może być używany zarówno we frontendach, jak i backendach do odczytywania danych z blockchaina, dokonywania transakcji, a nawet wdrażania smart kontraktów. + +Pierwszym krokiem jest dołączenie web3.js do Twojego projektu. Aby użyć go na stronie internetowej, możesz zaimportować bibliotekę bezpośrednio za pomocą CDN, takiego jak JSDeliver. + +```html + +``` + +Jeśli wolisz zainstalować bibliotekę do użycia w backendzie lub w projekcie frontendowym, który używa kompilacji, możesz ją zainstalować za pomocą npm: + +```bash +npm install web3 --save +``` + +Następnie, aby zaimportować Web3.js do skryptu Node.js lub projektu frontendowego Browserify, możesz użyć następującej linii JavaScriptu: + +```js +const Web3 = require("web3") +``` + +Teraz, gdy dołączyliśmy bibliotekę do projektu, musimy ją zainicjować. Twój projekt musi być w stanie komunikować się z blockchainem. Większość bibliotek Ethereum komunikuje się z [węzłem](/developers/docs/nodes-and-clients/) poprzez wywołania RPC. Aby zainicjować naszego dostawcę Web3, utworzymy instancję Web3, przekazując jako konstruktor adres URL dostawcy. Jeśli masz węzeł lub [instancję ganache działającą na twoim komputerze](https://ethereumdev.io/testing-your-smart-contract-with-existing-protocols-ganache-fork/), będzie to wyglądać tak: + +```js +const web3 = new Web3("http://localhost:8545") +``` + +Jeśli chcesz uzyskać bezpośredni dostęp do hostowanego węzła, możesz znaleźć opcje w [węzłach jako usłudze](/developers/docs/nodes-and-clients/nodes-as-a-service). + +```js +const web3 = new Web3("https://cloudflare-eth.com") +``` + +Aby przetestować, czy poprawnie skonfigurowaliśmy naszą instancję Web3, spróbujemy pobrać najnowszy numer bloku za pomocą funkcji `getBlockNumber`. Ta funkcja akceptuje callback jako parametr i zwraca numer bloku jako liczbę całkowitą. + +```js +var Web3 = require("web3") +const web3 = new Web3("https://cloudflare-eth.com") + +web3.eth.getBlockNumber(function (error, result) { + console.log(result) +}) +``` + +Jeśli wykonasz ten program, po prostu wydrukuje on najnowszy numer bloku: szczyt blockchainu. Możesz również użyć wywołań funkcji `await/async`, aby uniknąć zagnieżdżania wywołań zwrotnych w swoim kodzie: + +```js +async function getBlockNumber() { + const latestBlockNumber = await web3.eth.getBlockNumber() + console.log(latestBlockNumber) + return latestBlockNumber +} + +getBlockNumber() +``` + +Możesz zobaczyć wszystkie dostępne funkcje w instancji Web3 w [oficjalnej dokumentacji web3.js](https://docs.web3js.org/). + +Większość bibliotek Web3 jest asynchroniczna, ponieważ w tle biblioteka wykonuje wywołania JSON-RPC do węzła, który odsyła wynik. + + + +Jeśli pracujesz w przeglądarce, niektóre portfele bezpośrednio wstrzykują instancję Web3 i powinieneś starać się jej używać, gdy tylko jest to możliwe, zwłaszcza jeśli planujesz wchodzić w interakcję z adresem Ethereum użytkownika w celu dokonywania transakcji. + +Oto fragment kodu do wykrywania, czy portfel MetaMask jest dostępny, i próby jego włączenia, jeśli tak. Pozwoli to później na odczytanie salda użytkownika i umożliwi mu zatwierdzanie transakcji, które chcesz, aby wykonał na blockchainie Ethereum: + +```js +if (window.ethereum != null) { + state.web3 = new Web3(window.ethereum) + try { + // W razie potrzeby poproś o dostęp do konta + await window.ethereum.enable() + // Konta są teraz ujawnione + } catch (error) { + // Użytkownik odmówił dostępu do konta... + } +} +``` + +Istnieją alternatywy dla web3.js, takie jak [Ethers.js](https://docs.ethers.io/), i są one również powszechnie stosowane. W następnym samouczku zobaczymy, [jak łatwo nasłuchiwać nowych bloków przychodzących na blockchainie i zobaczyć, co zawierają](https://ethereumdev.io/listening-to-new-transactions-happening-on-the-blockchain/). diff --git a/public/content/translations/pl/developers/tutorials/short-abi/index.md b/public/content/translations/pl/developers/tutorials/short-abi/index.md new file mode 100644 index 00000000000..708dc6db4c1 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/short-abi/index.md @@ -0,0 +1,585 @@ +--- +title: "Krótkie ABI w celu optymalizacji Calldata" +description: "Optymalizacja inteligentnych kontraktów dla rollupów optymistycznych" +author: Ori Pomerantz +lang: pl +tags: [ "warstwa 2" ] +skill: intermediate +published: 2022-04-01 +--- + +## Wprowadzenie {#introduction} + +W tym artykule dowiesz się o [rollupach optymistycznych](/developers/docs/scaling/optimistic-rollups), kosztach transakcji na nich oraz o tym, jak odmienna struktura kosztów wymaga od nas optymalizacji pod kątem innych rzeczy niż w sieci głównej Ethereum. +Dowiesz się również, jak wdrożyć tę optymalizację. + +### Pełne ujawnienie {#full-disclosure} + +Jestem pracownikiem zatrudnionym na pełen etat w [Optimism](https://www.optimism.io/), więc przykłady w tym artykule będą działać na Optimism. +Jednakże wyjaśniona tutaj technika powinna działać równie dobrze w przypadku innych rollupów. + +### Terminologia {#terminology} + +W dyskusjach o rollupach termin „warstwa 1” (L1) jest używany w odniesieniu do sieci głównej (Mainnet), produkcyjnej sieci Ethereum. +Termin „warstwa 2” (L2) jest używany w odniesieniu do rollupu lub jakiegokolwiek innego systemu, który opiera się na L1 w kwestii bezpieczeństwa, ale wykonuje większość przetwarzania poza łańcuchem. + +## Jak możemy jeszcze bardziej obniżyć koszt transakcji L2? {#how-can-we-further-reduce-the-cost-of-L2-transactions} + +[Rollupy optymistyczne](/developers/docs/scaling/optimistic-rollups) muszą przechowywać zapis każdej historycznej transakcji, aby każdy mógł je przejrzeć i zweryfikować, czy obecny stan jest prawidłowy. +Najtańszym sposobem na wprowadzenie danych do sieci głównej Ethereum jest zapisanie ich jako calldata. +To rozwiązanie zostało wybrane zarówno przez [Optimism](https://help.optimism.io/hc/en-us/articles/4413163242779-What-is-a-rollup-), jak i [Arbitrum](https://developer.offchainlabs.com/docs/rollup_basics#intro-to-rollups). + +### Koszt transakcji L2 {#cost-of-l2-transactions} + +Koszt transakcji L2 składa się z dwóch składników: + +1. Przetwarzanie na L2, które jest zazwyczaj niezwykle tanie +2. Przechowywanie na L1, które jest powiązane z kosztami gazu w sieci głównej + +W chwili, gdy to piszę, na Optimism koszt gazu L2 wynosi 0,001 [Gwei](/developers/docs/gas/#pre-london). +Z drugiej strony, koszt gazu L1 wynosi około 40 gwei. +[Aktualne ceny można zobaczyć tutaj](https://public-grafana.optimism.io/d/9hkhMxn7z/public-dashboard?orgId=1&refresh=5m). + +Jeden bajt calldata kosztuje albo 4 gazu (jeśli jest to zero), albo 16 gazu (jeśli ma inną wartość). +Jedną z najdroższych operacji w EVM jest zapis do pamięci masowej. +Maksymalny koszt zapisu 32-bajtowego słowa do pamięci masowej na L2 wynosi 22100 gazu. Obecnie jest to 22,1 gwei. +Jeśli więc uda nam się zaoszczędzić jeden zerowy bajt calldata, będziemy w stanie zapisać około 200 bajtów do pamięci masowej i nadal wyjdziemy na tym na plus. + +### ABI {#the-abi} + +Zdecydowana większość transakcji uzyskuje dostęp do kontraktu z konta zewnętrznego. +Większość kontraktów jest napisana w Solidity i interpretuje swoje pole danych zgodnie z [binarnym interfejsem aplikacji (ABI)](https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding). + +Jednak ABI zostało zaprojektowane dla L1, gdzie bajt calldata kosztuje mniej więcej tyle samo co cztery operacje arytmetyczne, a nie dla L2, gdzie bajt calldata kosztuje ponad tysiąc operacji arytmetycznych. +Calldata jest podzielona w następujący sposób: + +| Sekcja | Długość | Bajty | Zmarnowane bajty | Zmarnowany gaz | Niezbędne bajty | Niezbędny gaz | +| ---------------- | ------: | ----: | ---------------: | -------------: | --------------: | ------------: | +| Selektor funkcji | 4 | 0-3 | 3 | 48 | 1 | 16 | +| Zera | 12 | 4-15 | 12 | 48 | 0 | 0 | +| Adres docelowy | 20 | 16-35 | 0 | 0 | 20 | 320 | +| Kwota | 32 | 36-67 | 17 | 64 | 15 | 240 | +| Łącznie | 68 | | | 160 | | 576 | + +Wyjaśnienie: + +- **Selektor funkcji**: Kontrakt ma mniej niż 256 funkcji, więc możemy je rozróżnić za pomocą jednego bajtu. + Te bajty są zazwyczaj niezerowe i dlatego [kosztują szesnaście gazu](https://eips.ethereum.org/EIPS/eip-2028). +- **Zera**: Te bajty są zawsze zerowe, ponieważ dwudziestobajtowy adres nie wymaga trzydziestodwubajtowego słowa, aby go pomieścić. + Bajty, które zawierają zero, kosztują cztery gazu ([zobacz yellow paper](https://ethereum.github.io/yellowpaper/paper.pdf), Dodatek G, + s. 27, wartość dla `G``txdatazero`). +- **Ilość**: Jeśli założymy, że w tym kontrakcie `decimals` wynosi osiemnaście (normalna wartość), a maksymalna ilość tokenów, które transferujemy, wyniesie 1018, otrzymamy maksymalną ilość 1036. + 25615 > 1036, więc piętnaście bajtów wystarczy. + +Strata 160 gazu na L1 jest normalnie znikoma. Transakcja kosztuje co najmniej [21 000 gazu](https://yakkomajuri.medium.com/blockchain-definition-of-the-week-ethereum-gas-2f976af774ed), więc dodatkowe 0,8% nie ma znaczenia. +Jednak na L2 sprawy mają się inaczej. Prawie cały koszt transakcji to zapisanie jej na L1. +Oprócz calldata transakcji, istnieje 109 bajtów nagłówka transakcji (adres docelowy, podpis itp.). +Całkowity koszt wynosi zatem `109*16+576+160=2480`, a my marnujemy około 6,5% tej kwoty. + +## Redukcja kosztów, gdy nie kontrolujesz kontraktu docelowego {#reducing-costs-when-you-dont-control-the-destination} + +Zakładając, że nie masz kontroli nad kontraktem docelowym, nadal możesz użyć rozwiązania podobnego do [tego](https://github.com/qbzzt/ethereum.org-20220330-shortABI). +Przejdźmy do odpowiednich plików. + +### Token.sol {#token-sol} + +[To jest kontrakt docelowy](https://github.com/qbzzt/ethereum.org-20220330-shortABI/blob/master/contracts/Token.sol). +Jest to standardowy kontrakt ERC-20, z jedną dodatkową funkcją. +Ta funkcja `faucet` pozwala każdemu użytkownikowi otrzymać trochę tokenów do wykorzystania. +Uczyniłoby to produkcyjny kontrakt ERC-20 bezużytecznym, ale ułatwia życie, gdy ERC-20 istnieje tylko w celu ułatwienia testowania. + +```solidity + /** + * @dev Daje wywołującemu 1000 tokenów do zabawy + */ + function faucet() external { + _mint(msg.sender, 1000); + } // function faucet +``` + +### CalldataInterpreter.sol {#calldatainterpreter-sol} + +[To jest kontrakt, który transakcje mają wywoływać z krótszymi calldata](https://github.com/qbzzt/ethereum.org-20220330-shortABI/blob/master/contracts/CalldataInterpreter.sol). +Przejdźmy przez niego linia po linii. + +```solidity +//SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.0; + + +import { OrisUselessToken } from "./Token.sol"; +``` + +Potrzebujemy interfejsu kontraktu tokena, aby wiedzieć, jak go wywoływać. + +```solidity +contract CalldataInterpreter { + + OrisUselessToken public immutable token; +``` + +Adres tokena, dla którego jesteśmy proxy. + +```solidity + + /** + * @dev Określ adres tokena + * @param tokenAddr_ adres kontraktu ERC-20 + */ + constructor( + address tokenAddr_ + ) { + token = OrisUselessToken(tokenAddr_); + } // constructor +``` + +Adres tokena jest jedynym parametrem, który musimy określić. + +```solidity + function calldataVal(uint startByte, uint length) + private pure returns (uint) { +``` + +Odczytaj wartość z calldata. + +```solidity + uint _retVal; + + require(length < 0x21, + "calldataVal limit długości to 32 bajty"); + + require(length + startByte <= msg.data.length, + "calldataVal próbuje czytać poza calldatasize"); +``` + +Zamierzamy załadować do pamięci pojedyncze 32-bajtowe (256-bitowe) słowo i usunąć bajty, które nie są częścią pola, które nas interesuje. +Ten algorytm nie działa dla wartości dłuższych niż 32 bajty i oczywiście nie możemy czytać poza końcem calldata. +Na L1 może być konieczne pominięcie tych testów w celu zaoszczędzenia na gazie, ale na L2 gaz jest niezwykle tani, co umożliwia wszelkie testy poprawności, jakie możemy sobie wyobrazić. + +```solidity + assembly { + _retVal := calldataload(startByte) + } +``` + +Moglibyśmy skopiować dane z wywołania do `fallback()` (patrz niżej), ale łatwiej jest użyć [Yul](https://docs.soliditylang.org/en/v0.8.12/yul.html), języka asemblera EVM. + +Tutaj używamy [opcodu CALLDATALOAD](https://www.evm.codes/#35), aby odczytać bajty od `startByte` do `startByte+31` na stos. +Ogólnie rzecz biorąc, składnia opcodu w Yul to `(, ...). + +```solidity + + _retVal = _retVal >> (256-length*8); +``` + +Tylko najbardziej znaczące bajty o długości `length` są częścią pola, więc wykonujemy [przesunięcie w prawo](https://en.wikipedia.org/wiki/Logical_shift), aby pozbyć się pozostałych wartości. +Ma to dodatkową zaletę, że przenosi wartość na prawo od pola, więc jest to sama wartość, a nie wartość pomnożona przez 256coś. + +```solidity + + return _retVal; + } + + + fallback() external { +``` + +Gdy wywołanie do kontraktu Solidity nie pasuje do żadnej z sygnatur funkcji, wywołuje [funkcję `fallback()`](https://docs.soliditylang.org/en/v0.8.12/contracts.html#fallback-function) (zakładając, że taka istnieje). +W przypadku `CalldataInterpreter` _każde_ wywołanie trafia tutaj, ponieważ nie ma innych funkcji `external` ani `public`. + +```solidity + uint _func; + + _func = calldataVal(0, 1); +``` + +Odczytaj pierwszy bajt calldata, który informuje nas o funkcji. +Istnieją dwa powody, dla których funkcja nie byłaby tutaj dostępna: + +1. Funkcje, które są `pure` lub `view`, nie zmieniają stanu i nie kosztują gazu (gdy są wywoływane poza łańcuchem). + Nie ma sensu próbować zmniejszać ich kosztu gazu. +2. Funkcje, które opierają się na [`msg.sender`](https://docs.soliditylang.org/en/v0.8.12/units-and-global-variables.html#block-and-transaction-properties). + Wartością `msg.sender` będzie adres `CalldataInterpreter`, a nie adres wywołującego. + +Niestety, [patrząc na specyfikacje ERC-20](https://eips.ethereum.org/EIPS/eip-20), pozostaje tylko jedna funkcja, `transfer`. +Pozostawia to nam tylko dwie funkcje: `transfer` (ponieważ możemy wywołać `transferFrom`) i `faucet` (ponieważ możemy przelać tokeny z powrotem do tego, kto nas wywołał). + +```solidity + + // Wywołaj metody zmieniające stan tokena, używając + // informacji z calldata + + // faucet + if (_func == 1) { +``` + +Wywołanie `faucet()`, które nie ma parametrów. + +```solidity + token.faucet(); + token.transfer(msg.sender, + token.balanceOf(address(this))); + } +``` + +Po wywołaniu `token.faucet()` otrzymujemy tokeny. Jednak jako kontrakt proxy **nie potrzebujemy** tokenów. +Potrzebuje ich EOA (konto należące do podmiotu zewnętrznego) lub kontrakt, który nas wywołał. +Więc transferujemy wszystkie nasze tokeny do tego, kto nas wywołał. + +```solidity + // transfer (zakładamy, że mamy na to zgodę) + if (_func == 2) { +``` + +Przesyłanie tokenów wymaga dwóch parametrów: adresu docelowego i kwoty. + +```solidity + token.transferFrom( + msg.sender, +``` + +Zezwalamy wywołującym tylko na transfer posiadanych przez nich tokenów + +```solidity + address(uint160(calldataVal(1, 20))), +``` + +Adres docelowy zaczyna się od bajtu nr 1 (bajt nr 0 to funkcja). +Jako adres, ma długość 20 bajtów. + +```solidity + calldataVal(21, 2) +``` + +W przypadku tego konkretnego kontraktu zakładamy, że maksymalna liczba tokenów, jaką ktokolwiek chciałby przenieść, mieści się w dwóch bajtach (mniej niż 65536). + +```solidity + ); + } +``` + +Ogólnie rzecz biorąc, transfer zajmuje 35 bajtów calldata: + +| Sekcja | Długość | Bajty | +| ---------------- | ------: | ----: | +| Selektor funkcji | 1 | 0 | +| Adres docelowy | 32 | 1-32 | +| Kwota | 2 | 33-34 | + +```solidity + } // fallback + +} // kontrakt CalldataInterpreter +``` + +### test.js {#test-js} + +[Ten test jednostkowy JavaScript](https://github.com/qbzzt/ethereum.org-20220330-shortABI/blob/master/test/test.js) pokazuje, jak używać tego mechanizmu (i jak zweryfikować jego prawidłowe działanie). +Zakładam, że rozumiesz [chai](https://www.chaijs.com/) i [ethers](https://docs.ethers.io/v5/) i wyjaśniam tylko te części, które dotyczą konkretnie kontraktu. + +```js +const { expect } = require("chai"); + +describe("CalldataInterpreter", function () { + it("Powinien pozwolić nam używać tokenów", async function () { + const Token = await ethers.getContractFactory("OrisUselessToken") + const token = await Token.deploy() + await token.deployed() + console.log("Adres tokena:", token.address) + + const Cdi = await ethers.getContractFactory("CalldataInterpreter") + const cdi = await Cdi.deploy(token.address) + await cdi.deployed() + console.log("Adres CalldataInterpreter:", cdi.address) + + const signer = await ethers.getSigner() +``` + +Zaczynamy od wdrożenia obu kontraktów. + +```javascript + // Pobierz tokeny do zabawy + const faucetTx = { +``` + +Nie możemy używać funkcji wysokiego poziomu, których normalnie byśmy użyli (takich jak `token.faucet()`) do tworzenia transakcji, ponieważ nie przestrzegamy ABI. +Zamiast tego musimy sami zbudować transakcję, a następnie ją wysłać. + +```javascript + to: cdi.address, + data: "0x01" +``` + +Istnieją dwa parametry, które musimy podać dla transakcji: + +1. `to`, adres docelowy. + To jest kontrakt interpretera calldata. +2. `data`, calldata do wysłania. + W przypadku wywołania `faucet`, danymi jest pojedynczy bajt, `0x01`. + +```javascript + + } + await (await signer.sendTransaction(faucetTx)).wait() +``` + +Wywołujemy [metodę `sendTransaction` sygnatariusza](https://docs.ethers.io/v5/api/signer/#Signer-sendTransaction), ponieważ już określiliśmy miejsce docelowe (`faucetTx.to`) i potrzebujemy, aby transakcja została podpisana. + +```javascript +// Sprawdź, czy faucet poprawnie dostarcza tokeny +expect(await token.balanceOf(signer.address)).to.equal(1000) +``` + +Tutaj weryfikujemy saldo. +Nie ma potrzeby oszczędzania gazu na funkcjach `view`, więc po prostu uruchamiamy je normalnie. + +```javascript +// Daj CDI upoważnienie (zatwierdzenia nie mogą być przekazywane przez proxy) +const approveTX = await token.approve(cdi.address, 10000) +await approveTX.wait() +expect(await token.allowance(signer.address, cdi.address)).to.equal(10000) +``` + +Daj interpreterowi calldata upoważnienie do wykonywania transferów. + +```javascript +// Transfer tokenów +const destAddr = "0xf5a6ead936fb47f342bb63e676479bddf26ebe1d" +const transferTx = { + to: cdi.address, + data: "0x02" + destAddr.slice(2, 42) + "0100", +} +``` + +Utwórz transakcję transferu. Pierwszy bajt to „0x02”, po nim następuje adres docelowy, a na końcu kwota (0x0100, co w systemie dziesiętnym daje 256). + +```javascript + await (await signer.sendTransaction(transferTx)).wait() + + // Sprawdź, czy mamy o 256 tokenów mniej + expect (await token.balanceOf(signer.address)).to.equal(1000-256) + + // I czy nasz cel je otrzymał + expect (await token.balanceOf(destAddr)).to.equal(256) + }) // it +}) // describe +``` + +## Zmniejszenie kosztów, gdy kontrolujesz kontrakt docelowy {#reducing-the-cost-when-you-do-control-the-destination-contract} + +Jeśli masz kontrolę nad kontraktem docelowym, możesz tworzyć funkcje, które omijają sprawdzanie `msg.sender`, ponieważ ufają interpreterowi calldata. +[Przykład działania można zobaczyć tutaj, w gałęzi `control-contract`](https://github.com/qbzzt/ethereum.org-20220330-shortABI/tree/control-contract). + +Gdyby kontrakt odpowiadał tylko na transakcje zewnętrzne, moglibyśmy sobie poradzić z jednym kontraktem. +Jednakże, złamałoby to [kompozycyjność](/developers/docs/smart-contracts/composability/). +O wiele lepiej jest mieć kontrakt, który odpowiada na normalne wywołania ERC-20, i inny kontrakt, który odpowiada na transakcje z krótkimi danymi wywołania. + +### Token.sol {#token-sol-2} + +W tym przykładzie możemy zmodyfikować `Token.sol`. +Pozwala to na posiadanie wielu funkcji, które może wywoływać tylko proxy. +Oto nowe części: + +```solidity + // Jedyny adres uprawniony do określenia adresu CalldataInterpreter + address owner; + + // Adres CalldataInterpreter + address proxy = address(0); +``` + +Kontrakt ERC-20 musi znać tożsamość autoryzowanego proxy. +Nie możemy jednak ustawić tej zmiennej w konstruktorze, ponieważ nie znamy jeszcze jej wartości. +Ten kontrakt jest tworzony jako pierwszy, ponieważ proxy oczekuje adresu tokena w swoim konstruktorze. + +```solidity + /** + * @dev Wywołuje konstruktor ERC20. + */ + constructor( + ) ERC20("Oris useless token-2", "OUT-2") { + owner = msg.sender; + } +``` + +Adres twórcy (o nazwie `owner`) jest tutaj przechowywany, ponieważ jest to jedyny adres uprawniony do ustawienia proxy. + +```solidity + /** + * @dev Ustawia adres dla proxy (CalldataInterpreter). + * Może być wywołane tylko raz przez właściciela + */ + function setProxy(address _proxy) external { + require(msg.sender == owner, "Może być wywołane tylko przez właściciela"); + require(proxy == address(0), "Proxy jest już ustawione"); + + proxy = _proxy; + } // funkcja setProxy +``` + +Proxy ma uprzywilejowany dostęp, ponieważ może omijać kontrole bezpieczeństwa. +Aby upewnić się, że możemy zaufać proxy, pozwalamy tylko `owner` na wywołanie tej funkcji, i to tylko raz. +Gdy `proxy` ma rzeczywistą wartość (niezerową), wartość ta nie może ulec zmianie, więc nawet jeśli właściciel zdecyduje się zbuntować lub jego mnemonik zostanie ujawniony, nadal jesteśmy bezpieczni. + +```solidity + /** + * @dev Niektóre funkcje mogą być wywoływane tylko przez proxy. + */ + modifier onlyProxy { +``` + +Jest to [funkcja `modifier`](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm), która modyfikuje działanie innych funkcji. + +```solidity + require(msg.sender == proxy); +``` + +Najpierw zweryfikuj, czy zostaliśmy wywołani przez proxy i nikogo innego. +Jeśli nie, `revert`. + +```solidity + _; + } +``` + +Jeśli tak, uruchom funkcję, którą modyfikujemy. + +```solidity + /* Funkcje, które pozwalają proxy na faktyczne pośredniczenie dla kont */ + + 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; + } +``` + +Są to trzy operacje, które normalnie wymagają, aby wiadomość pochodziła bezpośrednio od podmiotu transferującego tokeny lub zatwierdzającego upoważnienie. +Tutaj mamy wersję proxy tych operacji, która: + +1. Jest modyfikowana przez `onlyProxy()`, więc nikt inny nie może ich kontrolować. +2. Otrzymuje adres, który normalnie byłby `msg.sender` jako dodatkowy parametr. + +### CalldataInterpreter.sol {#calldatainterpreter-sol-2} + +Interpreter calldata jest prawie identyczny z powyższym, z wyjątkiem tego, że funkcje przekazywane przez proxy otrzymują parametr `msg.sender` i nie ma potrzeby posiadania upoważnienia do `transferu`. + +```solidity + // transfer (nie ma potrzeby posiadania upoważnienia) + if (_func == 2) { + token.transferProxy( + msg.sender, + address(uint160(calldataVal(1, 20))), + calldataVal(21, 2) + ); + } + + // zatwierdzenie + 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} + +Jest kilka zmian między poprzednim kodem testującym a tym. + +```js +const Cdi = await ethers.getContractFactory("CalldataInterpreter") +const cdi = await Cdi.deploy(token.address) +await cdi.deployed() +await token.setProxy(cdi.address) +``` + +Musimy poinformować kontrakt ERC-20, któremu proxy ma ufać + +```js +console.log("Adres CalldataInterpreter:", cdi.address) + +// Potrzebujemy dwóch sygnatariuszy do weryfikacji upoważnień +const signers = await ethers.getSigners() +const signer = signers[0] +const poorSigner = signers[1] +``` + +Aby sprawdzić `approve()` i `transferFrom()`, potrzebujemy drugiego sygnatariusza. +Nazywamy go `poorSigner`, ponieważ nie dostaje żadnych naszych tokenów (musi mieć oczywiście ETH). + +```js +// Transfer tokenów +const destAddr = "0xf5a6ead936fb47f342bb63e676479bddf26ebe1d" +const transferTx = { + to: cdi.address, + data: "0x02" + destAddr.slice(2, 42) + "0100", +} +await (await signer.sendTransaction(transferTx)).wait() +``` + +Ponieważ kontrakt ERC-20 ufa proxy (`cdi`), nie potrzebujemy upoważnienia do przekazywania transferów. + +```js +// zatwierdzenie i 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() + +// Sprawdź, czy kombinacja zatwierdzenia / transferFrom została wykonana poprawnie +expect(await token.balanceOf(destAddr2)).to.equal(255) +``` + +Przetestuj dwie nowe funkcje. +Zauważ, że `transferFromTx` wymaga dwóch parametrów adresu: dającego upoważnienie i odbiorcy. + +## Wnioski {#conclusion} + +Zarówno [Optimism](https://medium.com/ethereum-optimism/the-road-to-sub-dollar-transactions-part-2-compression-edition-6bb2890e3e92), jak i [Arbitrum](https://developer.offchainlabs.com/docs/special_features) szukają sposobów na zmniejszenie rozmiaru calldata zapisywanych w L1, a tym samym kosztów transakcji. +Jednak jako dostawcy infrastruktury poszukujący ogólnych rozwiązań, nasze możliwości są ograniczone. +Jako deweloper dApp, masz wiedzę specyficzną dla aplikacji, co pozwala na znacznie lepszą optymalizację calldata, niż moglibyśmy to zrobić w rozwiązaniu ogólnym. +Mamy nadzieję, że ten artykuł pomoże Ci znaleźć idealne rozwiązanie dla Twoich potrzeb. + +[Zobacz więcej mojej pracy tutaj](https://cryptodocguy.pro/). + diff --git a/public/content/translations/pl/developers/tutorials/smart-contract-security-guidelines/index.md b/public/content/translations/pl/developers/tutorials/smart-contract-security-guidelines/index.md index 62eec372652..f0d3396026b 100644 --- a/public/content/translations/pl/developers/tutorials/smart-contract-security-guidelines/index.md +++ b/public/content/translations/pl/developers/tutorials/smart-contract-security-guidelines/index.md @@ -1,21 +1,18 @@ --- -title: Wskazówki dotyczące bezpieczeństwa kontraktów inteligentnych -description: Lista kontrolna wytycznych bezpieczeństwa do rozważenia podczas tworzenia aplikacji zdecentralizowanych +title: "Wskazówki dotyczące bezpieczeństwa kontraktów inteligentnych" +description: "Lista kontrolna wytycznych bezpieczeństwa do rozważenia podczas tworzenia aplikacji zdecentralizowanych" author: "Trailofbits" -tags: - - "solidity" - - "inteligentne kontrakty" - - "ochrona" +tags: [ "solidity", "smart kontrakty", "bezpieczeństwo" ] skill: intermediate lang: pl published: 2020-09-06 -source: Tworzenie bezpiecznych kontraktów +source: Building secure contracts sourceUrl: https://github.com/crytic/building-secure-contracts/blob/master/development-guidelines/guidelines.md --- Postępuj zgodnie z tymi ogólnymi zaleceniami, aby tworzyć bezpieczniejsze inteligentne kontrakty. -## Wytyczne dotyczące projektowania {#design-guidelines} +## Wytyczne projektowe {#design-guidelines} Projekt kontraktu powinien być omówiony z wyprzedzeniem, przed napisaniem jakiejkolwiek linijki kodu. @@ -23,72 +20,72 @@ Projekt kontraktu powinien być omówiony z wyprzedzeniem, przed napisaniem jaki Dokumentacja może być pisana na różnych poziomach i powinna być aktualizowana w trakcie realizacji umów: -- **Prosty opis systemu w języku angielskim**, przedstawiający działanie kontraktów i wszelkie założenia dotyczące bazy kodu. -- **Diagramy schematów i architektury**, w tym interakcje kontraktów i maszyna stanu systemu. [Drukarki Slither](https://github.com/crytic/slither/wiki/Printer-documentation) mogą pomóc w wygenerowaniu tych schematów. -- **Dokładna dokumentacja kodu**, [format Natspec](https://solidity.readthedocs.io/en/develop/natspec-format.html) może być używany do Solidity. +- **Prosty opis systemu w języku angielskim**, opisujący działanie kontraktów i wszelkie założenia dotyczące bazy kodu. +- **Schematy i diagramy architektury**, w tym interakcje kontraktów i maszyna stanu systemu. [Slither printers](https://github.com/crytic/slither/wiki/Printer-documentation) mogą pomóc w generowaniu tych schematów. +- **Dokładna dokumentacja kodu**, dla Solidity można użyć [formatu Natspec](https://docs.soliditylang.org/en/develop/natspec-format.html). -### Obliczenia on-chain vs off-chain {#on-chain-vs-off-chain-computation} +### Obliczenia on-chain a off-chain {#onchain-vs-offchain-computation} -- **Zachowaj jak najwięcej kodu off-chain.** Zatrzymaj niewielką warstwę on-chain. Wstępnie przetwarzaj dane z kodem off-chain w taki sposób, aby weryfikacja on-chain była prosta. Potrzebujesz uporządkowanej listy? Posortuj listę off-chain, a następnie sprawdź tylko jej kolejność on-chain. +- **Jak najwięcej kodu trzymaj off-chain.** Utrzymuj jak najmniejszą warstwę on-chain. Wstępnie przetwarzaj dane za pomocą kodu off-chain w taki sposób, aby weryfikacja on-chain była prosta. Potrzebujesz uporządkowanej listy? Posortuj listę off-chain, a następnie sprawdź tylko jej kolejność on-chain. -### Możliwość uaktualnienia {#upgradeability} +### Możliwość aktualizacji {#upgradeability} -Omówiliśmy różne rozwiązania dotyczące możliwości uaktualnień w [poście na blogu](https://blog.trailofbits.com/2018/09/05/contract-upgrade-anti-patterns/). Dokonaj rozważnego wyboru, czy wspierać możliwość uaktualniania, czy nie, przed napisaniem jakiegokolwiek kodu. Decyzja wpłynie na sposób, w jaki ustrukturyzujesz kod. Generalnie zalecamy: +Omówiliśmy różne rozwiązania dotyczące możliwości aktualizacji w [naszym wpisie na blogu](https://blog.trailofbits.com/2018/09/05/contract-upgrade-anti-patterns/). Dokonaj rozważnego wyboru, czy wspierać możliwość uaktualniania, czy nie, przed napisaniem jakiegokolwiek kodu. Decyzja wpłynie na to, jak ustrukturyzujesz swój kod. Generalnie zalecamy: -- **Przedkładanie [migracji kontraktu](https://blog.trailofbits.com/2018/10/29/how-contract-migration-works/) nad możliwość uaktualnienia.** System migracji ma wiele takich samych zalet, jak możliwość uaktualnienia, bez ich wad. -- **Używanie wzorca separacji danych zamiast wzorca delegatecalproxy.** Jeśli projekt ma wyraźną separację abstrakcji, możliwość uaktualnienia przy użyciu separacji danych będzie wymagać tylko kilku dostosowań. Delegatecallproxy wymaga wiedzy specjalistycznej o EVM i jest wysoce podatny na błędy. -- **Dokumentowanie procedury migracji/uaktualnienia przed wdrożeniem.** Jeśli będziesz musiał reagować w stresie bez żadnych wytycznych, popełnisz błędy. Zapisz procedurę do wykonania z wyprzedzeniem. Powinna ona obejmować: +- **Preferowanie [migracji kontraktów](https://blog.trailofbits.com/2018/10/29/how-contract-migration-works/) zamiast możliwości aktualizacji.** Systemy migracji mają wiele tych samych zalet co systemy z możliwością aktualizacji, a przy tym są pozbawione ich wad. +- **Stosowanie wzorca separacji danych zamiast wzorca delegatecallproxy.** Jeśli Twój projekt ma wyraźną separację abstrakcji, możliwość aktualizacji z wykorzystaniem separacji danych będzie wymagać tylko kilku dostosowań. Delegatecallproxy wymaga wiedzy specjalistycznej o EVM i jest wysoce podatny na błędy. +- **Udokumentuj procedurę migracji/aktualizacji przed wdrożeniem.** Jeśli będziesz musiał(a) reagować w stresie bez żadnych wytycznych, popełnisz błędy. Zapisz procedurę do wykonania z wyprzedzeniem. Powinna ona obejmować: - Wywołania inicjujące nowe kontrakty - Gdzie są przechowywane klucze i jak uzyskać dostęp do nich - Jak sprawdzić wdrożenie! Opracowanie i przetestowanie skryptu po wdrożeniu. -## Wytyczne dotyczące wdrażania {#implementation-guidelines} +## Wytyczne dotyczące implementacji {#implementation-guidelines} -**Poszukaj prostoty.** Zawsze używaj najprostszego rozwiązania, które pasuje do Twojego celu. Każdy członek twojego zespołu powinien być w stanie zrozumieć Twoje rozwiązanie. +**Dąż do prostoty.** Zawsze używaj najprostszego rozwiązania, które odpowiada Twoim celom. Każdy członek twojego zespołu powinien być w stanie zrozumieć Twoje rozwiązanie. -### Skład funkcji {#function-composition} +### Składanie funkcji {#function-composition} Architektura Twojej bazy kodu powinna ułatwić sprawdzenie twojego kodu. Unikaj wyborów architektonicznych, które zmniejszają zdolność rozumowania o jego poprawności. -- **Podziel logikę swojego systemu** poprzez wiele umów lub grupowanie podobnych funkcji (na przykład uwierzytelniające, arytmetyczne, ...). -- **Pisz małe funkcje o wyraźnym celu celu.** To ułatwi sprawdzenie i umożliwi testowanie poszczególnych komponentów. +- **Podziel logikę swojego systemu** na wiele kontraktów lub grupuj podobne funkcje (np. uwierzytelnianie, arytmetyka itp.). +- **Pisz małe funkcje o jasno określonym celu.** Ułatwi to przegląd i pozwoli na testowanie poszczególnych komponentów. ### Dziedziczenie {#inheritance} -- **Zachowaj dziedziczenie do zarządzania.** Dziedzictwo powinno być używane do dzielenia logiki, jednak Twój projekt powinien mieć na celu zminimalizowanie głębokości i szerokości drzewa dziedziczenia. -- **Użyj [drukarki dziedziczenia Slither'a](https://github.com/crytic/slither/wiki/Printer-documentation#inheritance-graph), aby sprawdzić hierarchię kontraktów.** Drukarka dziedziczenia pomoże Ci sprawdzić rozmiar hierarchii. +- **Utrzymuj dziedziczenie na łatwym do zarządzania poziomie.** Dziedziczenie powinno służyć do podziału logiki, jednak projekt powinien dążyć do zminimalizowania głębokości i szerokości drzewa dziedziczenia. +- **Użyj [inheritance printer](https://github.com/crytic/slither/wiki/Printer-documentation#inheritance-graph) Slithera, aby sprawdzić hierarchię kontraktów.** Narzędzie to pomoże Ci przeanalizować rozmiar hierarchii. ### Zdarzenia {#events} -- **Rejestruj wszystkie kluczowe operacje.** Zdarzenia pomogą debugować kontrakt podczas jego oprawcowywania i będą go monitorować po wdrożeniu. +- **Rejestruj wszystkie kluczowe operacje.** Zdarzenia pomogą w debugowaniu kontraktu podczas jego tworzenia i monitorowaniu go po wdrożeniu. -### Unikanie znanych pułapek {#avoid-known-pitfalls} +### Unikaj znanych pułapek {#avoid-known-pitfalls} -- **Bądź świadomy najczęstszych problemów z bezpieczeństwem.** Istnieje wiele zasobów online do poznania wspólnych problemów, takich jak [Ethernaut CTF](https://ethernaut.openzeppelin.com/), [Zajmij Ether](https://capturetheether.com/)lub [Nie tak inteligentne kontrakty](https://github.com/crytic/not-so-smart-contracts/). -- **Zwróć uwagę na sekcje ostrzeżeń w [dokumentacji Solidity](https://solidity.readthedocs.io/en/latest/).** Sekcje z ostrzeżeniami poinformują Cię o nieoczywistym zachowaniu języka. +- **Bądź świadomy(-a) najczęstszych problemów z bezpieczeństwem.** Istnieje wiele zasobów online, z których można dowiedzieć się o powszechnych problemach, takich jak [Ethernaut CTF](https://ethernaut.openzeppelin.com/), [Capture the Ether](https://capturetheether.com/) lub [Not so smart contracts](https://github.com/crytic/not-so-smart-contracts/). +- **Zwróć uwagę na sekcje z ostrzeżeniami w [dokumentacji Solidity](https://docs.soliditylang.org/en/latest/).** Sekcje z ostrzeżeniami poinformują Cię o nieoczywistym zachowaniu języka. ### Zależności {#dependencies} - **Używaj dobrze przetestowanych bibliotek.** Importowanie kodu z dobrze przetestowanych bibliotek zmniejszy prawdopodobieństwo, że napiszesz kod z błędami. Jeśli chcesz napisać kontrakt ERC20, użyj [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20). -- **Użyj menedżera zależności; unikaj kopiowania kodu.** Jeśli opierasz się na źródle zewnętrznym, musisz na bieżąco aktualizować je w stosunku do źródła oryginalnego. +- **Używaj menedżera zależności; unikaj kopiowania kodu.** Jeśli polegasz na zewnętrznym źródle, musisz je na bieżąco aktualizować zgodnie z oryginalnym źródłem. -### Testy i weryfikacja {#testing-and-verification} +### Testowanie i weryfikacja {#testing-and-verification} -- **Zapisz dokładne testy jednostkowe.** Rozległy zestaw testowy ma kluczowe znaczenie dla budowy oprogramowania wysokiej jakości. -- **Napisz niestandardowe kontrole i właściwości dla narzędzi [Slither](https://github.com/crytic/slither), [Echidna](https://github.com/crytic/echidna) i [Manticore](https://github.com/trailofbits/manticore).** Automatyczne narzędzia pomogą zapewnić bezpieczeństwo umowy. Przejrzyj resztę tego przewodnika, aby dowiedzieć się, jak pisać skuteczne kontrole i właściwości. -- **Użyj [crytic.io](https://crytic.io/).** Crytic integruje się z Githubem, zapewnia dostęp do prywatnych detektorów Slither, i uruchamia niestandardowe kontrole właściwości z Echidny. +- **Pisz dokładne testy jednostkowe.** Rozbudowany pakiet testów ma kluczowe znaczenie dla tworzenia oprogramowania wysokiej jakości. +- **Pisz niestandardowe testy i właściwości dla narzędzi [Slither](https://github.com/crytic/slither), [Echidna](https://github.com/crytic/echidna) i [Manticore](https://github.com/trailofbits/manticore).** Zautomatyzowane narzędzia pomogą zapewnić bezpieczeństwo Twojego kontraktu. Przejrzyj resztę tego przewodnika, aby dowiedzieć się, jak pisać skuteczne kontrole i właściwości. +- **Użyj [crytic.io](https://crytic.io/).** Crytic integruje się z GitHubem, zapewnia dostęp do prywatnych detektorów Slither i uruchamia niestandardowe testy właściwości z Echidny. ### Solidity {#solidity} -- **Favor Solidity 0.5 ponad 0.4 i 0.6.** Naszym zdaniem Solidity 0.5 jest bezpieczniejszy i ma lepsze wbudowane praktyki niż 0.4. Solidity 0.6 okazała się zbyt niestabilna do produkcji i wymaga czasu, aby dojrzeć. -- **Użyj stabilnej wersji do kompilacji; użyj najnowszej wersji, aby sprawdzić ostrzeżenia.** Sprawdź, czy Twój kod nie ma zgłoszonych problemów z najnowszą wersją kompilatora. Solidity ma jednak szybki cykl wydawniczy i ma historię błędów kompilatora, więc nie zalecamy wdrażania najnowszej wersji (zobacz [zalecenie wersji Solc](https://github.com/crytic/slither/wiki/Detector-Documentation#recommendation-33)). -- **Nie używaj wbudowanego asemblera.** Asemblacja wymaga wiedzy fachowej na temat EVM. Nie pisz kodu EVM, jeśli nie _opanowałeś_ żółtej księgi. +- **Preferuj Solidity 0.5 zamiast 0.4 i 0.6.** Naszym zdaniem Solidity 0.5 jest bezpieczniejszy i ma lepsze wbudowane praktyki niż 0.4. Solidity 0.6 okazała się zbyt niestabilna do produkcji i wymaga czasu, aby dojrzeć. +- **Użyj stabilnej wersji do kompilacji; użyj najnowszej wersji, aby sprawdzić ostrzeżenia.** Sprawdź, czy Twój kod nie ma zgłoszonych problemów z najnowszą wersją kompilatora. Jednakże Solidity ma szybki cykl wydawniczy i historię błędów kompilatora, dlatego nie zalecamy najnowszej wersji do wdrożenia (zobacz [rekomendację wersji solc od Slithera](https://github.com/crytic/slither/wiki/Detector-Documentation#recommendation-33)). +- **Nie używaj asemblera wstawkowego.** Używanie asemblera wymaga specjalistycznej wiedzy na temat EVM. Nie pisz kodu EVM, jeśli nie _opanowałeś(-aś)_ do perfekcji żółtej księgi. ## Wytyczne dotyczące wdrażania {#deployment-guidelines} Po opracowaniu i wdrożeniu kontraktu: -- **Monitoruj swoje kontrakty.** Obserwuj dzienniki i bądź gotowy do reagowania w przypadku naruszenia kontraktu lub portfela. -- **Dodaj swoje dane kontaktowe do [blockchain-security-contacts](https://github.com/crytic/blockchain-security-contacts).** Ta lista pomaga firmom zewnętrznym skontaktować się z Tobą w przypadku wykrycia luki w zabezpieczeniach. +- **Monitoruj swoje kontrakty.** Obserwuj logi i bądź gotów(-owa) do reakcji w przypadku naruszenia bezpieczeństwa kontraktu lub portfela. +- **Dodaj swoje dane kontaktowe do [blockchain-security-contacts](https://github.com/crytic/blockchain-security-contacts).** Ta lista pomaga stronom trzecim skontaktować się z Tobą w przypadku odkrycia luki w zabezpieczeniach. - **Zabezpiecz portfele uprzywilejowanych użytkowników.** Postępuj zgodnie z naszymi [najlepszymi praktykami](https://blog.trailofbits.com/2018/11/27/10-rules-for-the-secure-use-of-cryptocurrency-hardware-wallets/), jeśli przechowujesz klucze w portfelach sprzętowych. -- **Opracuj plan reakcji na incydent.** Weź pod uwagę, że Twoje inteligentne kontrakty mogą zostać naruszone. Nawet jeśli twoje kontrakty są wolne od błędów, atakujący może przejąć kontrolę nad kluczami właściciela umowy. +- **Opracuj plan reakcji na incydenty.** Weź pod uwagę, że Twoje inteligentne kontrakty mogą zostać naruszone. Nawet jeśli twoje kontrakty są wolne od błędów, atakujący może przejąć kontrolę nad kluczami właściciela umowy. diff --git a/public/content/translations/pl/developers/tutorials/stealth-addr/index.md b/public/content/translations/pl/developers/tutorials/stealth-addr/index.md new file mode 100644 index 00000000000..7b341dc5cf0 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/stealth-addr/index.md @@ -0,0 +1,443 @@ +--- +title: "Korzystanie z ukrytych adresów" +description: "Ukryte adresy pozwalają użytkownikom na anonimowe przesyłanie aktywów. Po przeczytaniu tego artykułu będziesz w stanie: wyjaśnić, czym są ukryte adresy i jak działają, zrozumieć, jak używać ukrytych adresów w sposób zachowujący anonimowość, oraz napisać aplikację internetową, która używa ukrytych adresów." +author: Ori Pomerantz +tags: + [ + "Ukryty adres", + "prywatność", + "kryptografia", + "rust", + "wasm" + ] +skill: intermediate +published: 2025-11-30 +lang: pl +sidebarDepth: 3 +--- + +Jesteś Billem. Z powodów, których nie będziemy tu omawiać, chcesz przekazać darowiznę na kampanię „Alicja na królową świata” i chcesz, aby Alicja wiedziała, że to Ty przekazałeś darowiznę, aby mogła Cię nagrodzić, jeśli wygra. Niestety, jej zwycięstwo nie jest gwarantowane. Istnieje konkurencyjna kampania „Karolina na cesarzową Układu Słonecznego”. Jeśli Karolina wygra i dowie się, że wsparłeś Alicję, będziesz w kłopotach. Więc nie możesz po prostu przelać 200 ETH ze swojego konta na konto Alicji. + +Rozwiązaniem jest [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564). Ten ERC wyjaśnia, jak używać [ukrytych adresów](https://nerolation.github.io/stealth-utils) do anonimowych transferów. + +**Ostrzeżenie**: Kryptografia stojąca za ukrytymi adresami jest, o ile nam wiadomo, solidna. Istnieją jednak potencjalne ataki typu side-channel. [Poniżej](#go-wrong) zobaczysz, co możesz zrobić, aby zmniejszyć to ryzyko. + +## Jak działają ukryte adresy {#how} + +Ten artykuł spróbuje wyjaśnić działanie ukrytych adresów na dwa sposoby. Pierwszy to [jak z nich korzystać](#how-use). Ta część jest wystarczająca do zrozumienia reszty artykułu. Następnie znajduje się [wyjaśnienie matematyki stojącej za tym mechanizmem](#how-math). Jeśli interesujesz się kryptografią, przeczytaj również tę część. + +### Wersja prosta (jak używać ukrytych adresów) {#how-use} + +Alicja tworzy dwa klucze prywatne i publikuje odpowiadające im klucze publiczne (które można połączyć w jeden meta-adres o podwójnej długości). Bill również tworzy klucz prywatny i publikuje odpowiadający mu klucz publiczny. + +Używając klucza publicznego jednej strony i klucza prywatnego drugiej, można uzyskać wspólny sekret znany tylko Alicji i Billowi (nie można go uzyskać z samych kluczy publicznych). Używając tego wspólnego sekretu, Bill uzyskuje ukryty adres i może na niego wysyłać aktywa. + +Alicja również uzyskuje adres ze wspólnego sekretu, ale ponieważ zna klucze prywatne do opublikowanych przez siebie kluczy publicznych, może również uzyskać klucz prywatny, który pozwala jej wypłacić środki z tego adresu. + +### Matematyka (dlaczego ukryte adresy działają w ten sposób) {#how-math} + +Standardowe ukryte adresy używają [kryptografii krzywych eliptycznych (ECC)](https://blog.cloudflare.com/a-relatively-easy-to-understand-primer-on-elliptic-curve-cryptography/#elliptic-curves-building-blocks-of-a-better-trapdoor), aby uzyskać lepszą wydajność przy mniejszej liczbie bitów klucza, zachowując jednocześnie ten sam poziom bezpieczeństwa. Jednak w większości możemy to zignorować i udawać, że używamy zwykłej arytmetyki. + +Istnieje liczba, którą wszyscy znają, _G_. Można mnożyć przez _G_. Ale ze względu na naturę ECC, praktycznie niemożliwe jest dzielenie przez _G_. Sposób, w jaki ogólnie działa kryptografia klucza publicznego w Ethereum, polega na tym, że można użyć klucza prywatnego, _Ppriv_, do podpisywania transakcji, które są następnie weryfikowane przez klucz publiczny, _Ppub = GPpriv_. + +Alicja tworzy dwa klucze prywatne, _Kpriv_ i _Vpriv_. _Kpriv_ będzie używany do wydawania pieniędzy z ukrytego adresu, a _Vpriv_ do przeglądania adresów należących do Alicji. Alicja następnie publikuje klucze publiczne: _Kpub = GKpriv_ i _Vpub = GVpriv_ + +Bill tworzy trzeci klucz prywatny, _Rpriv_, i publikuje _Rpub = GRpriv_ w centralnym rejestrze (Bill mógłby również wysłać go do Alicji, ale zakładamy, że Karolina podsłuchuje). + +Bill oblicza _RprivVpub = GRprivVpriv_, co, jak oczekuje, Alicja również zna (wyjaśniono poniżej). Ta wartość nazywana jest _S_, wspólnym sekretem. Daje to Billowi klucz publiczny, _Ppub = Kpub+G\*hasz(S)_. Na podstawie tego klucza publicznego może obliczyć adres i wysłać na niego dowolne zasoby. W przyszłości, jeśli Alicja wygra, Bill może podać jej _Rpriv_, aby udowodnić, że zasoby pochodzą od niego. + +Alicja oblicza _RpubVpriv = GRprivVpriv_. Daje jej to ten sam wspólny sekret, _S_. Ponieważ zna klucz prywatny, _Kpriv_, może obliczyć _Ppriv = Kpriv+hasz(S)_. Ten klucz pozwala jej na dostęp do aktywów pod adresem wynikającym z _Ppub = GPpriv = GKpriv+G\*hasz(S) = Kpub+G\*hasz(S)_. + +Mamy oddzielny klucz do przeglądania, aby umożliwić Alicji zlecenie usług firmie Dave's World Domination Campaign Services. Alicja jest skłonna pozwolić Dave'owi poznać adresy publiczne i informować ją, gdy dostępne będą kolejne pieniądze, ale nie chce, aby wydawał on pieniądze z jej kampanii. + +Ponieważ przeglądanie i wydawanie używają oddzielnych kluczy, Alicja może dać Dave'owi _Vpriv_. Wtedy Dave może obliczyć _S = RpubVpriv = GRprivVpriv_ i w ten sposób uzyskać klucze publiczne (_Ppub = Kpub+G\*hasz(S)_). Ale bez _Kpriv_ Dave nie może uzyskać klucza prywatnego. + +Podsumowując, oto wartości znane przez różnych uczestników. + +| Alicja | Opublikowane | Bill | Dave | | +| ------------------------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------- | +| G | G | G | G | | +| _Kpriv_ | – | – | – | | +| _Vpriv_ | – | – | _Vpriv_ | | +| _Kpub = GKpriv_ | _Kpub_ | _Kpub_ | _Kpub_ | | +| _Vpub = GVpriv_ | _Vpub_ | _Vpub_ | _Vpub_ | | +| – | – | _Rpriv_ | – | | +| _Rpub_ | _Rpub_ | _Rpub = GRpriv_ | _Rpub_ | | +| _S = RpubVpriv = GRprivVpriv_ | – | _S = RprivVpub = GRprivVpriv_ | _S = _RpubVpriv_ = GRprivVpriv_ | | +| _Ppub = Kpub+G\*hasz(S)_ | – | _Ppub = Kpub+G\*hasz(S)_ | _Ppub = Kpub+G\*hasz(S)_ | | +| _Adres=f(Ppub)_ | – | _Adres=f(Ppub)_ | _Adres=f(Ppub)_ | _Adres=f(Ppub)_ | +| _Ppriv = Kpriv+hasz(S)_ | – | – | – | | + +## Kiedy ukryte adresy zawodzą {#go-wrong} + +_Na blockchainie nie ma żadnych tajemnic_. Chociaż ukryte adresy mogą zapewnić Ci prywatność, jest ona podatna na analizę ruchu. Jako trywialny przykład wyobraź sobie, że Bill zasila adres i natychmiast wysyła transakcję w celu opublikowania wartości _Rpub_. Bez _Vpriv_ Alicji nie możemy być pewni, że jest to ukryty adres, ale tak należy zakładać. Następnie widzimy inną transakcję, która przenosi całe ETH z tego adresu na adres funduszu kampanii Alicji. Możemy nie być w stanie tego udowodnić, ale jest prawdopodobne, że Bill właśnie przekazał darowiznę na kampanię Alicji. Karolina z pewnością by tak pomyślała. + +Bill może łatwo oddzielić publikację _Rpub_ od zasilenia ukrytego adresu (wykonać je w różnym czasie, z różnych adresów). To jednak nie wystarczy. Wzorzec, którego szuka Karolina, polega na tym, że Bill zasila adres, a następnie fundusz kampanii Alicji wypłaca z niego środki. + +Jednym z rozwiązań jest to, aby kampania Alicji nie wypłacała pieniędzy bezpośrednio, ale używała ich do zapłaty stronie trzeciej. Jeśli kampania Alicji wyśle 10 ETH do Dave's World Domination Campaign Services, Karolina będzie wiedziała tylko, że Bill przekazał darowiznę jednemu z klientów Dave'a. Jeśli Dave ma wystarczającą liczbę klientów, Karolina nie będzie w stanie stwierdzić, czy Bill przekazał darowiznę Alicji, która z nią konkuruje, czy Adamowi, Albertowi lub Abigail, na których Karolinie nie zależy. Alicja może dołączyć do płatności zahaszowaną wartość, a następnie dostarczyć Dave'owi jej preobraz, aby udowodnić, że była to jej darowizna. Alternatywnie, jak wspomniano powyżej, jeśli Alicja da Dave'owi swoje _Vpriv_, on już wie, od kogo pochodzi płatność. + +Głównym problemem tego rozwiązania jest to, że wymaga ono od Alicji dbania o tajemnicę, gdy ta tajemnica przynosi korzyści Billowi. Alicja może chcieć utrzymać swoją reputację, aby przyjaciel Billa, Bob, również przekazał jej darowiznę. Ale możliwe jest również, że nie będzie jej przeszkadzało zdemaskowanie Billa, ponieważ wtedy będzie się on bał, co się stanie, jeśli Karolina wygra. Bill może w końcu udzielić Alicji jeszcze większego wsparcia. + +### Używanie wielu warstw ukrytych {#multi-layer} + +Zamiast polegać na Alicji w kwestii zachowania prywatności Billa, Bill może zrobić to sam. Może wygenerować wiele meta-adresów dla fikcyjnych osób, Boba i Belli. Następnie Bill wysyła ETH do Boba, a „Bob” (który w rzeczywistości jest Billem) wysyła je do Belli. „Bella” (również Bill) wysyła je do Alicji. + +Karolina wciąż może przeprowadzić analizę ruchu i zobaczyć potok od Billa do Boba, do Belli, do Alicji. Jednakże, jeśli „Bob” i „Bella” również używają ETH do innych celów, nie będzie wyglądało na to, że Bill przekazał cokolwiek Alicji, nawet jeśli Alicja natychmiast wypłaci środki z ukrytego adresu na swój znany adres kampanii. + +## Pisanie aplikacji z ukrytymi adresami {#write-app} + +Ten artykuł wyjaśnia aplikację z ukrytymi adresami [dostępną na GitHub](https://github.com/qbzzt/251022-stealth-addresses.git). + +### Narzędzia {#tools} + +Istnieje [biblioteka ukrytych adresów w TypeScript](https://github.com/ScopeLift/stealth-address-sdk), której moglibyśmy użyć. Jednak operacje kryptograficzne mogą być intensywne dla procesora. Wolę je implementować w języku kompilowanym, takim jak [Rust](https://rust-lang.org/), i używać [WASM](https://webassembly.org/) do uruchamiania kodu w przeglądarce. + +Będziemy używać [Vite](https://vite.dev/) i [React](https://react.dev/). Są to standardowe narzędzia branżowe; jeśli ich nie znasz, możesz skorzystać z [tego samouczka](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/). Aby używać Vite, potrzebujemy Node. + +### Zobacz ukryte adresy w akcji {#in-action} + +1. Zainstaluj niezbędne narzędzia: [Rust](https://rust-lang.org/tools/install/) i [Node](https://nodejs.org/en/download). + +2. Sklonuj repozytorium GitHub. + + ```sh + git clone https://github.com/qbzzt/251022-stealth-addresses.git + cd 251022-stealth-addresses + ``` + +3. Zainstaluj wymagania wstępne i skompiluj kod Rust. + + ```sh + cd src/rust-wasm + rustup target add wasm32-unknown-unknown + cargo install wasm-pack + wasm-pack build --target web + ``` + +4. Uruchom serwer WWW. + + ```sh + cd ../.. + npm install + npm run dev + ``` + +5. Przejdź do [aplikacji](http://localhost:5173/). Ta strona aplikacji ma dwie ramki: jedną dla interfejsu użytkownika Alicji, a drugą dla Billa. Te dwie ramki nie komunikują się ze sobą; znajdują się na tej samej stronie tylko dla wygody. + +6. Jako Alicja kliknij **Wygeneruj ukryty meta-adres**. Wyświetli to nowy ukryty adres i odpowiadające mu klucze prywatne. Skopiuj ukryty meta-adres do schowka. + +7. Jako Bill, wklej nowy ukryty meta-adres i kliknij **Wygeneruj adres**. Otrzymasz adres do zasilenia dla Alicji. + +8. Skopiuj adres i klucz publiczny Billa i wklej je w polu „Klucz prywatny dla adresu wygenerowanego przez Billa” w interfejsie użytkownika Alicji. Po wypełnieniu tych pól zobaczysz klucz prywatny umożliwiający dostęp do aktywów pod tym adresem. + +9. Możesz użyć [kalkulatora online](https://iancoleman.net/ethereum-private-key-to-address/), aby upewnić się, że klucz prywatny odpowiada adresowi. + +### Jak działa program {#how-the-program-works} + +#### Komponent WASM {#wasm} + +Kod źródłowy, który kompiluje się do WASM, jest napisany w języku [Rust](https://rust-lang.org/). Możesz go zobaczyć w [`src/rust_wasm/src/lib.rs`](https://github.com/qbzzt/251022-stealth-addresses/blob/main/src/rust-wasm/src/lib.rs). Ten kod jest przede wszystkim interfejsem między kodem JavaScript a [biblioteką `eth-stealth-addresses`](https://github.com/kassandraoftroy/eth-stealth-addresses). + +**`Cargo.toml`** + +[`Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html) w Rust jest analogiczny do [`package.json`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json) w JavaScript. Zawiera informacje o pakiecie, deklaracje zależności itp. + +```toml +[package] +name = "rust-wasm" +version = "0.1.0" +edition = "2024" + +[dependencies] +eth-stealth-addresses = "0.1.0" +hex = "0.4.3" +wasm-bindgen = "0.2.104" +getrandom = { version = "0.2", features = ["js"] } +``` + +Pakiet [`getrandom`](https://docs.rs/getrandom/latest/getrandom/) musi generować wartości losowe. Nie można tego zrobić czysto algorytmicznymi środkami; wymaga to dostępu do procesu fizycznego jako źródła entropii. Ta definicja określa, że uzyskamy tę entropię, pytając przeglądarkę, w której działamy. + +```toml +console_error_panic_hook = "0.1.7" +``` + +[Ta biblioteka](https://docs.rs/console_error_panic_hook/latest/console_error_panic_hook/) dostarcza nam bardziej znaczących komunikatów o błędach, gdy kod WASM wpadnie w panikę i nie może kontynuować. + +```toml +[lib] +crate-type = ["cdylib", "rlib"] +``` + +Typ wyjściowy wymagany do wyprodukowania kodu WASM. + +**`lib.rs`** + +To jest właściwy kod Rust. + +```rust +use wasm_bindgen::prelude::*; +``` + +Definicje do stworzenia pakietu WASM z Rusta. Są one udokumentowane [tutaj](https://wasm-bindgen.github.io/wasm-bindgen/reference/attributes/index.html). + +```rust +use eth_stealth_addresses::{ + generate_stealth_meta_address, + generate_stealth_address, + compute_stealth_key +}; +``` + +Funkcje, których potrzebujemy z [biblioteki `eth-stealth-addresses`](https://github.com/kassandraoftroy/eth-stealth-addresses). + +```rust +use hex::{decode,encode}; +``` + +Rust zazwyczaj używa tablic bajtów ([arrays](https://doc.rust-lang.org/std/primitive.array.html)) (`[u8; ]`) dla wartości. Ale w JavaScript zazwyczaj używamy ciągów szesnastkowych. [Biblioteka `hex`](https://docs.rs/hex/latest/hex/) tłumaczy dla nas z jednej reprezentacji na drugą. + +```rust +#[wasm_bindgen] +``` + +Generuj powiązania WASM, aby móc wywołać tę funkcję z JavaScript. + +```rust +pub fn wasm_generate_stealth_meta_address() -> String { +``` + +Najprostszym sposobem na zwrócenie obiektu z wieloma polami jest zwrócenie ciągu znaków JSON. + +```rust + let (address, spend_private_key, view_private_key) = + generate_stealth_meta_address(); +``` + +Funkcja [`generate_stealth_meta_address`](https://docs.rs/eth-stealth-addresses/latest/eth_stealth_addresses/fn.generate_stealth_meta_address.html) zwraca trzy pola: + +- Meta-adres (_Kpub_ i _Vpub_) +- Klucz prywatny do przeglądania (_Vpriv_) +- Klucz prywatny do wydawania (_Kpriv_) + +Składnia [tupli](https://doc.rust-lang.org/std/primitive.tuple.html) pozwala nam ponownie rozdzielić te wartości. + +```rust + format!("{{\"address\":\"{}\",\"view_private_key\":\"{}\",\"spend_private_key\":\"{}\"}}", + encode(address), + encode(view_private_key), + encode(spend_private_key) + ) +} +``` + +Użyj makra [`format!`](https://doc.rust-lang.org/std/fmt/index.html), aby wygenerować ciąg znaków w formacie JSON. Użyj [`hex::encode`](https://docs.rs/hex/latest/hex/fn.encode.html), aby zamienić tablice na ciągi szesnastkowe. + +```rust +fn str_to_array(s: &str) -> Option<[u8; N]> { +``` + +Ta funkcja zamienia ciąg szesnastkowy (dostarczony przez JavaScript) na tablicę bajtów. Używamy jej do parsowania wartości dostarczonych przez kod JavaScript. Ta funkcja jest skomplikowana ze względu na sposób, w jaki Rust obsługuje tablice i wektory. + +Wyrażenie `` nazywane jest [generykiem](https://doc.rust-lang.org/book/ch10-01-syntax.html). `N` jest parametrem, który kontroluje długość zwracanej tablicy. Funkcja jest w rzeczywistości wywoływana jako `str_to_array::`, gdzie `n` to długość tablicy. + +Wartość zwracana to `Option<[u8; N]>`, co oznacza, że zwracana tablica jest [opcjonalna](https://doc.rust-lang.org/std/option/). Jest to typowy wzorzec w Rust dla funkcji, które mogą zakończyć się niepowodzeniem. + +Na przykład, jeśli wywołamy `str_to_array::10("bad060a7")`, funkcja ma zwrócić tablicę dziesięciu wartości, ale dane wejściowe mają tylko cztery bajty. Funkcja musi zakończyć się niepowodzeniem i robi to, zwracając `None`. Wartością zwrotną dla `str_to_array::4("bad060a7")` byłoby `Some<[0xba, 0xd0, 0x60, 0xa7]>`. + +```rust + // decode returns Result, _> + let vec = decode(s).ok()?; +``` + +Funkcja [`hex::decode`](https://docs.rs/hex/latest/hex/fn.decode.html) zwraca `Result, FromHexError>`. Typ [`Result`](https://doc.rust-lang.org/std/result/) może zawierać pomyślny wynik (`Ok(value)`) lub błąd (`Err(error)`). + +Metoda `.ok()` zamienia `Result` na `Option`, której wartością jest albo wartość `Ok()`, jeśli operacja się powiedzie, albo `None`, jeśli nie. Na koniec, [operator znaku zapytania](https://doc.rust-lang.org/std/option/#the-question-mark-operator-) przerywa bieżącą funkcję i zwraca `None`, jeśli `Option` jest puste. W przeciwnym razie odpakowuje wartość i zwraca ją (w tym przypadku, aby przypisać wartość do `vec`). + +Wygląda to na dziwnie zawiłą metodę obsługi błędów, ale `Result` i `Option` zapewniają, że wszystkie błędy są obsługiwane, w ten czy inny sposób. + +```rust + if vec.len() != N { return None; } +``` + +Jeśli liczba bajtów jest nieprawidłowa, jest to błąd i zwracamy `None`. + +```rust + // try_into consumes vec and attempts to make [u8; N] + let array: [u8; N] = vec.try_into().ok()?; +``` + +Rust ma dwa typy tablic. [Tablice](https://doc.rust-lang.org/std/primitive.array.html) mają stały rozmiar. [Wektory](https://doc.rust-lang.org/std/vec/index.html) mogą rosnąć i maleć. `hex::decode` zwraca wektor, ale biblioteka `eth_stealth_addresses` chce otrzymywać tablice. [`.try_into()`](https://doc.rust-lang.org/std/convert/trait.TryInto.html#required-methods) konwertuje wartość na inny typ, na przykład wektor na tablicę. + +```rust + Some(array) +} +``` + +Rust nie wymaga użycia słowa kluczowego [`return`](https://doc.rust-lang.org/std/keyword.return.html) przy zwracaniu wartości na końcu funkcji. + +```rust +#[wasm_bindgen] +pub fn wasm_generate_stealth_address(stealth_address: &str) -> Option { +``` + +Ta funkcja otrzymuje publiczny meta-adres, który zawiera zarówno _Vpub_, jak i _Kpub_. Zwraca ukryty adres, klucz publiczny do opublikowania (_Rpub_) oraz jednobajtową wartość skanowania, która przyspiesza identyfikację, które opublikowane adresy mogą należeć do Alicji. + +Wartość skanowania jest częścią wspólnego sekretu (_S = GRprivVpriv_). Ta wartość jest dostępna dla Alicji, a jej sprawdzenie jest znacznie szybsze niż sprawdzenie, czy _f(Kpub+G\*hasz(S))_ jest równe opublikowanemu adresowi. + +```rust + let (address, r_pub, scan) = + generate_stealth_address(&str_to_array::<66>(stealth_address)?); +``` + +Używamy biblioteki [`generate_stealth_address`](https://docs.rs/eth-stealth-addresses/latest/eth_stealth_addresses/fn.generate_stealth_address.html). + +```rust + format!("{{\"address\":\"{}\",\"rPub\":\"{}\",\"scan\":\"{}\"}}", + encode(address), + encode(r_pub), + encode(&[scan]) + ).into() +} +``` + +Przygotuj ciąg wyjściowy zakodowany w formacie JSON. + +```rust +#[wasm_bindgen] +pub fn wasm_compute_stealth_key( + address: &str, + bill_pub_key: &str, + view_private_key: &str, + spend_private_key: &str +) -> Option { + . + . + . +} +``` + +Ta funkcja używa biblioteki [`compute_stealth_key`](https://docs.rs/eth-stealth-addresses/latest/eth_stealth_addresses/fn.compute_stealth_key.html) do obliczenia klucza prywatnego do wypłaty z adresu (_Rpriv_). To obliczenie wymaga następujących wartości: + +- Adres (_Adres=f(Ppub)_) +- Klucz publiczny wygenerowany przez Billa (_Rpub_) +- Klucz prywatny do przeglądania (_Vpriv_) +- Klucz prywatny do wydawania (_Kpriv_) + +```rust +#[wasm_bindgen(start)] +``` + +[`#[wasm_bindgen(start)]`](https://wasm-bindgen.github.io/wasm-bindgen/reference/attributes/on-rust-exports/start.html) określa, że funkcja jest wykonywana po zainicjowaniu kodu WASM. + +```rust +pub fn main() { + console_error_panic_hook::set_once(); +} +``` + +Ten kod określa, że dane wyjściowe paniki są wysyłane do konsoli JavaScript. Aby zobaczyć to w działaniu, użyj aplikacji i podaj Billowi nieprawidłowy meta-adres (wystarczy zmienić jedną cyfrę szesnastkową). W konsoli JavaScript zobaczysz następujący błąd: + +``` +rust_wasm.js:236 panicked at /home/ori/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/subtle-2.6.1/src/lib.rs:701:9: +assertion `left == right` failed + left: 0 + right: 1 +``` + +Po którym następuje ślad stosu. Następnie podaj Billowi prawidłowy meta-adres, a Alicji nieprawidłowy adres lub nieprawidłowy klucz publiczny. Zobaczysz następujący błąd: + +``` +rust_wasm.js:236 panicked at /home/ori/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/eth-stealth-addresses-0.1.0/src/lib.rs:78:9: +klucze nie generują ukrytego adresu +``` + +Ponownie, po którym następuje ślad stosu. + +#### Interfejs użytkownika {#ui} + +Interfejs użytkownika jest napisany przy użyciu [React](https://react.dev/) i serwowany przez [Vite](https://vite.dev/). Możesz dowiedzieć się o nich, korzystając z [tego samouczka](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/). Nie ma tu potrzeby używania [WAGMI](https://wagmi.sh/), ponieważ nie wchodzimy w bezpośrednią interakcję z blockchainem ani portfelem. + +Jedyną nieoczywistą częścią interfejsu użytkownika jest łączność z WASM. Oto jak to działa. + +**`vite.config.js`** + +Ten plik zawiera [konfigurację Vite](https://vite.dev/config/). + +```js +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import wasm from "vite-plugin-wasm"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(), wasm()], +}) +``` + +Potrzebujemy dwóch wtyczek Vite: [react](https://www.npmjs.com/package/@vitejs/plugin-react) i [wasm](https://github.com/Menci/vite-plugin-wasm#readme). + +**`App.jsx`** + +Ten plik jest głównym komponentem aplikacji. Jest to kontener, który zawiera dwa komponenty: `Alice` i `Bill`, interfejsy użytkownika dla tych użytkowników. Istotną częścią dla WASM jest kod inicjalizacyjny. + +```jsx +import init from './rust-wasm/pkg/rust_wasm.js' +``` + +Gdy używamy [`wasm-pack`](https://rustwasm.github.io/docs/wasm-pack/), tworzy on dwa pliki, których tu używamy: plik wasm z właściwym kodem (tutaj, `src/rust-wasm/pkg/rust_wasm_bg.wasm`) oraz plik JavaScript z definicjami do jego użycia (tutaj, `src/rust_wasm/pkg/rust_wasm.js`). Domyślny eksport tego pliku JavaScript to kod, który musi zostać uruchomiony, aby zainicjować WASM. + +```jsx +function App() { + . + . + . + useEffect(() => { + const loadWasm = async () => { + try { + await init(); + setWasmReady(true) + } catch (err) { + console.error('Error loading wasm:', err) + alert("Wasm error: " + err) + } + } + + loadWasm() + }, [] + ) +``` + +Hak [`useEffect`](https://react.dev/reference/react/useEffect) pozwala określić funkcję, która jest wykonywana, gdy zmieniają się zmienne stanu. Tutaj lista zmiennych stanu jest pusta (`[]`), więc ta funkcja jest wykonywana tylko raz, gdy strona się ładuje. + +Funkcja efektu musi natychmiast zwrócić wartość. Aby użyć kodu asynchronicznego, takiego jak `init` WASM (który musi załadować plik `.wasm` i dlatego wymaga czasu), definiujemy wewnętrzną funkcję [`async`](https://en.wikipedia.org/wiki/Async/await) i uruchamiamy ją bez `await`. + +**`Bill.jsx`** + +To jest interfejs użytkownika dla Billa. Ma jedną akcję: tworzenie adresu na podstawie ukrytego meta-adresu dostarczonego przez Alicję. + +```jsx +import { wasm_generate_stealth_address } from './rust-wasm/pkg/rust_wasm.js' +``` + +Oprócz domyślnego eksportu, kod JavaScript wygenerowany przez `wasm-pack` eksportuje funkcję dla każdej funkcji w kodzie WASM. + +```jsx +