From 3e9610b93471dd7cf658669dce9ba75357b77569 Mon Sep 17 00:00:00 2001 From: Joshua <62268199+minimalsm@users.noreply.github.com> Date: Sat, 14 Feb 2026 00:01:19 +0000 Subject: [PATCH] i18n(ja): translation import part 07 of 13 (24 files) --- .../index.md | 180 +-- .../tutorials/all-you-can-cache/index.md | 866 ++++++++++++ .../developers/tutorials/app-plasma/index.md | 1255 +++++++++++++++++ .../index.md | 50 +- .../index.md | 585 ++++++++ .../index.md | 78 +- .../index.md | 372 +++++ .../index.md | 82 +- .../index.md | 66 +- .../erc-721-vyper-annotated-code/index.md | 526 ++++--- .../tutorials/erc20-annotated-code/index.md | 477 ++++--- .../erc20-with-safety-rails/index.md | 126 +- .../tutorials/ethereum-for-web2-auth/index.md | 886 ++++++++++++ .../index.md | 91 +- .../index.md | 119 +- .../index.md | 616 ++++---- .../hello-world-smart-contract/index.md | 190 ++- .../index.md | 38 +- .../tutorials/how-to-mint-an-nft/index.md | 164 +-- .../index.md | 32 +- .../index.md | 245 ++-- .../index.md | 283 ++-- .../index.md | 157 +-- .../how-to-use-tellor-as-your-oracle/index.md | 31 +- 24 files changed, 5754 insertions(+), 1761 deletions(-) create mode 100644 public/content/translations/ja/developers/tutorials/all-you-can-cache/index.md create mode 100644 public/content/translations/ja/developers/tutorials/app-plasma/index.md create mode 100644 public/content/translations/ja/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md create mode 100644 public/content/translations/ja/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md create mode 100644 public/content/translations/ja/developers/tutorials/ethereum-for-web2-auth/index.md diff --git a/public/content/translations/ja/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md b/public/content/translations/ja/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md index fc714c9ba56..97cfd4adbd8 100644 --- a/public/content/translations/ja/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md +++ b/public/content/translations/ja/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md @@ -1,33 +1,32 @@ --- -title: Pythonデベロッパーのためのイーサリアム入門、パート1 -description: イーサリアム開発の概要。特に、プログラミング言語であるPythonの知識があるデベロッパーに役立つ情報 +title: "Pythonデベロッパーのためのイーサリアム入門、パート1" +description: "イーサリアム開発の概要。特に、プログラミング言語であるPythonの知識があるデベロッパーに役立つ情報" author: Marc Garreau lang: ja -tags: - - "python" - - "web3.py" +tags: [ "python", "web3.py" ] skill: beginner published: 2020-09-08 source: Snake charmers sourceUrl: https://snakecharmers.ethereum.org/a-developers-guide-to-ethereum-pt-1/ --- -これまで耳にしてきたイーサリアムの世界へ飛び込む準備はできましたか? この投稿では、ブロックチェーンの基礎について簡単に説明し、次に、シミュレートされたイーサリアムノードと対話(ブロックデータの読み取り、アカウント残高の確認、トランザクションの送信)する方法について説明します。 その過程で、アプリを構築する従来の方法とこの新しい分散型パラダイムの違いに焦点を当てます。 +これまで耳にしてきたイーサリアムの世界へ飛び込む準備はできましたか? この投稿では、ブロックチェーンの基礎について簡単に説明し、次に、シミュレートされたイーサリアムノードとの対話(ブロックデータの読み取り、アカウント残高の確認、トランザクションの送信)の方法について説明します。 その過程で、アプリを構築する従来の方法とこの新しい分散型パラダイムの違いに焦点を当てます。 -## 前提条件(ソフト) {#soft-prerequisites} +## (ソフトな)前提条件 {#soft-prerequisites} -この投稿は、幅広いデベロッパーに参照していただきたいと考えています。 [ Pythonツール](/developers/docs/programming-languages/python/)を使用しますが、これらは概念を伝える手段にすぎませんので、Pythonデベロッパーでなくても問題ありません。 しかし、ここでは皆さんがPythonについての知識があることを前提に話を進めますので、すぐにイーサリアムに特化した部分の説明に移ります。 +この投稿は、幅広いデベロッパーに参照していただきたいと考えています。 [Pythonツール](/developers/docs/programming-languages/python/)を使用しますが、これらは概念を伝える手段にすぎませんので、Pythonデベロッパーでなくても問題ありません。 しかし、ここでは皆さんがすでに知っていることを前提に話を進めますので、すぐにイーサリアムに特化した部分の説明に移ります。 -前提知識: +前提知識: -- ターミナルを操作できる。 -- Pythonで数行のコードを書いたことがある。 -- Pythonバージョン3.6以降がマシンにインストールされている([仮想環境](https://realpython.com/effective-python-environment/#virtual-environments)の使用を強くお勧めします)。 -- Pythonのパッケージインストーラーである`pip`を使用したことがある。 これらのうちあてはまらないものがある方、あるいはこの記事にあるコードを実行することがない方でも十分に理解できる内容です。 +- ターミナルを操作できる、 +- Pythonで数行のコードを書いたことがある、 +- お使いのマシンにPythonバージョン3.6以降がインストールされていること([仮想環境](https://realpython.com/effective-python-environment/#virtual-environments)の使用を強く推奨します)、そして +- Pythonのパッケージインストーラーである`pip`を使用したことがある。 + もしこれらのいずれかに当てはまらない場合や、この記事のコードを再現する予定がない場合でも、問題なく読み進めることができるでしょう。 -## ブロックチェーンについて {#blockchains-briefly} +## ブロックチェーンの概要 {#blockchains-briefly} -イーサリアムを説明する方法はたくさんありますが、その中心となるのはブロックチェーンです。 ブロックチェーンは一連のブロックで構成されています。まずはその説明から始めましょう。 簡単に言うと、イーサリアムブロックチェーンの各ブロックは、数値などのメタデータとトランザクションのリストにすぎません。 JSON形式では、次のようになります。 +イーサリアムを説明する方法はたくさんありますが、その中心となるのはブロックチェーンです。 ブロックチェーンは一連のブロックで構成されています。まずはその説明から始めましょう。 簡単に言うと、イーサリアムブロックチェーンの各ブロックは、いくつかのメタデータとトランザクションのリストにすぎません。 JSON形式では、次のようになります。 ```json { @@ -39,81 +38,85 @@ sourceUrl: https://snakecharmers.ethereum.org/a-developers-guide-to-ethereum-pt- } ``` -各[ブロック](/developers/docs/blocks/)は、その前のブロックを参照します。`parentHash`は、単に前のブロックのハッシュ値です。 +各[ブロック](/developers/docs/blocks/)は、その前のブロックへの参照を持ちます。`parentHash`は、単に前のブロックのハッシュです。 -注: イーサリアムは ハッシュ関数を定期的に使用して、固定サイズの値(ハッシュ)を生成します。 イーサリアムではハッシュ値が重要な役割を果たしますが、今のところは固有のIDと考えておくとよいでしょう。 +注: イーサリアムはハッシュ関数を定期的に使用して、固定サイズの値(「ハッシュ」)を生成します。 ハッシュはイーサリアムで重要な役割を果たしますが、今のところはユニークIDとして考えて差し支えありません。 -![ブロックチェーンと各ブロック内のデータを表す図](./blockchain-diagram.png) +![ブロックチェーンと各ブロック内のデータを描いた図](./blockchain-diagram.png) -_ブロックチェーンは基本的にはリンクリストであり、各ブロックは前のブロックを参照します。_ +_ブロックチェーンは本質的にリンクリストであり、各ブロックは前のブロックへの参照を持ちます。_ -このデータ構造は目新しいものではありませんが、ネットワークを管理するルール(つまり、ピアツーピアプロトコル)は目新しいものです。 中央集権型ではないため、ピアのネットワークは連携してネットワークを維持する必要がありますが、次のブロックに含めるトランザクションを決定する際には競い合うことになります。 例えば、友人に送金する場合、トランザクションをネットワークにブロードキャストして、そのトランザクションが次のブロックに含まれるのを待つ必要があります。 +このデータ構造は新しいものではありませんが、ネットワークを統制するルール(ピアツーピアプロトコル)は新しいものです。 中央機関は存在しません。ピアのネットワークは、ネットワークを維持するために協調しなければならず、次のブロックにどのトランザクションを含めるかを決定するために競争しなければなりません。 ですから、友人に送金したいときは、そのトランザクションをネットワークにブロードキャストし、それが次のブロックに含まれるのを待つ必要があります。 -ブロックチェーンにおいて、あるユーザーから別のユーザーに本当に送金されたのかを検証するための唯一の手段は、そのブロックチェーンに固有の(そのブロックチェーンで作成され管理される)通貨を使用することです。 イーサリアムでは、この通貨は「イーサ(ETH)」と呼ばれます。イーサリアムブロックチェーンには、アカウント残高に関する唯一の公式な記録が含まれています。 +ブロックチェーンがあるユーザーから別のユーザーへのお金が本当に送金されたことを検証する唯一の方法は、そのブロックチェーンに固有の(つまり、そのブロックチェーンによって作成され、管理される)通貨を使用することです。 イーサリアムでは、この通貨はイーサと呼ばれ、イーサリアムブロックチェーンにはアカウント残高の唯一の公式記録が含まれています。 ## 新しいパラダイム {#a-new-paradigm} -この新しい分散型技術スタックは、新しいデベロッパーツールを生み出しました。 このようなツールは多くのプログラミング言語に存在しますが、今回はPythonを例にとって説明します。 繰り返しになりますが、Python以外の言語をご使用の場合でも、内容を理解するのはそれほど難しいことではありません。 +この新しい分散型技術スタックは、新しいデベロッパーツールを生み出しました。 このようなツールは多くのプログラミング言語に存在しますが、今回はPythonを例にとって説明します。 繰り返しになりますが、Pythonがあなたの選んだ言語でなくても、ついていくのにそれほど問題はないはずです。 -イーサリアムと対話する必要があるPythonデベロッパーは、[Web3.py](https://web3py.readthedocs.io/)にアクセスしてください。 Web3.pyは、イーサリアムノードへの接続と、そのノードとのデータの送受信を簡単に行えるようにするライブラリです。注: 「イーサリアムノード」と「イーサリアムクライアント」は同じ意味で使用されます。 どちらもイーサリアムネットワークの参加者が実行するソフトウェアを指します。 このソフトウェアは、ブロックデータの読み取り、新しいブロックがチェーンに追加されたときの更新データの受信、新しいトランザクションのブロードキャストなどを行います。 技術的には、クライアントはソフトウェアを意味し、ノードはソフトウェアを実行しているコンピュータを意味します。 +イーサリアムと対話したいPythonデベロッパーは、[Web3.py](https://web3py.readthedocs.io/)に手を伸ばすことになるでしょう。 Web3.pyは、イーサリアムノードへの接続、およびノードとのデータの送受信を大幅に簡素化するライブラリです。 -[イーサリアムクライアント](/developers/docs/nodes-and-clients/) は、[プロセス間通信(IPC)](https://wikipedia.org/wiki/Inter-process_communication)、HTTP、またはWebSocketで接続するように設定できるため、Web3.pyでも同じ設定にする必要があります。 Web3.pyは、これらの接続オプションを**プロバイダー**として参照します。 Web3.pyインスタンスをノードに接続するために、3つのプロバイダーのいずれかを選択する必要があります。 +注: 「イーサリアムノード」と「イーサリアムクライアント」は同じ意味で使用されます。 いずれの場合も、イーサリアムネットワークの参加者が実行するソフトウェアを指します。 このソフトウェアは、ブロックデータを読み取り、新しいブロックがチェーンに追加されたときに更新情報を受信し、新しいトランザクションをブロードキャストするなどの機能を持ちます。 技術的には、クライアントはソフトウェアであり、ノードはソフトウェアを実行しているコンピュータです。 -![Web3.pyがIPCを使用してアプリケーションをイーサリアムノードに接続する方法を表す図](./web3py-and-nodes.png) +[イーサリアムクライアント](/developers/docs/nodes-and-clients/)は、[IPC](https://wikipedia.org/wiki/Inter-process_communication)、HTTP、またはWebsocketsで到達可能に設定できるため、Web3.pyはこの設定を反映させる必要があります。 Web3.pyはこれらの接続オプションを**プロバイダー**と呼びます。 Web3.pyインスタンスをノードにリンクさせるには、3つのプロバイダーの中から1つを選択します。 -_同じプロトコル(この図ではプロセス間通信(IPC))を介して通信するようにイーサリアムノードとWeb3.pyを設定します。_ +![web3.pyがIPCを使用してアプリケーションをイーサリアムノードに接続する方法を示す図](./web3py-and-nodes.png) -Web3.pyが正しく設定できたら、ブロックチェーンとの対話を開始できます。 参考までに、Web3.pyの使用例をいくつか示します。 +_この図のIPCのように、イーサリアムノードとWeb3.pyが同じプロトコルで通信するように設定します。_ + +Web3.pyが正しく設定されると、ブロックチェーンとの対話を開始できます。 今後のプレビューとして、Web3.pyの使用例をいくつか紹介します。 ```python -# read block data: +# ブロックデータを読み込む: w3.eth.get_block('latest') -# send a transaction: +# トランザクションを送信する: w3.eth.send_transaction({'from': ..., 'to': ..., 'value': ...}) ``` ## インストール {#installation} -このチュートリアルでは、Pythonインタプリタ内で作業します。 ディレクトリ、ファイル、クラス、関数は作成しません。注: 以下の例では、「$」で始まるコマンドはターミナルで実行することを意味しています。 (「$」を入力しないでください。「$」は単に行の始まりを意味します。) +このウォークスルーでは、Pythonインタプリタ内でのみ作業します。 ディレクトリ、ファイル、クラス、関数は作成しません。 + +注: 以下の例では、`$`で始まるコマンドはターミナルで実行するためのものです。 (`$`は入力しないでください。行の始まりを示しているだけです。) -まず、使いやすい環境となるように[IPython](https://ipython.org/)をインストールしてください。 IPythonには、数ある機能の中でも特に便利なタブ補完機能があり、Web3.pyでの補完の候補を簡単に確認できます。 +まず、探索のためのユーザーフレンドリーな環境として、[IPython](https://ipython.org/)をインストールします。 IPythonは、タブ補完などの機能を提供しており、Web3.pyで何が可能かを確認するのが非常に簡単になります。 ```bash pip install ipython ``` -Web3.pyは`web3`という名前で公開されています。 次のようにしてインストールします。 +Web3.pyは`web3`という名前で公開されています。 次のようにインストールします。 ```bash pip install web3 ``` -あと1つ操作が必要です。後でブロックチェーンのシミュレーションを行いますが、それにはさらに数個の依存関係が必要となります。 それらは以下のコマンドでインストールできます。 +もう一つ、後でブロックチェーンをシミュレートしますが、それにはさらにいくつかの依存関係が必要です。 それらは次のようにインストールできます。 ```bash pip install 'web3[tester]' ``` -これで準備完了です。 +これで準備完了です! -注: `web3[tester]`パッケージは、Python 3.10.xxまで対応しています。 +注:`web3[tester]`パッケージは Python 3.10.xx まで動作します。 ## サンドボックスの起動 {#spin-up-a-sandbox} -ターミナルで`ipython`を実行し、新しいPython環境を開きます。 これは、`python`を実行するのと同じですが、より多くの機能を使用できます。 +ターミナルで`ipython`を実行して、新しいPython環境を開きます。 これは`python`の実行に似ていますが、より多くの追加機能が付属しています。 ```bash ipython ``` -実行しているPythonとIPythonのバージョンに関する情報が出力され、次のような入力待ちの状態になります。 +これにより、実行中のPythonとIPythonのバージョンに関する情報が出力され、入力を待つプロンプトが表示されます。 ```python In [1]: ``` -これは対話型のPythonシェルであり、 実質的には、さまざまなものを実行できるサンドボックスです。 ここまで完了したら、いよいよWeb3.pyをインポートします。 +現在、対話型のPythonシェルが表示されています。 基本的には、自由に試せるサンドボックスです。 ここまで来たら、Web3.pyをインポートしましょう。 ```python In [1]: from web3 import Web3 @@ -121,22 +124,22 @@ In [1]: from web3 import Web3 ## Web3モジュールの紹介 {#introducing-the-web3-module} -[Web3](https://web3py.readthedocs.io/en/stable/overview.html#base-api)モジュールは、イーサリアムへの入り口であるだけでなく、便利な関数も提供しています。 その一部をご紹介します。 +イーサリアムへのゲートウェイであることに加えて、[Web3](https://web3py.readthedocs.io/en/stable/overview.html#base-api)モジュールはいくつかの便利な機能を提供します。 いくつか見てみましょう。 -イーサリアムのアプリケーションでは、通常、通貨の単位を変換する必要があります。 Web3のモジュールには、この変換のためだけの[fromWei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.from_wei)や[toWei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.to_wei)のようなヘルパーメソッドがあります。 +イーサリアムアプリケーションでは、通貨の単位を変換する必要がよくあります。 Web3モジュールは、まさにこのためにいくつかのヘルパーメソッドを提供しています:[from_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.from_wei)と[to_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.to_wei)です。 -注: コンピュータは小数の計算処理が苦手です。 小数点以下を含む計算を避けるため、デベロッパーはしばしばドルをセントに換算して格納します。 例えば、価格が $5.99の商品は、599としてデータベースに保存されます。 +注:コンピュータは小数の計算が苦手なことで有名です。 これを回避するために、デベロッパーはドル額をセント単位で保存することがよくあります。 例えば、$5.99の価格のアイテムは、データベースに599として保存されることがあります。 -イーサ(ETH)でトランザクションを処理する場合も、同様のパターンが使用されます。 ただし、ETHの小数点は2桁ではなく、18桁です! ETHの最小単位はweiと呼ばれます。これが、トランザクションを送信するときに指定される値です。 +イーサでの取引を処理する際にも同様のパターンが使われます。 しかし、小数点以下2桁ではなく、イーサは18桁です! イーサの最小単位はweiと呼ばれ、それがトランザクション送信時に指定される値です。 -1 ETH = 1000000000000000000 wei +1 ether = 1000000000000000000 wei -1 wei = 0.000000000000000001 ETH +1 wei = 0.000000000000000001 ether -好きな数字でweiとETH(ether)を変換してみてください。 なお、ETHとweiの間には、[さまざまな単位の名称があります](https://web3py.readthedocs.io/en/stable/troubleshooting.html#how-do-i-convert-currency-denominations)。 その中でよく使われているのは、**gwei**です。これは基本的に手数料を意味します。 +いくつかの値をweiとの間で変換してみましょう。 イーサとweiの間には[多くの単位に名前がある](https://web3py.readthedocs.io/en/stable/troubleshooting.html#how-do-i-convert-currency-denominations)ことに注意してください。 その中でよく知られているものの一つが**gwei**です。トランザクション手数料がgweiで表されることが多いためです。 ```python In [2]: Web3.to_wei(1, 'ether') @@ -146,49 +149,50 @@ In [3]: Web3.from_wei(500000000, 'gwei') Out[3]: Decimal('0.5') ``` -Web3モジュールのその他のユーティリティメソッドとしては、データ形式のコンバータ(例: [`toHex`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.toHex)) 、アドレスヘルパー(例: [`isAddress`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.isAddress)) 、ハッシュ関数(例: [`keccak`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.keccak))が挙げられます。 これらについては、この連載の後半で取り上げます。 利用可能なすべてのメソッドとプロパティを表示するには、`Web3`と入力して、 ピリオドの後にタブキーを2回押してください。これにより、IPythonの自動補完機能が起動します。 +Web3モジュールの他のユーティリティメソッドには、データ形式コンバータ(例:[`toHex`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.toHex))、アドレスヘルパー(例:[`isAddress`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.isAddress))、ハッシュ関数(例:[`keccak`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.keccak))などがあります。 これらの多くは、このシリーズの後半で取り上げます。 利用可能なすべてのメソッドとプロパティを表示するには、IPythonのオートコンプリート機能を利用して`Web3.`と入力し、 ピリオドの後にTabキーを2回押します。 -## チェーンとの通信 {#talk-to-the-chain} +## チェーンと対話する {#talk-to-the-chain} -便利な手法も素晴らしいですが、ブロックチェーンの話に移りましょう。 次のステップでは、Web3.pyがイーサリアムノードと通信できるように設定します。 ここでは、IPC、HTTP、またはWebSocketプロバイダーを使用することができます。 +便利なメソッドも素晴らしいですが、ブロックチェーンに移りましょう。 次のステップは、イーサリアムノードと通信するようにWeb3.pyを設定することです。 ここでは、IPC、HTTP、またはWebsocketプロバイダーを使用するオプションがあります。 -HTTPプロバイダーを使用した場合の完全なワークフローの例としては、次のようなものがあります(ここでは、この手順は実行しません)。 +この道筋はたどりませんが、HTTPプロバイダーを使用した完全なワークフローの例は次のようになります。 -- イーサリアムノード([Geth](https://geth.ethereum.org/)など)をダウンロードします。 -- 1つのターミナルでGethを起動し、ネットワークの同期を待ちます。 デフォルトのHTTPポートは`8545`ですが、設定可能です。 -- 次のように`localhost:8545`で、Web3.pyを使用してHTTP経由でノードに接続します。 `w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))` +- [Geth](https://geth.ethereum.org/)などのイーサリアムノードをダウンロードします。 +- 一つのターミナルウィンドウでGethを開始し、ネットワークが同期するのを待ちます。 デフォルトのHTTPポートは`8545`ですが、設定可能です。 +- `localhost:8545`で、Web3.pyにHTTP経由でノードに接続するように指示します。 + `w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))` - `w3`インスタンスを使用してノードと対話します。 -これは「実際に行われている」方法ですが、同期プロセスに時間がかかるため、開発環境でのみ使用する場合は不要です。 Web3.pyは、開発環境でのみ使用するデベロッパー向けに、第4のプロバイダーとして**EthereumTesterProvider**を提供しています。 このテストプロバイダーは、制約の少ない権限が適用された、偽の通貨でさまざまなことを試せるようになっている、シミュレートされたイーサリアムノードにつながります。 +これは「実際の」方法の一つですが、同期プロセスには数時間かかり、開発環境が欲しいだけの場合は不要です。 Web3.pyはこの目的のために4番目のプロバイダー、**EthereumTesterProvider**を公開しています。 このテスタープロバイダーは、緩和された権限と自由に使える偽の通貨を持つシミュレートされたイーサリアムノードにリンクします。 -![シミュレートされたイーサリアムノードにWeb3.pyアプリケーションをつなげるEthereumTesterProviderを表す図](./ethereumtesterprovider.png) +![EthereumTesterProviderがweb3.pyアプリケーションをシミュレートされたイーサリアムノードにリンクする図](./ethereumtesterprovider.png) -_EthereumTesterProviderはシミュレートされたノードに接続します。これは、簡単な開発環境で使用する場合に便利です。_ +_EthereumTesterProviderはシミュレートされたノードに接続し、迅速な開発環境に便利です。_ -このシミュレートされたノードは[eth-tester](https://github.com/ethereum/eth-tester)と呼ばれ、前述の`pip install web3[tester]`コマンドでインストールされています。 以下のコマンドで、テストプロバイダーを使用するようWeb3.pyを簡単に設定できます。 +そのシミュレートされたノードは[eth-tester](https://github.com/ethereum/eth-tester)と呼ばれ、`pip install web3[tester]`コマンドの一部としてインストールしました。 Web3.pyをこのテスタープロバイダーを使用するように設定するのは、次のように簡単です。 ```python In [4]: w3 = Web3(Web3.EthereumTesterProvider()) ``` -これで「チェーンサーフィン」をする準備ができました。 そのような呼び方をする人はいないと思いますが、 そう呼ぶことにしました。 さあ、クイックツアーを始めましょう。 +これでチェーンをサーフィンする準備ができました! そんなことを言う人はいませんが。 私が今作った言葉です。 簡単なツアーを始めましょう。 ## クイックツアー {#the-quick-tour} -最初に、正常に接続できているかサニティチェックをします。 +まず最初に、サニティチェックです。 ```python In [5]: w3.is_connected() Out[5]: True ``` -テスタープロバイダーを使用しているため、このテストはあまり有用ではありません。失敗した場合は、`w3`変数をインスタンス化するときに入力ミスをした可能性があります。 内側の括弧、つまり`Web3.EthereumTesterProvider()`が含まれていることを再確認してください。 +テスタープロバイダーを使用しているので、これはあまり価値のあるテストではありませんが、もし失敗した場合、`w3`変数をインスタンス化する際に何かを間違って入力した可能性があります。 内側の括弧、つまり`Web3.EthereumTesterProvider()`を含めたか再確認してください。 -## ツアー #1: [アカウント](/developers/docs/accounts/) {#tour-stop-1-accounts} +## ツアーの立ち寄り先#1:[アカウント](/developers/docs/accounts/) {#tour-stop-1-accounts} -テスタープロバイダーでは便宜上、複数のアカウントが作成されており、テスト用のETHが入金されています。 +便宜上、テスタープロバイダーはいくつかのアカウントを作成し、テスト用のイーサをプリロードしています。 -まず、これらのアカウントの一覧を表示しましょう。 +まず、それらのアカウントのリストを見てみましょう。 ```python In [6]: w3.eth.accounts @@ -197,27 +201,27 @@ Out[6]: ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69', ...] ``` -このコマンドを実行すると、`0x`で始まる10個の文字列のリストが表示されます。 それぞれが**パブリックアドレス**であり、これらはある意味、当座預金の口座番号のようなものです。 あなた宛てにETHを送金する人に、これらのアドレスを教えることになります。 +このコマンドを実行すると、`0x`で始まる10個の文字列のリストが表示されるはずです。 それぞれが**公開アドレス**であり、ある意味では当座預金口座の口座番号に似ています。 あなたにイーサを送りたい人にこのアドレスを提供します。 -前述のとおり、テスタープロバイダーでは、これらの各アカウントにテスト用のETHがあらかじめ入金されています。 では、最初のアカウントの残高を確認しましょう。 +前述のように、テスタープロバイダーはこれらの各アカウントにテスト用のイーサをプリロードしています。 最初のアカウントにいくら入っているか見てみましょう。 ```python In [7]: w3.eth.get_balance(w3.eth.accounts[0]) Out[7]: 1000000000000000000000000 ``` -この0の数を見てください! 満面の笑みで偽の銀行に駆け込む前に、前述した通貨の単位の説明を思い出してください。 ETHの値は、最小単位であるweiで表されます。 ETH(ether)に変換すると次のようになります。 +たくさんのゼロですね! 偽の銀行に笑いながら行く前に、先ほどの通貨単位に関する教訓を思い出してください。 イーサの値は最小単位であるweiで表されます。 それをイーサに変換します。 ```python In [8]: w3.from_wei(1000000000000000000000000, 'ether') Out[8]: Decimal('1000000') ``` -テスト用とはいえ、100万ETHは依然としてすごい額です。 +100万テストイーサ――悪くないですね。 -## ツアー #2: ブロックデータ {#tour-stop-2-block-data} +## ツアーの立ち寄り先#2:ブロックデータ {#tour-stop-2-block-data} -このシミュレートされたブロックチェーンの状態を見てみましょう。 +このシミュレートされたブロックチェーンの状態を覗いてみましょう。 ```python In [9]: w3.eth.get_block('latest') @@ -230,15 +234,15 @@ Out[9]: AttributeDict({ }) ``` -ブロックに関する多くの情報が返されますが、いくつか注意すべき点があります。 +ブロックに関して多くの情報が返されますが、ここで指摘すべき点はいくつかあります。 -- テスタープロバイダーを設定した時期がいつであっても、ブロック番号はゼロになります。 実際のイーサリアムネットワーク(新しいブロックが12秒ごとに追加される)とは異なり、このシミュレーションは、作業を行うまで待機します。 -- まだ何も作業を行っていないため、同じ理由で、`transactions`は空のリストになります。 最初のブロックは**空のブロック**であり、チェーンを開始するためだけに使用されています。 -- `parentHash`は、単なる一連の空バイトである点に注意してください。 これは、**始まりのブロック**とも呼ばれる、チェーンの最初のブロックであることを意味します。 +- ブロック番号はゼロです――テスタープロバイダーをいつ設定したかに関係なく。 12秒ごとに新しいブロックを追加する実際のイーサリアムネットワークとは異なり、このシミュレーションは何か仕事を与えるまで待ちます。 +- `transactions`は空のリストです。同じ理由で、私たちはまだ何もしていません。 この最初のブロックは、チェーンを開始するための**空のブロック**です。 +- `parentHash`がただの空のバイトの束であることに注意してください。 これは、チェーンの最初のブロック、**ジェネシスブロック**としても知られていることを示しています。 -## ツアー #3: [トランザクション](/developers/docs/transactions/) {#tour-stop-3-transactions} +## ツアーの立ち寄り先#3:[トランザクション](/developers/docs/transactions/) {#tour-stop-3-transactions} -保留中のトランザクションが存在するまで、ゼロのブロックで止まっているので、トランザクションを作成してみましょう。 あるアカウントから別のアカウントに、テスト用のETH(ether)を少し送金します。 +保留中のトランザクションがあるまでブロックゼロで止まっているので、一つ与えてみましょう。 あるアカウントから別のアカウントに、数テストイーサを送金します。 ```python In [10]: tx_hash = w3.eth.send_transaction({ @@ -249,13 +253,16 @@ In [10]: tx_hash = w3.eth.send_transaction({ }) ``` -通常はここで、自分のトランザクションが新しいブロックに含まれるまで数秒間待つことになります。 全体のプロセスは次のようになります。 +これは通常、トランザクションが新しいブロックに含まれるのを数秒待つ時点です。 完全なプロセスは次のようになります。 -1. トランザクションを送信し、トランザクションハッシュを保持します。 トランザクションを含むブロックが作成され、ブロードキャストされるまで、トランザクションは「保留中(pending)」になります。 `tx_hash = w3.eth.send_transaction({ … })` -2. トランザクションがブロックに含まれるのを待ちます。 `w3.eth.wait_for_transaction_receipt(tx_hash)` -3. アプリケーションロジックが続行されます。 成功したトランザクションを表示します。 `w3.eth.get_transaction(tx_hash)` +1. トランザクションを送信し、トランザクションハッシュを保持します。 トランザクションを含むブロックが作成されブロードキャストされるまで、トランザクションは「保留中」です。 + `tx_hash = w3.eth.send_transaction({ … })` +2. トランザクションがブロックに含まれるのを待ちます: + `w3.eth.wait_for_transaction_receipt(tx_hash)` +3. アプリケーションロジックを続行します。 成功したトランザクションを表示するには: + `w3.eth.get_transaction(tx_hash)` -このシミュレーション環境では、トランザクションが新しいブロックに瞬時に追加されるので、そのトランザクションをすぐに確認できます。 +私たちのシミュレートされた環境は、トランザクションを新しいブロックに即座に追加するため、すぐにトランザクションを表示できます。 ```python In [11]: w3.eth.get_transaction(tx_hash) @@ -270,9 +277,9 @@ Out[11]: AttributeDict({ }) ``` -`from`、`to`、`value`の各フィールドは、`send_transaction`呼び出し時の入力と一致しなければなりません。 このトランザクションが、ブロック番号1の最初のトランザクション(`'transactionIndex': 0`)として含まれていることを確認できることも心強い点です。 +ここには見慣れた詳細がいくつかあります。`from`、`to`、`value`フィールドは、`send_transaction`呼び出しの入力と一致するはずです。 もう一つの安心できる点は、このトランザクションがブロック番号1内の最初のトランザクション(`'transactionIndex': 0`)として含まれていることです。 -さらに、関係する2つのアカウントの残高を確認することで、このトランザクションが成功していることを簡単に検証できます。 1つ目のアカウントから2つ目のアカウントへ、3 ETHが移動しているはずです。 +また、関与した2つのアカウントの残高を確認することで、このトランザクションの成功を簡単に確認できます。 3イーサが一方から他方へ移動したはずです。 ```python In [12]: w3.eth.get_balance(w3.eth.accounts[0]) @@ -282,13 +289,12 @@ In [13]: w3.eth.get_balance(w3.eth.accounts[1]) Out[13]: 1000003000000000000000000 ``` -2つ目のアカウントの残高は、 1,000,000 ETHから1,000,003 ETHに増えているので、正しいようです。 1つ目のアカウントはどうなったのでしょうか? 3 ETHより少し多い金額が失われたようです。 残念ながら、人生にはタダというものはなく、イーサリアムのパブリックネットワークを利用するには、そのサポート役であるピアに対価を支払う必要があります。 トランザクションを送信したアカウントから、少額のトランザクションフィーが差し引かれました。このフィーは、消費されたガスの量(ETH送金では21000単位のガス)にベースフィーを掛けたものです。ベースフィーは、ネットワークのアクティビティに加えて、ブロック内にトランザクションを含めるバリデータに送信されるチップによって異なります。 +後者は良さそうですね! 残高は1,000,000イーサから1,000,003イーサに増えました。 しかし、最初のアカウントはどうなったのでしょうか? 3イーサよりわずかに多く失われたようです。 残念ながら、人生に無料のものはありません。イーサリアムのパブリックネットワークを使用するには、その支援的な役割に対してピアに補償する必要があります。 トランザクションを送信したアカウントから少額のトランザクション手数料が差し引かれました。この手数料は、消費されたガスの量(ETH転送の場合は21000単位のガス)に、ネットワークのアクティビティに応じて変動するベースフィーと、トランザクションをブロックに含めるバリデータへのチップを加えたものを掛けたものです。 -[ガスの詳細](/developers/docs/gas/#post-london) +[ガス](/developers/docs/gas/#post-london)についての詳細 -注: パブリックネットワークにおいてトランザクションフィーは、ネットワークの需要やどれだけ迅速にトランザクションを処理する必要があるのかによって変動します。 フィー(手数料)の計算方法の内訳に興味がある場合は、 -ブロックに含まれるトランザクションの仕組みに関する以前の投稿をご覧ください。 +注:パブリックネットワークでは、トランザクション手数料はネットワークの需要とトランザクションをどれだけ早く処理したいかに基づいて変動します。 手数料がどのように計算されるかの内訳に興味がある場合は、トランザクションがどのようにブロックに含まれるかについての私の以前の投稿を参照してください。 -## ちょっと一息 {#and-breathe} +## 一息つきましょう {#and-breathe} -しばらく続けたので、ここで一息つきたいところです。 イーサリアムの世界はまだまだ広く、この連載の第2回ではさらに深く掘り下げていきます。 今後のコンセプトは、実際のノードへの接続、スマートコントラクト、トークンです。 何かご質問があれば、 ぜひお聞かせください。 皆様からのご意見は、今後の活動に反映させていきます。 [Twitter](https://twitter.com/wolovim)でリクエストを受け付けています。 +しばらくこれに取り組んできたので、一休みするには良い場所のようです。 ウサギの穴はまだ続きます。このシリーズのパート2で探求を続けます。 今後のコンセプト:実際のノードへの接続、スマートコントラクト、トークン。 フォローアップの質問はありますか? お知らせください! あなたのフィードバックが、ここからどこへ向かうかに影響します。 [Twitter](https://twitter.com/wolovim)経由でリクエストを歓迎します。 diff --git a/public/content/translations/ja/developers/tutorials/all-you-can-cache/index.md b/public/content/translations/ja/developers/tutorials/all-you-can-cache/index.md new file mode 100644 index 00000000000..e03556275ef --- /dev/null +++ b/public/content/translations/ja/developers/tutorials/all-you-can-cache/index.md @@ -0,0 +1,866 @@ +--- +title: "キャッシュでできること" +description: "ロールアップのトランザクションをより安くするキャッシュコントラクトの作成と使い方を学びます。" +author: Ori Pomerantz +tags: [ "レイヤー2", "キャッシュ", "ストレージ" ] +skill: intermediate +published: 2022-09-15 +lang: ja +--- + +ロールアップを使うと、トランザクションのバイトあたりのコストは、ストレージスロットのコストよりもはるかに高くなってしまいます。 そのため、オンチェーンに可能な限り多くの情報をキャッシュするほうが合理的です。 + +この記事では、複数回使用される可能性のあるパラメータの値をキャッシュして、(初回以降では) はるかに少ないバイト数で使えるようにするキャッシュコントラクトの作成および使用方法を学びます。また、このキャッシュを使用するオフチェーンコードの書き方についても説明します。 + +記事をスキップしてソースコードだけを見たい場合は、[こちら](https://github.com/qbzzt/20220915-all-you-can-cache)をご覧ください。 開発スタックは[Foundry](https://getfoundry.sh/introduction/installation/)です。 + +## 全体設計 {#overall-design} + +わかりやすくするために、すべてのトランザクションのパラメータは`uint256`、32バイト長であると仮定します。 トランザクションを受け取ると、次のように各パラメータをパースします。 + +1. 先頭のバイトが`0xFF`の場合、次の32バイトをパラメータの値として取得し、キャッシュに書き込みます。 + +2. 先頭のバイトが`0xFE`の場合、次の32バイトをパラメータの値として取得しますが、キャッシュには書き込み_ません_。 + +3. その他の値の場合、上位4ビットを追加のバイト数として、下位4ビットをキャッシュキーの最上位ビットとして取得します。 以下に、いくつかの例を示します。 + + | calldataのバイト | キャッシュキー | + | :-------------- | -------: | + | 0x0F | 0x0F | + | 0x10,0x10 | 0x10 | + | 0x12,0xAC | 0x02AC | + | 0x2D,0xEA, 0xD6 | 0x0DEAD6 | + +## キャッシュ操作 {#cache-manipulation} + +キャッシュは[`Cache.sol`](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol)に実装されています。 一行ずつ見ていきましょう。 + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + + +contract Cache { + + bytes1 public constant INTO_CACHE = 0xFF; + bytes1 public constant DONT_CACHE = 0xFE; +``` + +これらの定数は、すべての情報を提供し、それをキャッシュに書き込むかどうかの特殊なケースを解釈するために使用されます。 キャッシュへの書き込みには、以前は使用されていなかったストレージスロットに対して2回の[`SSTORE`](https://www.evm.codes/#55)操作が必要で、それぞれ22100ガスがかかるため、オプションとしています。 + +```solidity + + mapping(uint => uint) public val2key; +``` + +値とそのキーの間の[マッピング](https://www.geeksforgeeks.org/solidity/solidity-mappings/)です。 この情報は、トランザクションを送信する前に値をエンコードするために必要です。 + +```solidity + // 位置nにはキーn+1の値があります。なぜなら、 + // ゼロを「キャッシュにない」状態として保持する必要があるためです。 + uint[] public key2val; +``` + +キーを割り当て、簡単にするために順次行うため、キーから値へのマッピングには配列を使用できます。 + +```solidity + function cacheRead(uint _key) public view returns (uint) { + require(_key <= key2val.length, "初期化されていないキャッシュエントリを読み込んでいます"); + return key2val[_key-1]; + } // cacheRead +``` + +キャッシュから値を読み取ります。 + +```solidity + // 値がまだキャッシュにない場合に書き込む + // テストを機能させるためにのみpublicに設定 + function cacheWrite(uint _value) public returns (uint) { + // 値がすでにキャッシュにある場合は、現在のキーを返す + if (val2key[_value] != 0) { + return val2key[_value]; + } +``` + +同じ値をキャッシュに複数回入れる意味はありません。 値がすでに存在する場合は、既存のキーを返します。 + +```solidity + // 0xFEは特殊なケースであるため、キャッシュが保持できる + // 最大のキーは、0x0Dの後に15個の0xFFが続くものです。キャッシュ長がすでに + // その大きさになっている場合は失敗します。 + // 1 2 3 4 5 6 7 8 9 A B C D E F + require(key2val.length+1 < 0x0DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + "キャッシュオーバーフロー"); +``` + +これほど大きなキャッシュを得ることはないでしょう (約1.8\*1037エントリで、保存には約1027TBが必要です)。 しかし、私は["640kBあれば常に十分"](https://quoteinvestigator.com/2011/09/08/640k-enough/)という言葉を覚えているほどには年をとっています。 このテストは非常に安価です。 + +```solidity + // 次のキーを使用して値を書き込む + val2key[_value] = key2val.length+1; +``` + +逆引き参照 (値からキーへ) を追加します。 + +```solidity + key2val.push(_value); +``` + +順引き参照 (キーから値へ) を追加します。 値を順次割り当てるため、最後の配列値の後に追加するだけです。 + +```solidity + return key2val.length; + } // cacheWrite +``` + +新しい値が格納されているセルである`key2val`の新しい長さを返します。 + +```solidity + function _calldataVal(uint startByte, uint length) + private pure returns (uint) +``` + +この関数は、任意の長さ (最大32バイト、ワードサイズ) のcalldataから値を読み取ります。 + +```solidity + { + uint _retVal; + + require(length < 0x21, + "_calldataValの長さ制限は32バイトです"); + require(length + startByte <= msg.data.length, + "_calldataValがcalldatasizeを超えて読み取ろうとしています"); +``` + +この関数は内部関数であるため、コードの残りの部分が正しく書かれていれば、これらのテストは不要です。 しかし、大してコストがかからないので、あってもよいでしょう。 + +```solidity + assembly { + _retVal := calldataload(startByte) + } +``` + +このコードは[Yul](https://docs.soliditylang.org/en/v0.8.16/yul.html)で書かれています。 calldataから32バイトの値を読み取ります。 `startByte+32`より前にcalldataが停止しても、EVMでは初期化されていない領域はゼロとみなされるため、これは機能します。 + +```solidity + _retVal = _retVal >> (256-length*8); +``` + +必ずしも32バイトの値が必要というわけではありません。 これにより、余分なバイトが取り除かれます。 + +```solidity + } // _calldataVal + + + // calldataから_fromByteを開始位置として単一のパラメータを読み取る + function _readParam(uint _fromByte) internal + returns (uint _nextByte, uint _parameterValue) + { +``` + +calldataから単一のパラメータを読み取ります。 パラメータは1バイトから33バイトの範囲になる可能性があるため、読み取った値だけでなく、次のバイトの位置も返す必要があることに注意してください。 + +```solidity + // 最初のバイトで残りの解釈方法がわかる + uint8 _firstByte; + + _firstByte = uint8(_calldataVal(_fromByte, 1)); +``` + +Solidityは、危険な可能性のある[暗黙の型変換](https://docs.soliditylang.org/en/v0.8.16/types.html#implicit-conversions)を禁止することで、バグの数を減らそうとします。 ダウングレード、たとえば256ビットから8ビットへの変換は、明示的に行う必要があります。 + +```solidity + + // 値を読み取るが、キャッシュには書き込まない + if (_firstByte == uint8(DONT_CACHE)) + return(_fromByte+33, _calldataVal(_fromByte+1, 32)); + + // 値を読み取り、キャッシュに書き込む + if (_firstByte == uint8(INTO_CACHE)) { + uint _param = _calldataVal(_fromByte+1, 32); + cacheWrite(_param); + return(_fromByte+33, _param); + } + + // ここまで来たということは、キャッシュから読み取る必要があるということ + + // 読み取る追加バイト数 + uint8 _extraBytes = _firstByte / 16; +``` + +下位[ニブル](https://en.wikipedia.org/wiki/Nibble)を取り、それを他のバイトと組み合わせてキャッシュから値を読み取ります。 + +```solidity + uint _key = (uint256(_firstByte & 0x0F) << (8*_extraBytes)) + + _calldataVal(_fromByte+1, _extraBytes); + + return (_fromByte+_extraBytes+1, cacheRead(_key)); + + } // _readParam + + + // n個のパラメータを読み取る (関数は期待するパラメータ数を知っている) + function _readParams(uint _paramNum) internal returns (uint[] memory) { +``` + +パラメータ数はcalldata自体から取得できますが、私たちを呼び出す関数は期待するパラメータ数を知っています。 それらの関数に教えてもらう方が簡単です。 + +```solidity + // 読み取ったパラメータ + uint[] memory params = new uint[](_paramNum); + + // パラメータは4バイト目から始まります。それより前は関数シグネチャです + uint _atByte = 4; + + for(uint i=0; i<_paramNum; i++) { + (_atByte, params[i]) = _readParam(_atByte); + } +``` + +必要な数になるまでパラメータを読み取ります。 calldataの末尾を超えた場合、`_readParams`は呼び出しをリバートします。 + +```solidity + + return(params); + } // readParams + + // _readParamsのテストのため、4つのパラメータの読み取りをテストする + function fourParam() public + returns (uint256,uint256,uint256,uint256) + { + uint[] memory params; + params = _readParams(4); + return (params[0], params[1], params[2], params[3]); + } // fourParam +``` + +Foundryの大きな利点の1つは、Solidityでテストを書くことができる点です([下記の「キャッシュのテスト」を参照](#testing-the-cache))。 これにより、単体テストが非常に簡単になります。 これは4つのパラメータを読み取って返し、テストでそれらが正しいことを検証できるようにする関数です。 + +```solidity + // 値を取得し、それをエンコードするバイト列を返す (可能であればキャッシュを使用) + function encodeVal(uint _val) public view returns(bytes memory) { +``` + +`encodeVal`は、オフチェーンコードがキャッシュを使用するcalldataの作成を支援するために呼び出す関数です。 単一の値を受け取り、それをエンコードするバイト列を返します。 この関数は`view`なので、トランザクションを必要とせず、外部から呼び出してもガスはかかりません。 + +```solidity + uint _key = val2key[_val]; + + // 値はまだキャッシュにないので、追加する + if (_key == 0) + return bytes.concat(INTO_CACHE, bytes32(_val)); +``` + +[EVM](/developers/docs/evm/)では、初期化されていないストレージはすべてゼロであるとみなされます。 そのため、存在しない値のキーを探すと、ゼロが返されます。 その場合、それをエンコードするバイト列は`INTO_CACHE` (次回キャッシュされるように) となり、その後に実際の値が続きます。 + +```solidity + // キーが0x10未満の場合、単一バイトとして返す + if (_key < 0x10) + return bytes.concat(bytes1(uint8(_key))); +``` + +単一バイトが最も簡単です。 [`bytes.concat`](https://docs.soliditylang.org/en/v0.8.16/types.html#the-functions-bytes-concat-and-string-concat)を使用して、`bytes`型を任意の長さのバイト配列に変換するだけです。 その名前にもかかわらず、引数が1つだけ提供された場合でも正常に動作します。 + +```solidity + // 2バイト値、0x1vvvとしてエンコード + if (_key < 0x1000) + return bytes.concat(bytes2(uint16(_key) | 0x1000)); +``` + +163未満のキーがある場合、それを2バイトで表現できます。 まず、256ビットの値である`_key`を16ビットの値に変換し、論理和を使用して最初のバイトに追加バイト数を加えます。 次に、それを`bytes`に変換できる`bytes2`の値に入れます。 + +```solidity + // 以降の行をループとして実行する賢い方法があるかもしれませんが、 + // これはview関数なので、プログラマの時間と + // 単純さを最適化しています。 + + if (_key < 16*256**2) + return bytes.concat(bytes3(uint24(_key) | (0x2 * 16 * 256**2))); + if (_key < 16*256**3) + return bytes.concat(bytes4(uint32(_key) | (0x3 * 16 * 256**3))); + . + . + . + if (_key < 16*256**14) + return bytes.concat(bytes15(uint120(_key) | (0xE * 16 * 256**14))); + if (_key < 16*256**15) + return bytes.concat(bytes16(uint128(_key) | (0xF * 16 * 256**15))); +``` + +その他の値 (3バイト、4バイトなど) は、フィールドサイズが異なるだけで、同じように処理されます。 + +```solidity + // ここに到達した場合、何かが間違っています。 + revert("encodeValのエラー、発生するはずがありません"); +``` + +ここに到達するということは、16\*25615以上のキーを取得したということです。 しかし、`cacheWrite`はキーを制限するため、14\*25616 (最初のバイトが0xFEとなり、`DONT_CACHE`のように見える) に達することさえありません。 しかし、将来のプログラマーがバグを混入させる場合に備えてテストを追加しても、大したコストはかかりません。 + +```solidity + } // encodeVal + +} // Cache +``` + +### キャッシュのテスト {#testing-the-cache} + +Foundryの利点の1つは、[Solidityでテストを書くことができる](https://getfoundry.sh/forge/tests/overview/)ことで、これにより単体テストが書きやすくなります。 `Cache`クラスのテストは[こちら](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/Cache.t.sol)にあります。 テストコードは繰り返しが多くなりがちなので、この記事では興味深い部分のみを説明します。 + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; + + +// consoleを使用するには、`forge test -vv`を実行する必要があります。 +import "forge-std/console.sol"; +``` + +これは、テストパッケージと`console.log`を使用するために必要な、単なる定型コードです。 + +```solidity +import "src/Cache.sol"; +``` + +テスト対象のコントラクトをインポートする必要があります。 + +```solidity +contract CacheTest is Test { + Cache cache; + + function setUp() public { + cache = new Cache(); + } +``` + +`setUp`関数は各テストの前に呼び出されます。 この場合、新しいキャッシュを作成するだけなので、テストが互いに影響することはありません。 + +```solidity + function testCaching() public { +``` + +テストは`test`で始まる名前の関数です。 この関数は、値を書き込んで再度読み取ることにより、基本的なキャッシュ機能を確認します。 + +```solidity + for(uint i=1; i<5000; i++) { + cache.cacheWrite(i*i); + } + + for(uint i=1; i<5000; i++) { + assertEq(cache.cacheRead(i), i*i); +``` + +これは、[`assert...` 関数](https://getfoundry.sh/reference/forge-std/std-assertions/)を使用して実際のテストを行う方法です。 この場合、書き込んだ値が読み取った値と等しいことを確認します。 `cache.cacheWrite`の結果は、キャッシュキーが線形に割り当てられることがわかっているので、破棄できます。 + +```solidity + } + } // testCaching + + + // 同じ値を複数回キャッシュし、キーが + // 同じままであることを確認する + function testRepeatCaching() public { + for(uint i=1; i<100; i++) { + uint _key1 = cache.cacheWrite(i); + uint _key2 = cache.cacheWrite(i); + assertEq(_key1, _key2); + } +``` + +まず、各値をキャッシュに2回書き込み、キーが同じであることを確認します (つまり、2回目の書き込みは実際には行われなかったということです)。 + +```solidity + for(uint i=1; i<100; i+=3) { + uint _key = cache.cacheWrite(i); + assertEq(_key, i); + } + } // testRepeatCaching +``` + +理論上は、連続したキャッシュ書き込みに影響しないバグが存在する可能性があります。 そのため、ここでは連続していない書き込みをいくつか行い、値がまだ書き換えられていないことを確認します。 + +```solidity + // メモリバッファからuintを読み取る (送信したパラメータが + // 返ってくることを確認するため) + function toUint256(bytes memory _bytes, uint256 _start) internal pure + returns (uint256) +``` + +`bytes memory`バッファから256ビットのワードを読み取ります。 このユーティリティ関数を使用すると、キャッシュを使用する関数呼び出しを実行したときに、正しい結果を受け取ったことを確認できます。 + +```solidity + { + require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); + uint256 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x20), _start)) + } +``` + +Yulは`uint256`を超えるデータ構造をサポートしていないため、メモリバッファ`_bytes`のような、より高度なデータ構造を参照すると、その構造のアドレスが取得されます。 Solidityは`bytes memory`の値を、長さを含む32バイトのワードとして格納し、その後に実際のバイトが続くため、`_start`番目のバイトを取得するには`_bytes+32+_start`を計算する必要があります。 + +```solidity + + return tempUint; + } // toUint256 + + // fourParams()の関数シグネチャ、提供元: + // https://www.4byte.directory/signatures/?bytes4_signature=0x3edc1e6d + bytes4 constant FOUR_PARAMS = 0x3edc1e6d; + + // 正しい値が返ってくることを確認するための定数値 + uint256 constant VAL_A = 0xDEAD60A7; + uint256 constant VAL_B = 0xBEEF; + uint256 constant VAL_C = 0x600D; + uint256 constant VAL_D = 0x600D60A7; +``` + +テストに必要な定数です。 + +```solidity + function testReadParam() public { +``` + +`readParams`を使用する関数`fourParams()`を呼び出し、パラメータを正しく読み取れることをテストします。 + +```solidity + address _cacheAddr = address(cache); + bool _success; + bytes memory _callInput; + bytes memory _callOutput; +``` + +キャッシュを使用する関数を呼び出すには、通常のABIメカニズムが使用できないため、低レベルの[`
.call()`](https://docs.soliditylang.org/en/v0.8.16/types.html#members-of-addresses)メカニズムを使用する必要があります。 このメカニズムは、入力として`bytes memory`を受け取り、それを出力として(ブール値と同様に)返します。 + +```solidity + // 最初の呼び出し、キャッシュは空 + _callInput = bytes.concat( + FOUR_PARAMS, +``` + +同じコントラクトが、キャッシュされた関数(トランザクションからの直接呼び出し用)とキャッシュされていない関数(他のスマートコントラクトからの呼び出し用)の両方をサポートすると便利です。 そのためには、すべてを[フォールバック関数](https://docs.soliditylang.org/en/v0.8.16/contracts.html#fallback-function)に置くのではなく、引き続きSolidityのメカニズムに頼って正しい関数を呼び出す必要があります。 これにより、構成可能性が大幅に向上します。 ほとんどの場合、関数を識別するには1バイトで十分なため、3バイト(16\*3=48ガス)を無駄にしています。 しかし、この記事を書いている時点では、その48ガスのコストは0.07セントであり、これはより単純でバグが発生しにくいコードのための妥当なコストです。 + +```solidity + // 最初の値、キャッシュに追加する + cache.INTO_CACHE(), + bytes32(VAL_A), +``` + +最初の値: キャッシュに書き込む必要がある完全な値であることを示すフラグで、その後に32バイトの値が続きます。 他の3つの値も同様ですが、`VAL_B`はキャッシュに書き込まれず、`VAL_C`は3番目と4番目の両方のパラメータである点が異なります。 + +```solidity + . + . + . + ); + (_success, _callOutput) = _cacheAddr.call(_callInput); +``` + +ここで実際に`Cache`コントラクトを呼び出します。 + +```solidity + assertEq(_success, true); +``` + +呼び出しが成功することを期待します。 + +```solidity + assertEq(cache.cacheRead(1), VAL_A); + assertEq(cache.cacheRead(2), VAL_C); +``` + +空のキャッシュから始めて、`VAL_A`の後に`VAL_C`を追加します。 最初のキーが1で、2番目のキーが2であることを期待します。 + +``` + assertEq(toUint256(_callOutput,0), VAL_A); + assertEq(toUint256(_callOutput,32), VAL_B); + assertEq(toUint256(_callOutput,64), VAL_C); + assertEq(toUint256(_callOutput,96), VAL_C); +``` + +出力は4つのパラメータです。 ここで、それが正しいことを検証します。 + +```solidity + // 2回目の呼び出し、キャッシュを使用できる + _callInput = bytes.concat( + FOUR_PARAMS, + + // キャッシュ内の最初の値 + bytes1(0x01), +``` + +16未満のキャッシュキーは、ちょうど1バイトになります。 + +```solidity + // 2番目の値、キャッシュに追加しない + cache.DONT_CACHE(), + bytes32(VAL_B), + + // 3番目と4番目の値、同じ値 + bytes1(0x02), + bytes1(0x02) + ); + . + . + . + } // testReadParam +``` + +呼び出し後のテストは、最初の呼び出し後のテストと同一です。 + +```solidity + function testEncodeVal() public { +``` + +この関数は`testReadParam`に似ていますが、パラメータを明示的に書き込む代わりに`encodeVal()`を使用する点が異なります。 + +```solidity + . + . + . + _callInput = bytes.concat( + FOUR_PARAMS, + cache.encodeVal(VAL_A), + cache.encodeVal(VAL_B), + cache.encodeVal(VAL_C), + cache.encodeVal(VAL_D) + ); + . + . + . + assertEq(_callInput.length, 4+1*4); + } // testEncodeVal +``` + +`testEncodeVal()`での唯一の追加テストは、`_callInput`の長さが正しいかを確認することです。 最初の呼び出しでは、4+33\*4となります。 2回目では、すべての値がすでにキャッシュ内にあるため、4+1\*4となります。 + +```solidity + // キーが1バイト以上の場合のencodeValのテスト + // 4バイトまでキャッシュを埋めるのは + // 時間がかかりすぎるため、最大3バイトとする。 + function testEncodeValBig() public { + // いくつかの値をキャッシュに入れる。 + // 簡単にするため、値nにキーnを使用する。 + for(uint i=1; i<0x1FFF; i++) { + cache.cacheWrite(i); + } +``` + +上記の`testEncodeVal`関数は4つの値しかキャッシュに書き込まないため、[マルチバイト値を扱う関数の部分](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol#L144-L171)はチェックされません。 しかし、そのコードは複雑でエラーが発生しやすくなっています。 + +この関数の最初の部分は、1から0x1FFFまでのすべての値を順番にキャッシュに書き込むループなので、これらの値をエンコードして、どこに行くのかを知ることができます。 + +```solidity + . + . + . + + _callInput = bytes.concat( + FOUR_PARAMS, + cache.encodeVal(0x000F), // 1バイト 0x0F + cache.encodeVal(0x0010), // 2バイト 0x1010 + cache.encodeVal(0x0100), // 2バイト 0x1100 + cache.encodeVal(0x1000) // 3バイト 0x201000 + ); +``` + +1バイト、2バイト、3バイトの値をテストします。 十分なスタックエントリ (少なくとも0x10000000、約2億5千万) を書き込むには時間がかかりすぎるため、それ以上のテストは行いません。 + +```solidity + . + . + . + . + } // testEncodeValBig + + + // 短すぎるバッファでリバートされることをテストする + function testShortCalldata() public { +``` + +パラメータが不十分な異常ケースで何が起こるかをテストします。 + +```solidity + . + . + . + (_success, _callOutput) = _cacheAddr.call(_callInput); + assertEq(_success, false); + } // testShortCalldata +``` + +リバートされるので、得られる結果は`false`になるはずです。 + +``` + // 存在しないキャッシュキーで呼び出す + function testNoCacheKey() public { + . + . + . + _callInput = bytes.concat( + FOUR_PARAMS, + + // 最初の値、キャッシュに追加する + cache.INTO_CACHE(), + bytes32(VAL_A), + + // 2番目の値 + bytes1(0x0F), + bytes2(0x1234), + bytes11(0xA10102030405060708090A) + ); +``` + +この関数は、キャッシュが空で読み込む値がないことを除けば、4つの完全に正当なパラメータを取得します。 + +```solidity + . + . + . + // 長すぎるバッファでもすべてがうまくいくことをテストする + function testLongCalldata() public { + address _cacheAddr = address(cache); + bool _success; + bytes memory _callInput; + bytes memory _callOutput; + + // 最初の呼び出し、キャッシュは空 + _callInput = bytes.concat( + FOUR_PARAMS, + + // 最初の値、キャッシュに追加する + cache.INTO_CACHE(), bytes32(VAL_A), + + // 2番目の値、キャッシュに追加する + cache.INTO_CACHE(), bytes32(VAL_B), + + // 3番目の値、キャッシュに追加する + cache.INTO_CACHE(), bytes32(VAL_C), + + // 4番目の値、キャッシュに追加する + cache.INTO_CACHE(), bytes32(VAL_D), + + // 「幸運を祈って」もう1つの値 + bytes4(0x31112233) + ); +``` + +この関数は5つの値を送信します。 5番目の値は有効なキャッシュエントリではないため無視されることがわかります。もし含まれていなければリバートを引き起こしていたでしょう。 + +```solidity + (_success, _callOutput) = _cacheAddr.call(_callInput); + assertEq(_success, true); + . + . + . + } // testLongCalldata + +} // CacheTest + +``` + +## サンプルアプリケーション {#a-sample-app} + +Solidityでテストを書くのは非常に良いことですが、結局のところ、dappが役立つためにはチェーンの外部からのリクエストを処理できる必要があります。 この記事では、「Write Once, Read Many (一度書き込み、多数回読み取り)」を意味する`WORM`を使用して、dappでキャッシングを使用する方法を実演します。 キーがまだ書き込まれていない場合は、値を書き込むことができます。 キーがすでに書き込まれている場合は、リバートされます。 + +### コントラクト {#the-contract} + +[こちらがコントラクト](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/WORM.sol)です。 これは主に`Cache`と`CacheTest`で既に行ったことを繰り返しているので、興味深い部分のみを取り上げます。 + +```solidity +import "./Cache.sol"; + +contract WORM is Cache { +``` + +`Cache`を使用する最も簡単な方法は、自分のコントラクトでそれを継承することです。 + +```solidity + function writeEntryCached() external { + uint[] memory params = _readParams(2); + writeEntry(params[0], params[1]); + } // writeEntryCached +``` + +この関数は、上記の`CacheTest`の`fourParam`に似ています。 ABI仕様に従っていないため、関数にパラメータを宣言しないのが最善です。 + +```solidity + // 呼び出しを容易にする + // writeEntryCached()の関数シグネチャ、提供元: + // https://www.4byte.directory/signatures/?bytes4_signature=0xe4e4f2d3 + bytes4 constant public WRITE_ENTRY_CACHED = 0xe4e4f2d3; +``` + +ABI仕様に従っていないため、`writeEntryCached`を呼び出す外部コードは、`worm.writeEntryCached`を使用する代わりに、手動でcalldataを構築する必要があります。 この定数値があると、その記述が楽になります。 + +`WRITE_ENTRY_CACHED`を状態変数として定義しても、それを外部から読み取るには、そのゲッター関数である`worm.WRITE_ENTRY_CACHED()`を使用する必要があることに注意してください。 + +```solidity + function readEntry(uint key) public view + returns (uint _value, address _writtenBy, uint _writtenAtBlock) +``` + +読み取り関数は`view`なので、トランザクションを必要とせず、ガスもかかりません。 結果として、パラメータにキャッシュを使うメリットはありません。 ビュー関数では、より単純な標準メカニズムを使う方が最善です。 + +### テストコード {#the-testing-code} + +[こちらがコントラクトのテストコード](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/WORM.t.sol)です。 繰り返しになりますが、興味深い部分のみを見ていきましょう。 + +```solidity + function testWReadWrite() public { + worm.writeEntry(0xDEAD, 0x60A7); + + vm.expectRevert(bytes("entry already written")); + worm.writeEntry(0xDEAD, 0xBEEF); +``` + +[これ (`vm.expectRevert`)](https://book.getfoundry.sh/cheatcodes/expect-revert#expectrevert)は、Foundryテストで次の呼び出しが失敗すること、および失敗の報告理由を指定する方法です。 これは、`.`構文を使用する場合に適用されます。これは、calldataを構築して低レベルインターフェース (`.call()`など) を使用してコントラクトを呼び出すのとは異なります。 + +```solidity + function testReadWriteCached() public { + uint cacheGoat = worm.cacheWrite(0x60A7); +``` + +ここでは、`cacheWrite`がキャッシュキーを返すという事実を利用します。 これは本番環境での使用を想定していません。`cacheWrite`は状態を変更するため、トランザクション中にしか呼び出せません。 トランザクションには戻り値がありません。結果がある場合、その結果はイベントとして発行されることになっています。 そのため、`cacheWrite`の戻り値はオンチェーンコードからのみアクセスでき、オンチェーンコードはパラメータのキャッシュを必要としません。 + +```solidity + (_success,) = address(worm).call(_callInput); +``` + +これは、`.call()`には2つの戻り値があるが、最初の値しか気にしないことをSolidityに伝える方法です。 + +```solidity + (_success,) = address(worm).call(_callInput); + assertEq(_success, false); +``` + +低レベルの`
.call()`関数を使用しているため、`vm.expectRevert()`を使用できず、呼び出しから得られるブール値の成功値を見る必要があります。 + +```solidity + event EntryWritten(uint indexed key, uint indexed value); + + . + . + . + + _callInput = bytes.concat( + worm.WRITE_ENTRY_CACHED(), worm.encodeVal(a), worm.encodeVal(b)); + vm.expectEmit(true, true, false, false); + emit EntryWritten(a, b); + (_success,) = address(worm).call(_callInput); +``` + +これは、Foundryでコードが[イベントを正しく発行する](https://getfoundry.sh/reference/cheatcodes/expect-emit/)ことを検証する方法です。 + +### クライアント {#the-client} + +Solidityのテストでは得られないものの1つは、自分のアプリケーションにコピー&ペーストできるJavaScriptコードです。 そのコードを書くために、WORMを[Optimism](https://www.optimism.io/)の新しいテストネットである[Optimism Goerli](https://community.optimism.io/docs/useful-tools/networks/#optimism-goerli)にデプロイしました。 アドレスは[`0xd34335b1d818cee54e3323d3246bd31d94e6a78a`](https://goerli-optimism.etherscan.io/address/0xd34335b1d818cee54e3323d3246bd31d94e6a78a)です。 + +[クライアントのJavaScriptコードはこちらで確認できます](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/javascript/index.js)。 使用方法は次のとおりです。 + +1. gitリポジトリをクローンします。 + + ```sh + git clone https://github.com/qbzzt/20220915-all-you-can-cache.git + ``` + +2. 必要なパッケージをインストールします。 + + ```sh + cd javascript + yarn + ``` + +3. 設定ファイルをコピーします。 + + ```sh + cp .env.example .env + ``` + +4. `.env`を編集して設定を行います。 + + | パラメータ | 値 | + | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | + | MNEMONIC | トランザクションの支払いに十分なETHを持っているアカウントのニーモニック。 [こちらでOptimismのGoerliネットワークの無料ETHを手に入れられます](https://optimismfaucet.xyz/)。 | + | OPTIMISM_GOERLI_URL | Optimism GoerliのURL。 公開エンドポイント`https://goerli.optimism.io`は、レート制限がありますが、ここで必要な用途には十分です。 | + +5. `index.js`を実行します。 + + ```sh + node index.js + ``` + + このサンプルアプリケーションは、まずWORMにエントリを書き込み、calldataとEtherscan上のトランザクションへのリンクを表示します。 次に、そのエントリを読み返し、使用するキーとエントリ内の値 (値、ブロック番号、作成者) を表示します。 + +クライアントのほとんどは通常のDapp JavaScriptです。 そのため、ここでも興味深い部分のみを取り上げます。 + +```javascript +. +. +. +const main = async () => { + const func = await worm.WRITE_ENTRY_CACHED() + + // 毎回新しいキーが必要 + const key = await worm.encodeVal(Number(new Date())) +``` + +特定のスロットには一度しか書き込めないため、タイムスタンプを使用してスロットを再利用しないようにします。 + +```javascript +const val = await worm.encodeVal("0x600D") + +// エントリを書き込む +const calldata = func + key.slice(2) + val.slice(2) +``` + +Ethersは、コールデータが16進文字列、つまり`0x`の後に偶数個の16進数が続くことを期待します。 `key`と`val`は両方とも`0x`で始まるため、これらのヘッダーを削除する必要があります。 + +```javascript +const tx = await worm.populateTransaction.writeEntryCached() +tx.data = calldata + +sentTx = await wallet.sendTransaction(tx) +``` + +Solidityのテストコードと同様に、キャッシュされた関数を通常の方法で呼び出すことはできません。 代わりに、より低レベルのメカニズムを使用する必要があります。 + +```javascript + . + . + . + // 書き込んだばかりのエントリを読み取る + const realKey = '0x' + key.slice(4) // FFフラグを削除する + const entryRead = await worm.readEntry(realKey) + . + . + . +``` + +エントリの読み取りには、通常のメカニズムを使用できます。 `view`関数では、パラメータのキャッシュを使う必要はありません。 + +## 結論 {#conclusion} + +この記事のコードは概念実証 (プルーフ・オブ・コンセプト) であり、アイデアを理解しやすくすることを目的としています。 本番環境のシステムでは、いくつかの追加機能を実装することをお勧めします。 + +- `uint256`ではない値を処理します。 例えば、文字列です。 +- グローバルキャッシュの代わりに、ユーザーとキャッシュの間のマッピングを持つことを検討します。 異なるユーザーは異なる値を使用します。 +- アドレスに使用される値は、他の目的で使用される値とは異なります。 アドレス専用の個別のキャッシュを持つことが合理的かもしれません。 +- 現在、キャッシュキーは「先着、最小キー」アルゴリズムに基づいています。 最初の16個の値は、単一バイトとして送信できます。 次の4080個の値は、2バイトとして送信できます。 次の約100万個の値は3バイト、などとなります。 本番システムでは、キャッシュエントリの使用カウンタを保持し、最も_一般的な_16個の値が1バイト、次に一般的な4080個の値が2バイトになるように再編成する必要があります。 + + ただし、これは潜在的に危険な操作です。 次の一連のイベントを想像してみてください。 + + 1. Noam Naiveが`encodeVal`を呼び出して、トークンを送りたいアドレスをエンコードします。 そのアドレスはアプリケーションで最初に使用されたアドレスの1つなので、エンコードされた値は0x06です。 これはトランザクションではなく`view`関数なので、Noamと彼が使用するノード間のやり取りであり、他の誰も知りません。 + + 2. Owen Ownerがキャッシュの並べ替え操作を実行します。 そのアドレスを実際に使用する人はほとんどいないため、現在は0x201122としてエンコードされています。 別の値である1018に、0x06が割り当てられます。 + + 3. Noam Naiveは自分のトークンを0x06に送信します。 トークンはアドレス`0x0000000000000000000000000de0b6b3a7640000`に送られますが、そのアドレスの秘密鍵を誰も知らないため、そこにスタックしてしまいます。 Noamは_不満_です。 + + この問題、およびキャッシュの再順序付け中にメモリプール内にあるトランザクションの関連問題を解決する方法はありますが、その存在を認識しておく必要があります。 + +私はOptimismの従業員であり、これが最もよく知っているロールアップなので、ここではOptimismを使ってキャッシュを実演しました。 しかし、これは内部処理に最小限のコストしかかからないどのロールアップでも機能するはずです。そのため、比較するとL1へのトランザクションデータの書き込みが主な費用となります。 + +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). + diff --git a/public/content/translations/ja/developers/tutorials/app-plasma/index.md b/public/content/translations/ja/developers/tutorials/app-plasma/index.md new file mode 100644 index 00000000000..04582a32dfc --- /dev/null +++ b/public/content/translations/ja/developers/tutorials/app-plasma/index.md @@ -0,0 +1,1255 @@ +--- +title: "プライバシーを保護するアプリ固有のPlasmaを作成する" +description: "このチュートリアルでは、預金用の半秘密の銀行を構築します。 銀行は中央集権的なコンポーネントであり、各ユーザーの残高を把握しています。 しかし、この情報はオンチェーンには保存されません。 代わりに、銀行は状態のハッシュをポストします。 トランザクションが発生するたびに、銀行は新しいハッシュと、ハッシュの状態を新しいものに変更する署名済みトランザクションを持っているというゼロ知識証明をポストします。 このチュートリアルを読むと、ゼロ知識証明の使用方法だけでなく、なぜそれを使用するのか、そしてどのように安全に使用するのかを理解できるようになります。" +author: Ori Pomerantz +tags: [ "ゼロ知識", "サーバー", "オフチェーン", "プライバシー" ] +skill: advanced +lang: ja +published: 2025-10-15 +--- + +## はじめに {#introduction} + +[ロールアップ](/developers/docs/scaling/zk-rollups/)とは対照的に、[Plasma](/developers/docs/scaling/plasma)は完全性のためにイーサリアムメインネットを使用しますが、可用性のためには使用しません。 この記事では、Plasmaのように動作するアプリケーションを作成します。イーサリアムは完全性(不正な変更がないこと)を保証しますが、可用性(中央集権的なコンポーネントがダウンしてシステム全体が無効になる可能性があること)は保証しません。 + +ここで作成するアプリケーションは、プライバシーを保護する銀行です。 異なるアドレスは残高を持つアカウントを所有し、他のアカウントにお金(ETH)を送ることができます。 銀行は状態(アカウントとその残高)とトランザクションのハッシュをポストしますが、実際の残高はプライベートに保つことができるオフチェーンに保持します。 + +## 設計 {#design} + +これは本番環境対応のシステムではなく、教育用ツールです。 そのため、いくつかの単純化された仮定のもとに書かれています。 + +- 固定アカウントプール。 特定のアカウント数があり、各アカウントはあらかじめ決められたアドレスに属します。 ゼロ知識証明では可変サイズのデータ構造を扱うのが難しいため、これによりシステムが大幅に簡素化されます。 本番環境対応のシステムでは、状態ハッシュとして[Merkleルート](/developers/tutorials/merkle-proofs-for-offline-data-integrity/)を使用し、必要な残高のためにMerkle証明を提供できます。 + +- メモリストレージ。 本番システムでは、再起動に備えてすべてのアカウント残高をディスクに書き込む必要があります。 ここでは、情報が単に失われても問題ありません。 + +- 送金のみ。 本番システムでは、資産を銀行に預け入れたり引き出したりする方法が必要になります。 しかし、ここでの目的は概念を説明することだけなので、この銀行は送金に限定されています。 + +### ゼロ知識証明 {#zero-knowledge-proofs} + +根本的なレベルでは、ゼロ知識証明は、証明者が何らかの公開データ_Datapublic_と_Dataprivate_の間に_Relationship_という関係が存在するような、何らかのデータ_Dataprivate_を知っていることを示します。 検証者は、_Relationship_と_Datapublic_を知っています。 + +プライバシーを保護するためには、状態とトランザクションがプライベートである必要があります。 しかし、完全性を確保するためには、状態の[暗号論的ハッシュ](https://en.wikipedia.org/wiki/Cryptographic_hash_function)が公開されている必要があります。 トランザクションを送信する人々に、そのトランザクションが実際に発生したことを証明するために、トランザクションハッシュをポストする必要もあります。 + +ほとんどの場合、_Dataprivate_はゼロ知識証明プログラムへの入力であり、_Datapublic_は出力です。 + +_Dataprivate_のこれらのフィールド: + +- _Staten_、古い状態 +- _Staten+1_、新しい状態 +- _Transaction_、古い状態から新しい状態へ変更するトランザクション。 このトランザクションには、以下のフィールドが含まれている必要があります。 + - 送金を受け取る_宛先アドレス_ + - 送金される_金額_ + - 各トランザクションが一度しか処理されないようにするための_ノンス (nonce)_。 + 送信元アドレスは署名から復元できるため、トランザクションに含める必要はありません。 +- トランザクションを実行する権限を持つ_署名_。 このケースでは、トランザクションを実行する権限を持つアドレスは送信元アドレスのみです。 ゼロ知識システムはこのように動作するため、イーサリアムの署名に加えて、アカウントの公開鍵も必要です。 + +_Datapublic_のフィールドは次のとおりです。 + +- _Hash(Staten)_ 古い状態のハッシュ +- _Hash(Staten+1)_ 新しい状態のハッシュ +- _Hash(Transaction)_ 状態を_Staten_から_Staten+1_へ変更するトランザクションのハッシュ。 + +この関係は、いくつかの条件をチェックします。 + +- 公開ハッシュが、実際にプライベートフィールドの正しいハッシュであること。 +- トランザクションが古い状態に適用されると、新しい状態になること。 +- 署名がトランザクションの送信元アドレスからのものであること。 + +暗号論的ハッシュ関数の特性により、これらの条件を証明するだけで完全性を確保できます。 + +### データ構造 {#data-structures} + +主要なデータ構造は、サーバーが保持する状態です。 すべてのアカウントについて、サーバーはアカウントの残高と[ノンス (nonce)](https://en.wikipedia.org/wiki/Cryptographic_nonce)を追跡し、[リプレイ攻撃](https://en.wikipedia.org/wiki/Replay_attack)を防止するために使用します。 + +### コンポーネント {#components} + +このシステムには2つのコンポーネントが必要です。 + +- トランザクションを受信し、処理し、ゼロ知識証明とともにハッシュをチェーンにポストする_サーバー_。 +- ハッシュを保存し、ゼロ知識証明を検証して状態遷移が正当であることを保証する_スマートコントラクト_。 + +### データと制御フロー {#flows} + +これらは、さまざまなコンポーネントが通信して、あるアカウントから別のアカウントに送金する方法です。 + +1. ウェブブラウザは、署名者のアカウントから別のアカウントへの送金を要求する署名済みトランザクションを送信します。 + +2. サーバーはトランザクションが有効であることを検証します。 + + - 署名者は銀行に十分な残高のあるアカウントを持っている。 + - 受信者は銀行にアカウントを持っている。 + +3. サーバーは、送金された金額を署名者の残高から差し引き、受信者の残高に加算することで、新しい状態を計算します。 + +4. サーバーは、状態の変更が有効であることを示すゼロ知識証明を計算します。 + +5. サーバーは、以下を含むトランザクションをイーサリアムに送信します。 + + - 新しい状態ハッシュ + - トランザクションハッシュ (トランザクションの送信者が処理されたことを知るため) + - 新しい状態への移行が有効であることを証明するゼロ知識証明 + +6. スマートコントラクトはゼロ知識証明を検証します。 + +7. ゼロ知識証明が確認された場合、スマートコントラクトは次のアクションを実行します。 + - 現在の状態ハッシュを新しい状態ハッシュに更新する + - 新しい状態ハッシュとトランザクションハッシュを含むログエントリを出力する + +### ツール {#tools} + +クライアント側のコードには、[Vite](https://vite.dev/)、[React](https://react.dev/)、[Viem](https://viem.sh/)、[Wagmi](https://wagmi.sh/)を使用します。 これらは業界標準のツールです。もし馴染みがない場合は、[このチュートリアル](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/)を使用できます。 + +サーバーの大部分は、[Node](https://nodejs.org/en)を使用したJavaScriptで書かれています。 ゼロ知識の部分は[Noir](https://noir-lang.org/)で書かれています。 バージョン`1.0.0-beta.10`が必要なので、[指示に従ってNoirをインストール](https://noir-lang.org/docs/getting_started/quick_start)した後、次を実行してください。 + +``` +noirup -v 1.0.0-beta.10 +``` + +使用するブロックチェーンは、[Foundry](https://getfoundry.sh/introduction/installation)の一部であるローカルテスト用ブロックチェーンの`anvil`です。 + +## 実装 {#implementation} + +これは複雑なシステムなので、段階的に実装していきます。 + +### ステージ1 - 手動のゼロ知識 {#stage-1} + +最初のステージでは、ブラウザでトランザクションに署名し、その情報を手動でゼロ知識証明に提供します。 ゼロ知識コードは、その情報を`server/noir/Prover.toml`で受け取ることを想定しています([こちら](https://noir-lang.org/docs/getting_started/project_breakdown#provertoml-1)にドキュメントがあります)。 + +動作を確認するには: + +1. [Node](https://nodejs.org/en/download)と[Noir](https://noir-lang.org/install)がインストールされていることを確認してください。 できれば、macOS、Linux、[WSL](https://learn.microsoft.com/en-us/windows/wsl/install)などのUNIXシステムにインストールしてください。 + +2. ステージ1のコードをダウンロードし、Webサーバーを起動してクライアントコードを提供します。 + + ```sh + git clone https://github.com/qbzzt/250911-zk-bank.git -b 01-manual-zk + cd 250911-zk-bank + cd client + npm install + npm run dev + ``` + + ここでWebサーバーが必要な理由は、特定の種類の不正行為を防ぐため、多くのウォレット(MetaMaskなど)がディスクから直接提供されるファイルを受け入れないためです。 + +3. ウォレットでブラウザを開きます。 + +4. ウォレットで新しいパスフレーズを入力します。 これにより既存のパスフレーズが削除されるため、_必ずバックアップを取っておいてください_。 + + パスフレーズは`test test test test test test test test test test test junk`で、これは`anvil`のデフォルトのテスト用パスフレーズです。 + +5. [クライアント側のコード](http://localhost:5173/)にアクセスします。 + +6. ウォレットに接続し、宛先アカウントと金額を選択します。 + +7. **「Sign (署名)」** をクリックし、トランザクションに署名します。 + +8. **Prover.toml**の見出しの下にテキストがあります。 `server/noir/Prover.toml`をそのテキストに置き換えます。 + +9. ゼロ知識証明を実行します。 + + ```sh + cd ../server/noir + nargo execute + ``` + + 出力は以下のようになります + + ``` + ori@CryptoDocGuy:~/noir/250911-zk-bank/server/noir$ nargo execute + + [zkBank] Circuit witness successfully solved + [zkBank] Witness saved to target/zkBank.gz + [zkBank] Circuit output: (0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b, 0x0cfc0a67cb7308e4e9b254026b54204e34f6c8b041be207e64c5db77d95dd82d, 0x450cf9da6e180d6159290554ae3d8787, 0x6d8bc5a15b9037e52fb59b6b98722a85) + ``` + +10. 最後の2つの値をウェブブラウザに表示されるハッシュと比較して、メッセージが正しくハッシュ化されているかを確認します。 + +#### `server/noir/Prover.toml` {#server-noir-prover-toml} + +[このファイル](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml)は、Noirが期待する情報形式を示しています。 + +```toml +message="send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 500 finney (milliEth) 0 " +``` + +メッセージはテキスト形式であり、ユーザーが理解しやすく(署名時に必要)、Noirコードが解析しやすくなっています。 金額はfinneyで表記されており、一方では分割送金を可能にし、他方では読みやすくしています。 最後の数字は[ノンス (nonce)](https://en.wikipedia.org/wiki/Cryptographic_nonce)です。 + +文字列の長さは100文字です。 ゼロ知識証明は可変長データをうまく扱えないため、データをパディングすることがしばしば必要になります。 + +```toml +pubKeyX=["0x83",...,"0x75"] +pubKeyY=["0x35",...,"0xa5"] +signature=["0xb1",...,"0x0d"] +``` + +これら3つのパラメータは固定サイズのバイト配列です。 + +```toml +[[accounts]] +address="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +balance=100_000 +nonce=0 + +[[accounts]] +address="0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +balance=100_000 +nonce=0 +``` + +これは構造体の配列を指定する方法です。 各エントリについて、アドレス、残高(milliETH、別名)を指定します。 [finney](https://cryptovalleyjournal.com/glossary/finney/))、および次のノンス (nonce)値を指定します。 + +#### `client/src/Transfer.tsx` {#client-src-transfer-tsx} + +[このファイル](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/client/src/Transfer.tsx)は、クライアント側の処理を実装し、`server/noir/Prover.toml`ファイル(ゼロ知識パラメータを含むファイル)を生成します。 + +ここでは、より興味深い部分について説明します。 + +```tsx +export default attrs => { +``` + +この関数は`Transfer` Reactコンポーネントを作成し、他のファイルからインポートできます。 + +```tsx + const accounts = [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + "0x90F79bf6EB2c4f870365E785982E1f101E93b906", + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + ] +``` + +これらはアカウントのアドレスで、`test ...`によって作成されたアドレスです。 test junk`パスフレーズです。 独自のアドレスを使用したい場合は、この定義を変更してください。 + +```tsx + const account = useAccount() + const wallet = createWalletClient({ + transport: custom(window.ethereum!) + }) +``` + +これらの[Wagmiフック](https://wagmi.sh/react/api/hooks)により、[viem](https://viem.sh/)ライブラリとウォレットにアクセスできます。 + +```tsx + const message = `send ${toAccount} ${ethAmount*1000} finney (milliEth) ${nonce}`.padEnd(100, " ") +``` + +これは、スペースでパディングされたメッセージです。 [`useState`](https://react.dev/reference/react/useState)変数のいずれかが変更されるたびに、コンポーネントが再描画され、`message`が更新されます。 + +```tsx + const sign = async () => { +``` + +この関数は、ユーザーが\*\*「Sign (署名)」\*\*ボタンをクリックしたときに呼び出されます。 メッセージは自動的に更新されますが、署名にはウォレットでのユーザーの承認が必要であり、必要でない限りは要求したくありません。 + +```tsx + const signature = await wallet.signMessage({ + account: fromAccount, + message, + }) +``` + +ウォレットに[メッセージへの署名](https://viem.sh/docs/accounts/local/signMessage)を依頼します。 + +```tsx + const hash = hashMessage(message) +``` + +メッセージハッシュを取得します。 (Noirコードの)デバッグのためにユーザーに提供すると便利です。 + +```tsx + const pubKey = await recoverPublicKey({ + hash, + signature + }) +``` + +[公開鍵を取得](https://viem.sh/docs/utilities/recoverPublicKey)します。 これは[Noir `ecrecover`](https://github.com/colinnielsen/ecrecover-noir)関数に必要です。 + +```tsx + setSignature(signature) + setHash(hash) + setPubKey(pubKey) +``` + +状態変数を設定します。 これにより、コンポーネントが再描画され(`sign`関数が終了した後)、ユーザーに更新された値が表示されます。 + +```tsx + let proverToml = ` +``` + +`Prover.toml`のテキストです。 + +```tsx +message="${message}" + +pubKeyX=${hexToArray(pubKey.slice(4,4+2*32))} +pubKeyY=${hexToArray(pubKey.slice(4+2*32))} +``` + +Viemは公開鍵を65バイトの16進数文字列として提供します。 最初のバイトはバージョンマーカーである`0x04`です。 これに続いて、公開鍵の`x`に32バイト、公開鍵の`y`に32バイトが続きます。 + +しかし、Noirはこの情報を`x`と`y`の2つのバイト配列として受け取ることを想定しています。 ゼロ知識証明の一部としてではなく、ここでクライアント上で解析する方が簡単です。 + +これは一般的にゼロ知識の良い実践であることに注意してください。 ゼロ知識証明内のコードは高価であるため、ゼロ知識証明の外で実行できる処理は、ゼロ知識証明の外で_実行されるべき_です。 + +```tsx +signature=${hexToArray(signature.slice(2,-2))} +``` + +署名も65バイトの16進数文字列として提供されます。 しかし、最後のバイトは公開鍵を復元するためにのみ必要です。 公開鍵はすでにNoirコードに提供されるため、署名を検証するために必要ではなく、Noirコードもそれを要求しません。 + +```tsx +${accounts.map(accountInProverToml).reduce((a,b) => a+b, "")} +` +``` + +アカウントを提供します。 + +```tsx + setProverToml(proverToml) + } + + return ( + <> +

Transfer

+``` + +これはコンポーネントのHTML(より正確には、[JSX](https://react.dev/learn/writing-markup-with-jsx))形式です。 + +#### `server/noir/src/main.nr` {#server-noir-src-main-nr} + +[このファイル](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/src/main.nr)は、実際のゼロ知識コードです。 + +``` +use std::hash::pedersen_hash; +``` + +[Pedersenハッシュ](https://rya-sge.github.io/access-denied/2024/05/07/pedersen-hash-function/)は、[Noir標準ライブラリ](https://noir-lang.org/docs/noir/standard_library/cryptographic_primitives/hashes#pedersen_hash)で提供されています。 ゼロ知識証明では、このハッシュ関数が一般的に使用されます。 標準のハッシュ関数と比較して、[算術回路](https://rareskills.io/post/arithmetic-circuit)内で計算するのがはるかに簡単です。 + +``` +use keccak256::keccak256; +use dep::ecrecover; +``` + +これら2つの関数は外部ライブラリで、[`Nargo.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Nargo.toml)で定義されています。 これらはその名の通り、[keccak256ハッシュ](https://emn178.github.io/online-tools/keccak_256.html)を計算する関数と、イーサリアム署名を検証して署名者のイーサリアムアドレスを復元する関数です。 + +``` +global ACCOUNT_NUMBER : u32 = 5; +``` + +Noirは[Rust](https://www.rust-lang.org/)に触発されています。 変数は、デフォルトで定数です。 これがグローバルな設定定数を定義する方法です。 具体的には、`ACCOUNT_NUMBER`は私たちが保存するアカウントの数です。 + +`u`という名前のデータ型は、そのビット数の符号なし整数です。 サポートされている型は`u8`、`u16`、`u32`、`u64`、`u128`のみです。 + +``` +global FLAT_ACCOUNT_FIELDS : u32 = 2; +``` + +この変数は、後述するように、アカウントのPedersenハッシュに使用されます。 + +``` +global MESSAGE_LENGTH : u32 = 100; +``` + +上述のように、メッセージの長さは固定です。 ここで指定されています。 + +``` +global ASCII_MESSAGE_LENGTH : [u8; 3] = [0x31, 0x30, 0x30]; +global HASH_BUFFER_SIZE : u32 = 26+3+MESSAGE_LENGTH; +``` + +[EIP-191署名](https://eips.ethereum.org/EIPS/eip-191)には、26バイトのプレフィックス、その後にASCIIでのメッセージ長、最後にメッセージ自体を含むバッファが必要です。 + +``` +struct Account { + balance: u128, + address: Field, + nonce: u32, +} +``` + +アカウントについて保存する情報。 [`Field`](https://noir-lang.org/docs/noir/concepts/data_types/fields)は、通常最大253ビットの数値で、ゼロ知識証明を実装する[算術回路](https://rareskills.io/post/arithmetic-circuit)で直接使用できます。 ここでは`Field`を使用して160ビットのイーサリアムアドレスを保存します。 + +``` +struct TransferTxn { + from: Field, + to: Field, + amount: u128, + nonce: u32 +} +``` + +送金トランザクションで保存する情報です。 + +``` +fn flatten_account(account: Account) -> [Field; FLAT_ACCOUNT_FIELDS] { +``` + +関数定義です。 パラメータは`Account`情報です。 結果は`Field`変数の配列で、その長さは`FLAT_ACCOUNT_FIELDS`です。 + +``` + let flat = [ + account.address, + ((account.balance << 32) + account.nonce.into()).into(), + ]; +``` + +配列の最初の値はアカウントアドレスです。 2番目の値には、残高とノンス (nonce)の両方が含まれています。 `.into()`の呼び出しは、数値を必要なデータ型に変更します。 `account.nonce`は`u32`値ですが、`u128`値である`account.balance « 32`に加算するには、`u128`である必要があります。 それが最初の`.into()`です。 2番目のものは、`u128`の結果を`Field`に変換して、配列に収まるようにします。 + +``` + flat +} +``` + +Noirでは、関数は最後にのみ値を返すことができます(早期リターンはありません)。 戻り値を指定するには、関数の閉じ括弧の直前で評価します。 + +``` +fn flatten_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] { +``` + +この関数は、アカウント配列を`Field`配列に変換し、Petersenハッシュの入力として使用できます。 + +``` + let mut flat: [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] = [0; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER]; +``` + +これは、可変変数、つまり定数では_ない_変数を指定する方法です。 Noirの変数は常に値を持つ必要があるため、この変数をすべてゼロに初期化します。 + +``` + for i in 0..ACCOUNT_NUMBER { +``` + +`for`ループです。 境界が定数であることに注意してください。 Noirのループは、コンパイル時に境界がわかっている必要があります。 その理由は、算術回路がフロー制御をサポートしていないためです。 `for`ループを処理するとき、コンパイラは単にその中のコードを、各反復ごとに複数回配置します。 + +``` + let fields = flatten_account(accounts[i]); + for j in 0..FLAT_ACCOUNT_FIELDS { + flat[i*FLAT_ACCOUNT_FIELDS + j] = fields[j]; + } + } + + flat +} + +fn hash_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> Field { + pedersen_hash(flatten_accounts(accounts)) +} +``` + +最後に、アカウント配列をハッシュ化する関数にたどり着きました。 + +``` +fn find_account(accounts: [Account; ACCOUNT_NUMBER], address: Field) -> u32 { + let mut account : u32 = ACCOUNT_NUMBER; + + for i in 0..ACCOUNT_NUMBER { + if accounts[i].address == address { + account = i; + } + } +``` + +この関数は、特定のアドレスを持つアカウントを検索します。 この関数は、アドレスを見つけた後でもすべてのアカウントを反復処理するため、標準的なコードでは非常に非効率的です。 + +しかし、ゼロ知識証明ではフロー制御はありません。 条件をチェックする必要がある場合は、毎回チェックする必要があります。 + +`if`文でも同様のことが起こります。 上記のループの`if`文は、これらの数式に変換されます。 + +_conditionresult = accounts[i].address == address_ // 等しい場合は1、そうでない場合は0 + +_accountnew = conditionresult\*i + (1-conditionresult)\*accountold_ + +```rust + assert (account < ACCOUNT_NUMBER, f"{address} does not have an account"); + + account +} +``` + +[`assert`](https://noir-lang.org/docs/dev/noir/concepts/assert)関数は、アサーションが偽の場合にゼロ知識証明をクラッシュさせます。 この場合、関連するアドレスを持つアカウントが見つからない場合です。 アドレスを報告するには、[フォーマット文字列](https://noir-lang.org/docs/noir/concepts/data_types/strings#format-strings)を使用します。 + +```rust +fn apply_transfer_txn(accounts: [Account; ACCOUNT_NUMBER], txn: TransferTxn) -> [Account; ACCOUNT_NUMBER] { +``` + +この関数は送金トランザクションを適用し、新しいアカウント配列を返します。 + +```rust + let from = find_account(accounts, txn.from); + let to = find_account(accounts, txn.to); + + let (txnFrom, txnAmount, txnNonce, accountNonce) = + (txn.from, txn.amount, txn.nonce, accounts[from].nonce); +``` + +Noirのフォーマット文字列内で構造体要素にアクセスできないため、使用可能なコピーを作成します。 + +```rust + assert (accounts[from].balance >= txn.amount, + f"{txnFrom} does not have {txnAmount} finney"); + + assert (accounts[from].nonce == txn.nonce, + f"Transaction has nonce {txnNonce}, but the account is expected to use {accountNonce}"); +``` + +これらは、トランザクションを無効にする可能性のある2つの条件です。 + +```rust + let mut newAccounts = accounts; + + newAccounts[from].balance -= txn.amount; + newAccounts[from].nonce += 1; + newAccounts[to].balance += txn.amount; + + newAccounts +} +``` + +新しいアカウント配列を作成して返します。 + +```rust +fn readAddress(messageBytes: [u8; MESSAGE_LENGTH]) -> Field +``` + +この関数は、メッセージからアドレスを読み取ります。 + +```rust +{ + let mut result : Field = 0; + + for i in 7..47 { +``` + +アドレスは常に20バイト(別名 40桁の16進数)で、7文字目から始まります。 + +```rust + result *= 0x10; + if messageBytes[i] >= 48 & messageBytes[i] <= 57 { // 0-9 + result += (messageBytes[i]-48).into(); + } + if messageBytes[i] >= 65 & messageBytes[i] <= 70 { // A-F + result += (messageBytes[i]-65+10).into() + } + if messageBytes[i] >= 97 & messageBytes[i] <= 102 { // a-f + result += (messageBytes[i]-97+10).into() + } + } + + result +} + +fn readAmountAndNonce(messageBytes: [u8; MESSAGE_LENGTH]) -> (u128, u32) +``` + +メッセージから金額とノンス (nonce)を読み取ります。 + +```rust +{ + let mut amount : u128 = 0; + let mut nonce: u32 = 0; + let mut stillReadingAmount: bool = true; + let mut lookingForNonce: bool = false; + let mut stillReadingNonce: bool = false; +``` + +メッセージでは、アドレスの後の最初の数字は、送金するfinney(別名 ETHの1000分の1)の量です。 2番目の数字はノンス (nonce)です。 それらの間のテキストは無視されます。 + +```rust + for i in 48..MESSAGE_LENGTH { + if messageBytes[i] >= 48 & messageBytes[i] <= 57 { // 0-9 + let digit = (messageBytes[i]-48); + + if stillReadingAmount { + amount = amount*10 + digit.into(); + } + + if lookingForNonce { // We just found it + stillReadingNonce = true; + lookingForNonce = false; + } + + if stillReadingNonce { + nonce = nonce*10 + digit.into(); + } + } else { + if stillReadingAmount { + stillReadingAmount = false; + lookingForNonce = true; + } + if stillReadingNonce { + stillReadingNonce = false; + } + } + } + + (amount, nonce) +} +``` + +[タプル](https://noir-lang.org/docs/noir/concepts/data_types/tuples)を返すことは、Noirで関数から複数の値を返す方法です。 + +```rust +fn readTransferTxn(message: str) -> TransferTxn +{ + let mut txn: TransferTxn = TransferTxn { from: 0, to: 0, amount:0, nonce:0 }; + let messageBytes = message.as_bytes(); + + txn.to = readAddress(messageBytes); + let (amount, nonce) = readAmountAndNonce(messageBytes); + txn.amount = amount; + txn.nonce = nonce; + + txn +} +``` + +この関数はメッセージをバイトに変換し、次に金額を`TransferTxn`に変換します。 + +```rust +// ViemのhashMessageと同等 +// https://viem.sh/docs/utilities/hashMessage#hashmessage +fn hashMessage(message: str) -> [u8;32] { +``` + +アカウントはゼロ知識証明の内部でのみハッシュ化されるため、Pedersenハッシュを使用することができました。 しかし、このコードでは、ブラウザによって生成されたメッセージの署名をチェックする必要があります。 そのためには、[EIP 191](https://eips.ethereum.org/EIPS/eip-191)のイーサリアム署名形式に従う必要があります。 これは、標準のプレフィックス、ASCIIでのメッセージ長、およびメッセージ自体を含む結合されたバッファを作成し、それをハッシュ化するためにイーサリアム標準のkeccak256を使用する必要があることを意味します。 + +```rust + // ASCII prefix + let prefix_bytes = [ + 0x19, // \x19 + 0x45, // 'E' + 0x74, // 't' + 0x68, // 'h' + 0x65, // 'e' + 0x72, // 'r' + 0x65, // 'e' + 0x75, // 'u' + 0x6D, // 'm' + 0x20, // ' ' + 0x53, // 'S' + 0x69, // 'i' + 0x67, // 'g' + 0x6E, // 'n' + 0x65, // 'e' + 0x64, // 'd' + 0x20, // ' ' + 0x4D, // 'M' + 0x65, // 'e' + 0x73, // 's' + 0x73, // 's' + 0x61, // 'a' + 0x67, // 'g' + 0x65, // 'e' + 0x3A, // ':' + 0x0A // '\n' + ]; +``` + +アプリケーションがユーザーにトランザクションやその他の目的で使用できるメッセージへの署名を要求するケースを避けるため、EIP 191では、すべての署名済みメッセージは、文字0x19(有効なASCII文字ではない)で始まり、その後に`Ethereum Signed Message:`と改行が続くことを指定しています。 + +```rust + let mut buffer: [u8; HASH_BUFFER_SIZE] = [0u8; HASH_BUFFER_SIZE]; + for i in 0..26 { + buffer[i] = prefix_bytes[i]; + } + + let messageBytes : [u8; MESSAGE_LENGTH] = message.as_bytes(); + + if MESSAGE_LENGTH <= 9 { + for i in 0..1 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+1] = messageBytes[i]; + } + } + + if MESSAGE_LENGTH >= 10 & MESSAGE_LENGTH <= 99 { + for i in 0..2 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+2] = messageBytes[i]; + } + } + + if MESSAGE_LENGTH >= 100 { + for i in 0..3 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+3] = messageBytes[i]; + } + } + + assert(MESSAGE_LENGTH < 1000, "Messages whose length is over three digits are not supported"); +``` + +メッセージ長が999までを処理し、それより大きい場合は失敗させます。 メッセージの長さは定数ですが、変更しやすくするためにこのコードを追加しました。 本番システムでは、パフォーマンスを向上させるために`MESSAGE_LENGTH`は変更されないと仮定するでしょう。 + +```rust + keccak256::keccak256(buffer, HASH_BUFFER_SIZE) +} +``` + +イーサリアム標準の`keccak256`関数を使用します。 + +```rust +fn signatureToAddressAndHash( + message: str, + pubKeyX: [u8; 32], + pubKeyY: [u8; 32], + signature: [u8; 64] + ) -> (Field, Field, Field) // address, first 16 bytes of hash, last 16 bytes of hash +{ +``` + +この関数は署名を検証し、それにはメッセージハッシュが必要です。 そして、署名したアドレスとメッセージハッシュを提供してくれます。 メッセージハッシュは2つの`Field`値で提供されます。これは、バイト配列よりもプログラムの残りの部分で使いやすいためです。 + +フィールドの計算は大きな数を[法](https://en.wikipedia.org/wiki/Modulo)として行われますが、その数は通常256ビット未満であるため(そうでなければEVMでこれらの計算を実行するのが難しくなるため)、2つの`Field`値を使用する必要があります。 + +```rust + let hash = hashMessage(message); + + let mut (hash1, hash2) = (0,0); + + for i in 0..16 { + hash1 = hash1*256 + hash[31-i].into(); + hash2 = hash2*256 + hash[15-i].into(); + } +``` + +`hash1`と`hash2`を可変変数として指定し、ハッシュをバイトごとに書き込みます。 + +```rust + ( + ecrecover::ecrecover(pubKeyX, pubKeyY, signature, hash), +``` + +これは[Solidityの`ecrecover`](https://docs.soliditylang.org/en/v0.8.30/cheatsheet.html#mathematical-and-cryptographic-functions)に似ていますが、2つの重要な違いがあります。 + +- 署名が有効でない場合、呼び出しは`assert`に失敗し、プログラムは中止されます。 +- 公開鍵は署名とハッシュから復元できますが、これは外部で実行できる処理であり、したがってゼロ知識証明の内部で行う価値はありません。 ここで誰かが私たちをだまそうとすると、署名の検証は失敗します。 + +```rust + hash1, + hash2 + ) +} + +fn main( + accounts: [Account; ACCOUNT_NUMBER], + message: str, + pubKeyX: [u8; 32], + pubKeyY: [u8; 32], + signature: [u8; 64], + ) -> pub ( + Field, // Hash of old accounts array + Field, // Hash of new accounts array + Field, // First 16 bytes of message hash + Field, // Last 16 bytes of message hash + ) +``` + +最後に、`main`関数に到達します。 アカウントのハッシュが古い値から新しい値に正当に変更されるトランザクションがあることを証明する必要があります。 また、特定のトランザクションハッシュを持っていることを証明する必要もあります。そうすることで、送信した人が自分のトランザクションが処理されたことを知ることができます。 + +```rust +{ + let mut txn = readTransferTxn(message); +``` + +`txn`は可変である必要があります。なぜなら、fromアドレスはメッセージからではなく、署名から読み取るからです。 + +```rust + let (fromAddress, txnHash1, txnHash2) = signatureToAddressAndHash( + message, + pubKeyX, + pubKeyY, + signature); + + txn.from = fromAddress; + + let newAccounts = apply_transfer_txn(accounts, txn); + + ( + hash_accounts(accounts), + hash_accounts(newAccounts), + txnHash1, + txnHash2 + ) +} +``` + +### ステージ2 - サーバーの追加 {#stage-2} + +第2ステージでは、ブラウザからの送金トランザクションを受信して実装するサーバーを追加します。 + +動作を確認するには: + +1. Viteが実行中の場合は停止します。 + +2. サーバーを含むブランチをダウンロードし、必要なモジュールがすべて揃っていることを確認します。 + + ```sh + git checkout 02-add-server + cd client + npm install + cd ../server + npm install + ``` + + Noirコードをコンパイルする必要はありません。ステージ1で使用したコードと同じです。 + +3. サーバーを起動します。 + + ```sh + npm run start + ``` + +4. 別のコマンドラインウィンドウでViteを実行して、ブラウザコードを提供します。 + + ```sh + cd client + npm run dev + ``` + +5. クライアントコード([http://localhost:5173](http://localhost:5173))にアクセスします + +6. トランザクションを発行する前に、ノンス (nonce)と送信できる金額を知る必要があります。 この情報を取得するには、\*\*「Update account data (アカウントデータを更新)」\*\*をクリックしてメッセージに署名します。 + + ここでジレンマがあります。 一方で、再利用可能なメッセージ([リプレイ攻撃](https://en.wikipedia.org/wiki/Replay_attack))に署名したくないため、そもそもノンス (nonce)が必要です。 しかし、まだノンス (nonce)がありません。 解決策は、一度しか使用できず、両側で既に持っているノンス (nonce)、例えば現在時刻などを選択することです。 + + この解決策の問題は、時刻が完全に同期していない可能性があることです。 そこで代わりに、毎分変わる値に署名します。 これは、リプレイ攻撃に対する脆弱性のウィンドウが最大1分であることを意味します。 本番環境では署名されたリクエストがTLSで保護されること、またトンネルの反対側であるサーバーは既に残高とノンス (nonce)を開示できる(動作するためにそれらを知る必要がある)ことを考えると、これは許容できるリスクです。 + +7. ブラウザが残高とノンス (nonce)を取得すると、送金フォームが表示されます。 宛先アドレスと金額を選択し、\*\*「Transfer (送金)」\*\*をクリックします。 このリクエストに署名します。 + +8. 送金を確認するには、\*\*「Update account data (アカウントデータを更新)」\*\*するか、サーバーを実行しているウィンドウを確認します。 サーバーは状態が変更されるたびにログを記録します。 + + ``` + ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start + + > server@1.0.0 start + > node --experimental-json-modules index.mjs + + Listening on port 3000 + Txn send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 36000 finney (milliEth) 0 processed + New state: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 64000 (1) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 100000 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 136000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0) + Txn send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 7200 finney (milliEth) 1 processed + New state: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 56800 (2) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 107200 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 136000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0) + Txn send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 3000 finney (milliEth) 2 processed + New state: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 53800 (3) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 107200 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 139000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0) + ``` + +#### `server/index.mjs` {#server-index-mjs-1} + +[このファイル](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/index.mjs)はサーバープロセスを含み、[`main.nr`](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/noir/src/main.nr)のNoirコードと相互作用します。 興味深い部分を説明します。 + +```js +import { Noir } from '@noir-lang/noir_js' +``` + +[noir.js](https://www.npmjs.com/package/@noir-lang/noir_js)ライブラリは、JavaScriptコードとNoirコードの間のインターフェースです。 + +```js +const circuit = JSON.parse(await fs.readFile("./noir/target/zkBank.json")) +const noir = new Noir(circuit) +``` + +算術回路(前の段階で作成したコンパイル済みのNoirプログラム)をロードし、実行準備をします。 + +```js +// We only provide account information in return to a signed request +const accountInformation = async signature => { + const fromAddress = await recoverAddress({ + hash: hashMessage("Get account data " + Math.floor((new Date().getTime())/60000)), + signature + }) +``` + +アカウント情報を提供するには、署名のみが必要です。 その理由は、メッセージがどうなるかを既に知っており、したがってメッセージハッシュも知っているからです。 + +```js +const processMessage = async (message, signature) => { +``` + +メッセージを処理し、エンコードされたトランザクションを実行します。 + +```js + // Get the public key + const pubKey = await recoverPublicKey({ + hash, + signature + }) +``` + +サーバーでJavaScriptを実行するようになったので、クライアントではなくサーバーで公開鍵を取得できます。 + +```js + let noirResult + try { + noirResult = await noir.execute({ + message, + signature: signature.slice(2,-2).match(/.{2}/g).map(x => `0x${x}`), + pubKeyX, + pubKeyY, + accounts: Accounts + }) +``` + +`noir.execute`はNoirプログラムを実行します。 パラメータは、[`Prover.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml)で提供されるものと同等です。 Viemが行うように、長い値は単一の16進数値(`0x60A7`)ではなく、16進文字列の配列(`["0x60", "0xA7"]`)として提供されることに注意してください。 + +```js + } catch (err) { + console.log(`Noir error: ${err}`) + throw Error("Invalid transaction, not processed") + } +``` + +エラーが発生した場合は、それをキャッチし、簡略化されたバージョンをクライアントにリレーします。 + +```js + Accounts[fromAccountNumber].nonce++ + Accounts[fromAccountNumber].balance -= amount + Accounts[toAccountNumber].balance += amount +``` + +トランザクションを適用します。 Noirコードでは既に実行しましたが、そこから結果を抽出するよりもここで再度実行する方が簡単です。 + +```js +let Accounts = [ + { + address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + balance: 5000, + nonce: 0, + }, +``` + +初期の`Accounts`構造体。 + +### ステージ3 - イーサリアムスマートコントラクト {#stage-3} + +1. サーバーとクライアントプロセスを停止します。 + +2. スマートコントラクトを含むブランチをダウンロードし、必要なモジュールがすべて揃っていることを確認します。 + + ```sh + git checkout 03-smart-contracts + cd client + npm install + cd ../server + npm install + ``` + +3. 別のコマンドラインウィンドウで`anvil`を実行します。 + +4. 検証キーとSolidityベリファイアを生成し、ベリファイアコードをSolidityプロジェクトにコピーします。 + + ```sh + cd noir + bb write_vk -b ./target/zkBank.json -o ./target --oracle_hash keccak + bb write_solidity_verifier -k ./target/vk -o ./target/Verifier.sol + cp target/Verifier.sol ../../smart-contracts/src + ``` + +5. スマートコントラクトに移動し、`anvil`ブロックチェーンを使用するように環境変数を設定します。 + + ```sh + cd ../../smart-contracts + export ETH_RPC_URL=http://localhost:8545 + ETH_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + ``` + +6. `Verifier.sol`をデプロイし、アドレスを環境変数に保存します。 + + ```sh + VERIFIER_ADDRESS=`forge create src/Verifier.sol:HonkVerifier --private-key $ETH_PRIVATE_KEY --optimize --broadcast | awk '/Deployed to:/ {print $3}'` + echo $VERIFIER_ADDRESS + ``` + +7. `ZkBank`コントラクトをデプロイします。 + + ```sh + ZKBANK_ADDRESS=`forge create ZkBank --private-key $ETH_PRIVATE_KEY --broadcast --constructor-args $VERIFIER_ADDRESS 0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b | awk '/Deployed to:/ {print $3}'` + echo $ZKBANK_ADDRESS + ``` + + `0x199..67b`の値は、`Accounts`の初期状態のPedersonハッシュです。 `server/index.mjs`でこの初期状態を変更した場合、トランザクションを実行してゼロ知識証明によって報告される初期ハッシュを確認できます。 + +8. サーバーを実行します。 + + ```sh + cd ../server + npm run start + ``` + +9. 別のコマンドラインウィンドウでクライアントを実行します。 + + ```sh + cd client + npm run dev + ``` + +10. いくつかのトランザクションを実行します。 + +11. 状態がオンチェーンで変更されたことを確認するには、サーバープロセスを再起動します。 トランザクションの元のハッシュ値がオンチェーンに保存されているハッシュ値と異なるため、`ZkBank`がトランザクションを受け入れなくなったことを確認してください。 + + これは予想されるエラーの種類です。 + + ``` + ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start + + > server@1.0.0 start + > node --experimental-json-modules index.mjs + + Listening on port 3000 + Verification error: ContractFunctionExecutionError: The contract function "processTransaction" reverted with the following reason: + Wrong old state hash + + Contract Call: + address: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + function: processTransaction(bytes _proof, bytes32[] _publicInputs) + args: (0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf00000000000000000000000000000000000000000000000b75c020998797da7800000000000000000000000000000000000000000000000) + ``` + +#### `server/index.mjs` {#server-index-mjs-2} + +このファイルの変更点は、主に実際の証明を作成し、オンチェーンで送信することに関連しています。 + +```js +import { exec } from 'child_process' +import util from 'util' + +const execPromise = util.promisify(exec) +``` + +オンチェーンで送信する実際の証明を作成するには、[Barretenbergパッケージ](https://github.com/AztecProtocol/aztec-packages/tree/next/barretenberg)を使用する必要があります。 このパッケージは、コマンドラインインターフェイス(`bb`)を実行するか、[JavaScriptライブラリ、`bb.js`](https://www.npmjs.com/package/@aztec/bb.js)を使用して使用できます。 JavaScriptライブラリはネイティブでコードを実行するよりもはるかに遅いため、ここではコマンドラインを使用するために[`exec`](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback)を使用します。 + +`bb.js`を使用することにした場合、使用しているNoirのバージョンと互換性のあるバージョンを使用する必要があることに注意してください。 執筆時点では、現在のNoirバージョン(1.0.0-beta.11)は`bb.js`バージョン0.87を使用しています。 + +```js +const zkBankAddress = process.env.ZKBANK_ADDRESS || "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" +``` + +ここでのアドレスは、クリーンな`anvil`で開始し、上記の手順に従ったときに取得するものです。 + +```js +const walletClient = createWalletClient({ + chain: anvil, + transport: http(), + account: privateKeyToAccount("0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6") +}) +``` + +この秘密鍵は、`anvil`のデフォルトの事前資金提供アカウントの1つです。 + +```js +const generateProof = async (witness, fileID) => { +``` + +`bb`実行可能ファイルを使用して証明を生成します。 + +```js + const fname = `witness-${fileID}.gz` + await fs.writeFile(fname, witness) +``` + +ウィットネスをファイルに書き込みます。 + +```js + await execPromise(`bb prove -b ./noir/target/zkBank.json -w ${fname} -o ${fileID} --oracle_hash keccak --output_format fields`) +``` + +実際に証明を作成します。 このステップでは、公開変数を持つファイルも作成しますが、それは必要ありません。 これらの変数は既に`noir.execute`から取得しています。 + +```js + const proof = "0x" + JSON.parse(await fs.readFile(`./${fileID}/proof_fields.json`)).reduce((a,b) => a+b, "").replace(/0x/g, "") +``` + +証明は`Field`値のJSON配列であり、それぞれが16進数値として表されます。 ただし、トランザクションでは単一の`bytes`値として送信する必要があり、Viemはこれを大きな16進数文字列で表します。 ここでは、すべての値を連結し、すべての`0x`を削除し、最後に1つ追加することで形式を変更します。 + +```js + await execPromise(`rm -r ${fname} ${fileID}`) + + return proof +} +``` + +クリーンアップして証明を返します。 + +```js +const processMessage = async (message, signature) => { + . + . + . + + const publicFields = noirResult.returnValue.map(x=>'0x' + x.slice(2).padStart(64, "0")) +``` + +公開フィールドは32バイト値の配列である必要があります。 ただし、トランザクションハッシュを2つの`Field`値に分割する必要があったため、16バイト値として表示されます。 ここでは、Viemが実際には32バイトであることを理解できるようにゼロを追加します。 + +```js + const proof = await generateProof(noirResult.witness, `${fromAddress}-${nonce}`) +``` + +各アドレスは各ノンス (nonce)を一度しか使用しないため、`fromAddress`と`nonce`の組み合わせをウィットネスファイルと出力ディレクトリの一意の識別子として使用できます。 + +```js + try { + await zkBank.write.processTransaction([ + proof, publicFields]) + } catch (err) { + console.log(`Verification error: ${err}`) + throw Error("Can't verify the transaction onchain") + } + . + . + . +} +``` + +トランザクションをチェーンに送信します。 + +#### `smart-contracts/src/ZkBank.sol` {#smart-contracts-src-zkbank-sol} + +これは、トランザクションを受け取るオンチェーンコードです。 + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.21; + +import {HonkVerifier} from "./Verifier.sol"; + +contract ZkBank { + HonkVerifier immutable myVerifier; + bytes32 currentStateHash; + + constructor(address _verifierAddress, bytes32 _initialStateHash) { + currentStateHash = _initialStateHash; + myVerifier = HonkVerifier(_verifierAddress); + } +``` + +オンチェーンコードは、2つの変数を追跡する必要があります。ベリファイア(`nargo`によって作成される別のコントラクト)と現在の状態ハッシュです。 + +```solidity + event TransactionProcessed( + bytes32 indexed transactionHash, + bytes32 oldStateHash, + bytes32 newStateHash + ); +``` + +状態が変更されるたびに、`TransactionProcessed`イベントを発行します。 + +```solidity + function processTransaction( + bytes calldata _proof, + bytes32[] calldata _publicFields + ) public { +``` + +この関数はトランザクションを処理します。 証明(`bytes`として)と公開入力(`bytes32`配列として)を、ベリファイアが必要とする形式で取得します(オンチェーン処理を最小限に抑え、したがってガスコストを削減するため)。 + +```solidity + require(_publicInputs[0] == currentStateHash, + "Wrong old state hash"); +``` + +ゼロ知識証明は、トランザクションが現在のハッシュから新しいハッシュに変更されることである必要があります。 + +```solidity + myVerifier.verify(_proof, _publicFields); +``` + +ベリファイアコントラクトを呼び出して、ゼロ知識証明を検証します。 このステップは、ゼロ知識証明が間違っている場合にトランザクションをリバートします。 + +```solidity + currentStateHash = _publicFields[1]; + + emit TransactionProcessed( + _publicFields[2]<<128 | _publicFields[3], + _publicFields[0], + _publicFields[1] + ); + } +} +``` + +すべてが問題なければ、状態ハッシュを新しい値に更新し、`TransactionProcessed`イベントを発行します。 + +## 中央集権型コンポーネントによる悪用 {#abuses} + +情報セキュリティは、3つの属性で構成されます。 + +- _機密性_、ユーザーは読む権限のない情報を読むことができません。 +- _完全性_、情報は、許可されたユーザーによって、許可された方法でのみ変更できます。 +- _可用性_、承認されたユーザーはシステムを使用できます。 + +このシステムでは、ゼロ知識証明を通じて完全性が提供されます。 可用性の保証ははるかに困難であり、銀行は各アカウントの残高とすべてのトランザクションを知る必要があるため、機密性は不可能です。 情報を持っているエンティティがその情報を共有するのを防ぐ方法はありません。 + +[ステルスアドレス](https://vitalik.eth.limo/general/2023/01/20/stealth.html)を使用して真に機密性の高い銀行を作成することは可能かもしれませんが、それはこの記事の範囲を超えています。 + +### 偽情報 {#false-info} + +サーバーが完全性を侵害する方法の1つは、[データが要求された](https://github.com/qbzzt/250911-zk-bank/blob/03-smart-contracts/server/index.mjs#L278-L291)ときに偽の情報を提供することです。 + +これを解決するために、アカウントをプライベート入力として受け取り、情報が要求されたアドレスをパブリック入力として受け取る2番目のNoirプログラムを作成できます。 出力は、そのアドレスの残高とノンス (nonce)、およびアカウントのハッシュです。 + +もちろん、ノンス (nonce)と残高をオンチェーンにポストしたくないため、この証明はオンチェーンで検証できません。 しかし、ブラウザで実行されているクライアントコードで検証することはできます。 + +### 強制トランザクション {#forced-txns} + +L2での可用性を確保し、検閲を防ぐための通常のメカニズムは、[強制トランザクション](https://docs.optimism.io/stack/transactions/forced-transaction)です。 しかし、強制トランザクションはゼロ知識証明と組み合わせられません。 サーバーは、トランザクションを検証できる唯一のエンティティです。 + +`smart-contracts/src/ZkBank.sol`を変更して、強制トランザクションを受け入れ、処理されるまでサーバーが状態を変更するのを防ぐことができます。 しかし、これにより、単純なサービス拒否攻撃にさらされることになります。 強制トランザクションが無効で処理できない場合はどうなるでしょうか? + +解決策は、強制トランザクションが無効であることを示すゼロ知識証明を持つことです。 これにより、サーバーには3つのオプションが与えられます。 + +- 強制トランザクションを処理し、処理されたことと新しい状態ハッシュを示すゼロ知識証明を提供する。 +- 強制トランザクションを拒否し、トランザクションが無効であること(不明なアドレス、不正なノンス (nonce)、または不十分な残高)をコントラクトにゼロ知識証明で提供する。 +- 強制トランザクションを無視する。 サーバーに実際にトランザクションを処理させる方法はありませんが、それはシステム全体が利用できなくなることを意味します。 + +#### 可用性ボンド {#avail-bonds} + +実際の導入では、サーバーを稼働させ続けるための何らかの利益動機があるでしょう。 このインセンティブを強化するには、サーバーに可用性ボンドをポストさせ、強制トランザクションが一定期間内に処理されない場合に誰でもそれをバーンできるようにします。 + +### 不正なNoirコード {#bad-noir-code} + +通常、人々にスマートコントラクトを信頼してもらうには、ソースコードを[ブロックエクスプローラー](https://eth.blockscout.com/address/0x7D16d2c4e96BCFC8f815E15b771aC847EcbDB48b?tab=contract)にアップロードします。 しかし、ゼロ知識証明の場合は、それだけでは不十分です。 + +`Verifier.sol`には検証キーが含まれており、これはNoirプログラムの関数です。 しかし、そのキーはNoirプログラムが何であったかを教えてくれません。 実際に信頼できるソリューションを持つには、Noirプログラム(とそれを作成したバージョン)をアップロードする必要があります。 そうしないと、ゼロ知識証明は、バックドアを持つ別のプログラムを反映する可能性があります。 + +ブロックエクスプローラーがNoirプログラムのアップロードと検証を許可するようになるまで、自分でそれを行う必要があります(できれば[IPFS](/developers/tutorials/ipfs-decentralized-ui/)に)。 そうすれば、高度なユーザーはソースコードをダウンロードし、自分でコンパイルし、`Verifier.sol`を作成し、それがオンチェーンのものと同一であることを検証できます。 + +## 結論 {#conclusion} + +Plasmaタイプのアプリケーションには、情報ストレージとして中央集権的なコンポーネントが必要です。 これにより、潜在的な脆弱性が生じますが、その見返りとして、ブロックチェーン自体では利用できない方法でプライバシーを保護できます。 ゼロ知識証明を使用することで、完全性を確保し、中央集権型コンポーネントを運営している人が可用性を維持することが経済的に有利になる可能性があります。 + +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). + +## 謝辞 {#acknowledgements} + +- Josh Critesはこの記事の草稿を読み、厄介なNoirの問題を手伝ってくれました。 + +残りの誤りは私の責任です。 diff --git a/public/content/translations/ja/developers/tutorials/calling-a-smart-contract-from-javascript/index.md b/public/content/translations/ja/developers/tutorials/calling-a-smart-contract-from-javascript/index.md index 037235d134c..ff0e19a72a3 100644 --- a/public/content/translations/ja/developers/tutorials/calling-a-smart-contract-from-javascript/index.md +++ b/public/content/translations/ja/developers/tutorials/calling-a-smart-contract-from-javascript/index.md @@ -1,12 +1,8 @@ --- -title: JavaScriptからスマートコントラクトを呼び出す -description: Daiトークンを使ってJavaScriptでスマートコントラクトを呼び出す方法 +title: "JavaScriptからスマートコントラクトを呼び出す" +description: "Daiトークンを例に、JavaScriptでスマートコントラクトの関数を呼び出す方法" author: jdourlens -tags: - - "トランザクション" - - "フロントエンド" - - "JavaScript" - - "web3.js" +tags: [ "トランザクション", "フロントエンド", "JavaScript", "web3.js" ] skill: beginner lang: ja published: 2020-04-19 @@ -15,15 +11,15 @@ sourceUrl: https://ethereumdev.io/calling-a-smart-contract-from-javascript/ address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" --- -このチュートリアルでは、JavaScriptから[スマートコントラクト](/developers/docs/smart-contracts/)関数を呼び出す方法を見ていきます。 最初はスマートコントラクトの状態(ERC20保有者の残高など)を読み取り、次にトークンの送金を行うことでブロックチェーンの状態を変更します。 [JS環境を設定して](/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/)ブロックチェーンと対話することにはすでに慣れているはずです。 +このチュートリアルでは、JavaScriptから[スマートコントラクト](/developers/docs/smart-contracts/)関数を呼び出す方法を見ていきます。 最初はスマートコントラクトの状態(ERC20保有者の残高など)を読み取り、次にトークンの送金を行うことでブロックチェーンの状態を変更します。 すでに[ブロックチェーンとやり取りするためのJS環境の設定](/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/)に精通している必要があります。 -今回の例では、DAIトークンを使います。テストのために、ganache-cliを使ってブロックチェーンをフォークし、すでに大量のDAIを持っているアドレスをアンロックします。 +今回の例では、DAIトークンを使います。テストのために、ganache-cliを使ってブロックチェーンをフォークし、すでに大量のDAIを持っているアドレスのロックを解除します。 ```bash ganache-cli -f https://mainnet.infura.io/v3/[YOUR INFURA KEY] -d -i 66 1 --unlock 0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81 ``` -スマートコントラクトと対話するのに必要なアドレスとABI: +スマートコントラクトとやり取りするには、そのアドレスとABIが必要です。 ```js const ERC20TransferABI = [ @@ -74,7 +70,7 @@ const ERC20TransferABI = [ const DAI_ADDRESS = "0x6b175474e89094c44da98b954eedeac495271d0f" ``` -今回は、完全なERC20 ABIではなく、簡略化して`balanceOf`と`transfer`関数だけを残しました。ERC20 ABIの完全版は[こちら](https://ethereumdev.io/abi-for-erc20-contract-on-ethereum/)でご覧いただけます。 +このプロジェクトでは、完全なERC20 ABIから`balanceOf`関数と`transfer`関数のみを残しました。完全なERC20のABIは[こちら](https://ethereumdev.io/abi-for-erc20-contract-on-ethereum/)で確認できます。 次に、スマートコントラクトをインスタンス化する必要があります。 @@ -84,52 +80,52 @@ const web3 = new Web3("http://localhost:8545") const daiToken = new web3.eth.Contract(ERC20TransferABI, DAI_ADDRESS) ``` -次の2つのアドレスも設定します: +また、2つのアドレスを設定します。 -- 送金先アドレス -- アンロック済みの送金元アドレス +- 送金先のアドレス +- ロック解除済みの送金元アドレス: ```js const senderAddress = "0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81" const receiverAddress = "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" ``` -次のパートでは、両方のアドレスが保有している現在のトークンの量を取得するために`balanceOf`関数を呼び出します。 +次のパートでは、`balanceOf`関数を呼び出して、両方のアドレスが保有している現在のトークン量を取得します。 -## Call: スマートコントラクトから値を読み込む {#call-reading-value-from-a-smart-contract} +## コール:スマートコントラクトからの値の読み取り {#call-reading-value-from-a-smart-contract} -最初の例では、トランザクションを送信することなく、「constant」メソッドを呼び出し、スマートコントラクトメソッドをEVMで実行します。 そのために、まずはアドレスのERC20の残高を読み込みます。 [ERC20トークンに関する記事](/developers/tutorials/understand-the-erc-20-token-smart-contract/)をご覧ください。 +最初の例では、「constant」メソッドを呼び出し、トランザクションを送信することなく、EVMでスマートコントラクトメソッドを実行します。 そのために、アドレスのERC20残高を読み取ります。 [ERC20トークンに関する記事をお読みください](/developers/tutorials/understand-the-erc-20-token-smart-contract/) -ABIを提供した、インスタンス化されたスマートコントラクトのメソッドには、`yourContract.methods.methodname`でアクセスできます。 `call`関数を使用して、関数を実行した結果を受け取ることができます。 +ABIを提供してインスタンス化されたスマートコントラクトのメソッドには、`yourContract.methods.methodname`のようにアクセスできます。 `call`関数を使用することで、関数を実行した結果を受け取ることができます。 ```js daiToken.methods.balanceOf(senderAddress).call(function (err, res) { if (err) { - console.log("An error occurred", err) + console.log("エラーが発生しました", err) return } - console.log("The balance is: ", res) + console.log("残高は: ", res) }) ``` -ERC20のDAIには18桁の0があり、正しい数値を得るためには18桁の0を削除する必要があります。 Javascriptは大きな数値を扱えないため、uint256は文字列で返されます。 JavaScriptで大きな数字を扱う方法がご不明の場合、[JavaScriptで大きな数字を扱うbignumber.jsのチュートリアルをご覧ください。](https://ethereumdev.io/how-to-deal-with-big-numbers-in-javascript/) +DAI ERC20は18桁の小数位を持つことを覚えておいてください。これは、正しい量を得るには、値から18個のゼロを削除する必要があることを意味します。 JavaScriptは大きな数値を扱えないため、uint256は文字列として返されます。 JSで大きな数値を扱う方法がわからない場合は、[bignumber.jsに関するチュートリアル](https://ethereumdev.io/how-to-deal-with-big-numbers-in-javascript/)をご覧ください。 -## send: スマートコントラクト関数にトランザクションを送信する {#send-sending-a-transaction-to-a-smart-contract-function} +## センド:スマートコントラクト関数へのトランザクションの送信 {#send-sending-a-transaction-to-a-smart-contract-function} -2番目の例では、DAIスマートコントラクトのtransfer関数を呼び出し、2番目のアドレスに10DAIを送信します。 このtransfer関数では、受取人のアドレスと送金するトークン数の2つのパラメータが必要です。 +2番目の例では、DAIスマートコントラクトの`transfer`関数を呼び出して、2つ目のアドレスに10 DAIを送信します。 `transfer`関数は2つのパラメータを受け取ります。送金先のアドレスと送金するトークンの量です。 ```js daiToken.methods .transfer(receiverAddress, "100000000000000000000") .send({ from: senderAddress }, function (err, res) { if (err) { - console.log("An error occurred", err) + console.log("エラーが発生しました", err) return } - console.log("Hash of the transaction: " + res) + console.log("トランザクションのハッシュ: " + res) }) ``` -また、call関数は、ブロックチェーンに組み込まれるトランザクションのハッシュを返します。 イーサリアムではトランザクションハッシュを予測できるため、トランザクションが実行される前にハッシュを取得することができます([ここでハッシュの計算方法を学びます](https://ethereum.stackexchange.com/questions/45648/how-to-calculate-the-assigned-txhash-of-a-transaction))。 +`send`関数は、ブロックチェーンにマイニングされるトランザクションのハッシュを返します。 イーサリアムでは、トランザクションハッシュは予測可能です。そのため、トランザクションが実行される前にハッシュを取得できます([ハッシュの計算方法はこちら](https://ethereum.stackexchange.com/questions/45648/how-to-calculate-the-assigned-txhash-of-a-transaction)をご覧ください)。 -関数はブロックチェーンにトランザクションを送るだけなので、マイニングされてブロックチェーンに組み込まれるまで結果は分かりません。 次のチュートリアルでは、[ハッシュ](https://ethereumdev.io/waiting-for-a-transaction-to-be-mined-on-ethereum-with-js/)を使用してブロックチェーンでトランザクションが実行されるのを待つ方法を学びます。 +この関数はトランザクションをブロックチェーンに送信するだけなので、それがマイニングされブロックチェーンに含まれるまでは結果を確認できません。 次のチュートリアルでは、[ハッシュを使って、トランザクションがブロックチェーン上で実行されるのを待つ方法](https://ethereumdev.io/waiting-for-a-transaction-to-be-mined-on-ethereum-with-js/)を学びます。 diff --git a/public/content/translations/ja/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md b/public/content/translations/ja/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md new file mode 100644 index 00000000000..fd9e843697b --- /dev/null +++ b/public/content/translations/ja/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md @@ -0,0 +1,585 @@ +--- +title: "コントラクトのユーザーインターフェースを構築する" +description: "TypeScript、React、Vite、Wagmiといった最新のコンポーネントを使用して、モダンでありながら最小限のユーザーインターフェースをレビューし、ウォレットをユーザーインターフェースに接続する方法、スマートコントラクトを呼び出して情報を読み取る方法、スマートコントラクトにトランザクションを送信する方法、スマートコントラクトからのイベントを監視して変更を特定する方法を学びます。" +author: Ori Pomerantz +tags: [ "typescript", "react", "vite", "wagmi", "フロントエンド" ] +skill: beginner +published: 2023-11-01 +lang: ja +sidebarDepth: 3 +--- + +Ethereumエコシステムに必要な機能を見つけました。 それを実装するためにスマートコントラクトを作成し、オフチェーンで実行される関連コードもいくつか作成したかもしれません。 素晴らしいことです! 残念ながら、ユーザーインターフェースがなければユーザーはつきませんし、前回ウェブサイトを作成したときは、人々はダイヤルアップモデムを使い、JavaScriptはまだ新しいものでした。 + +この記事は、そんなあなたのためのものです。 プログラミングの知識があり、JavaScriptやHTMLについても少しは知っているけれど、ユーザーインターフェースのスキルは錆びついて時代遅れだと想定しています。 一緒にシンプルな最新のアプリケーションをレビューし、最近ではどのように行われているかを見ていきましょう。 + +## なぜこれが重要なのか {#why-important} + +[Etherscan](https://holesky.etherscan.io/address/0x432d810484add7454ddb3b5311f0ac2e95cecea8#writeContract)や[Blockscout](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=write_contract)を使って、人々にコントラクトを操作してもらうことも理論上は可能です。 経験豊富なEthereanにとっては、それで十分でしょう。 しかし、私たちは[さらに10億人の人々](https://blog.ethereum.org/2021/05/07/ethereum-for-the-next-billion)にサービスを提供しようとしています。 これは優れたユーザーエクスペリエンスなしには実現できません。そして、フレンドリーなユーザーインターフェースはその大きな部分を占めています。 + +## Greeterアプリケーション {#greeter-app} + +モダンなUIの仕組みの背景には多くの理論があり、[それを説明する](https://wagmi.sh/core/getting-started)[優れたサイトもたくさんあります](https://react.dev/learn/thinking-in-react)。 これらのサイトで行われている素晴らしい作業を繰り返す代わりに、実際に触って学べるアプリケーションから始めることを好むと仮定します。 物事を進めるにはまだ理論が必要で、それにも触れます。ただ、ソースファイルごとに進め、それらに到達するたびに物事を議論していきます。 + +### インストール {#installation} + +1. 必要に応じて、[Holeskyブロックチェーン](https://chainlist.org/?search=holesky&testnets=true)をウォレットに追加し、[テストETHを入手](https://www.holeskyfaucet.io/)してください。 + +2. GitHubリポジトリをクローンします。 + + ```sh + git clone https://github.com/qbzzt/20230801-modern-ui.git + ``` + +3. 必要なパッケージをインストールします。 + + ```sh + cd 20230801-modern-ui + pnpm install + ``` + +4. アプリケーションを起動します。 + + ```sh + pnpm dev + ``` + +5. アプリケーションによって表示されたURLに移動します。 ほとんどの場合、それは[http://localhost:5173/](http://localhost:5173/)です。 + +6. HardhatのGreeterを少し修正したコントラクトのソースコードを、[ブロックチェーンエクスプローラー](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=contract)で確認できます。 + +### ファイルのウォークスルー {#file-walk-through} + +#### `index.html` {#index-html} + +このファイルは、スクリプトファイルをインポートするこの行を除いて、標準的なHTMLの定型文です。 + +```html + +``` + +#### `src/main.tsx` {#main-tsx} + +このファイル拡張子は、このファイルが[TypeScript](https://www.typescriptlang.org/)で書かれた[Reactコンポーネント](https://www.w3schools.com/react/react_components.asp)であることを示しています。TypeScriptは、[型チェック](https://en.wikipedia.org/wiki/Type_system#Type_checking)をサポートするJavaScriptの拡張機能です。 TypeScriptはJavaScriptにコンパイルされるため、クライアントサイドでの実行に使用できます。 + +```tsx +import '@rainbow-me/rainbowkit/styles.css' +import { RainbowKitProvider } from '@rainbow-me/rainbowkit' +import * as React from 'react' +import * as ReactDOM from 'react-dom/client' +import { WagmiConfig } from 'wagmi' +import { chains, config } from './wagmi' +``` + +必要なライブラリコードをインポートします。 + +```tsx +import { App } from './App' +``` + +アプリケーションを実装するReactコンポーネントをインポートします(下記参照)。 + +```tsx +ReactDOM.createRoot(document.getElementById('root')!).render( +``` + +ルートのReactコンポーネントを作成します。 `render`のパラメータは[JSX](https://www.w3schools.com/react/react_jsx.asp)です。これはHTMLとJavaScript/TypeScriptの両方を使用する拡張言語です。 ここでの感嘆符は、TypeScriptコンポーネントに「`document.getElementById('root')`が`ReactDOM.createRoot`の有効なパラメータになるかどうかはわからないでしょうが、心配しないでください。私は開発者であり、そうなることを保証します」と伝えています。 + +```tsx + +``` + +アプリケーションは[`React.StrictMode`コンポーネント](https://react.dev/reference/react/StrictMode)の中に入ります。 このコンポーネントは、Reactライブラリに追加のデバッグチェックを挿入するように指示します。これは開発中に役立ちます。 + +```tsx + +``` + +アプリケーションは[`WagmiConfig`コンポーネント](https://wagmi.sh/react/api/WagmiProvider)の中にもあります。 [wagmi (we are going to make it) ライブラリ](https://wagmi.sh/)は、ReactのUI定義を[viemライブラリ](https://viem.sh/)に接続し、イーサリアムの分散型アプリケーションを作成します。 + +```tsx + +``` + +そして最後に、[`RainbowKitProvider`コンポーネント](https://www.rainbowkit.com/)です。 このコンポーネントは、ログインとウォレットとアプリケーション間の通信を処理します。 + +```tsx + +``` + +これで、実際にUIを実装するアプリケーション用のコンポーネントを持つことができます。 コンポーネントの最後にある`/>`は、XML標準に従い、このコンポーネント内に定義がないことをReactに伝えます。 + +```tsx + + + , +) +``` + +もちろん、他のコンポーネントも閉じる必要があります。 + +#### `src/App.tsx` {#app-tsx} + +```tsx +import { ConnectButton } from '@rainbow-me/rainbowkit' +import { useAccount } from 'wagmi' +import { Greeter } from './components/Greeter' + +export function App() { +``` + +これはReactコンポーネントを作成する標準的な方法です。レンダリングが必要になるたびに呼び出される関数を定義します。 この関数は通常、上部にTypeScriptまたはJavaScriptコードがあり、その後にJSXコードを返す`return`ステートメントが続きます。 + +```tsx + const { isConnected } = useAccount() +``` + +ここで[`useAccount`](https://wagmi.sh/react/api/hooks/useAccount)を使用して、ウォレットを介してブロックチェーンに接続されているかどうかを確認します。 + +慣例として、Reactでは`use...`という名前の関数は、何らかのデータを返す[フック](https://www.w3schools.com/react/react_hooks.asp)です。 このようなフックを使用すると、コンポーネントがデータを取得するだけでなく、そのデータが変更されると、コンポーネントは更新された情報で再レンダリングされます。 + +```tsx + return ( + <> +``` + +ReactコンポーネントのJSXは、1つのコンポーネントを返す_必要_があります。 複数のコンポーネントがあり、「自然に」まとめるものがない場合は、空のコンポーネント(`<> ...` )を使用して、それらを1つのコンポーネントにまとめます。 )を使用して、それらを1つのコンポーネントにまとめます。 + +```tsx +

Greeter

+ +``` + +[`ConnectButton`コンポーネント](https://www.rainbowkit.com/docs/connect-button)はRainbowKitから取得します。 接続されていない場合は、`Connect Wallet`ボタンが表示され、ウォレットについて説明し、使用するウォレットを選択できるモーダルが開きます。 接続されると、使用しているブロックチェーン、アカウントアドレス、ETH残高が表示されます。 これらの表示を使用して、ネットワークを切り替えたり、切断したりできます。 + +```tsx + {isConnected && ( +``` + +実際のJavaScript(またはJavaScriptにコンパイルされるTypeScript)をJSXに挿入する必要がある場合は、括弧(`{}`)を使用します。 + +`a && b`という構文は、[`a ?`の短縮形です。 `b : a`](https://www.w3schools.com/react/react_es6_ternary.asp)。 つまり、`a`が真の場合、`b`に評価され、それ以外の場合は`a`(`false`、`0`など)に評価されます。 これは、特定の条件が満たされた場合にのみコンポーネントを表示するようにReactに指示する簡単な方法です。 + +この場合、ユーザーがブロックチェーンに接続されている場合にのみ、ユーザーに`Greeter`を表示したいと考えています。 + +```tsx + + )} + + ) +} +``` + +#### `src/components/Greeter.tsx` {#greeter-tsx} + +このファイルには、UI機能のほとんどが含まれています。 これには通常、複数のファイルに含まれる定義が含まれていますが、これはチュートリアルであるため、プログラムはパフォーマンスやメンテナンスの容易さよりも、初回で理解しやすいように最適化されています。 + +```tsx +import { useState, ChangeEventHandler } from 'react' +import { useNetwork, + useReadContract, + usePrepareContractWrite, + useContractWrite, + useContractEvent + } from 'wagmi' +``` + +これらのライブラリ関数を使用します。 繰り返しになりますが、これらは使用される場所で以下に説明されています。 + +```tsx +import { AddressType } from 'abitype' +``` + +[`abitype`ライブラリ](https://abitype.dev/)は、[`AddressType`](https://abitype.dev/config#addresstype)など、さまざまなイーサリアムのデータ型に対するTypeScriptの定義を提供します。 + +```tsx +let greeterABI = [ + . + . + . +] as const // greeterABI +``` + +`Greeter`コントラクトのABIです。 +コントラクトとUIを同時に開発している場合、通常はそれらを同じリポジトリに配置し、Solidityコンパイラによって生成されたABIをアプリケーション内のファイルとして使用します。 ただし、コントラクトはすでに開発済みで変更されないため、ここではその必要はありません。 + +```tsx +type AddressPerBlockchainType = { + [key: number]: AddressType +} +``` + +TypeScriptは静的型付け言語です。 この定義を使用して、`Greeter`コントラクトが異なるチェーンにデプロイされているアドレスを指定します。 キーは数値(chainId)で、値は`AddressType`(アドレス)です。 + +```tsx +const contractAddrs: AddressPerBlockchainType = { + // Holesky + 17000: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8', + + // Sepolia + 11155111: '0x7143d5c190F048C8d19fe325b748b081903E3BF0' +} +``` + +サポートされている2つのネットワーク、[Holesky](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=contact_code)と[Sepolia](https://eth-sepolia.blockscout.com/address/0x7143d5c190F048C8d19fe325b748b081903E3BF0?tab=contact_code)上のコントラクトのアドレスです。 + +注意: 実際にはRedstone Holesky用に3番目の定義がありますが、これについては後ほど説明します。 + +```tsx +type ShowObjectAttrsType = { + name: string, + object: any +} +``` + +この型は、`ShowObject`コンポーネント(後述)へのパラメータとして使用されます。 これには、デバッグ目的で表示されるオブジェクトの名前とその値が含まれます。 + +```tsx +type ShowGreetingAttrsType = { + greeting: string | undefined +} +``` + +どの時点でも、あいさつの内容を知っているか(ブロックチェーンから読み取ったため)、知らないか(まだ受信していないため)のどちらかです。 したがって、文字列または何もないかのどちらかになりうる型があると便利です。 + +##### `Greeter`コンポーネント {#greeter-component} + +```tsx +const Greeter = () => { +``` + +最後に、コンポーネントを定義します。 + +```tsx + const { chain } = useNetwork() +``` + +使用しているチェーンに関する情報は、[wagmi](https://wagmi.sh/react/hooks/useNetwork)から提供されます。 +これはフック(`use...`)なので、この情報が変更されるたびにコンポーネントが再描画されます。 + +```tsx + const greeterAddr = chain && contractAddrs[chain.id] +``` + +Greeterコントラクトのアドレスはチェーンによって異なり、チェーン情報がない場合や、そのコントラクトが存在しないチェーン上にある場合は`undefined`になります。 + +```tsx + const readResults = useReadContract({ + address: greeterAddr, + abi: greeterABI, + functionName: "greet" , // No arguments + watch: true + }) +``` + +[`useReadContract`フック](https://wagmi.sh/react/api/hooks/useReadContract)は、コントラクトから情報を読み取ります。 UIで`readResults`を展開すると、それが返す情報を正確に確認できます。 この場合、あいさつが変更されたときに通知されるように、監視を続けたいと考えています。 + +**注意:** [`setGreeting`イベント](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=logs)をリッスンして、あいさつが変更されたことを知り、そのように更新することもできます。 ただし、より効率的かもしれませんが、すべての場合に適用できるわけではありません。 ユーザーが異なるチェーンに切り替えると、あいさつも変更されますが、その変更にはイベントが伴いません。 イベントをリッスンするコード部分と、チェーンの変更を識別する部分を分けることもできますが、それは単に[`watch`パラメータ](https://wagmi.sh/react/api/hooks/useReadContract#watch-optional)を設定するよりも複雑になります。 + +```tsx + const [ newGreeting, setNewGreeting ] = useState("") +``` + +Reactの[`useState`フック](https://www.w3schools.com/react/react_usestate.asp)を使用すると、コンポーネントのレンダリング間で値が持続する状態変数を指定できます。 初期値はパラメータであり、この場合は空の文字列です。 + +`useState`フックは、2つの値を持つリストを返します。 + +1. 状態変数の現在の値。 +2. 必要に応じて状態変数を変更する関数。 これはフックなので、呼び出されるたびにコンポーネントが再レンダリングされます。 + +この場合、ユーザーが設定したい新しいあいさつのために状態変数を使用しています。 + +```tsx + const greetingChange : ChangeEventHandler = (evt) => + setNewGreeting(evt.target.value) +``` + +これは、新しいあいさつ入力フィールドが変更されたときのイベントハンドラです。 型[`ChangeEventHandler`](https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forms_and_events/)は、これがHTML入力要素の値の変更に対するハンドラであることを指定します。 この``の部分は、これが[ジェネリック型](https://www.w3schools.com/typescript/typescript_basic_generics.php)であるために使用されます。 + +```tsx + const preparedTx = usePrepareContractWrite({ + address: greeterAddr, + abi: greeterABI, + functionName: 'setGreeting', + args: [ newGreeting ] + }) + const workingTx = useContractWrite(preparedTx.config) +``` + +これは、クライアント側からブロックチェーントランザクションを送信するプロセスです。 + +1. [`eth_estimateGas`](https://docs.alchemy.com/reference/eth-estimategas)を使用して、ブロックチェーン上のノードにトランザクションを送信します。 +2. ノードからの応答を待ちます。 +3. 応答が受信されたら、ウォレットを介してユーザーにトランザクションへの署名を求めます。 このステップは、ノードの応答が受信された後に行う_必要_があります。なぜなら、ユーザーは署名する前にトランザクションのガス代を表示されるからです。 +4. ユーザーが承認するのを待ちます。 +5. 今度は[`eth_sendRawTransaction`](https://docs.alchemy.com/reference/eth-sendrawtransaction)を使用して、トランザクションを再度送信します。 + +ステップ2は、体感できるほどの時間がかかる可能性があり、その間、ユーザーは自分のコマンドがユーザーインターフェースで本当に受信されたのか、なぜまだトランザクションへの署名を求められないのか、疑問に思うでしょう。 それは、悪いユーザーエクスペリエンス(UX)につながります。 + +解決策は、[prepareフック](https://wagmi.sh/react/prepare-hooks)を使用することです。 パラメータが変更されるたびに、すぐにノードに`eth_estimateGas`リクエストを送信します。 そして、ユーザーが実際にトランザクションを送信したいとき(この場合は**あいさつの更新**を押す)、ガス代が既知であるため、ユーザーはすぐにウォレットページを見ることができます。 + +```tsx + return ( +``` + +これで、ついに返す実際のHTMLを作成できます。 + +```tsx + <> +

Greeter

+ { + !readResults.isError && !readResults.isLoading && + + } +
+``` + +`ShowGreeting`コンポーネント(後述)を作成しますが、あいさつがブロックチェーンから正常に読み取られた場合に限ります。 + +```tsx + +``` + +これは、ユーザーが新しいあいさつを設定できる入力テキストフィールドです。 ユーザーがキーを押すたびに、`greetingChange`が呼び出され、それが`setNewGreeting`を呼び出します。 `setNewGreeting`は`useState`フックから来ているので、これにより`Greeter`コンポーネントが再びレンダリングされます。 このことは、次のことを意味します。 + +- 新しいあいさつの値を保持するために`value`を指定する必要があります。そうしないと、デフォルトの空の文字列に戻ってしまいます。 +- `usePrepareContractWrite`は`newGreeting`が変更されるたびに呼び出されるため、準備されたトランザクションには常に最新の`newGreeting`が含まれます。 + +```tsx + +``` + +`workingTx.write`がない場合は、あいさつの更新を送信するために必要な情報をまだ待っているため、ボタンは無効になります。 `workingTx.write`の値がある場合、それがトランザクションを送信するために呼び出す関数です。 + +```tsx +
+ + + + + ) +} +``` + +最後に、何をしているかを確認しやすくするために、使用する3つのオブジェクトを表示します。 + +- `readResults` +- `preparedTx` +- `workingTx` + +##### `ShowGreeting`コンポーネント {#showgreeting-component} + +このコンポーネントは、 + +```tsx +const ShowGreeting = (attrs : ShowGreetingAttrsType) => { +``` + +コンポーネント関数は、コンポーネントのすべての属性を持つパラメータを受け取ります。 + +```tsx + return {attrs.greeting} +} +``` + +##### `ShowObject`コンポーネント {#showobject-component} + +情報提供を目的として、`ShowObject`コンポーネントを使用して、重要なオブジェクト(あいさつを読み取るための`readResults`と、作成するトランザクションのための`preparedTx`および`workingTx`)を表示します。 + +```tsx +const ShowObject = (attrs: ShowObjectAttrsType ) => { + const keys = Object.keys(attrs.object) + const funs = keys.filter(k => typeof attrs.object[k] == "function") + return <> +
+``` + +すべての情報でUIが乱雑にならないように、表示したり閉じたりできるようにするために、[`details`](https://www.w3schools.com/tags/tag_details.asp)タグを使用します。 + +```tsx + {attrs.name} +
+        {JSON.stringify(attrs.object, null, 2)}
+```
+
+ほとんどのフィールドは、[`JSON.stringify`](https://www.w3schools.com/js/js_json_stringify.asp)を使用して表示されます。
+
+```tsx
+      
+ { funs.length > 0 && + <> + Functions: +
    +``` + +例外は関数で、[JSON標準](https://www.json.org/json-en.html)の一部ではないため、別々に表示する必要があります。 + +```tsx + {funs.map((f, i) => +``` + +JSX内では、`{`中括弧`}`内のコードはJavaScriptとして解釈されます。 そして、`(`丸括弧`)`内のコードは、再びJSXとして解釈されます。 + +```tsx + (
  • {f}
  • ) + )} +``` + +Reactは、[DOMツリー](https://www.w3schools.com/js/js_htmldom.asp)内のタグに一意の識別子を必要とします。 これは、同じタグの子(この場合は、[順序なしリスト](https://www.w3schools.com/tags/tag_ul.asp))が、異なる`key`属性を必要とすることを意味します。 + +```tsx +
+ + } +
+ +} +``` + +様々なHTMLタグを閉じます。 + +##### 最後の`export` {#the-final-export} + +```tsx +export { Greeter } +``` + +`Greeter`コンポーネントは、アプリケーションにエクスポートする必要があるものです。 + +#### `src/wagmi.ts` {#wagmi-ts} + +最後に、WAGMIに関連するさまざまな定義が`src/wagmi.ts`にあります。 ほとんどが変更する必要のない定型文であるため、ここではすべてを説明するつもりはありません。 + +ここでのコードは、記事の後半で別のチェーン([Redstone Holesky](https://redstone.xyz/docs/network-info))を追加するため、[GitHub上のもの](https://github.com/qbzzt/20230801-modern-ui/blob/main/src/wagmi.ts)と完全に同じではありません。 + +```ts +import { getDefaultWallets } from '@rainbow-me/rainbowkit' +import { configureChains, createConfig } from 'wagmi' +import { holesky, sepolia } from 'wagmi/chains' +``` + +アプリケーションがサポートするブロックチェーンをインポートします。 サポートされているチェーンのリストは、[viemのGitHub](https://github.com/wagmi-dev/viem/tree/main/src/chains/definitions)で確認できます。 + +```ts +import { publicProvider } from 'wagmi/providers/public' + +const walletConnectProjectId = 'c96e690bb92b6311e8e9b2a6a22df575' +``` + +[WalletConnect](https://walletconnect.com/)を使用するには、アプリケーションのプロジェクトIDが必要です。 これは[cloud.walletconnect.com](https://cloud.walletconnect.com/sign-in)で取得できます。 + +```ts +const { chains, publicClient, webSocketPublicClient } = configureChains( + [ holesky, sepolia ], + [ + publicProvider(), + ], +) + +const { connectors } = getDefaultWallets({ + appName: 'My wagmi + RainbowKit App', + chains, + projectId: walletConnectProjectId, +}) + +export const config = createConfig({ + autoConnect: true, + connectors, + publicClient, + webSocketPublicClient, +}) + +export { chains } +``` + +### 別のブロックチェーンの追加 {#add-blockchain} + +最近では多くの[L2スケーリングソリューション](/layer-2/)があり、viemがまだサポートしていないものをサポートしたいと思うかもしれません。 そのためには、`src/wagmi.ts`を修正します。 これらの手順は、[Redstone Holesky](https://redstone.xyz/docs/network-info)を追加する方法を説明しています。 + +1. viemから`defineChain`型をインポートします。 + + ```ts + import { defineChain } from 'viem' + ``` + +2. ネットワーク定義を追加します。 + + ```ts + const redstoneHolesky = defineChain({ + id: 17_001, + name: 'Redstone Holesky', + network: 'redstone-holesky', + nativeCurrency: { + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + rpcUrls: { + default: { + http: ['https://rpc.holesky.redstone.xyz'], + webSocket: ['wss://rpc.holesky.redstone.xyz/ws'], + }, + public: { + http: ['https://rpc.holesky.redstone.xyz'], + webSocket: ['wss://rpc.holesky.redstone.xyz/ws'], + }, + }, + blockExplorers: { + default: { name: 'Explorer', url: 'https://explorer.holesky.redstone.xyz' }, + }, + }) + ``` + +3. `configureChains`呼び出しに新しいチェーンを追加します。 + + ```ts + const { chains, publicClient, webSocketPublicClient } = configureChains( + [ holesky, sepolia, redstoneHolesky ], + [ publicProvider(), ], + ) + ``` + +4. アプリケーションが新しいネットワーク上のコントラクトのアドレスを認識していることを確認します。 この場合、`src/components/Greeter.tsx`を次のように変更します。 + + ```ts + const contractAddrs : AddressPerBlockchainType = { + // Holesky + 17000: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8', + + // Redstone Holesky + 17001: '0x4919517f82a1B89a32392E1BF72ec827ba9986D3', + + // Sepolia + 11155111: '0x7143d5c190F048C8d19fe325b748b081903E3BF0' + } + ``` + +## 結論 {#conclusion} + +もちろん、`Greeter`のユーザーインターフェースを提供することには、実際には関心がないでしょう。 独自のコントラクト用のユーザーインターフェースを作成したいはずです。 独自のアプリケーションを作成するには、次の手順を実行します。 + +1. wagmiアプリケーションを作成するよう指定します。 + + ```sh copy + pnpm create wagmi + ``` + +2. アプリケーションに名前を付けます。 + +3. **React**フレームワークを選択します。 + +4. **Vite**バリアントを選択します。 + +5. [Rainbow kitを追加](https://www.rainbowkit.com/docs/installation#manual-setup)できます。 + +さあ、あなたのコントラクトを世界中の人々が使えるようにしましょう。 + +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). + diff --git a/public/content/translations/ja/developers/tutorials/deploying-your-first-smart-contract/index.md b/public/content/translations/ja/developers/tutorials/deploying-your-first-smart-contract/index.md index d16956e8b7c..6adbb91865d 100644 --- a/public/content/translations/ja/developers/tutorials/deploying-your-first-smart-contract/index.md +++ b/public/content/translations/ja/developers/tutorials/deploying-your-first-smart-contract/index.md @@ -1,12 +1,8 @@ --- -title: はじめてスマートコントラクトをデプロイする -description: はじめてイーサリアムのテスト用ネットワークにスマートコントラクトをデプロイするユーザー向けのイントロダクション +title: "はじめてスマートコントラクトをデプロイする" +description: "初めてイーサリアムのテストネットにスマートコントラクトをデプロイするためのイントロダクション" author: "jdourlens" -tags: - - "スマートコントラクト" - - "Remix" - - "Solidity" - - "デプロイ" +tags: [ "スマート契約", "Remix", "Solidity", "デプロイ" ] skill: beginner lang: ja published: 2020-04-03 @@ -15,15 +11,15 @@ sourceUrl: https://ethereumdev.io/deploying-your-first-smart-contract/ address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" --- -皆さんも、私たちと同じように、はじめて[スマートコントラクト](/developers/docs/smart-contracts/)をイーサリアムのブロックチェーン上で[デプロイ](/developers/docs/smart-contracts/deploying/)し、やり取りを行うことにドキドキしていることでしょう。 +皆さんも私たちと同じように、イーサリアムのブロックチェーン上で初めての[スマートコントラクト](/developers/docs/smart-contracts/)を[デプロイ](/developers/docs/smart-contracts/deploying/)し、操作することにワクワクしていることでしょう。 -最初のスマートコントラクトは、 [ローカルテストネットワーク](/developers/docs/networks/) にデプロイするので心配は要りません。コストはまったくかからず、好きなだけ楽しむことができます。 +心配はいりません。初めてのスマートコントラクトなので、[ローカルテストネットワーク](/developers/docs/networks/)にデプロイします。デプロイしたり、好きなだけ試したりするのに費用はかかりません。 -## コントラクトの記述 {#writing-our-contract} +## コントラクトの作成 {#writing-our-contract} -まずはじめに、[Remix](https://remix.ethereum.org/)にアクセスし、新規ファイルを作成してください。 Remix画面の左上にあるアイコンから新規ファイルを追加し、適当なファイル名を付けてください。 +まず、[Remix](https://remix.ethereum.org/)にアクセスし、新規ファイルを作成します。 Remixインターフェースの左上で新規ファイルを追加し、好きなファイル名を入力します。 -![Remixインターフェースに新規ファイルを追加する](./remix.png) +![Remixインターフェースで新規ファイルを追加する](./remix.png) 作成したファイルに、以下のコードをペーストします。 @@ -33,15 +29,15 @@ pragma solidity >=0.5.17; contract Counter { - // Public variable of type unsigned int to keep the number of counts + // カウント数を保持するための符号なし整数のパブリック変数 uint256 public count = 0; - // Function that increments our counter + // カウンターをインクリメントする関数 function increment() public { count += 1; } - // Not necessary getter to get the count value + // カウント値を取得するためのゲッター(必須ではない) function getCount() public view returns (uint256) { return count; } @@ -49,51 +45,51 @@ contract Counter { } ``` -プログラミングの経験があれば、このプログラムの内容はすぐに推測できるでしょう。 以下は、各行ごとの説明です。 +プログラミングの経験があれば、このプログラムが何をするものか簡単に推測できるでしょう。 以下に各行を説明します: -- 4行目: `Counter`という名前のコントラクトを定義します。 -- 7行目:このコントラクトでは、`count`という名称を持つ、符号なしの0から始まる整数を保存します。 -- 10行目:最初の関数は、コントラクトの状態(ステート)を変更し、変数`の値`を`1増やします`。 -- 15行目:次の関数は、このスマートコントラクトに含まれない`count`変数の値を読み取るためのゲッターです。 ただし、このプログラムでは`count`変数をpublicで定義しているため、実際にはこの関数は必要ありません。例として挙げている点に注意してください。 +- 4行目: `Counter`という名前のコントラクトを定義します。 +- 7行目: このコントラクトは、0から始まる`count`という名前の符号なし整数を1つ保存します。 +- 10行目: 最初の関数はコントラクトの状態 (ステート) を変更し、`increment()`で変数`count`の値を1増やします。 +- 15行目: 2番目の関数は、スマートコントラクトの外部から`count`変数の値を読み取ることができるようにするための、単なるゲッターです。 `count`変数はpublicとして定義されているため、この関数は必須ではありませんが、例として示しています。 -皆さんがはじめて作成するシンプルなスマートコントラクトは、これですべてです。 ご覧のように、JavaやC++のようなオブジェクト指向のプログラミング言語のクラスに似ていますね。 それではさっそく、このコントラクトを使ってみましょう。 +初めてのシンプルなスマートコントラクトについては以上です。 ご存知かもしれませんが、これはJavaやC++のようなOOP (オブジェクト指向プログラミング) 言語のクラスに似ています。 それではさっそく、このコントラクトを使ってみましょう。 -## コントラクトをデプロイする {#deploying-our-contract} +## コントラクトのデプロイ {#deploying-our-contract} -最初のスマートコントラクトが作成できたので、ブロックチェーン上でデプロイして使用してみましょう。 +最初のスマートコントラクトが作成できたので、ブロックチェーン上でデプロイして使ってみましょう。 -[ブロックチェーン上でスマートコントラクトをデプロイする](/developers/docs/smart-contracts/deploying/)とは、実際のところ、受取人を指定せずに、コンパイルしたスマートコントラクトのコードを含むトランザクションを送信することです。 +[ブロックチェーン上でスマートコントラクトをデプロイする](/developers/docs/smart-contracts/deploying/)とは、実際には、受取人を指定せずに、コンパイルしたスマートコントラクトのコードを含むトランザクションを送信することです。 -コントラクトをコンパイルするには、まず、画面左側にある「compile(コンパイル)」のアイコンをクリックして[コントラクトをコンパイルします](/developers/docs/smart-contracts/compiling/)。 +まず、左側にあるコンパイルアイコンをクリックして[コントラクトをコンパイル](/developers/docs/smart-contracts/compiling/)します: -![Remixツールバー上の「compile」アイコン](./remix-compile-button.png) +![Remixツールバーのコンパイルアイコン](./remix-compile-button.png) -次に、「Compile(コンパイル)」ボタンをクリックします: +次に、コンパイルボタンをクリックします: -![Remix solidityコンパイラ上の「compile」ボタン](./remix-compile.png) +![Remix Solidityコンパイラのコンパイルボタン](./remix-compile.png) -「Auto compile(自動コンパイル)」のオプションを選択すると、テキストエディタ上で内容を保存するたびにコントラクトがコンパイルされるようになります。 +「Auto compile」オプションを選択すると、テキストエディタで内容を保存するたびにコントラクトが自動でコンパイルされるようになります。 -次に、「Deploy and run transactions(トランザクションのデプロイおよび実行」画面に移動します: +次に、「Deploy and run transactions」(トランザクションのデプロイと実行) 画面に移動します: -![Remixツールバー上の「deploy」アイコン](./remix-deploy.png) +![Remixツールバーのデプロイアイコン](./remix-deploy.png) -「Deploy and run transactions(トランザクションをデプロイし、実行する) 」の画面に移動したら、作成したコントラクト名が表示されていることをダブルチェックしてから、「Deploy(デプロイ)」をクリックします。 画面の上部から、現在の環境が「JavaScript VM」であると表示されているのを確認してください。これにより、作成したスマートコントラクトをローカルのテスト用ブロックチェーン上でデプロイし、やりとりを行うため、より高速なテストを無料で行うことができます。 +「Deploy and run transactions」画面で、ご自身のコントラクト名が表示されていることを再確認し、「Deploy」をクリックします。 ページ上部にあるように、現在の環境は「JavaScript VM」です。これは、スマートコントラクトをローカルのテスト用ブロックチェーン上にデプロイして操作することを意味し、より速く、手数料なしでテストができます。 -![Remix solidityコンパイラ上の「deploy」ボタン](./remix-deploy-button.png) +![Remix Solidityコンパイラのデプロイボタン](./remix-deploy-button.png) -「Deploy」ボタンをクリックすると、画面下部に作成したコントラクトが表示されます。 画面左側にある矢印をクリックすると、コントラクトの内容が表示されます。 これが、このコントラクトにおける`counter`変数、`increment()`関数、およびゲッター`getCounter()`です。 +「Deploy」ボタンをクリックすると、画面下部にコントラクトが表示されます。 左側の矢印をクリックして展開すると、コントラクトの内容が表示されます。 これが、変数`counter`、`increment()`関数、そしてゲッターの`getCounter()`です。 -`count`もしくは`getCount`ボタンをクリックすると、このコントラクトの`count`変数の内容を取得して表示します。 この時点では`increment` 関数を呼び出していないので、「0」が表示されます。 +`count`または`getCount`ボタンをクリックすると、コントラクトの`count`変数の内容が取得、表示されます。 まだ`increment`関数を呼び出していないため、0が表示されるはずです。 -![Remix solidityコンパイラ上の「function」ボタン](./remix-function-button.png) +![Remix Solidityコンパイラの関数ボタン](./remix-function-button.png) -次に、 `increment`ボタンをクリックして、increment関数を呼び出しましょう。 ご覧のように、実行したトランザクションのログは、ウィンドウの下部に表示されます。 `increment`ボタンではなくデータ取得のボタンをクリックした場合、ログが変化することが分かると思います。 これは、ブロックチェーン上のデータを読み込む際には、トランザクション(書き込み)や手数料が必要ないためです。 つまり、トランザクションが必要となるのは、ブロックチェーンの状態を変更する場合のみです。 +では、ボタンをクリックして`increment`関数を呼び出してみましょう。 ウィンドウ下部に、実行されたトランザクションのログが表示されます。 `increment`ボタンではなくデータ取得のボタンを押した場合、ログが異なることがわかります。 これは、ブロックチェーン上のデータを読み込む際には、トランザクション (書き込み) や手数料が必要ないためです。 トランザクションが必要となるのは、ブロックチェーンの状態を変更する場合のみだからです: -![トランザクションログ](./transaction-log.png) +![トランザクションのログ](./transaction-log.png) -`increment()`機能を呼び出すトランザクションを作成する「increment」ボタンをクリックしてから「count」または「getCount」ボタンを再度クリックすると、count変数が「0」以上である更新後の状態のスマートコントラクトが読み込まれます。 +`increment()`関数を呼び出すトランザクションを生成するincrementボタンを押した後、`count`または`getCount`ボタンをもう一度クリックすると、`count`変数が0より大きくなった、スマートコントラクトの更新された状態を読み取ることができます。 -![更新後のスマートコントラクトの状態](./updated-state.png) +![スマートコントラクトの更新された状態](./updated-state.png) -次のチュートリアルでは、[スマートコントラクトにイベントを追加する方法](/developers/tutorials/logging-events-smart-contracts/)を学びます。 イベントログは、スマートコントラクトをデバッグし、関数の呼び出し中に何が起こっているかを理解するのに便利な方法です。 +次のチュートリアルでは、[スマートコントラクトにイベントを追加する方法](/developers/tutorials/logging-events-smart-contracts/)について説明します。 イベントをロギングすることは、スマートコントラクトをデバッグし、関数の呼び出し中に何が起きているかを理解するのに便利な方法です。 diff --git a/public/content/translations/ja/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md b/public/content/translations/ja/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md new file mode 100644 index 00000000000..b526d4117d6 --- /dev/null +++ b/public/content/translations/ja/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md @@ -0,0 +1,372 @@ +--- +title: "ローカルのマルチクライアントテストネットでdAppを開発・テストする方法" +description: "このガイドでは、まずマルチクライアントのローカルイーサリアムテストネットをインスタンス化して設定する方法を説明し、次にそのテストネットを使用してdAppをデプロイ&テストします。" +author: "Tedi Mitiku" +tags: + [ + "クライアント", + "ノード", + "スマート契約", + "構成可能性", + "コンセンサスレイヤー", + "実行レイヤー", + "テスト" + ] +skill: intermediate +lang: ja +published: 2023-04-11 +--- + +## はじめに {#introduction} + +このガイドでは、設定可能なローカルイーサリアムテストネットのインスタンス化、そこへのスマートコントラクトのデプロイ、およびdAppに対するテストを実行するためのテストネットの使用というプロセスを順を追って説明します。 このガイドは、ライブのテストネットやメインネットにデプロイする前に、さまざまなネットワーク構成に対してdAppをローカルで開発・テストしたいdApp開発者向けに設計されています。 + +このガイドでは、次のことを行います。 + +- [Kurtosis](https://www.kurtosis.com/)を使用して、[`eth-network-package`](https://github.com/kurtosis-tech/eth-network-package)でローカルイーサリアムテストネットをインスタンス化する +- Hardhat dApp開発環境をローカルテストネットに接続し、dAppをコンパイル、デプロイ、テストする、そして +- ノード数や特定のEL/CLクライアントのペアリングなどのパラメータを含むローカルテストネットを設定し、さまざまなネットワーク構成に対する開発およびテストのワークフローを可能にする。 + +### Kurtosisとは? {#what-is-kurtosis} + +[Kurtosis](https://www.kurtosis.com/)は、マルチコンテナのテスト環境を構成するために設計された、構成可能なビルドシステムです。 これにより、開発者は、ブロックチェーンテストネットなどの動的なセットアップロジックを必要とする再現可能な環境を作成できます。 + +このガイドでは、Kurtosis eth-network-packageが、[`geth`](https://geth.ethereum.org/)実行レイヤー(EL)クライアント、および[`teku`](https://consensys.io/teku)、[`lighthouse`](https://lighthouse.sigmaprime.io/)、[`lodestar`](https://lodestar.chainsafe.io/)コンセンサスレイヤー(CL)クライアントをサポートするローカルイーサリアムテストネットを起動します。 このパッケージは、Hardhat Network、Ganache、Anvilのようなフレームワークのネットワークに代わる、設定可能で構成可能な代替手段として機能します。 Kurtosisは、開発者が使用するテストネットに対してより優れた制御と柔軟性を提供します。これは、[イーサリアム・ファウンデーションがマージのテストにKurtosisを使用した](https://www.kurtosis.com/blog/testing-the-ethereum-merge)主な理由であり、ネットワークのアップグレードをテストするためにKurtosisを使い続けている理由でもあります。 + +## Kurtosisのセットアップ {#setting-up-kurtosis} + +続行する前に、次のものがあることを確認してください。 + +- ローカルマシンに[Dockerエンジンをインストールして起動](https://docs.kurtosis.com/install/#i-install--start-docker)していること +- [Kurtosis CLIをインストール](https://docs.kurtosis.com/install#ii-install-the-cli)していること(CLIがすでにインストールされている場合は、最新リリースにアップグレードしていること) +- [Node.js](https://nodejs.org/en)、[yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable)、および[npx](https://www.npmjs.com/package/npx) (dApp環境用) をインストールしていること + +## ローカルイーサリアムテストネットのインスタンス化 {#instantiate-testnet} + +ローカルイーサリアムテストネットを起動するには、次を実行します。 + +```python +kurtosis --enclave local-eth-testnet run github.com/kurtosis-tech/eth-network-package +``` + +注: このコマンドは、`--enclave`フラグを使用して、ネットワークに「local-eth-testnet」という名前を付けます。 + +Kurtosisは、指示を解釈、検証、そして実行する際に、内部で行われている手順を出力します。 最後に、次のような出力が表示されるはずです。 + +```python +INFO[2023-04-04T18:09:44-04:00] ====================================================== +INFO[2023-04-04T18:09:44-04:00] || Created enclave: local-eth-testnet || +INFO[2023-04-04T18:09:44-04:00] ====================================================== +Name: local-eth-testnet +UUID: 39372d756ae8 +Status: RUNNING +Creation Time: Tue, 04 Apr 2023 18:09:03 EDT + +========================================= Files Artifacts ========================================= +UUID Name +d4085a064230 cl-genesis-data +1c62cb792e4c el-genesis-data +bd60489b73a7 genesis-generation-config-cl +b2e593fe5228 genesis-generation-config-el +d552a54acf78 geth-prefunded-keys +5f7e661eb838 prysm-password +054e7338bb59 validator-keystore-0 + +========================================== User Services ========================================== +UUID Name Ports Status +e20f129ee0c5 cl-client-0-beacon http: 4000/tcp -> RUNNING + metrics: 5054/tcp -> + tcp-discovery: 9000/tcp -> 127.0.0.1:54263 + udp-discovery: 9000/udp -> 127.0.0.1:60470 +a8b6c926cdb4 cl-client-0-validator http: 5042/tcp -> 127.0.0.1:54267 RUNNING + metrics: 5064/tcp -> +d7b802f623e8 el-client-0 engine-rpc: 8551/tcp -> 127.0.0.1:54253 RUNNING + rpc: 8545/tcp -> 127.0.0.1:54251 + tcp-discovery: 30303/tcp -> 127.0.0.1:54254 + udp-discovery: 30303/udp -> 127.0.0.1:53834 + ws: 8546/tcp -> 127.0.0.1:54252 +514a829c0a84 prelaunch-data-generator-1680646157905431468 STOPPED +62bd62d0aa7a prelaunch-data-generator-1680646157915424301 STOPPED +05e9619e0e90 prelaunch-data-generator-1680646157922872635 STOPPED + +``` + +おめでとうございます! Kurtosisを使用して、CL (`lighthouse`)とELクライアント(`geth`)を持つローカルイーサリアムテストネットをDocker経由でインスタンス化しました。 + +### レビュー {#review-instantiate-testnet} + +このセクションでは、Kurtosisに[GitHubでリモートホストされている`eth-network-package`](https://github.com/kurtosis-tech/eth-network-package)を使用して、Kurtosis [Enclave](https://docs.kurtosis.com/advanced-concepts/enclaves/)内にローカルイーサリアムテストネットを起動するよう指示するコマンドを実行しました。 エンクレーブ内には、「ファイルアーティファクト」と「ユーザーサービス」の両方があります。 + +エンクレーブ内の[ファイルアーティファクト](https://docs.kurtosis.com/advanced-concepts/files-artifacts/)には、ELおよびCLクライアントをブートストラップするために生成および利用されたすべてのデータが含まれます。 データは、この[Dockerイメージ](https://github.com/ethpandaops/ethereum-genesis-generator)から構築された `prelaunch-data-generator` サービスを使用して作成されました。 + +ユーザーサービスには、エンクレーブで動作しているすべてのコンテナ化されたサービスが表示されます。 ELクライアントとCLクライアントの両方を備えた単一のノードが作成されていることがわかります。 + +## dApp開発環境をローカルイーサリアムテストネットに接続する {#connect-your-dapp} + +### dApp開発環境のセットアップ {#set-up-dapp-env} + +実行中のローカルテストネットができたので、dApp開発環境を接続してローカルテストネットを使用できます。 このガイドでは、Hardhatフレームワークを使用して、ブラックジャックdAppをローカルテストネットにデプロイします。 + +dApp開発環境をセットアップするには、サンプルdAppを含むリポジトリをクローンし、その依存関係をインストールして、次を実行します。 + +```python +git clone https://github.com/kurtosis-tech/awesome-kurtosis.git && cd awesome-kurtosis/smart-contract-example && yarn +``` + +ここで使用する[smart-contract-example](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example)フォルダには、[Hardhat](https://hardhat.org/)フレームワークを使用するdApp開発者のための一般的なセットアップが含まれています。 + +- [`contracts/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/contracts) には、Blackjack dApp用のいくつかのシンプルなスマートコントラクトが含まれています +- [`scripts/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/scripts) には、ローカルのイーサリアムネットワークにトークンコントラクトをデプロイするためのスクリプトが含まれています +- [`test/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/test) には、Blackjack dAppの各プレイヤーに1000がミントされていることを確認するための、トークンコントラクト用の簡単な .js テストが含まれています +- [`hardhat.config.ts`](https://github.com/kurtosis-tech/awesome-kurtosis/blob/main/smart-contract-example/hardhat.config.ts) はHardhatセットアップを構成します + +### Hardhatがローカルテストネットを使用するように設定する {#configure-hardhat} + +dApp開発環境がセットアップされたので、今度はHardhatを接続して、Kurtosisを使用して生成されたローカルイーサリアムテストネットを使用します。 これを実現するには、`hardhat.config.ts`設定ファイルの`localnet`構造体にある`<$YOUR_PORT>`を、任意の`el-client-`サービスから出力されたrpc uriのポートに置き換えます。 このサンプルケースでは、ポートは`64248`になります。 ポートは異なります。 + +`hardhat.config.ts`での例: + +```js +localnet: { +url: 'http://127.0.0.1:<$YOUR_PORT>',// TODO: $YOUR_PORTを、ETH NETWORK KURTOSIS PACKAGEによって生成されたノードURIのポートに置き換えてください + +// これらはeth-network-packageによって作成された、事前に入金済みのテストアカウントに関連付けられた秘密鍵です +// +accounts: [ + "ef5177cd0b6b21c87db5a0bf35d4084a8a57a9d6a064f86d51ac85f2b873a4e2", + "48fcc39ae27a0e8bf0274021ae6ebd8fe4a0e12623d61464c498900b28feb567", + "7988b3a148716ff800414935b305436493e1f25237a2a03e5eebc343735e2f31", + "b3c409b6b0b3aa5e65ab2dc1930534608239a478106acf6f3d9178e9f9b00b35", + "df9bb6de5d3dc59595bcaa676397d837ff49441d211878c024eabda2cd067c9f", + "7da08f856b5956d40a72968f93396f6acff17193f013e8053f6fbb6c08c194d6", + ], +}, +``` + +ファイルを保存すると、Hardhat dApp開発環境がローカルイーサリアムテストネットに接続されます。 次を実行して、テストネットが機能していることを確認できます。 + +```python +npx hardhat balances --network localnet +``` + +出力は次のようになります。 + +```python +0x878705ba3f8Bc32FCf7F4CAa1A35E72AF65CF766 has balance 10000000000000000000000000 +0x4E9A3d9D1cd2A2b2371b8b3F489aE72259886f1A has balance 10000000000000000000000000 +0xdF8466f277964Bb7a0FFD819403302C34DCD530A has balance 10000000000000000000000000 +0x5c613e39Fc0Ad91AfDA24587e6f52192d75FBA50 has balance 10000000000000000000000000 +0x375ae6107f8cC4cF34842B71C6F746a362Ad8EAc has balance 10000000000000000000000000 +0x1F6298457C5d76270325B724Da5d1953923a6B88 has balance 10000000000000000000000000 +``` + +これにより、Hardhatがローカルテストネットを使用しており、`eth-network-package`によって作成された事前に入金されたアカウントを検出していることが確認できます。 + +### dAppをローカルでデプロイしてテストする {#deploy-and-test-dapp} + +dApp開発環境がローカルイーサリアムテストネットに完全に接続されたので、ローカルテストネットを使用してdAppに対する開発およびテストワークフローを実行できます。 + +ローカルでのプロトタイピングと開発のために`ChipToken.sol`スマートコントラクトをコンパイルしてデプロイするには、次を実行します。 + +```python +npx hardhat compile +npx hardhat run scripts/deploy.ts --network localnet +``` + +出力は次のようになります。 + +```python +ChipTokenのデプロイ先: 0xAb2A01BC351770D09611Ac80f1DE076D56E0487d +``` + +次に、ローカルdAppに対して`simple.js`テストを実行し、Blackjack dAppの各プレイヤーに1000がミントされていることを確認します。 + +出力は次のようになります。 + +```python +npx hardhat test --network localnet +``` + +出力は次のようになります。 + +```python +ChipToken + mint + ✔ PLAYER ONEに1000チップがミントされるべき + + 1件合格 (654ms) +``` + +### レビュー {#review-dapp-workflows} + +この時点で、dApp開発環境をセットアップし、Kurtosisによって作成されたローカルイーサリアムネットワークに接続し、dAppに対してコンパイル、デプロイ、および簡単なテストを実行しました。 + +次に、さまざまなネットワーク構成でdAppをテストするために、基盤となるネットワークをどのように構成できるかを見ていきましょう。 + +## ローカルイーサリアムテストネットの設定 {#configure-testnet} + +### クライアント構成とノード数の変更 {#configure-client-config-and-num-nodes} + +ローカルイーサリアムテストネットは、開発またはテストしたいシナリオや特定のネットワーク構成に応じて、異なるELおよびCLクライアントペア、ならびにさまざまな数のノードを使用するように構成できます。 これは、一度セットアップすれば、カスタマイズされたローカルテストネットを起動し、それを使用して同じワークフロー(デプロイ、テストなど)を実行できることを意味します。 さまざまなネットワーク構成の下で、すべてが期待どおりに機能することを確認します。 変更できる他のパラメータの詳細については、このリンクをご覧ください。 + +試してみましょう。 JSONファイルを介して、さまざまな構成オプションを `eth-network-package` に渡すことができます。 このネットワークパラメータJSONファイルは、Kurtosisがローカルイーサリアムネットワークをセットアップするために使用する特定の設定を提供します。 + +デフォルトの設定ファイルを取得し、それを編集して、異なるEL/CLペアを持つ2つのノードを起動します。 + +- ノード1:`geth`/`lighthouse` +- ノード2:`geth`/`lodestar` +- ノード3:`geth`/`teku` + +この構成により、dAppをテストするためのイーサリアムノード実装の異種ネットワークが作成されます。 設定ファイルは次のようになります。 + +```yaml +{ + "participants": + [ + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "lighthouse", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "lodestar", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "teku", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + ], + "network_params": + { + "preregistered_validator_keys_mnemonic": "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete", + "num_validator_keys_per_node": 64, + "network_id": "3151908", + "deposit_contract_address": "0x4242424242424242424242424242424242424242", + "seconds_per_slot": 12, + "genesis_delay": 120, + "capella_fork_epoch": 5, + }, +} +``` + +各`participants`構造体はネットワーク内のノードにマッピングされるため、3つの`participants`構造体はKurtosisにネットワーク内で3つのノードを起動するように指示します。 各`participants`構造体では、その特定のノードに使用されるELとCLのペアを指定できます。 + +`network_params`構造体は、各ノードのジェネシスファイルを作成するために使用されるネットワーク設定や、ネットワークのスロットごとの秒数などの他の設定を構成します。 + +編集したパラメータファイルを任意のディレクトリに保存し(以下の例ではデスクトップに保存)、次を実行してKurtosisパッケージを実行します。 + +```python +kurtosis clean -a && kurtosis run --enclave local-eth-testnet github.com/kurtosis-tech/eth-network-package "$(cat ~/eth-network-params.json)" +``` + +注: ここでは、`kurtosis clean -a`コマンドを使用して、新しいテストネットを開始する前に古いテストネットとその内容を破棄するようにKurtosisに指示します。 + +再び、Kurtosisは少しの間動作し、実行されている個々のステップを出力します。 最終的に、出力は次のようになります。 + +```python +Starlark code successfully run. No output was returned. +INFO[2023-04-07T11:43:16-04:00] ========================================================== +INFO[2023-04-07T11:43:16-04:00] || Created enclave: local-eth-testnet || +INFO[2023-04-07T11:43:16-04:00] ========================================================== +Name: local-eth-testnet +UUID: bef8c192008e +Status: RUNNING +Creation Time: Fri, 07 Apr 2023 11:41:58 EDT + +========================================= Files Artifacts ========================================= +UUID Name +cc495a8e364a cl-genesis-data +7033fcdb5471 el-genesis-data +a3aef43fc738 genesis-generation-config-cl +8e968005fc9d genesis-generation-config-el +3182cca9d3cd geth-prefunded-keys +8421166e234f prysm-password +d9e6e8d44d99 validator-keystore-0 +23f5ba517394 validator-keystore-1 +4d28dea40b5c validator-keystore-2 + +========================================== User Services ========================================== +UUID Name Ports Status +485e6fde55ae cl-client-0-beacon http: 4000/tcp -> http://127.0.0.1:65010 RUNNING + metrics: 5054/tcp -> http://127.0.0.1:65011 + tcp-discovery: 9000/tcp -> 127.0.0.1:65012 + udp-discovery: 9000/udp -> 127.0.0.1:54455 +73739bd158b2 cl-client-0-validator http: 5042/tcp -> 127.0.0.1:65016 RUNNING + metrics: 5064/tcp -> http://127.0.0.1:65017 +1b0a233cd011 cl-client-1-beacon http: 4000/tcp -> 127.0.0.1:65021 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65023 + tcp-discovery: 9000/tcp -> 127.0.0.1:65024 + udp-discovery: 9000/udp -> 127.0.0.1:56031 + validator-metrics: 5064/tcp -> 127.0.0.1:65022 +949b8220cd53 cl-client-1-validator http: 4000/tcp -> 127.0.0.1:65028 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65030 + tcp-discovery: 9000/tcp -> 127.0.0.1:65031 + udp-discovery: 9000/udp -> 127.0.0.1:60784 + validator-metrics: 5064/tcp -> 127.0.0.1:65029 +c34417bea5fa cl-client-2 http: 4000/tcp -> 127.0.0.1:65037 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65035 + tcp-discovery: 9000/tcp -> 127.0.0.1:65036 + udp-discovery: 9000/udp -> 127.0.0.1:63581 +e19738e6329d el-client-0 engine-rpc: 8551/tcp -> 127.0.0.1:64986 RUNNING + rpc: 8545/tcp -> 127.0.0.1:64988 + tcp-discovery: 30303/tcp -> 127.0.0.1:64987 + udp-discovery: 30303/udp -> 127.0.0.1:55706 + ws: 8546/tcp -> 127.0.0.1:64989 +e904687449d9 el-client-1 engine-rpc: 8551/tcp -> 127.0.0.1:64993 RUNNING + rpc: 8545/tcp -> 127.0.0.1:64995 + tcp-discovery: 30303/tcp -> 127.0.0.1:64994 + udp-discovery: 30303/udp -> 127.0.0.1:58096 + ws: 8546/tcp -> 127.0.0.1:64996 +ad6f401126fa el-client-2 engine-rpc: 8551/tcp -> 127.0.0.1:65003 RUNNING + rpc: 8545/tcp -> 127.0.0.1:65001 + tcp-discovery: 30303/tcp -> 127.0.0.1:65000 + udp-discovery: 30303/udp -> 127.0.0.1:57269 + ws: 8546/tcp -> 127.0.0.1:65002 +12d04a9dbb69 prelaunch-data-generator-1680882122181135513 STOPPED +5b45f9c0504b prelaunch-data-generator-1680882122192182847 STOPPED +3d4aaa75e218 prelaunch-data-generator-1680882122201668972 STOPPED +``` + +おめでとうございます! ローカルテストネットを1つではなく3つのノードを持つように正常に設定しました。 dAppに対して以前と同じワークフロー(デプロイ&テスト)を実行するには、新しい3ノードのローカルテストネットの`el-client-`サービスから出力されたrpc uriのポートで、`hardhat.config.ts`構成ファイルの`localnet`構造体にある`<$YOUR_PORT>`を置き換えることで、以前と同じ操作を実行します。 + +## 結論 {#conclusion} + +完成です! この短いガイドを要約すると、次のことを行いました。 + +- Kurtosisを使用してDocker上にローカルイーサリアムテストネットを作成 +- ローカルdApp開発環境をローカルイーサリアムネットワークに接続 +- dAppをデプロイし、ローカルイーサリアムネットワーク上で簡単なテストを実行 +- 基盤となるイーサリアムネットワークを3つのノードを持つように構成 + +何がうまくいったか、何を改善できるか、また質問への回答など、ご意見をお聞かせください。 [GitHub](https://github.com/kurtosis-tech/kurtosis/issues/new/choose)または[メール](mailto:feedback@kurtosistech.com)でお気軽にご連絡ください。 + +### その他の例とガイド {#other-examples-guides} + +[クイックスタート](https://docs.kurtosis.com/quickstart)(PostgresデータベースとAPIを構築します)や、[awesome-kurtosisリポジトリ](https://github.com/kurtosis-tech/awesome-kurtosis)にあるその他の例を確認することをお勧めします。そこには、次のようなパッケージを含む素晴らしい例があります。 + +- [同じローカルイーサリアムテストネットを起動](https://github.com/kurtosis-tech/eth2-package)しますが、トランザクションスパマー(トランザクションをシミュレートするため)、フォークモニター、接続されたGrafanaおよびPrometheusインスタンスなどの追加サービスも接続します +- 同じローカルイーサリアムネットワークに対して[サブネットワーキングテスト](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/ethereum-network-partition-test)を実行する diff --git a/public/content/translations/ja/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md b/public/content/translations/ja/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md index fea349f4956..f06f5fd736b 100644 --- a/public/content/translations/ja/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md +++ b/public/content/translations/ja/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md @@ -1,60 +1,55 @@ --- title: "コントラクトのサイズ制限に対処するためのコントラクトのサイズ削減" -description: スマートコントラクトが大きくなりすぎるのを防ぐためにできること +description: "スマートコントラクトが大きくなりすぎるのを防ぐためにできること" author: Markus Waas lang: ja -tags: - - "Solidity" - - "スマートコントラクト" - - "ストレージ" +tags: [ "Solidity", "スマート契約", "ストレージ" ] skill: intermediate published: 2020-06-26 source: soliditydeveloper.com sourceUrl: https://soliditydeveloper.com/max-contract-size --- -## 制限がある理由 {#why-is-there-a-limit} +## なぜ制限があるのですか? {#why-is-there-a-limit} -[2016年11月22日](https://blog.ethereum.org/2016/11/18/hard-fork-no-4-spurious-dragon/)、Spurious Dragonのハードフォークで[EIP-170](https://eips.ethereum.org/EIPS/eip-170)が導入され、24.576 KBのスマートコントラクトのサイズ制限が追加されました。 Solidityデベロッパーにとって、これはコントラクトに機能をどんどん追加していくうちに、ある時点でサイズ制限に達し、デプロイした際に以下のエラーが表示されてしまうということを意味します。 +2016年11月22日の[Spurious Dragonハードフォーク](https://blog.ethereum.org/2016/11/18/hard-fork-no-4-spurious-dragon/)で、[EIP-170](https://eips.ethereum.org/EIPS/eip-170)が導入され、24.576kbのスマートコントラクトサイズ制限が追加されました。 Solidityデベロッパーにとって、これはコントラクトに機能をどんどん追加していくうちに、ある時点でサイズ制限に達し、デプロイした際に以下のエラーが表示されてしまうということを意味します。 -`Warning: Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries.` +`Warning: Contract code size exceeds 24576 bytes (Spurious Dragonで導入された制限)。 このコントラクトはメインネットではデプロイできない可能性があります。 オプティマイザを有効にする(低い\"runs\"値で!)、revert文字列をオフにする、またはライブラリを使用することを検討してください。` -この制限は、サービス拒否(DOS)攻撃を防ぐために導入されました。 コントラクトの呼び出しは、ガスの観点では比較的安価です。 しかし、イーサリアムノードのコントラクト呼び出しの影響は、(ディスクからのコードの読み込み、コードの前処理、マークルプルーフへのデータの追加の対象となる)呼び出されたコントラクトコードのサイズによっては、過度に増加することになります。 攻撃者がリソースをほとんど必要とせずに、他のノードでの大量の処理を生じさせるそうした状況では、DOS攻撃を受ける可能性が常に存在します。 +この制限は、サービス拒否(DOS)攻撃を防ぐために導入されました。 コントラクトの呼び出しは、ガスの観点では比較的安価です。 しかし、イーサリアムノードにとってコントラクト呼び出しの影響は、呼び出されるコントラクトコードのサイズ(ディスクからのコードの読み取り、コードの前処理、マークルプルーフへのデータの追加)に応じて不釣り合いに大きくなります。 攻撃者がリソースをほとんど必要とせずに、他のノードでの大量の処理を生じさせるそうした状況では、DOS攻撃を受ける可能性が常に存在します。 -コントラクトコードの固有のサイズ制限が、ブロックのガスリミットとなるため、本来これは問題ではありませんでした。 コントラクトは、コントラクトのバイトコードをすべて含むトランザクション内でデプロイされる必要があることは言うまでもありません。 ブロックに1つのトランザクションのみを含めると、そのガスのすべてを使うことができますが、無限ではありません。 [ロンドンアップグレード](/ethereum-forks/#london)以降、ブロックのガスリミットは、ネットワークの需要に応じて15M~30M間で変えられるようになりました。 +元々、これはそれほど問題ではありませんでした。なぜなら、ブロックのガスリミットが、コントラクトサイズの自然な制限となっていたからです。 言うまでもなく、コントラクトは、コントラクトのバイトコードをすべて含むトランザクション内でデプロイされる必要があります。 ブロックに1つのトランザクションのみを含めると、そのガスのすべてを使うことができますが、無限ではありません。 [ロンドンアップグレード](/ethereum-forks/#london)以降、ブロックのガスリミットはネットワークの需要に応じて15M~30Mユニットの間で変動可能になりました。 -次に、いくつかの方法を、効果が大きいものから順に見ていきます。 減量の観点から考えてみましょう。 目標体重(この場合は24 KB)を達成するための最良の戦略は、まず効果が大きい方法に集中して取り組むことです。 ほとんどの場合、食生活を改善するだけで解決しますが、もう少し何かが必要な場合もあります。 その場合は、運動(中程度の効果)やサプリメント(小さな効果)を加えるとよいでしょう。 +以下では、いくつかの方法を、その影響の大きい順に見ていきます。 減量の観点から考えてみましょう。 目標体重(この場合は24kb)を達成するための最良の戦略は、まず影響の大きい方法に集中することです。 ほとんどの場合、食生活を改善するだけで解決しますが、もう少し何かが必要な場合もあります。 その場合は、運動(中程度の影響)やサプリメント(小程度の影響)を追加することになるでしょう。 -## サイズ削減効果: 大 {#big-impact} +## 大きな影響 {#big-impact} -### コントラクトの分割 {#separate-your-contracts} +### コントラクトを分割する {#separate-your-contracts} -これは常に最初のアプローチであるべきです。 コントラクトを複数の小さなまとまりに分割するにはどうすればよいでしょうか? 一般的には、コントラクトのための良いアーキテクチャを考えなければなりません。 コードの読みやすさの観点からは、常に、小さなコントラクトコードが好まれます。 コントラクトの分割については、以下の質問を自問してください。 +これは常に最初のアプローチであるべきです。 コントラクトを複数の小さなまとまりに分割するにはどうすればよいでしょうか? 一般的には、コントラクトのための良いアーキテクチャを考えなければなりません。 コードの可読性の観点からは、より小さなコントラクトが常に好まれます。 コントラクトを分割するには、自問してみてください: -- どの関数がセットになっていますか? 関数の各セットは、そのコントラクト内にあることが最善策となる場合があります。 +- どの関数がセットになっていますか? それぞれの関数セットは、独自のコントラクトにまとめるのが最善かもしれません。 - コントラクトの状態や、状態の特定のサブセットのみの読み取りを必要としないのは、どの関数ですか? - ストレージと機能を分けることはできますか? ### ライブラリ {#libraries} -機能コードをストレージから移動させる簡単な方法としては、[ライブラリ](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#libraries)の使用が挙げられます。 コンパイル中に直接[コントラクトに追加](https://ethereum.stackexchange.com/questions/12975/are-internal-functions-in-libraries-not-covered-by-linking)されるので、ライブラリ関数をinternalで宣言しないでください。 しかし、public関数を使用する場合、それらは実際には別のライブラリコントラクトに含まれることになります。 ライブラリをより便利に利用するには、[using for](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#using-for)の使用を検討してください。 +機能に関するコードをストレージから分離する簡単な方法の1つは、[ライブラリ](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#libraries)を使用することです。 ライブラリ関数をinternalとして宣言しないでください。internal関数はコンパイル時にコントラクトに直接[追加される](https://ethereum.stackexchange.com/questions/12975/are-internal-functions-in-libraries-not-covered-by-linking)ためです。 しかし、public関数を使用する場合、それらは実際には別のライブラリコントラクトに含まれることになります。 ライブラリをより便利に使うために、[`using for`](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#using-for)の使用を検討してください。 ### プロキシ {#proxies} -より高度な戦略としては、プロキシシステムが挙げられます。 このシステムではライブラリが、呼び出し元のコントラクトの状態で単に別のコントラクトの関数を実行する`DELEGATECALL`を裏で使用します。 プロキシシステムの詳細については、[こちらのブログ](https://hackernoon.com/how-to-make-smart-contracts-upgradable-2612e771d5a2)をご覧ください。 これで機能性が向上します。例えば、アップグレード可能になりますが、複雑さも増します。 何らかの理由によりプロキシシステムが唯一の選択肢でない限り、コントラクトサイズを減らすためだけにプロキシシステムを追加することはお勧めしません。 +より高度な戦略としては、プロキシシステムが挙げられます。 ライブラリは内部で `DELEGATECALL` を使用します。これは、呼び出し元のコントラクトの状態で、別のコントラクトの関数を実行するものです。 プロキシシステムについてさらに学ぶには、[こちらのブログ記事](https://hackernoon.com/how-to-make-smart-contracts-upgradable-2612e771d5a2)をご覧ください。 プロキシは、アップグレード可能性を有効にするなど、より多くの機能を提供しますが、多くの複雑さも加わります。 何らかの理由によりプロキシシステムが唯一の選択肢でない限り、コントラクトサイズを減らすためだけにプロキシシステムを追加することはお勧めしません。 -## サイズ削減効果: 中 {#medium-impact} +## 中程度の影響 {#medium-impact} -### 関数の削除 {#remove-functions} +### 関数を削除する {#remove-functions} -これは当然実行すべきことです。 関数はコントラクトサイズをかなり増大させます。 +これは明白なはずです。 関数はコントラクトサイズをかなり増大させます。 -- **external**: 利便性の理由から、多くのview関数が頻繁に追加されます。 サイズ制限に達するまでは、追加してもかまいません。 その後で、絶対に必要なもの以外のすべての関数を削除することを真剣に検討します。 -- **internal**: internal関数やprivate関数を削除し、関数が一度だけ呼び出される場合に限り、コードを単にインライン化することもできます。 +- **External**: 利便性のために、多くの`view`関数を追加することがよくあります。 サイズ制限に達するまでは、それで全く問題ありません。 その場合は、本当に必要なもの以外はすべて削除することを真剣に検討した方がよいでしょう。 +- **Internal**: `internal`/`private`関数を削除し、関数が一度しか呼び出されない場合は、コードをインライン化することもできます。 -### 変数の追加を回避 {#avoid-additional-variables} - -以下のような簡単な変更をするだけで、 +### 追加の変数を避ける {#avoid-additional-variables} ```solidity function get(uint id) returns (address,address) { @@ -69,24 +64,23 @@ function get(uint id) returns (address,address) { } ``` -**0.28 KB**もの差が出ます。 コントラクトでは類似の状況が多く見られます。結果的に、それらがサイズをかなり増大させています。 +このような簡単な変更で、**0.28kb**の差が生まれます。 コントラクトには同様の状況が数多く見られる可能性があり、それらが積み重なって大きな量になることがあります。 -### エラーメッセージの短縮 {#shorten-error-message} +### エラーメッセージを短縮する {#shorten-error-message} -長いリバート(元に戻す)メッセージ、特に多くの異なるリバートメッセージは、コントラクトを肥大化させる可能性があります。 代わりに、短いエラーコードを使用し、コントラクト内でそれらをデコードします。 そうすると、以下のように、長いメッセージをかなり短くすることができます。 +長いrevertメッセージ、特に多くの異なるrevertメッセージは、コントラクトを肥大化させる可能性があります。 代わりに短いエラーコードを使用し、コントラクト内でデコードしてください。 長いメッセージを、以下のように大幅に短くすることができます: ```solidity -require(msg.sender == owner, "Only the owner of this contract can call this function"); - +require(msg.sender == owner, "このコントラクトのオーナーのみがこの関数を呼び出せます"); ``` ```solidity require(msg.sender == owner, "OW1"); ``` -### エラーメッセージのかわりにカスタムエラーを使用 +### エラーメッセージの代わりにカスタムエラーを使用する -[Solidity 0.8.4](https://blog.soliditylang.org/2021/04/21/custom-errors/)で、カスタムエラーが導入されました。 カスタムエラーは、コントラクトのサイズを削減するのに効果的な方法です。なぜなら、(関数と同様に)セレクターとしてABIエンコードされるためです。 +カスタムエラーは[Solidity 0.8.4](https://blog.soliditylang.org/2021/04/21/custom-errors/)で導入されました。 カスタムエラーは、コントラクトのサイズを削減するための優れた方法です。なぜなら、(関数と同様に)セレクターとしてABIエンコードされるためです。 ```solidity error Unauthorized(); @@ -96,15 +90,15 @@ if (msg.sender != owner) { } ``` -### オプティマイザでの低い実行値を検討 {#consider-a-low-run-value-in-the-optimizer} +### オプティマイザで低いruns値を検討する {#consider-a-low-run-value-in-the-optimizer} -オプティマイザの設定も変更できます。 デフォルト値の200は、関数が200回呼び出される場合と同様にバイトコードを最適化しようとしていることを意味します。 これを1に変更すると、通常、各関数を1回だけ実行するケースに最適化するよう、オプティマイザに指示します。 1回だけ実行するように最適化された関数とは、その関数自体のデプロイのために最適化されていることを意味します。 ただし、**1に設定すると関数の実行にかかる[ガス代](/developers/docs/gas/)が高くなる**ことに注意してください。 +オプティマイザの設定を変更することもできます。 デフォルト値の200は、関数が200回呼び出されるかのようにバイトコードを最適化しようとすることを意味します。 これを1に変更すると、基本的には、各関数を一度だけ実行する場合に最適化するようにオプティマイザに指示することになります。 一度しか実行されないように最適化された関数は、デプロイ自体に対して最適化されていることを意味します。 **これにより関数の実行にかかる[ガス代](/developers/docs/gas/)が増加する**ため、望ましくない場合があることに注意してください。 -## サイズ削減効果: 小 {#small-impact} +## 小さな影響 {#small-impact} -### 関数への構造体渡しを回避 {#avoid-passing-structs-to-functions} +### 関数に構造体を渡すのを避ける {#avoid-passing-structs-to-functions} -[ABIEncoderV2](https://solidity.readthedocs.io/en/v0.6.10/layout-of-source-files.html#abiencoderv2)を使用している場合は、関数に構造体を渡さないようにすることができます。 以下のように、パラメータを構造体として渡す代わりに、 +[ABIEncoderV2](https://solidity.readthedocs.io/en/v0.6.10/layout-of-source-files.html#abiencoderv2)を使用している場合、関数に構造体を渡さないようにすることが有効です。 パラメータを構造体として渡す代わりに、必要なパラメータを直接渡します。 この例では、さらに**0.1kb**を節約しました。 ```solidity function get(uint id) returns (address,address) { @@ -126,16 +120,14 @@ function _get(address addr1, address addr2) private view returns(address,address } ``` -必要なパラメータを直接渡すようにします。 この例では、さらに**0.1 KB**を節約しました。 - -### 関数と変数の正しい可視性の宣言 {#declare-correct-visibility-for-functions-and-variables} +### 関数と変数に正しい可視性を宣言する {#declare-correct-visibility-for-functions-and-variables} -- 外部からのみ呼び出される関数や変数ですか? その場合は、`public`ではなく`external`として宣言します。 -- コントラクト内からのみ呼び出される関数または変数ですか? その場合は、`public`ではなく`private`あるいは`internal`として宣言します。 +- 外部からのみ呼び出される関数や変数ですか? `public`ではなく`external`として宣言します。 +- コントラクト内部からのみ呼び出される関数や変数ですか? `public`ではなく`private`または`internal`として宣言します。 -### modifierの削除 {#remove-modifiers} +### 修飾子を削除する {#remove-modifiers} -modifier修飾子を過剰に使用すると、コントラクトのサイズに大きな影響を与える可能性があります。 そのため、modifierの代わりに関数を使用することを検討してください。 +修飾子は、特に多用されると、コントラクトのサイズに大きな影響を与える可能性があります。 修飾子を削除し、代わりに関数を使用することを検討してください。 ```solidity modifier checkStuff() {} @@ -149,4 +141,4 @@ function checkStuff() private {} function doSomething() { checkStuff(); } ``` -これらのヒントを実践することで、コントラクトのサイズを大幅に削減することができます。 繰り返しになりますが、最大の効果を得るためには、可能な限りコントラクトを分割することが重要です。 +これらのヒントは、コントラクトのサイズを大幅に削減するのに役立つはずです。 繰り返しになりますが、最大の影響を得るためには、可能であれば常にコントラクトの分割に注力してください。 diff --git a/public/content/translations/ja/developers/tutorials/eip-1271-smart-contract-signatures/index.md b/public/content/translations/ja/developers/tutorials/eip-1271-smart-contract-signatures/index.md index f01f894f8a3..a7041058559 100644 --- a/public/content/translations/ja/developers/tutorials/eip-1271-smart-contract-signatures/index.md +++ b/public/content/translations/ja/developers/tutorials/eip-1271-smart-contract-signatures/index.md @@ -1,20 +1,16 @@ --- title: "EIP-1271: スマートコントラクト署名に対する署名と検証" -description: EIP-1271を使ったスマートコントラクト署名の生成および検証の概要。 スマートコントラクト・デベロッパーが構築できるように具体的な例としてSafe (旧 Gnosis Safe) で使用される EIP-1271実装についても説明します。 +description: "EIP-1271を使ったスマートコントラクト署名の生成および検証の概要。 スマートコントラクト・デベロッパーが構築できるように具体的な例としてSafe (旧 Gnosis Safe) で使用される EIP-1271実装についても説明します。" author: Nathan H. Leung lang: ja -tags: - - "eip-1271" - - "スマートコントラクト" - - "検証" - - "署名(signing)" +tags: [ "eip-1271", "スマート契約", "検証", "署名(signing)" ] skill: intermediate published: 2023-01-12 --- -[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271)標準は、スマートコントラクトで署名の検証を可能にします。 +[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271)標準は、スマートコントラクトによる署名の検証を可能にします。 -このチュートリアルでは、デジタル署名、EIP-1271の背景、EIP-1271の具体的な実装である[Safe](https://safe.global/) (旧Gnosis Safe) の概要を説明します。 このチュートリアル全体を通して、EIP-1271を自身のコントラクトへ実装するための出発点として使うことができます。 +このチュートリアルでは、デジタル署名、EIP-1271の背景、および[Safe](https://safe.global/) (旧Gnosis Safe) が使用するEIP-1271の特定の実装の概要を説明します。 このチュートリアル全体を通して、EIP-1271を自身のコントラクトへ実装するための出発点として使うことができます。 ## 「署名」とは何か @@ -23,8 +19,8 @@ published: 2023-01-12 具体的には、デジタル署名は次のようなものです。 1. メッセージ: 「イーサリアムウォレットを使用してこのWebサイトにログインしたい」 -2. 署名者: 私のアドレスは、`0x000…`です。 -3. 証拠: ここに、私「`0x000…`」がこのメッセージ全体を実際に作成したという証拠 (これは通常暗号化される) があります。 +2. 署名者: 私のアドレスは`0x000…`です。 +3. 証拠: 私(`0x000…`)が、このメッセージ全体を実際に作成したという証拠がこちらです(これは通常、暗号化されたものです)。 デジタル署名には、「メッセージ」と「署名」の両方が含まれていることが重要です。 @@ -36,11 +32,11 @@ published: 2023-01-12 イーサリアムベースのブロックチェーン上でデジタル署名を作成するには、通常、他の人が知らない秘密鍵が必要になります。 秘密鍵により、あなたの署名が自身のものになります (誰も秘密鍵を知らない限り、同じ署名を作成できません) 。 -イーサリアムアカウント (すなわち、外部所有アカウント/EOA) には、アカウントに紐づいた秘密鍵があります。この秘密鍵が、通常、ウェブサイトやDappで求められる署名に使われます (例:「イーサリアムでログイン」) 。 +イーサリアムアカウント(i.e.、外部所有アカウント/EOA)には秘密鍵が関連付けられています。この秘密鍵は、ウェブサイトやdappが署名(例: 「イーサリアムでログイン」)を求める際に通常使用されるものです。 -アプリでは、ethers.jsのようなサードパーティライブラリを使って[秘密鍵を知らせることなく](https://en.wikipedia.org/wiki/Public-key_cryptography)、作成した[署名を検証でき](https://docs.alchemy.com/docs/how-to-verify-a-message-signature-on-ethereum)、署名を作成したのは_あなた_であると確信することができます。 +アプリは、ethers.jsのようなサードパーティのライブラリを使い、あなたが作成した署名を[検証](https://www.alchemy.com/docs/how-to-verify-a-message-signature-on-ethereum)し、[あなたの秘密鍵を知ることなく](https://en.wikipedia.org/wiki/Public-key_cryptography)、_あなた_がその署名を作成したと確信できます。 -> 事実として、EOA (外部所有アカウント) のデジタル署名は公開鍵暗号技術を使っているため、**オフチェーン**で生成および検証できます。 これは、ガスレスDAO投票の仕組みです。オンチェーンで投票を送信する代わりに、暗号化ライブラリを使用してオフチェーンでデジタル署名を作成し、検証可能です。 +> 事実、EOAのデジタル署名は公開鍵暗号を使用するため、**オフチェーン**で生成・検証できます。 これは、ガスレスDAO投票の仕組みです。オンチェーンで投票を送信する代わりに、暗号ライブラリを使用してオフチェーンでデジタル署名を作成し、検証できます。 EOAの各アカウントには秘密鍵がありますが、スマートコントラクトアカウントにはいかなる種類の共通鍵も秘密鍵もありません (そのため、「イーサリアムでログイン」などはスマートコントラクトのアカウントにおいては、ネイティブで機能しません) 。 @@ -50,17 +46,17 @@ EIP-1271は、スマートコントラクトで署名に使う「秘密鍵」が スマートコントラクトには、メッセージの署名に使用する秘密鍵がありません。 署名が本物かどうかをどのようにして判断するのでしょうか? -アイデアの一つとして、署名が本物かどうかをスマートコントラクトに_尋ねる_ことがあります。 +そこで考えられるのが、署名が本物かどうかをスマートコントラクトに直接_問い合わせて_みることです。 EIP-1271が行っているのは、与えられた署名が有効かどうかをスマートコントラクトに「尋ねる」アイデアを標準化です。 -EIP-1271を実装するコントラクトでは、メッセージと署名を受け取る`isValidSignature`という関数が必要になります。 次にコントラクトは、いくつかの検証ロジックを実行し (仕様書では、具体的なことを何も強制していません) 、署名が有効かどうかを示す値を返します。 +EIP-1271を実装するコントラクトは、メッセージと署名を受け取る`isValidSignature`という名前の関数を持つ必要があります。 次にコントラクトは、いくつかの検証ロジックを実行し (仕様書では、具体的なことを何も強制していません) 、署名が有効かどうかを示す値を返します。 -`isValidSignature`が有効な結果を返せば、そのコントラクトが「はい、この署名とメッセージを承認します!」と言っているのと、ほぼ同じこととなります。 +`isValidSignature`が有効な結果を返した場合、それはコントラクトが「はい、この署名とメッセージを承認します!」と言っているのとほぼ同じです。 ### インターフェース -EIP-1271仕様の正確なインターフェイスは、次のようになります。`_hash`パラメータについては、後ほど説明しますので、ここでは検証されるメッセージであると考えてください。 +以下がEIP-1271仕様の正確なインターフェイスです(`_hash`パラメータについては後述しますが、ここでは検証対象のメッセージだと考えてください): ```jsx pragma solidity ^0.5.0; @@ -71,13 +67,13 @@ contract ERC1271 { bytes4 constant internal MAGICVALUE = 0x1626ba7e; /** - * @dev Should return whether the signature provided is valid for the provided hash - * @param _hash Hash of the data to be signed - * @param _signature Signature byte array associated with _hash + * @dev 提供されたハッシュに対して、提供された署名が有効かどうかを返すべきです + * @param _hash 署名されるデータのハッシュ + * @param _signature _hashに関連付けられた署名バイト配列 * - * MUST return the bytes4 magic value 0x1626ba7e when function passes. - * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5) - * MUST allow external calls + * 関数が成功した場合、bytes4のマジック値0x1626ba7eを返さなければなりません(MUST)。 + * 状態を変更してはいけません(MUST NOT)。(solc < 0.5の場合はSTATICCALLを使用、solc > 0.5の場合はview修飾子を使用) + * 外部呼び出しを許可しなければなりません(MUST)。 */ function isValidSignature( bytes32 _hash, @@ -90,28 +86,28 @@ contract ERC1271 { ## EIP-1271実装の例: Safe -さまざまな方法で`isValidSignature`をコントラクトに実装することができます。仕様では、正確な実装についてあまり触れていません。 +コントラクトはさまざまな方法で`isValidSignature`を実装できます。仕様では、正確な実装についてはあまり言及されていません。 EIP-1271を実装した有名なコントラクトの1つにSafe (旧Gnosis Safe) があります。 -Safeのコードでは、署名の作成および検証において、次の[2つの方法](https://ethereum.stackexchange.com/questions/122635/signing-messages-as-a-gnosis-safe-eip1271-support)を取れるように`isValidSignature`が[実装されています](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol)。 +Safeのコードでは、`isValidSignature`は、署名が[2つの方法](https://ethereum.stackexchange.com/questions/122635/signing-messages-as-a-gnosis-safe-eip1271-support)で作成・検証できるように[実装](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol)されています: 1. オンチェーンメッセージ - 1. 作成: Safeの所有者は、メッセージに「署名」するための新しいSafeのトランザクションを作成し、メッセージをデータとしてトランザクションに渡します。 十分な数の所有者がトランザクションに署名してマルチシグのしきい値に達すると、トランザクションがブロードキャストされて実行されます。 このトランザクションでは、「承認された」メッセージのリストに追加するSafe関数が呼び出されます。 - 2. 検証: Safeコントラクトでは、`isValidSignature`を呼び出し、検証するメッセージをメッセージパラメータとして、[署名パラメータを空の値](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol#L32) (すなわち、`0x`) にして渡します。 Safeは、署名パラメータが空であることを確認し、署名を暗号的に検証する代わりに、メッセージが「承認された」メッセージのリストに存在するかのみを確認します。 -2. オフチェーンメッセージ - 1. 作成: Safeの所有者は、オフチェーンでメッセージを作成し、他のSafeの所有者に、マルチシグの承認のしきい値を超えるのに十分な署名が得られるまでメッセージに対して個別に署名してもらいます。 - 2. 検証: `isValidSignature`を呼び出します。 メッセージパラメータの中に、検証するメッセージを渡します。 署名パラメーターでは、Safe所有者各自の署名をすべて連結して、一斉に渡します。 Safeでは、しきい値を満たすのに十分な署名があること**および**各署名が有効であることをチェックします。 有効であれば、署名の検証が成功したことを示す値を返します。 + 1. 作成: Safeの所有者は、メッセージに「署名」するための新しいSafeのトランザクションを作成し、メッセージをデータとしてトランザクションに渡します。 十分な数の所有者がトランザクションに署名してマルチシグのしきい値に達すると、トランザクションがブロードキャストされて実行されます。 トランザクションには、メッセージを「承認済み」メッセージのリストに追加する (`signMessage(bytes calldata _data)`) というSafe関数があります。 + 2. 検証: Safeコントラクトで`isValidSignature`を呼び出し、検証するメッセージをメッセージパラメータとして、[署名パラメータには空の値](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol#L32) (i.e.、`0x`) を渡します。 Safeは、署名パラメータが空であることを確認し、署名を暗号的に検証する代わりに、メッセージが「承認された」メッセージのリストに存在するかのみを確認します。 +2. オフチェーンメッセージ: + 1. 作成: Safeの所有者はオフチェーンでメッセージを作成し、マルチシグの承認しきい値を超えるのに十分な署名が集まるまで、他のSafe所有者に個別にメッセージへ署名してもらいます。 + 2. 検証: `isValidSignature`を呼び出します。 メッセージパラメータの中に、検証するメッセージを渡します。 署名パラメーターでは、Safe所有者各自の署名をすべて連結して、一斉に渡します。 Safeは、しきい値を満たすのに十分な署名があること、**かつ**各署名が有効であることをチェックします。 有効であれば、署名の検証が成功したことを示す値を返します。 -## `_hash`パラメータは、正確には何なのか およびメッセージ全体を渡さない理由 +## `_hash`パラメータとは正確には何ですか? およびメッセージ全体を渡さない理由 -あなたは、[EIP-1271インターフェース](https://eips.ethereum.org/EIPS/eip-1271)の`isValidSignature`関数がメッセージ自体を取らない代わりに `_hash`パラメーターを取ることに気付いたかもしれません。 これは、任意の長さのメッセージ全体を `isValidSignature`に渡す代わりに、そのメッセージの32バイトのハッシュ (通常はkeccak256) を渡しています。 +[EIP-1271インターフェイス](https://eips.ethereum.org/EIPS/eip-1271)の`isValidSignature`関数が、メッセージ自体ではなく`_hash`パラメータを受け取ることに気づいたかもしれません。 これは、任意の長さのメッセージ全体を`isValidSignature`に渡す代わりに、メッセージの32バイトのハッシュ(通常はkeccak256)を渡すことを意味します。 -コールデータの各バイト、すなわち、スマートコントラクトへ渡される関数パラメータのデータには、[16ガスのコストがかかります](https://eips.ethereum.org/EIPS/eip-2028)(0バイトの場合は4ガス) 。 そのため、メッセージが長ければガスを大幅に節約できます。 +コールデータの各バイト、すなわちスマートコントラクト関数に渡される関数パラメータデータは、[16ガス(ゼロバイトの場合は4ガス)のコストがかかる](https://eips.ethereum.org/EIPS/eip-2028)ため、メッセージが長い場合は多くのガスを節約できます。 ### 以前のEIP-1271仕様 -すでに出回っているEIP-1271仕様に、最初のパラメータでパラメータ名が`message`で、`bytes`型 (固定長の`bytes32`ではなく、任意の長さ) の`isValidSignature`関数があります。 これは、[古いバージョン](https://github.com/safe-global/safe-contracts/issues/391#issuecomment-1075427206)のEIP-1271標準です。 +実際には、最初のパラメータが`bytes`型(固定長の`bytes32`ではなく任意長)で、パラメータ名が`message`である`isValidSignature`関数を持つEIP-1271仕様が存在します。 これは、EIP-1271標準の[古いバージョン](https://github.com/safe-global/safe-contracts/issues/391#issuecomment-1075427206)です。 ## EIP-1271を各自のコントラクトにどのように実装すべきか @@ -124,4 +120,4 @@ Safeのコードでは、署名の作成および検証において、次の[2 ## まとめ -[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271)は、スマートコントラクトによる署名の検証を可能する標準で、多くの用途があります。 EIP-1271により、スマートコントラクトをEOAのように動作させることができます。例えば、「イーサリアムでログイン」をスマートコントラクトで動作させる方法を提供できます。また、さまざまな方法で実装できます (考慮するのに興味深い重要な実装としてSafeがあります) 。 +[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271)は、スマートコントラクトが署名を検証できるようにする汎用性の高い標準です。 EIP-1271により、スマートコントラクトをEOAのように動作させることができます。例えば、「イーサリアムでログイン」をスマートコントラクトで動作させる方法を提供できます。また、さまざまな方法で実装できます (考慮するのに興味深い重要な実装としてSafeがあります) 。 diff --git a/public/content/translations/ja/developers/tutorials/erc-721-vyper-annotated-code/index.md b/public/content/translations/ja/developers/tutorials/erc-721-vyper-annotated-code/index.md index 8a073d19f70..f33129b9ca4 100644 --- a/public/content/translations/ja/developers/tutorials/erc-721-vyper-annotated-code/index.md +++ b/public/content/translations/ja/developers/tutorials/erc-721-vyper-annotated-code/index.md @@ -1,31 +1,33 @@ --- -title: "Vyperで作成したERC-721コントラクトの紹介" -description: 中村龍矢氏のERC-721コントラクトとその仕組み +title: "Vyper ERC-721コントラクトウォークスルー" +description: "中村龍矢氏のERC-721コントラクトとその仕組み" author: Ori Pomerantz lang: ja -tags: - - "Vyper" - - "ERC-721" - - "Python" +tags: [ "vyper", "ERC-721", "python" ] skill: beginner published: 2021-04-01 --- ## はじめに {#introduction} -[ERC-721](/developers/docs/standards/tokens/erc-721/)標準規格は、非代替性トークン(NFT)の所有権を保持するために使用されます。 [ERC-20](/developers/docs/standards/tokens/erc-20/)トークンは代替性を持つため、コモディティとして機能します。 一方、ERC-721トークンは、様々な[猫のイラスト](https://www.cryptokitties.co/)や不動産物件の各部分に対する権原など、同じ種類ではあるが異なるアセットを対象として設計されたものです。 +[ERC-721](/developers/docs/standards/tokens/erc-721/)標準は、非代替性トークン(NFT)の所有権を保持するために使用されます。 +[ERC-20](/developers/docs/standards/tokens/erc-20/)トークンは、個々のトークンに違いがないため、コモディティのように振る舞います。 +対照的に、ERC-721トークンは、さまざまな猫の +漫画や、異なる不動産の所有権など、似ているが同一ではない資産のために設計されています。 -本記事では、[中村龍矢氏のERC-721コントラクト](https://github.com/vyperlang/vyper/blob/master/examples/tokens/ERC721.vy)を分析します。 このコントラクトは、[Vyper](https://vyper.readthedocs.io/en/latest/index.html)で作成されました。Vyperは、Pythonに似たコントラクト開発言語であり、Solidityと比較するとセキュリティが低いコードを作成しにくくなっています。 +この記事では、[中村龍矢氏のERC-721コントラクト](https://github.com/vyperlang/vyper/blob/master/examples/tokens/ERC721.vy)を分析します。 +このコントラクトは、Pythonに似たコントラクト言語である[Vyper](https://vyper.readthedocs.io/en/latest/index.html)で書かれています。Vyperは、Solidityよりも安全でないコードを書くのが難しくなるように設計されています。 -## コントラクトの内容 {#contract} +## コントラクト {#contract} ```python -# @dev Implementation of ERC-721 non-fungible token standard. +# @dev ERC-721非代替性トークン標準の実装。 # @author Ryuya Nakamura (@nrryuya) -# Modified from: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy +# 変更元: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy ``` -Pythonの場合と同様に、Vyperのコメントはハッシュ(`@/code>)で開始し、行末まで続けます。 @`を含むコメントは、 [NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html) を通じて人間が読みやすいドキュメントに変換されます。 +Vyperのコメントは、Pythonと同様に、ハッシュ記号(`#`)で始まり、行末まで続きます。 `@<キーワード>`を含むコメントは、[NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html)によって、人間が読める +ドキュメントを生成するために使用されます。 ```python from vyper.interfaces import ERC721 @@ -33,176 +35,206 @@ from vyper.interfaces import ERC721 implements: ERC721 ``` -ERC-721インターフェイスは、Vyper言語に組み込まれています。 [コードの定義はこちら](https://github.com/vyperlang/vyper/blob/master/vyper/builtin_interfaces/ERC721.py)をご覧ください。 インターフェイスの定義は、VyperではなくPythonで書かれています。これは、インターフェイスがブロックチェーン内だけでなく、Pythonで書かれた外部クライアントからブロックチェーンにトランザクションを送る際にも使われるからです。 +ERC-721インターフェイスはVyper言語に組み込まれています。 +[コードの定義はこちらで確認できます](https://github.com/vyperlang/vyper/blob/master/vyper/builtin_interfaces/ERC721.py)。 +インターフェイスの定義はVyperではなくPythonで書かれています。なぜなら、インターフェイスはブロックチェーン内で使用されるだけでなく、Pythonで書かれている可能性のある外部クライアントからブロックチェーンにトランザクションを送信する際にも使用されるからです。 -第1行はインターフェイスをインポートし、第2行はここで実装することを指示します。 +最初の行でインターフェイスをインポートし、2行目でここで実装することを指定しています。 -### ERC-721のReceiverインターフェイス {#receiver-interface} +### ERC721Receiverインターフェイス {#receiver-interface} ```python -# Interface for the contract called by safeTransferFrom() +# safeTransferFrom()によって呼び出されるコントラクトのインターフェイス interface ERC721Receiver: def onERC721Received( ``` -ERC-721は、2つの送信方法をサポートしています。 +ERC-721は2種類の送金をサポートしています。 -- `transferFrom`は、送信者が任意の送信先アドレスを指定するもので、送信の責任は送信者が担います。 このため無効なアドレスにも送信できますが、その場合はNFTは永遠に失われます。 -- `safeTransferFrom`は、送信先アドレスがコントラクトかどうかを確認します。 送信先アドレスがコントラクトであることを確認すると、ERC-721コントラクトが受信側のコントラクトにNFTを受け取りたいかどうかを尋ねます。 +- `transferFrom`: 送信者が任意の宛先アドレスを指定でき、 + 送金の責任は送信者にあります。 これは、無効なアドレスに送金できることを意味し、その場合 + NFTは永久に失われます。 +- `safeTransferFrom`: 宛先アドレスがコントラクトであるかどうかをチェックします。 もしそうであれば、ERC-721コントラクトは + 受信側のコントラクトにNFTを受け取りたいかどうかを尋ねます。 -`safeTransferFrom`リクエストに応答するには、受信側コントラクトが`ERC721Receiver`を実装していなければなりません。 +`safeTransferFrom`リクエストに応答するには、受信側コントラクトが`ERC721Receiver`を実装する必要があります。 ```python _operator: address, _from: address, ``` -`_from`のアドレスは、トークンの現在の所有者のアドレスです。 `_operator`のアドレスは、 送信を要求したアドレスです(アローワンスにより、これらの 2 つが同一でない場合があります)。 +`_from`アドレスは、トークンの現在の所有者です。 `_operator`アドレスは、 +送金をリクエストしたアドレスです(アローワンスのため、この2つは同じでない場合があります)。 ```python _tokenId: uint256, ``` -ERC-721トークンのIDは、256ビットです。 通常トークンIDは、トークンの内容をハッシュ化して生成されます。 +ERC-721トークンIDは256ビットです。 通常、トークンIDはトークンが表すものの説明を +ハッシュ化して作成されます。 ```python _data: Bytes[1024] ``` -このリクエストには、最大1024バイトのユーザーデータを含めることができます。 +リクエストには最大1024バイトのユーザーデータを含めることができます。 ```python ) -> bytes32: view ``` -コントラクトが誤送信を受け入れるケースを防止するため、戻り値はブール値ではなく、256ビットの特定の値になっています。 +コントラクトが誤って送金を受け入れるケースを防ぐため、戻り値はブール値ではなく、 +特定の価を持つ256ビットです。 -この関数は`view`関数であるため、ブロックチェーンの状態を読み取るだけで、変更はできません。 +この関数は`view`であり、ブロックチェーンの状態を読み取ることはできますが、変更することはできません。 ### イベント {#events} -[イベント](https://media.consensys.net/technical-introduction-to-events-and-logs-in-ethereum-a074d65dd61e)は、ブロックチェーン外のユーザーおよびサーバーにイベントを通知するために発行されます。 イベントの内容は、ブロックチェーン上のコントラクトでは利用できないことに注意してください。 +[イベント](https://media.consensys.net/technical-introduction-to-events-and-logs-in-ethereum-a074d65dd61e)は、ブロックチェーン外のユーザーやサーバーにイベントを通知するために発行されます。 イベントの内容は、ブロックチェーン上のコントラクトからは +利用できないことに注意してください。 ```python -# @dev Emits when ownership of any NFT changes by any mechanism. This event emits when NFTs are -# created (`from` == 0) and destroyed (`to` == 0). Exception: during contract creation, any -# number of NFTs may be created and assigned without emitting Transfer. At the time of any -# transfer, the approved address for that NFT (if any) is reset to none. -# @param _from Sender of NFT (if address is zero address it indicates token creation). -# @param _to Receiver of NFT (if address is zero address it indicates token destruction). -# @param _tokenId The NFT that got transferred. +# @dev 何らかのメカニズムによってNFTの所有権が変更されたときに発行されます。このイベントは、NFTが作成 +# (`from` == 0)および破棄(`to` == 0)されたときに発行されます。例外:コントラクト作成中、任意の +# 数のNFTがTransferを発行せずに作成および割り当てられる場合があります。任意の +# 送金時、そのNFTの承認済みアドレス(もしあれば)はnoneにリセットされます。 +# @param _from NFTの送信者(アドレスがゼロアドレスの場合、トークンの作成を示します)。 +# @param _to NFTの受信者(アドレスがゼロアドレスの場合、トークンの破棄を示します)。 +# @param _tokenId 送金されたNFT。 event Transfer: sender: indexed(address) receiver: indexed(address) tokenId: indexed(uint256) ``` -これは、金額の代わりに`tokenId`を伝える点を除くとERC-20のTransferイベントと類似しています。 アドレスを所有しないユーザーはいないので、慣習的に、このイベントを使ってトークンの発行と破壊を報告しています。 +これは、金額の代わりに`tokenId`を報告する点を除けば、ERC-20のTransferイベントと似ています。 +ゼロアドレスを所有する者はいないため、慣例的にトークンの作成と破棄を報告するために使用します。 ```python -# @dev This emits when the approved address for an NFT is changed or reaffirmed. The zero -# address indicates there is no approved address. When a Transfer event emits, this also -# indicates that the approved address for that NFT (if any) is reset to none. -# @param _owner Owner of NFT. -# @param _approved Address that we are approving. -# @param _tokenId NFT which we are approving. +# @dev NFTの承認済みアドレスが変更または再確認されたときに発行されます。ゼロ +# アドレスは、承認済みアドレスがないことを示します。Transferイベントが発行されると、これも +# そのNFTの承認済みアドレス(もしあれば)がnoneにリセットされることを示します。 +# @param _owner NFTの所有者。 +# @param _approved 承認するアドレス。 +# @param _tokenId 承認するNFT。 event Approval: owner: indexed(address) approved: indexed(address) tokenId: indexed(uint256) ``` -ERC-721の承認は、ERC-20におけるアローワンスに類似した機能です。 特定のアドレスから、特定のトークンを送信することが可能です。 これにより、コントラクトがトークンを受け入れる際に対応するためのメカニズムが実現します。 コントラクトは、イベントをリッスンできないため、トークンが送信されてもそれを「把握」できないのです。 つまり、所有者はまず承認を提出してから、コントラクトに対して「私はあなたがトークンXを送信することを承認しましたので、〜を実行してください」というリクエストを送信するわけです。 +ERC-721の承認はERC-20のアローワンスに似ています。 特定のアドレスが特定の +トークンを送金することが許可されます。 これにより、コントラクトがトークンを受け入れる際に応答するためのメカニズムが提供されます。 コントラクトは +イベントをリッスンできないため、トークンを送金しただけでは、コントラクトはそのことを「知り」ません。 この方法では、 +所有者はまず承認を送信し、次にコントラクトに「トークン +Xの送金を承認しました。どうぞ...」というリクエストを送信します。 -これは、ERC-721標準をERC-20と類似の標準にするための設計上の選択によるものです。 ERC-721トークンは非代替性であるため、コントラクトは、トークンの所有権を調べて特定のトークンを受け取ったかどうかを確認できます。 +これは、ERC-721標準をERC-20標準と類似させるための設計上の選択です。 ERC-721トークンは非代替性であるため、 +コントラクトはトークンの所有権を見ることで、特定のトークンを受け取ったことを +識別することもできます。 ```python -# @dev This emits when an operator is enabled or disabled for an owner. The operator can manage -# all NFTs of the owner. -# @param _owner Owner of NFT. -# @param _operator Address to which we are setting operator rights. -# @param _approved Status of operator rights(true if operator rights are given and false if -# revoked). +# @dev 所有者のオペレーターが有効または無効になったときに発行されます。オペレーターは +# 所有者のすべてのNFTを管理できます。 +# @param _owner NFTの所有者。 +# @param _operator オペレーター権限を設定するアドレス。 +# @param _approved オペレーター権限のステータス(権限が付与された場合はtrue、 +# 取り消された場合はfalse)。 event ApprovalForAll: owner: indexed(address) operator: indexed(address) approved: bool ``` -場合によっては、委任状の機能のように、アカウントが所有する特定タイプのトークン(特定のコントラクトが管理するトークン)をすべて管理できる_オペレーター_ を持つことが便利な場合もあります。 例えば、私がコントラクトに対して、6カ月にわたりそのコントラクトとのやりとりを行っていないかどうかを確認する権限を与え、もしやりとりがない場合には、私のアセットを相続人に分配するように設定したいとします(相続人の一人が遺産分配を求めても、トランザクションによって呼び出されない限りコントラクトは何も実行できません)。 ERC-20では、継承コントラクトに対して大きなアローワンスを設定できますが、ERC-721の場合、非代替性を持つために不可能です。 そこで、オペレーターが同等の機能を提供します。 +委任状のように、特定タイプの(特定のコントラクトによって管理される)アカウントの全トークンを管理できる_オペレーター_がいると便利な場合があります。 例えば、私が6ヶ月間連絡を取っていないかどうかをチェックし、もしそうであれば私の資産を相続人に分配する権限をコントラクトに与えたいかもしれません(相続人の一人が要求した場合、コントラクトはトランザクションによって呼び出されない限り何もできません)。 ERC-20では、継承コントラクトに高いアローワンスを与えることができますが、 +ERC-721ではトークンが非代替性であるため、これは機能しません。 これが同等の機能です。 -`approved`の値を通じて、当該イベントが承認を求めるものか、承認を取り消したものかを確認することができます。 +`approved`の値は、イベントが承認のためのものか、承認の取り消しのためのものかを示します。 ### 状態変数 {#state-vars} -状態変数には、トークンの現在の状態、つまりどのトークンが存在し、誰が所有するかの情報が含まれます。 大部分の状態変数は`HashMap`オブジェクトであり、[2つのタイプ間に存在する一方向のマッピング](https://vyper.readthedocs.io/en/latest/types.html#mappings)です。 +これらの変数には、トークンの現在の状態、つまりどのトークンが利用可能で誰が所有しているかの情報が含まれています。 これらのほとんどは`HashMap`オブジェクトであり、[2つの型間に存在する一方向のマッピング](https://vyper.readthedocs.io/en/latest/types.html#mappings)です。 ```python -# @dev Mapping from NFT ID to the address that owns it. +# @dev NFT IDからそれを所有するアドレスへのマッピング。 idToOwner: HashMap[uint256, address] -# @dev Mapping from NFT ID to approved address. +# @dev NFT IDから承認済みアドレスへのマッピング。 idToApprovals: HashMap[uint256, address] ``` -イーサリアムにおいて、ユーザーおよびコントラクトのIDは160ビットのアドレスで表示されます。 これら2つの変数は、トークンIDから、所有者および送信することを承認された者(それぞれにつき最大1つの変数)がマッピングされます。 イーサリアムでは、未初期化データは常にゼロであるため、所有者または承認済み送信者が存在しなければ、トークンの値はゼロになります。 +イーサリアムでは、ユーザーとコントラクトのIDは160ビットのアドレスで表されます。 これら2つの変数は、トークンIDから、 +その所有者と送金を承認された者(それぞれ最大1つ)にマッピングします。 イーサリアムでは、未初期化データは常にゼロであるため、 +所有者または承認済み送金者がいない場合、そのトークンの値はゼロになります。 ```python -# @dev Mapping from owner address to count of his tokens. +# @dev 所有者アドレスからそのトークン数へのマッピング。 ownerToNFTokenCount: HashMap[address, uint256] ``` -この変数は、各所有者ごとのトークン数を保持します。 所有者とトークンはマッピングされないため、特定の所有者が持つトークンを特定するには、ブロックチェーンのイベント履歴を過去に遡って確認し、当該の`Transfer`イベントを確認するしかありません。 この変数を使用して、すべてのNFTでいつ取得したか知ることができ、それ以上調べる必要がありません。 +この変数は、各所有者のトークン数を保持します。 所有者からトークンへのマッピングはないため、 +特定の所有者が所有するトークンを識別する唯一の方法は、ブロックチェーンのイベント履歴を遡り、 +適切な`Transfer`イベントを見ることです。 この変数を使用して、すべてのNFTをいつ取得したかを知ることができ、 +それ以上調べる必要がありません。 -ただし、このアルゴリズムはユーザーインターフェイスおよび外部サーバーのみを対象とする点に注意してください。 ブロックチェーン上で実行されるコード自体は、過去イベントを読み込むことができません。 +このアルゴリズムは、ユーザーインターフェイスと外部サーバーに対してのみ機能することに注意してください。 ブロックチェーン上で実行されるコード +自体は、過去のイベントを読み取ることはできません。 ```python -# @dev Mapping from owner address to mapping of operator addresses. +# @dev 所有者アドレスからオペレーターアドレスのマッピングへのマッピング。 ownerToOperators: HashMap[address, HashMap[address, bool]] ``` -1つのアカウントには、複数のオペレーターを含めることができます。 しかし、単純な`HashMap`では、各キーごとに単一の値を持つため、複数のオペレーターを追跡するのに十分ではありません。 その代わりに、`HashMap[address, bool]`を値として使用します。 デフォルトでは、各アドレスの値は`False`になっていますが、これはオペレーターでないことを示します。 オペレーターに設定するには、値を`True`に変更してください。 +1つのアカウントに複数のオペレーターがいる場合があります。 単純な`HashMap`では、各キーが単一の値につながるため、 +それらを追跡するには不十分です。 代わりに、値として +`HashMap[address, bool]`を使用できます。 デフォルトでは、各アドレスの値は`False`であり、 +オペレーターではないことを意味します。 必要に応じて値を`True`に設定できます。 ```python -# @dev Address of minter, who can mint a token +# @dev トークンをミントできるミンターのアドレス minter: address ``` -何らかの方法で、新規トークンを作成する必要があります。 このコントラクトでは、新規トークンを作成する権限を持つ唯一のエンティティは`minter`です。 使用事例がゲームなどの場合は、これで十分でしょう。 その他の目的の場合、より複雑なビジネスロジックを作成する必要があるでしょう。 +新しいトークンは何らかの方法で作成されなければなりません。 このコントラクトには、それを許可された単一のエンティティ、 +`minter`が存在します。 例えば、ゲームにとってはこれで十分でしょう。 他の目的のためには、 +より複雑なビジネスロジックを作成する必要があるかもしれません。 ```python -# @dev Mapping of interface id to bool about whether or not it's supported +# @dev インターフェイスIDから、それがサポートされているかどうかを示すbool値へのマッピング supportedInterfaces: HashMap[bytes32, bool] -# @dev ERC165 interface ID of ERC165 +# @dev ERC165のERC165インターフェイスID ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7 -# @dev ERC165 interface ID of ERC721 +# @dev ERC721のERC165インターフェイスID ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cd ``` -[ERC-165](https://eips.ethereum.org/EIPS/eip-165)では、コントラクトにおいてアプリケーションがどのようなやりとりを行うか、およびどのERC規格に準拠するのかを開示するメカニズムの仕様が規定されています。 このコントラクトの場合、ERC-165とERC-721に準拠しています。 +[ERC-165](https://eips.ethereum.org/EIPS/eip-165)は、コントラクトがアプリケーションと +通信する方法、つまりどのERCに準拠しているかを公開するためのメカニズムを指定します。 この場合、コントラクトはERC-165とERC-721に準拠しています。 ### 関数 {#functions} -以下は、実際にERC-721で実装できる関数です。 +これらは、実際にERC-721を実装する関数です。 -#### コンストラクタ関数 {#constructor} +#### コンストラクタ {#constructor} ```python @external def __init__(): ``` -Pythonの場合と同様に、Vyperでもコンストラクタ関数は`__init__`で呼び出します。 +Vyperでは、Pythonと同様に、コンストラクタ関数は`__init__`と呼ばれます。 ```python """ - @dev Contract constructor. + @dev コントラクトコンストラクタ。 """ ``` -PythonおよびVyperでは、複数行の文字列(文頭および文末を`"""`に付ける)を指定し、この文字列を利用しないことでもコメントを作成できます。 これらのコメントには、 [NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html)を含めることも可能です。 +PythonやVyperでは、複数行の文字列(`"""`で始まり終わる)を指定し、それを一切使用しないことでコメントを作成することもできます。 これらのコメントには、 +[NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html)も含まれる場合があります。 ```python self.supportedInterfaces[ERC165_INTERFACE_ID] = True @@ -210,57 +242,64 @@ PythonおよびVyperでは、複数行の文字列(文頭および文末を`"" self.minter = msg.sender ``` -状態変数にアクセスするには、`self.`を使用します(これも、Pythonと同様です)。 +状態変数にアクセスするには`self.<変数名>`を使用します(これもPythonと同じです)。 #### ビュー関数 {#views} -ビュー関数は、ブロックチェーンの状態を変更しない関数であり、外部から呼び出されると無料で実行されます。 一方、コントラクトがビュー関数を呼び出す場合は全ノードで実行する必要があるため、ガス代が発生します。 +これらはブロックチェーンの状態を変更しない関数であり、したがって外部から +呼び出された場合は無料で実行できます。 ビュー関数がコントラクトによって呼び出された場合でも、 +すべてのノードで実行する必要があるため、ガス代がかかります。 ```python @view @external ``` -アットマーク (`@`)で始まる関数定義の前に置かれたこれらのキーワードを、_デコレータ_と呼びます。 デコレータは、関数を呼び出せる状況を特定します。 +関数定義の前にある、アットマーク(`@`)で始まるこれらのキーワードは、_デコレータ_と呼ばれます。 これらは +関数を呼び出すことができる状況を指定します。 -- `@view`は、関数がビューであることを規定します。 -- `@external`は、関数がトランザクションおよび他のコントラクトで呼び出し可能であることを規定します。 +- `@view`は、この関数がビューであることを指定します。 +- `@external`は、この特定の関数がトランザクションや他のコントラクトによって呼び出されることを指定します。 ```python def supportsInterface(_interfaceID: bytes32) -> bool: ``` -Pythonとは異なり、Vyperは[静的型付け言語](https://wikipedia.org/wiki/Type_system#Static_type_checking)です。 つまり、[データ型](https://vyper.readthedocs.io/en/latest/types.html)を指定しないと変数、つまり関数のパラメータを宣言できません。 この場合では、入力パラメータは、256ビット値の`bytes32`です (256ビットは、[イーサリアム仮想マシン](/developers/docs/evm/)のネイティブワードサイズです) 。 出力は、ブール値です。 慣習により、関数におけるパラメータの名称はアンダースコア (`_`) で開始します。 +Pythonとは対照的に、Vyperは[静的型付け言語](https://wikipedia.org/wiki/Type_system#Static_type_checking)です。 +[データ型](https://vyper.readthedocs.io/en/latest/types.html)を特定せずに、変数や関数パラメータを宣言することはできません。 この場合、入力パラメータは`bytes32`、つまり256ビット値です +(256ビットは[イーサリアム仮想マシン](/developers/docs/evm/)のネイティブワードサイズです)。 出力はブール +値です。 慣例により、関数パラメータの名前はアンダースコア(`_`)で始まります。 ```python """ - @dev Interface identification is specified in ERC-165. - @param _interfaceID Id of the interface + @dev インターフェイスの識別はERC-165で指定されています。 + @param _interfaceID インターフェイスのID """ return self.supportedInterfaces[_interfaceID] ``` -コンストラクタ(`__init__`)で設定した`self.supportedInterfaces`ハッシュマップが値を返します。 +コンストラクタ(`__init__`)で設定された`self.supportedInterfaces` HashMapから値を返します。 ```python -### VIEW FUNCTIONS ### +### ビュー関数 ### + ``` -これらは、ユーザーや他のコントラクトが利用できるトークンについての情報を提供するビュー関数です。 +これらは、トークンに関する情報をユーザーや他のコントラクトが利用できるようにするビュー関数です。 ```python @view @external def balanceOf(_owner: address) -> uint256: """ - @dev Returns the number of NFTs owned by `_owner`. - Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid. - @param _owner Address for whom to query the balance. + @dev `_owner`が所有するNFTの数を返します。 + `_owner`がゼロアドレスの場合はスローします。ゼロアドレスに割り当てられたNFTは無効と見なされます。 + @param _owner 残高を照会するアドレス。 """ assert _owner != ZERO_ADDRESS ``` -この行は、`_owner`値がゼロでないことを[宣言](https://vyper.readthedocs.io/en/latest/statements.html#assert)します。 値がゼロの場合、エラーが発生し操作が元に戻されます。 +この行は、`_owner`がゼロでないことを[アサート](https://vyper.readthedocs.io/en/latest/statements.html#assert)します。 もしゼロであれば、エラーが発生し、操作は取り消されます。 ```python return self.ownerToNFTokenCount[_owner] @@ -269,70 +308,74 @@ def balanceOf(_owner: address) -> uint256: @external def ownerOf(_tokenId: uint256) -> address: """ - @dev Returns the address of the owner of the NFT. - Throws if `_tokenId` is not a valid NFT. - @param _tokenId The identifier for an NFT. + @dev NFTの所有者のアドレスを返します。 + `_tokenId`が有効なNFTでない場合はスローします。 + @param _tokenId NFTの識別子。 """ owner: address = self.idToOwner[_tokenId] - # Throws if `_tokenId` is not a valid NFT + # `_tokenId`が有効なNFTでない場合はスローします assert owner != ZERO_ADDRESS return owner ``` -イーサリアム仮想マシン(EVM)では、値が格納されていないストレージはゼロになります。 `_tokenId`にトークンが含まれない場合、`self.idToOwner[_tokenId]`の値はゼロになります。 その場合、関数は元に戻されます。 +イーサリアム仮想マシン(EVM)では、値が格納されていないストレージはゼロになります。 +`_tokenId`にトークンがない場合、`self.idToOwner[_tokenId]`の値はゼロになります。 その場合、 +関数は元に戻されます。 ```python @view @external def getApproved(_tokenId: uint256) -> address: """ - @dev Get the approved address for a single NFT. - Throws if `_tokenId` is not a valid NFT. - @param _tokenId ID of the NFT to query the approval of. + @dev 単一のNFTの承認済みアドレスを取得します。 + `_tokenId`が有効なNFTでない場合はスローします。 + @param _tokenId 承認を照会するNFTのID。 """ - # Throws if `_tokenId` is not a valid NFT + # `_tokenId`が有効なNFTでない場合はスローします assert self.idToOwner[_tokenId] != ZERO_ADDRESS return self.idToApprovals[_tokenId] ``` -`getApproved`は、ゼロの値を返す_可能性_がある点に注意してください。 有効なトークンが存在する場合、`self.idToApprovals[_tokenId]`の値が返されます。 承認者が存在しない場合、値はゼロになります。 +`getApproved`はゼロを返す_可能性がある_ことに注意してください。 トークンが有効な場合、`self.idToApprovals[_tokenId]`を返します。 +承認者がいない場合、その値はゼロです。 ```python @view @external def isApprovedForAll(_owner: address, _operator: address) -> bool: """ - @dev Checks if `_operator` is an approved operator for `_owner`. - @param _owner The address that owns the NFTs. - @param _operator The address that acts on behalf of the owner. + @dev `_operator`が`_owner`の承認済みオペレーターであるかどうかをチェックします。 + @param _owner NFTを所有するアドレス。 + @param _operator 所有者の代わりに機能するアドレス。 """ return (self.ownerToOperators[_owner])[_operator] ``` -この関数は、「`_operator`」がこのコントラクト内の「`_owner`」のトークンをすべて管理できるかどうかをチェックします。 複数のオペレーターが存在する可能性があるため、ハッシュマップも2つのレベルが存在します。 +この関数は、`_operator`がこのコントラクト内の`_owner`のすべてのトークンを管理できるかどうかをチェックします。 +複数のオペレーターが存在する可能性があるため、これは2レベルのHashMapです。 -#### 送信ヘルパー関数 {#transfer-helpers} +#### 送金ヘルパー関数 {#transfer-helpers} -これらの関数は、トークンの送信またはトークン管理の一部のオペレーションを実装しています。 +これらの関数は、トークンの送金または管理の一部である操作を実装します。 ```python -### TRANSFER FUNCTION HELPERS ### +### 送金関数ヘルパー ### @view @internal ``` -`@internal`のデコレータは、関数が同一コントラクト内の他の関数からのみアクセス可能であることを意味します。 これらの関数も、慣習によりアンダースコア (`_`) で開始します。 +このデコレータ`@internal`は、この関数が同じコントラクト内の他の関数からのみアクセス可能であることを意味します。 慣例により、これらの関数名もアンダースコア(`_`)で始まります。 ```python def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool: """ - @dev Returns whether the given spender can transfer a given token ID - @param spender address of the spender to query - @param tokenId uint256 ID of the token to be transferred - @return bool whether the msg.sender is approved for the given token ID, - is an operator of the owner, or is the owner of the token + @dev 指定されたスペンダーが指定されたトークンIDを送金できるかどうかを返します + @param spender 照会するスペンダーのアドレス + @param tokenId 送金されるトークンのuint256 ID + @return bool msg.senderが指定されたトークンIDに対して承認されているか、 + 所有者のオペレーターであるか、トークンの所有者であるかどうか """ owner: address = self.idToOwner[_tokenId] spenderIsOwner: bool = owner == _spender @@ -341,117 +384,122 @@ def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool: return (spenderIsOwner or spenderIsApproved) or spenderIsApprovedForAll ``` -アドレスからトークンを送信できるようにするには、次の3通りの方法があります: +アドレスがトークンを送金できるようにするには、3つの方法があります。 -1. アドレスが、トークンの所有者であること -2. アドレスが、トークンの使用を承認されていること -3. アドレスが、トークンの所有者のオペレーターであること +1. アドレスがトークンの所有者である +2. アドレスがそのトークンを使用することを承認されている +3. アドレスがトークンの所有者のオペレーターである -上記の機能は、状態を変更しないためビュー機能とすることもできます。 オペレーションコストを軽減するには、ビュー機能と_指定できる_関数はビュー機能に_するべき_です。 +上記の関数は状態を変更しないため、ビューにすることができます。 運用コストを削減するために、ビューに_できる_関数は +すべてビューに_すべき_です。 ```python @internal def _addTokenTo(_to: address, _tokenId: uint256): """ - @dev Add a NFT to a given address - Throws if `_tokenId` is owned by someone. + @dev 指定されたアドレスにNFTを追加します + `_tokenId`が誰かによって所有されている場合はスローします。 """ - # Throws if `_tokenId` is owned by someone + # `_tokenId`が誰かによって所有されている場合はスローします assert self.idToOwner[_tokenId] == ZERO_ADDRESS - # Change the owner + # 所有者を変更 self.idToOwner[_tokenId] = _to - # Change count tracking + # カウント追跡を変更 self.ownerToNFTokenCount[_to] += 1 @internal def _removeTokenFrom(_from: address, _tokenId: uint256): """ - @dev Remove a NFT from a given address - Throws if `_from` is not the current owner. + @dev 指定されたアドレスからNFTを削除します + `_from`が現在の所有者でない場合はスローします。 """ - # Throws if `_from` is not the current owner + # `_from`が現在の所有者でない場合はスローします assert self.idToOwner[_tokenId] == _from - # Change the owner + # 所有者を変更 self.idToOwner[_tokenId] = ZERO_ADDRESS - # Change count tracking + # カウント追跡を変更 self.ownerToNFTokenCount[_from] -= 1 ``` -送信に問題が発生した場合、呼び出しは取り消されます。 +送金に問題がある場合、呼び出しを元に戻します。 ```python @internal def _clearApproval(_owner: address, _tokenId: uint256): """ - @dev Clear an approval of a given address - Throws if `_owner` is not the current owner. + @dev 指定されたアドレスの承認をクリアします + `_owner`が現在の所有者でない場合はスローします。 """ - # Throws if `_owner` is not the current owner + # `_owner`が現在の所有者でない場合はスローします assert self.idToOwner[_tokenId] == _owner if self.idToApprovals[_tokenId] != ZERO_ADDRESS: - # Reset approvals + # 承認をリセット self.idToApprovals[_tokenId] = ZERO_ADDRESS ``` -必要な場合のみ、値を変更してください。 状態変数は、ストレージ上に保存されます。 ストレージへの書き込みは、EVM(イーサリアム仮想マシン)において[ガス代](/developers/docs/gas/)が最も高価なオペレーションの1つです。 既存の値の書き込みもコストが高いため、ストレージへの書き込みは最低限に抑えることをお勧めします。 +必要な場合のみ、値を変更してください。 状態変数はストレージに存在します。 ストレージへの書き込みは、 +EVM (イーサリアム仮想マシン) が行う最も高価な操作の1つです([ガス](/developers/docs/gas/)の観点から)。 したがって、既存の値を書き込むだけでも +コストが高いため、これを最小限に抑えることをお勧めします。 ```python @internal def _transferFrom(_from: address, _to: address, _tokenId: uint256, _sender: address): """ - @dev Execute transfer of a NFT. - Throws unless `msg.sender` is the current owner, an authorized operator, or the approved - address for this NFT. (NOTE: `msg.sender` not allowed in private function so pass `_sender`.) - Throws if `_to` is the zero address. - Throws if `_from` is not the current owner. - Throws if `_tokenId` is not a valid NFT. + @dev NFTの送金を実行します。 + `msg.sender`が現在の所有者、承認されたオペレーター、またはこのNFTの承認 + 済みアドレスでない限り、スローします。(注:`msg.sender`はプライベート関数では許可されていないため、`_sender`を渡します。) + `_to`がゼロアドレスの場合はスローします。 + `_from`が現在の所有者でない場合はスローします。 + `_tokenId`が有効なNFTでない場合はスローします。 """ ``` -トークンの送信には2つの方法(通常モードと安全モード)が存在するため、この内部関数が提供されていますが、監査を容易にするために、コード内で送信するロケーションは1つだけにする必要があります。 +トークンを送金するには2つの方法(通常と安全)があるため、この内部関数がありますが、 +監査を容易にするために、コード内の1つの場所でのみ実行するようにしています。 ```python - # Check requirements + # 要件をチェック assert self._isApprovedOrOwner(_sender, _tokenId) - # Throws if `_to` is the zero address + # `_to`がゼロアドレスの場合はスローします assert _to != ZERO_ADDRESS - # Clear approval. Throws if `_from` is not the current owner + # 承認をクリアします。`_from`が現在の所有者でない場合はスローします self._clearApproval(_from, _tokenId) - # Remove NFT. Throws if `_tokenId` is not a valid NFT + # NFTを削除します。`_tokenId`が有効なNFTでない場合はスローします self._removeTokenFrom(_from, _tokenId) - # Add NFT + # NFTを追加 self._addTokenTo(_to, _tokenId) - # Log the transfer + # 送金をログに記録 log Transfer(_from, _to, _tokenId) ``` -Vyper上でイベントを発行するには、`log`ステートメントを使用します(詳細は、[こちら](https://vyper.readthedocs.io/en/latest/event-logging.html#event-logging)をご覧ください)。 +Vyperでイベントを発行するには、`log`ステートメントを使用します([詳細はこちら](https://vyper.readthedocs.io/en/latest/event-logging.html#event-logging))。 -#### 送信関数 {#transfer-funs} +#### 送金関数 {#transfer-funs} ```python -### TRANSFER FUNCTIONS ### +### 送金関数 ### @external def transferFrom(_from: address, _to: address, _tokenId: uint256): """ - @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved - address for this NFT. - Throws if `_from` is not the current owner. - Throws if `_to` is the zero address. - Throws if `_tokenId` is not a valid NFT. - @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else - they maybe be permanently lost. - @param _from The current owner of the NFT. - @param _to The new owner. - @param _tokenId The NFT to transfer. + @dev `msg.sender`が現在の所有者、承認されたオペレーター、またはこのNFTの承認 + 済みアドレスでない限り、スローします。 + `_from`が現在の所有者でない場合はスローします。 + `_to`がゼロアドレスの場合はスローします。 + `_tokenId`が有効なNFTでない場合はスローします。 + @notice 呼び出し元は、`_to`がNFTを受信できることを確認する責任があります。さもないと、 + 永久に失われる可能性があります。 + @param _from NFTの現在の所有者。 + @param _to 新しい所有者。 + @param _tokenId 送金するNFT。 """ self._transferFrom(_from, _to, _tokenId, msg.sender) ``` -この関数は、任意のアドレスに送信する機能を提供します。 送信先のアドレスがユーザーであるか、トークンの送信方法が分かっているコントラクトでない場合、送信するトークンはそのアドレスに置かれたまま利用できない状態になります。 +この関数を使用すると、任意のアドレスに送金できます。 アドレスがユーザーであるか、トークンの送金方法を知っているコントラクトでない限り、 +送金したトークンはそのアドレスでスタックし、使用できなくなります。 ```python @external @@ -462,75 +510,80 @@ def safeTransferFrom( _data: Bytes[1024]=b"" ): """ - @dev Transfers the ownership of an NFT from one address to another address. - Throws unless `msg.sender` is the current owner, an authorized operator, or the - approved address for this NFT. - Throws if `_from` is not the current owner. - Throws if `_to` is the zero address. - Throws if `_tokenId` is not a valid NFT. - If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if - the return value is not `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. - NOTE: bytes4 is represented by bytes32 with padding - @param _from The current owner of the NFT. - @param _to The new owner. - @param _tokenId The NFT to transfer. - @param _data Additional data with no specified format, sent in call to `_to`. + @dev NFTの所有権をあるアドレスから別のアドレスに送金します。 + `msg.sender`が現在の所有者、承認されたオペレーター、またはこの + NFTの承認済みアドレスでない限り、スローします。 + `_from`が現在の所有者でない場合はスローします。 + `_to`がゼロアドレスの場合はスローします。 + `_tokenId`が有効なNFTでない場合はスローします。 + `_to`がスマートコントラクトの場合、`_to`で`onERC721Received`を呼び出し、 + 戻り値が`bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`でない場合にスローします。 + 注:bytes4はパディング付きのbytes32で表されます + @param _from NFTの現在の所有者。 + @param _to 新しい所有者。 + @param _tokenId 送金するNFT。 + @param _data 指定されたフォーマットのない追加データで、`_to`への呼び出しで送信されます。 """ self._transferFrom(_from, _to, _tokenId, msg.sender) ``` -問題が発生した場合にはトランザクションが元に戻され、コールに含まれるすべての事項が取り消されるため、先に送信を行っても構いません。 +問題が発生した場合はとにかく元に戻すので、先に送金しても問題ありません。 +呼び出しで行われたことはすべてキャンセルされます。 ```python - if _to.is_contract: # check if `_to` is a contract address + if _to.is_contract: # `_to`がコントラクトアドレスかどうかをチェック ``` -まず、アドレスがコントラクトであるか(コードを持つか)を確認してください。 アドレスがコントラクトでない場合、ユーザーのアドレスであれば、そのトークンを使用/送信できるようになります。 しかし、これがセキュリティを強化すると考えるべきではありません。 `safeTransferFrom`を使用した場合でも、秘密鍵が不明なアドレスに送信したトークンは失われる場合があります。 +まず、アドレスがコントラクト(コードがある場合)かどうかを確認します。 そうでない場合は、それがユーザーアドレスであり、ユーザーが +トークンを使用または送金できると想定します。 しかし、それで +安心しきってはいけません。 `safeTransferFrom`を使用しても、誰も秘密鍵を知らないアドレスに +送金すると、トークンを失う可能性があります。 ```python returnValue: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) ``` -ERC-721トークンを受信できるかどうかを確認するには、送信先のコントラクトを呼び出します。 +ターゲットコントラクトを呼び出して、ERC-721トークンを受信できるかどうかを確認します。 ```python - # Throws if transfer destination is a contract which does not implement 'onERC721Received' + # 送金先が'onERC721Received'を実装していないコントラクトの場合はスローします assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes32) ``` -送信先がコントラクトであるが、ERC-721トークンを受け付けない場合(または、この特定の送信を受け付けないと選択した場合)、操作を取り消してください。 +宛先がコントラクトであっても、ERC-721トークンを受け付けない(またはこの特定の +送金を受け付けないと決定した)場合は、元に戻します。 ```python @external def approve(_approved: address, _tokenId: uint256): """ - @dev Set or reaffirm the approved address for an NFT. The zero address indicates there is no approved address. - Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner. - Throws if `_tokenId` is not a valid NFT. (NOTE: This is not written the EIP) - Throws if `_approved` is the current owner. (NOTE: This is not written the EIP) - @param _approved Address to be approved for the given NFT ID. - @param _tokenId ID of the token to be approved. + @dev NFTの承認済みアドレスを設定または再確認します。ゼロアドレスは、承認済みアドレスがないことを示します。 + `msg.sender`が現在のNFT所有者または現在の所有者の承認済みオペレーターでない限り、スローします。 + `_tokenId`が有効なNFTでない場合はスローします。(注:これはEIPには書かれていません) + `_approved`が現在の所有者である場合はスローします。(注:これはEIPには書かれていません) + @param _approved 指定されたNFT IDに対して承認されるアドレス。 + @param _tokenId 承認されるトークンのID。 """ owner: address = self.idToOwner[_tokenId] - # Throws if `_tokenId` is not a valid NFT + # `_tokenId`が有効なNFTでない場合はスローします assert owner != ZERO_ADDRESS - # Throws if `_approved` is the current owner + # `_approved`が現在の所有者である場合はスローします assert _approved != owner ``` -慣習により、承認者を設定したくない場合、自分のアドレスではなく、ゼロのアドレスを指定してください。 +慣例により、承認者を設定したくない場合は、自分自身ではなくゼロアドレスを指定します。 ```python - # Check requirements + # 要件をチェック senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender] assert (senderIsOwner or senderIsApprovedForAll) ``` -承認を設定するには、所有者であるか、所有者が承認したオペレーターである必要があります。 +承認を設定するには、所有者であるか、所有者によって承認されたオペレーターである必要があります。 ```python - # Set the approval + # 承認を設定 self.idToApprovals[_tokenId] = _approved log Approval(owner, _approved, _tokenId) @@ -538,95 +591,110 @@ def approve(_approved: address, _tokenId: uint256): @external def setApprovalForAll(_operator: address, _approved: bool): """ - @dev Enables or disables approval for a third party ("operator") to manage all of - `msg.sender`'s assets. It also emits the ApprovalForAll event. - Throws if `_operator` is the `msg.sender`. (NOTE: This is not written the EIP) - @notice This works even if sender doesn't own any tokens at the time. - @param _operator Address to add to the set of authorized operators. - @param _approved True if the operators is approved, false to revoke approval. + @dev サードパーティ(「オペレーター」)が`msg.sender`の + すべての資産を管理するための承認を有効または無効にします。また、ApprovalForAllイベントも発行します。 + `_operator`が`msg.sender`である場合はスローします。(注:これはEIPには書かれていません) + @notice これは、送信者がその時点でトークンを所有していなくても機能します。 + @param _operator 承認済みオペレーターのセットに追加するアドレス。 + @param _approved オペレーターが承認されている場合はTrue、承認を取り消す場合はfalse。 """ - # Throws if `_operator` is the `msg.sender` + # `_operator`が`msg.sender`である場合はスローします assert _operator != msg.sender self.ownerToOperators[msg.sender][_operator] = _approved log ApprovalForAll(msg.sender, _operator, _approved) ``` -#### 新しいトークンのミントと既存トークンの破壊 {#mint-burn} +#### 新しいトークンのミントと既存トークンの破棄 {#mint-burn} -コントラクトを作成したアカウントは`minter`となり、新規のNFTをミントする承認を受けたスーパーユーザーとなります。 ただし、ミンターは既存トークンをバーンすることはできません。 バーンできるのは、所有者および所有者の承認を受けたエンティティのみです。 +コントラクトを作成したアカウントは`minter`であり、新しいNFTをミントする権限を持つ +スーパーユーザーです。 ただし、既存のトークンをバーンすることは許可されていません。 所有者、または所有者によって承認された +エンティティのみがそれを行うことができます。 ```python -### MINT & BURN FUNCTIONS ### +### ミント&バーン関数 ### @external def mint(_to: address, _tokenId: uint256) -> bool: ``` -操作が実行されない場合は常に元に戻されるため、この関数は常に`True`の値が返されます。 +操作が失敗した場合は元に戻されるため、この関数は常に`True`を返します。 ```python """ - @dev Function to mint tokens - Throws if `msg.sender` is not the minter. - Throws if `_to` is zero address. - Throws if `_tokenId` is owned by someone. - @param _to The address that will receive the minted tokens. - @param _tokenId The token id to mint. - @return A boolean that indicates if the operation was successful. + @dev トークンをミントする関数 + `msg.sender`がミンターでない場合はスローします。 + `_to`がゼロアドレスの場合はスローします。 + `_tokenId`が誰かによって所有されている場合はスローします。 + @param _to ミントされたトークンを受け取るアドレス。 + @param _tokenId ミントするトークンID。 + @return 操作が成功したかどうかを示すブール値。 """ - # Throws if `msg.sender` is not the minter + # `msg.sender`がミンターでない場合はスローします assert msg.sender == self.minter ``` -新たなトークンをミントできるのは、このミンター(この ERC-721コントラクトを作成したアカウント)のみです。 ミンターの身元を変更したい場合、この点は将来的に問題になる可能性があります。 本番環境のコントラクトでは、ミンターがミンター特権を他のユーザーに譲渡できる機能が必要になるでしょう。 +ミンター(ERC-721コントラクトを作成したアカウント)のみが新しいトークンをミントできます。 これは、将来ミンターの +IDを変更したい場合に問題になる可能性があります。 本番環境のコントラクトでは、ミンターが +ミンター権限を他の誰かに譲渡できる関数が必要になるでしょう。 ```python - # Throws if `_to` is zero address + # `_to`がゼロアドレスの場合はスローします assert _to != ZERO_ADDRESS - # Add NFT. Throws if `_tokenId` is owned by someone + # NFTを追加します。`_tokenId`が誰かによって所有されている場合はスローします self._addTokenTo(_to, _tokenId) log Transfer(ZERO_ADDRESS, _to, _tokenId) return True ``` -慣習により、新規トークンのミントは、ゼロアドレスからの送信としてカウントされます。 +慣例により、新しいトークンのミントはゼロアドレスからの送金としてカウントされます。 ```python @external def burn(_tokenId: uint256): """ - @dev Burns a specific ERC721 token. - Throws unless `msg.sender` is the current owner, an authorized operator, or the approved - address for this NFT. - Throws if `_tokenId` is not a valid NFT. - @param _tokenId uint256 id of the ERC721 token to be burned. + @dev 特定のERC721トークンをバーンします。 + `msg.sender`が現在の所有者、承認されたオペレーター、またはこのNFTの承認 + 済みアドレスでない限り、スローします。 + `_tokenId`が有効なNFTでない場合はスローします。 + @param _tokenId バーンされるERC721トークンのuint256 ID。 """ - # Check requirements + # 要件をチェック assert self._isApprovedOrOwner(msg.sender, _tokenId) owner: address = self.idToOwner[_tokenId] - # Throws if `_tokenId` is not a valid NFT + # `_tokenId`が有効なNFTでない場合はスローします assert owner != ZERO_ADDRESS self._clearApproval(owner, _tokenId) self._removeTokenFrom(owner, _tokenId) log Transfer(owner, ZERO_ADDRESS, _tokenId) ``` -トークンの送信が許可されているユーザーは、そのトークンをバーンすることができます。 バーンは、ゼロアドレスへの送信と同じ外見を持ちますが、このゼロアドレスは実際にはこのトークンを受け取りません。 バーンを通じて、このトークンに使用されたすべてのストレージを解放できるため、トランザクションのガス代を軽減することができます。 +トークンの送金を許可されている人は誰でも、それをバーンすることが許可されています。 バーンはゼロアドレスへの送金と +同等に見えますが、ゼロアドレスは実際にはトークンを受け取りません。 これにより、トークンに使用されていたすべてのストレージを +解放でき、トランザクションのガス代を削減できます。 + +## このコントラクトの使用 {#using-contract} + +Solidityとは対照的に、Vyperには継承がありません。 これは、コードをより明確にし、 +したがってセキュリティを確保しやすくするための意図的な設計上の選択です。 したがって、独自のVyper ERC-721コントラクトを作成するには、この +コントラクトを取得し、 +必要なビジネスロジックを実装するように変更します。 -## コントラクトの使用方法 {#using-contract} +## 結論 {#conclusion} -Solidityとは異なり、Viperは相続機能を提供しません。 これは、コードをより明確にし、セキュリティを確保しやすくするための意図的な設計上の選択によるものです。 このため、あなた自身がVyperでERC-721コントラクトを作成する際は、[このコントラクト](https://github.com/vyperlang/vyper/blob/master/examples/tokens/ERC721.vy)を用いて修正し、必要なビジネスロジックを実装してください。 +復習のために、このコントラクトの最も重要なアイデアをいくつか紹介します。 -### まとめ {#conclusion} +- 安全な送金でERC-721トークンを受信するには、コントラクトは`ERC721Receiver`インターフェイスを実装する必要があります。 +- 安全な送金を使用しても、秘密鍵が不明なアドレスに送信すると、 + トークンがスタックする可能性があります。 +- 操作に問題がある場合は、単に失敗値を返すのではなく、 + 呼び出しを`revert`することをお勧めします。 +- ERC-721トークンは、所有者がいる場合に存在します。 +- NFTの送金を承認するには、3つの方法があります。 所有者であるか、特定のトークンに対して承認されているか、 + または所有者のすべてのトークンのオペレーターであることができます。 +- 過去のイベントはブロックチェーンの外部からのみ表示されます。 ブロックチェーン内で実行されるコードは、それらを表示できません。 -このコントラクトにつき、最も重要なポイントを以下にまとめました: +では、セキュアなVyperコントラクトを実装してみましょう。 -- 安全モードの送信を使ってERC-721トークンを受け取るには、コントラクトに`ERC721Receiver`インターフェイスを実装する必要があります。 -- 安全モードの送信を実行した場合でも、秘密鍵が不明なアドレスに送信したトークンは行方不明になる可能性があります。 -- 操作に問題が発生した場合、単に失敗値をリターンするのではなく、コール自体を`取り消す`ことをお勧めします。 -- ERC-721トークンには、必ず所有者が必要です。 -- NFTの送信を承認するには、3通りの方法があります。 承認できるのは、所有者であるか、特定トークンの送信を承認されているか、所有者のトークンすべてに対するオペレーターであるユーザーのみです。 -- 過去のイベントは、ブロックチェーン外でのみ表示されます。 ブロックチェーン内で実行されるコードは、表示できません。 +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). -それではさっそく、セキュアなVyperコントラクトを実装してみましょう。 diff --git a/public/content/translations/ja/developers/tutorials/erc20-annotated-code/index.md b/public/content/translations/ja/developers/tutorials/erc20-annotated-code/index.md index 56cf9b6b772..98fff8f466d 100644 --- a/public/content/translations/ja/developers/tutorials/erc20-annotated-code/index.md +++ b/public/content/translations/ja/developers/tutorials/erc20-annotated-code/index.md @@ -1,30 +1,28 @@ --- -title: "ERC-20コントラクトの詳細" -description: OpenZeppelinのERC-20コントラクトの内容とそれが存在する理由 +title: "ERC-20コントラクトのウォークスルー" +description: "OpenZeppelinのERC-20コントラクトには何が含まれており、それはなぜ存在するのでしょうか?" author: Ori Pomerantz lang: ja -tags: - - "Solidity" - - "erc-20" +tags: [ "Solidity", "ERC-20" ] skill: beginner published: 2021-03-09 --- ## はじめに {#introduction} -イーサリアムの最も一般的な用途の1つは、グループが取引可能なトークン、いわば独自の通貨を作ることです。 これらのトークンは通常、[ERC-20](/developers/docs/standards/tokens/erc-20/)という規格に準拠しています。 この規格により、流動性プールやウォレットなど、すべてのERC-20トークンで利用できるツールの作成が可能になります。 今回は、[OpenZeppelin Solidity ERC-20の実装](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)と、[インターフェース定義](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol)について分析していきます。 +イーサリアムの最も一般的な用途の1つは、グループが取引可能なトークン、いわば独自の通貨を作ることです。 これらのトークンは、通常[ERC-20](/developers/docs/standards/tokens/erc-20/)という規格に従います。 この規格により、流動性プールやウォレットなど、すべてのERC-20トークンで利用できるツールの作成が可能になります。 この記事では、[OpenZeppelinのSolidityによるERC20実装](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)と、[インターフェース定義](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol)を分析します。 -ここではアノテーション付きのソースコードを見ていきます。 ERC-20を実装する場合は、[こちらのチュートリアル](https://docs.openzeppelin.com/contracts/2.x/erc20-supply)をご覧ください。 +ここではアノテーション付きのソースコードを見ていきます。 ERC-20を実装したい場合は、[このチュートリアル](https://docs.openzeppelin.com/contracts/2.x/erc20-supply)をお読みください。 ## インターフェース {#the-interface} -ERC-20のような規格の目的は、ウォレットや分散型取引所のようなアプリケーション間で相互運用可能な多くのトークンの実装を可能にすることです。 しかしその実現には[インターフェース](https://www.geeksforgeeks.org/solidity-basics-of-interface/)が必要です。 トークンコントラクトを使用する必要があるすべてのコードは、インターフェースで同じ定義を使用することができ、その定義を使用するすべてのトークンコントラクトと互換性があります。それらにはMetaMaskなどのウォレット、etherscan.ioなどの分散型アプリケーション(Dapp)、流動性プールなどの異なるコントラクトが含まれます。 +ERC-20のような規格の目的は、ウォレットや分散型取引所のようなアプリケーション間で相互運用可能な多くのトークンの実装を可能にすることです。 それを実現するために、[インターフェース](https://www.geeksforgeeks.org/solidity/solidity-basics-of-interface/)を作成します。 トークンコントラクトを使用する必要があるコードは、それがMetaMaskのようなウォレット、etherscan.ioのようなdapp、または流動性プールのような別のコントラクトであっても、インターフェースで同じ定義を使用でき、そのインターフェースを使用するすべてのトークンコントラクトと互換性があります。 ![ERC-20インターフェースの図](erc20_interface.png) -経験豊富なプログラマーであれば、[Java](https://www.w3schools.com/java/java_interface.asp)や[C言語のヘッダーファイル](https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html)で同様の構造を見たことがあるのではないでしょうか。 +経験豊富なプログラマーであれば、[Java](https://www.w3schools.com/java/java_interface.asp)や[Cのヘッダーファイル](https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html)で同様の構造を見たことがあるかもしれません。 -これはOpenZeppelinによる[ERC-20インターフェースの定義](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol)です。 [人間が読めるように](https://eips.ethereum.org/EIPS/eip-20)Solidityのコードにしています。 もちろん、インターフェースそのものは、_何をするか_を定義していません。 これは後述のコントラクトのソースコードで説明されています。 +これはOpenZeppelinによる[ERC-20インターフェース](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol)の定義です。 これは、[人間が読める標準規格](https://eips.ethereum.org/EIPS/eip-20)をSolidityコードに変換したものです。 もちろん、インターフェース自体は、何かを行う_方法_を定義するものではありません。 これは後述のコントラクトのソースコードで説明されています。   @@ -32,7 +30,7 @@ ERC-20のような規格の目的は、ウォレットや分散型取引所の // SPDX-License-Identifier: MIT ``` -Solidityのファイルには、ライセンス識別子が含まれているはずです。 ライセンス一覧は[こちら](https://spdx.org/licenses/)でご覧いただけます。 別のライセンスが必要な場合は、コメントしてください。 +Solidityのファイルには、ライセンス識別子が含まれているはずです。 [ライセンスのリストはこちら](https://spdx.org/licenses/)で確認できます。 別のライセンスが必要な場合は、コメントでその旨を説明してください。   @@ -40,13 +38,13 @@ Solidityのファイルには、ライセンス識別子が含まれているは pragma solidity >=0.6.0 <0.8.0; ``` -Solidity言語は現在も急速に進化しており、新しいバージョンは古いコードと互換性がない場合があります(詳しくは[こちら](https://docs.soliditylang.org/en/v0.7.0/070-breaking-changes.html))。 そのため、この言語の最小バージョンだけでなく、コードをテストした最新バージョンも指定することをお勧めします。 +Solidity言語は現在も急速に進化しており、新しいバージョンは古いコードと互換性がない場合があります([詳しくはこちら](https://docs.soliditylang.org/en/v0.7.0/070-breaking-changes.html))。 そのため、言語の最小バージョンだけでなく、最大バージョン、つまりコードをテストした最新のバージョンも指定することをお勧めします。   ```solidity /** - * @dev Interface of the ERC20 standard as defined in the EIP. + * @dev EIPで定義されているERC20標準のインターフェース。 */ ``` @@ -58,135 +56,135 @@ Solidity言語は現在も急速に進化しており、新しいバージョン interface IERC20 { ``` -慣例では、インターフェース名は「`I`」で始まります。 +慣例では、インターフェース名は`I`で始まります。   ```solidity /** - * @dev Returns the amount of tokens in existence. + * @dev 存在するトークンの総量を返します。 */ function totalSupply() external view returns (uint256); ``` -この関数は`external`です。つまり、[コントラクトの外部からのみ呼び出すことができます](https://docs.soliditylang.org/en/v0.7.0/cheatsheet.html#index-2)。 コントラクト内のトークンの総供給量を返します。 この値は、イーサリアムで最も一般的な型である符号なし256ビット(イーサリアム仮想マシン(EVM)のネイティブワードサイズ)で返されます。 この関数も`view`であり、状態を変更しないため、ブロックチェーンのすべてのノードで実行するのではなく、単一のノードで実行できます。 このような関数はトランザクションを発生させず、[ガス代](/developers/docs/)もかかりません。 +この関数は`external`であり、[コントラクトの外部からのみ呼び出すことができる](https://docs.soliditylang.org/en/v0.7.0/cheatsheet.html#index-2)ことを意味します。 +コントラクト内のトークンの総供給量を返します。 この値は、イーサリアムで最も一般的な型である符号なし256ビット(256ビットはEVMのネイティブワードサイズ)で返されます。 この関数も`view`であり、状態を変更しないため、ブロックチェーンのすべてのノードで実行するのではなく、単一のノードで実行できます。 このような関数はトランザクションを生成せず、[ガス](/developers/docs/gas/)代もかかりません。 -**注:** 理論的には、コントラクト作成者が、実際の値よりも少ない総供給量を返し、各トークンを実際の価格よりも高価に見せることで、不正を行うことが出来るように思えるかもしれません。 しかし、そのような心配をするということは、ブロックチェーンの本質を忘れているということになります。 ブロックチェーン上で起こるすべてのことは、すべてのノードで検証できます。 検証をするためのすべてのコントラクトの機械語コードとストレージは、全ノードで入手可能です。 コントラクトのSolidityコードを公開する必要はありませんが、ソースコードとコンパイルに使用したSolidityのバージョンを公開しない限り、不正への懸念を真剣に受け止めてもらうことはできません。その場合は、提供した機械語コードで検証することができます。 例えば、[このコントラクト](https://etherscan.io/address/0xa530F85085C6FE2f866E7FdB716849714a89f4CD#code)をご覧ください。 +**注:** 理論的には、コントラクト作成者が、実際の値よりも少ない総供給量を返し、各トークンを実際の価格よりも高価に見せることで、不正を行うことが出来るように思えるかもしれません。 しかし、そのような心配をするということは、ブロックチェーンの本質を忘れているということになります。 ブロックチェーン上で起こるすべてのことは、すべてのノードで検証できます。 これを実現するため、すべてのコントラクトの機械語コードとストレージは、全ノードで入手可能です。 コントラクトのSolidityコードを公開する必要はありませんが、提供した機械語コードと照合して検証できるように、ソースコードとそれをコンパイルしたSolidityのバージョンを公開しない限り、誰もあなたのコントラクトを本気で相手にしないでしょう。 +例えば、[このコントラクト](https://eth.blockscout.com/address/0xa530F85085C6FE2f866E7FdB716849714a89f4CD?tab=contract)をご覧ください。   ```solidity /** - * @dev Returns the amount of tokens owned by `account`. + * @dev `account`が所有するトークンの量を返します。 */ function balanceOf(address account) external view returns (uint256); ``` -その名の通り、`balanceOf`はアカウントの残高を返します。 Solidityでは、イーサリアムアカウントは160ビットを保持する`address`型で識別されます。 また、`external`や`view`もあります。 +その名の通り、`balanceOf`はアカウントの残高を返します。 Solidityでは、イーサリアムアカウントは160ビットを保持する`address`型で識別されます。 +この関数も`external`かつ`view`です。   ```solidity /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. + * @dev `amount`のトークンを呼び出し元のアカウントから`recipient`に移動させます。 * - * Returns a boolean value indicating whether the operation succeeded. + * 操作が成功したかどうかを示すブール値を返します。 * - * Emits a {Transfer} event. + * {Transfer}イベントを発行します。 */ function transfer(address recipient, uint256 amount) external returns (bool); ``` -`transfer`関数は、呼び出し元から別のアドレスにトークンを転送します。 これは状態の変化を伴うので、`view`ではありません。 ユーザーがこの関数を呼び出すと、トランザクションが作成され、ガス代がかかります。 さらに、`Transfer`というイベントが発行され、ブロックチェーン上の全ノードにそのイベントが通知されます。 +`transfer`関数は、呼び出し元から別のアドレスにトークンを転送します。 これは状態の変化を伴うので、`view`ではありません。 +ユーザーがこの関数を呼び出すと、トランザクションが作成され、ガス代がかかります。 さらに、`Transfer`というイベントが発行され、ブロックチェーン上の全員にそのイベントが通知されます。 この関数には、2つの異なる呼び出し元のタイプに応じて、2つのタイプの出力があります。 -- ユーザーがユーザーインターフェースから関数を直接呼び出す場合。 通常、トランザクションを送信したユーザーは、その応答を待ちません。応答にどれくらいの時間がかかるか分からないからです。 ユーザーは、トランザクションレシート(トランザクションハッシュによって識別可能)を探すか、`Transfer`イベントを探すことで状況を把握できます。 -- 他のコントラクトがトランザクション全体の一部として関数を呼び出す場合。 これらのコントラクトは、すぐに結果を得ることができます。関数が同じトランザクション内で実行されるため、関数の戻り値を使用できるからです。 +- ユーザーがユーザーインターフェースから関数を直接呼び出す場合。 通常、ユーザーはトランザクションを送信すると、応答を待ちません。応答には不確定な時間がかかる可能性があるためです。 ユーザーは、トランザクションレシート(トランザクションハッシュによって識別される)を探すか、`Transfer`イベントを探すことで状況を把握できます。 +- 他のコントラクトが、全体的なトランザクションの一部として関数を呼び出す場合。 これらのコントラクトは、同じトランザクション内で実行されるため、関数の戻り値を使用でき、すぐに結果を得ることができます。 同じタイプの出力は、コントラクトの状態を変更する他の関数によって作成されます。   -割当量(allowance)を設定すると、あるアカウントが別の所有者に属しているトークンの一部を使えるようになります。 これは、コントラクトが売り手として機能する場合などに役立ちます。 コントラクトはイベントを監視できないため、買い手が売り手のコントラクトにトークンを直接転送した場合、売り手のコントラクトはその支払いを認識できません。 代わりに、買い手が売り手のコントラクトに一定量の使用を許可し、売り手がその量を転送するようにします。 この処理は売り手のコントラクトが呼び出した関数を通して行われるため、売り手のコントラクトは処理が成功したかどうかを確認できます。 +割当量(`allowance`)により、あるアカウントが別の所有者に属しているトークンの一部を使えるようになります。 +これは、例えば、コントラクトが売り手として機能する場合などに役立ちます。 コントラクトはイベントを監視できないため、買い手が売り手のコントラクトにトークンを直接転送した場合、売り手のコントラクトはその支払いを認識できません。 代わりに、買い手が売り手のコントラクトに一定量の使用を許可し、売り手がその量を転送します。 +この処理は売り手のコントラクトが呼び出す関数を通して行われるため、売り手のコントラクトは処理が成功したかどうかを確認できます。 ```solidity /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. これは - * デフォルトでゼロです。 + * @dev {transferFrom}を通じて`spender`が`owner`に代わって使用できる、残りのトークン数を返します。これは + * デフォルトではゼロです。 * - * This value changes when {approve} or {transferFrom} are called. + * この値は、{approve}または{transferFrom}が呼び出されると変化します。 */ function allowance(address owner, address spender) external view returns (uint256); ``` -`allowance`関数を使用すると、誰でも、あるアドレス(`owner`)が別のアドレス(`spender`)に使用を許可している割当量(allowance)を照会できます。 +`allowance`関数により、誰でも、あるアドレス(`owner`)が別のアドレス(`spender`)に使用を許可している割当量を照会できます。   ```solidity /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * @dev `spender`が呼び出し元のトークンに対して持つ割当量を`amount`に設定します。 * - * Returns a boolean value indicating whether the operation succeeded. + * 操作が成功したかを示すブール値を返します。 * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: + * 重要: このメソッドで割当量を変更すると、不運なトランザクションの順序によって、誰かが古い割当量と新しい割当量の両方を使用するリスクがあります。 + * この競合状態を緩和するための一つの解決策は、まずspenderの割当量を0に減らしてから、目的の値を設定することです。 * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * - * Emits an {Approval} event. + * {Approval}イベントを発行します。 */ function approve(address spender, uint256 amount) external returns (bool); ``` -`approve`関数は、割当量を作成します。 これがどのように悪用される可能性があるのかについて、該当のメッセージを必ず読んでください。 イーサリアムでは、自分のトランザクションの順序は制御できますが、他のユーザーのトランザクションが発生したことを確認してからトランザクションを送信しない限り、他のユーザーのトランザクションが実行される順序を制御することはできません。 +`approve`関数は、割当量を作成します。 これがどのように悪用される可能性があるのかについて、該当のメッセージを必ず読んでください。 イーサリアムでは、自分のトランザクションの順序は制御できますが、相手方のトランザクションが発生したことを確認してから自分のトランザクションを送信しない限り、他のユーザーのトランザクションが実行される順序を制御することはできません。   ```solidity /** - * @dev Moves `amount` tokens from `sender` to `recipient` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. + * @dev 割当メカニズムを使用して、`amount`のトークンを`sender`から`recipient`に移動させます。`amount`は呼び出し元の割当量から差し引かれます。 * - * Returns a boolean value indicating whether the operation succeeded. + * 操作が成功したかを示すブール値を返します。 * - * Emits a {Transfer} event. + * {Transfer}イベントを発行します。 */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); ``` -最後に、使用者(spender)が`transferFrom`を使用して、割当量 (allowance)を実際に使用します。 +最後に、使用者(`spender`)が`transferFrom`を使用して、割当量(`allowance`)を実際に使用します。   ```solidity /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). + * @dev `value`のトークンがあるアカウント(`from`)から + * 別のアカウント(`to`)に移動したときに発行されます。 * - * Note that `value` may be zero. + * `value`はゼロになる場合があることに注意してください。 */ event Transfer(address indexed from, address indexed to, uint256 value); /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. + * @dev {approve}の呼び出しによって`owner`に対する`spender`の割当量が設定されたときに発行されます。`value`は新しい割当量です。 */ event Approval(address indexed owner, address indexed spender, uint256 value); } ``` -これらのイベントは、ERC-20コントラクトの状態が変更されるタイミングで発行されます。 +これらのイベントは、ERC-20コントラクトの状態が変更されると発行されます。 ## 実際のコントラクト {#the-actual-contract} -以下は、[こちらから取得した](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)、ERC-20規格を採用している実際のコントラクトです。 そのまま使うためのものではありませんが、[継承](https://www.tutorialspoint.com/solidity/solidity_inheritance.htm)することで使用可能なコントラクトに拡張することができます。 +これはERC-20規格を実装した実際のコントラクトで、[こちら](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)から引用しました。 +これはそのまま使用するためのものではありませんが、それから[継承](https://www.tutorialspoint.com/solidity/solidity_inheritance.htm)して、使えるものに拡張することができます。 ```solidity // SPDX-License-Identifier: MIT @@ -195,7 +193,7 @@ pragma solidity >=0.6.0 <0.8.0;   -### インポートステートメント {#import-statements} +### インポート文 {#import-statements} コントラクト定義では、上記のインターフェース定義に加え、別の2つのファイルをインポートします。 @@ -206,48 +204,43 @@ import "./IERC20.sol"; import "../../math/SafeMath.sol"; ``` -- `GSN/Context.sol`は、イーサ(ETH)を持たないユーザーがブロックチェーンを使用できるようにするシステムである、[OpenGSN](https://www.opengsn.org/)を使用するために必要な定義です。 これは古いバージョンであることに注意してください。OpenGSNと統合する場合は、[こちらのチュートリアルをご覧ください](https://docs.opengsn.org/javascript-client/tutorial.html)。 -- [ SafeMathライブラリ](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/)は、オーバーフローを起こさずに加算と減算を実行できるようにするために使用されます。 このライブラリが必要な理由は、これがないと、1つのトークンを持っているユーザーが何らかの方法で2つのトークンを使用した場合、2^256-1のトークンを持ってしまう可能性があるためです。 +- `GSN/Context.sol`は、etherを持たないユーザーがブロックチェーンを使用できるようにするシステムである[OpenGSN](https://www.opengsn.org/)を使用するために必要な定義です。 これは古いバージョンであることに注意してください。OpenGSNと統合する場合は、[このチュートリアル](https://docs.opengsn.org/javascript-client/tutorial.html)を使用してください。 +- [SafeMathライブラリ](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/)は、Solidityバージョン\*\*<0.8.0\*\*の算術オーバーフロー/アンダーフローを防ぎます。 Solidity ≥0.8.0では、算術演算はオーバーフロー/アンダーフローで自動的にリバートするため、SafeMathは不要です。 このコントラクトは、古いコンパイラバージョンとの後方互換性のためにSafeMathを使用しています。   -以下のコメントは、コントラクトの目的を説明しています。 +このコメントは、コントラクトの目的を説明しています。 ```solidity /** - * @dev Implementation of the {IERC20} interface. + * @dev {IERC20}インターフェースの実装。 * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * For a generic mechanism see {ERC20PresetMinterPauser}. + * この実装は、トークンが作成される方法には依存しません。これは + * 供給メカニズムを、{_mint}を使用して派生コントラクトに追加する必要があることを意味します。 + * 一般的なメカニズムについては、{ERC20PresetMinterPauser}を参照してください。 * - * TIP: For a detailed writeup see our guide - * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. + * ヒント: 詳細な解説については、ガイド + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[供給メカニズムの実装方法]を参照してください。 * - * We have followed general OpenZeppelin guidelines: functions revert instead - * of returning `false` on failure. This behavior is nonetheless conventional - * and does not conflict with the expectations of ERC20 applications. + * OpenZeppelinの一般的なガイドラインに従っています。関数は失敗時に`false`を返すのではなく、リバートします。この動作は慣例的であり、ERC20アプリケーションの期待と矛盾しません。 * - * Additionally, an {Approval} event is emitted on calls to {transferFrom}. - * This allows applications to reconstruct the allowance for all accounts just - * by listening to said events. Other implementations of the EIP may not emit - * these events, as it isn't required by the specification. + * さらに、{transferFrom}の呼び出し時に{Approval}イベントが発行されます。 + * これにより、アプリケーションは、これらのイベントをリッスンするだけで、すべてのアカウントの割当量を再構築できます。EIPの他の実装では、 + * 仕様で要求されていないため、これらのイベントを発行しない場合があります。 * - * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} - * functions have been added to mitigate the well-known issues around setting - * allowances. See {IERC20-approve}. + * 最後に、割当量設定に関する既知の問題を緩和するために、非標準の{decreaseAllowance}および{increaseAllowance}関数が追加されています。 + * {IERC20-approve}を参照してください。 */ ``` -### コントラクトの定義 {#contract-definition} +### コントラクト定義 {#contract-definition} ```solidity contract ERC20 is Context, IERC20 { ``` -この行では、継承を指定しています。ここでは、`IERC20`とOpenGSNのための`Context`を継承しています。 +この行では、継承を指定しています。ここでは、上記の`IERC20`と、OpenGSNのための`Context`を継承しています。   @@ -257,19 +250,19 @@ contract ERC20 is Context, IERC20 { ``` -この行では、`SafeMath`ライブラリを`uint256`型にアタッチしています。 このライブラリの詳細については、[こちら](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol)をご覧ください。 +この行では、`SafeMath`ライブラリを`uint256`型にアタッチしています。 このライブラリは[こちら](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol)で確認できます。 -### 変数の定義 {#variable-definitions} +### 変数定義 {#variable-definitions} -これらの定義では、コントラクトの状態変数を指定します。 変数には、`private`で宣言しているものがありますが、ブロックチェーン上の他のコントラクトから読み取れないというだけです。 _ブロックチェーンに秘密はありません_。すべてのノードのソフトウェアは、すべてのブロックのすべてのコントラクトの状態を保持します。 状態変数は、慣例として`_`のように命名されます。 +これらの定義では、コントラクトの状態変数を指定します。 これらの変数は`private`で宣言されていますが、これはブロックチェーン上の他のコントラクトから読み取れないというだけです。 _ブロックチェーンに秘密はありません_。すべてのノードのソフトウェアは、すべてのブロックですべてのコントラクトの状態を保持します。 慣例として、状態変数は`_`と命名されます。 -最初の2つの変数は、[マッピング](https://www.tutorialspoint.com/solidity/solidity_mappings.htm)であり、キーが数値であることを除いて、[連想配列](https://wikipedia.org/wiki/Associative_array)とほぼ同じような振る舞いをします。 ストレージは、デフォルト(ゼロ)とは異なる値を持つエントリのみに割り当てられます。 +最初の2つの変数は[マッピング](https://www.tutorialspoint.com/solidity/solidity_mappings.htm)であり、キーが数値である点を除けば、おおよそ[連想配列](https://wikipedia.org/wiki/Associative_array)と同じように動作します。 ストレージは、デフォルト(ゼロ)とは異なる値を持つエントリにのみ割り当てられます。 ```solidity mapping (address => uint256) private _balances; ``` -最初のマッピングである`_balances`は、トークンを保持しているアドレスとそれぞれの残高です。 残高にアクセスするには、`_balances[
]`という構文を使用します。 +最初のマッピングである`_balances`は、アドレスとそれぞれのこのトークンの残高です。 残高にアクセスするには、`_balances[
]`という構文を使用します。   @@ -277,7 +270,7 @@ contract ERC20 is Context, IERC20 { mapping (address => mapping (address => uint256)) private _allowances; ``` -この変数`_allowances`には、前述の割当量(allowance)が格納されます。 最初のインデックスは、トークンの所有者であり、2番目のインデックスは割当量(allowance)を使用するコントラクトです。 アドレスAが、アドレスBのアカウントから使える量にアクセスするには、`_allowances[B][A]`のようにします。 +この変数`_allowances`には、前述の割当量が格納されます。 最初のインデックスはトークンの所有者であり、2番目のインデックスは割当量を持つコントラクトです。 アドレスAがアドレスBのアカウントから使える量にアクセスするには、`_allowances[B][A]`を使用します。   @@ -297,78 +290,77 @@ contract ERC20 is Context, IERC20 { この3つの変数は、可読性を向上させるために使用されます。 最初の2つの変数は自明ですが、`_decimals`はそうではありません。 -イーサリアムには浮動小数点変数も小数変数もない一方で、 ユーザーはトークンの分割を好みます。 人々が金を通貨にすると決めた理由の一つとして、誰かがアヒル一羽の値段の分だけ牛を買おうとしたときに、お金を崩すのが難しかったことが挙げられます。 +一方で、イーサリアムには浮動小数点変数も小数変数もありません。 他方で、人間はトークンを分割できることを好みます。 人々が金を通貨に決めた理由の一つは、誰かが牛の価値のアヒル分を買おうとしたときに、お釣りを作るのが難しかったからです。 -これを解決するには整数で追跡すればよいのですが、実際のトークンの代わりに、価値が非常に小さな小数トークンで計算します。 イーサ(ETH)の場合、小数トークンはウェイ(wei)と呼ばれており、10^18 weiが 1 ETHと等価です。 執筆時点では、10,000,000,000,000 weiが、約1米ドルまたは約1ユーロセントです。 +解決策は、整数で追跡し、実際のトークンの代わりに、ほとんど価値のない小数トークンを数えることです。 etherの場合、小数トークンはweiと呼ばれ、10^18 weiが1 ETHと等価です。 執筆時点で、10,000,000,000,000 weiは、約1米ドルまたは1ユーロセントです。 -アプリケーションには、トークンの残高を表示する方法が必要です。 ユーザーが3,14,000,000,000,000,000,000,000weiを持っている場合、それは3.14 ETHでしょうか? それとも、31.41 ETHでしょうか? 3,141 ETHでしょうか? イーサ (ETH) の場合、1 ETHが10^18 weiと定義されていますが、トークンでは別の値を選択可能です。 トークンを分割する必要がなければ、値がゼロの`_decimals`を使用できます。 ETHと同じ基準を使用したい場合は、**18**の値を指定してください。 +アプリケーションは、トークンの残高を表示する方法を知る必要があります。 ユーザーが3,141,000,000,000,000,000 weiを持っている場合、それは3.14 ETHでしょうか? 31.41 ETHでしょうか? 3,141 ETHでしょうか? etherの場合、10^18 weiが1 ETHと定義されていますが、あなたのトークンでは別の値を選択できます。 トークンを分割する必要がなければ、値がゼロの`_decimals`を使用できます。 ETHと同じ基準を使用したい場合は、値**18**を使用してください。 ### コンストラクタ {#the-constructor} ```solidity /** - * @dev Sets the values for {name} and {symbol}, initializes {decimals} with - * a default value of 18. + * @dev {name}と{symbol}の値を設定し、{decimals}を + * デフォルト値の18で初期化します。 * - * To select a different value for {decimals}, use {_setupDecimals}. + * {decimals}に別の値を選択するには、{_setupDecimals}を使用します。 * - * All three of these values are immutable: they can only be set once during - * construction. + * これら3つの値はすべて不変です。構築時に一度しか設定できません。 */ constructor (string memory name_, string memory symbol_) public { + // Solidity ≥0.7.0では、'public'は暗黙的であり、省略できます。 + _name = name_; _symbol = symbol_; _decimals = 18; } ``` -コンストラクタは、コントラクトが最初に作成されたときに呼び出されます。 慣例として関数パラメータは、 `_`のように命名されます。 +コンストラクタは、コントラクトが最初に作成されたときに呼び出されます。 慣例として関数パラメータは、`_`のように命名されます。 ### ユーザーインターフェース関数 {#user-interface-functions} ```solidity /** - * @dev Returns the name of the token. + * @dev トークンの名前を返します。 */ function name() public view returns (string memory) { return _name; } /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. + * @dev トークンのシンボルを返します。通常は名前の短いバージョンです。 + * 名前です。 */ function symbol() public view returns (string memory) { return _symbol; } /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5,05` (`505 / 10 ** 2`). + * @dev ユーザー表現を取得するために使用される小数点以下の桁数を返します。 + * 例えば、`decimals`が`2`の場合、`505`トークンの残高は + * `5,05` (`505 / 10 ** 2`)としてユーザーに表示されるべきです。 * - * Tokens usually opt for a value of 18, imitating the relationship between - * ether and wei. This is the value {ERC20} uses, unless {_setupDecimals} is - * called. + * トークンは通常、etherとweiの関係を模倣して、値18を選択します。これは、{_setupDecimals}が呼び出されない限り、{ERC20}が使用する値です。 * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. + * 注: この情報は_表示_目的でのみ使用されます。これは + * {IERC20-balanceOf}や{IERC20-transfer}を含む、コントラクトのどの算術にも + * 影響を与えません。 */ function decimals() public view returns (uint8) { return _decimals; } ``` -これらの関数(`name`、`symbol`、`decimals`)は、ユーザーインターフェースがコントラクトの情報を入手できるようするため、ユーザーインターフェースにコントラクトの情報が正しく表示されるようになります。 +これらの関数`name`、`symbol`、`decimals`は、ユーザーインターフェースがあなたのコントラクトについて知り、正しく表示できるようにするのに役立ちます。 -戻り値の型は、 `string memory`です。メモリに格納されている文字列が返されることを意味します。 文字列などの変数は、以下の3つの場所に格納できます。 +戻り値の型は`string memory`で、メモリに格納されている文字列を返すことを意味します。 文字列などの変数は、3つの場所に格納できます。 -| | 存続期間 | コントラクトアドレス | ガス代 | -| ------ | -------- | ---------- | ------------------------ | -| メモリ | 関数呼び出しの間 | 読み取り/書き込み | 数十または数百(上位のロケーションほど高い) | -| コールデータ | 関数呼び出しの間 | 読み取り専用 | 戻り値型としては使用できず、関数パラメータ型のみ | -| ストレージ | 変更されるまで | 読み取り/書き込み | 高額(読み取りに800、書き込みに20 k) | +| | 存続期間 | コントラクトアクセス | ガス代 | +| ------ | ------- | ---------- | ----------------------------------------- | +| Memory | 関数呼び出し中 | 読み取り/書き込み | 数十または数百(上位のロケーションほど高い) | +| コールデータ | 関数呼び出し中 | 読み取り専用 | 戻り値型としては使用できず、関数パラメータ型のみ | +| ストレージ | 変更されるまで | 読み取り/書き込み | 高額(読み取りに800、書き込みに20k) | このケースでは、`memory`の使用が最善の選択肢です。 @@ -378,7 +370,7 @@ contract ERC20 is Context, IERC20 { ```solidity /** - * @dev See {IERC20-totalSupply}. + * @dev {IERC20-totalSupply}を参照してください。 */ function totalSupply() public view override returns (uint256) { return _totalSupply; @@ -391,30 +383,30 @@ contract ERC20 is Context, IERC20 { ```solidity /** - * @dev See {IERC20-balanceOf}. + * @dev {IERC20-balanceOf}を参照してください。 */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } ``` -アカウントの残高を読み取ります。 誰でも他のユーザーのアカウント残高を取得できることに注意してください。 どのノードでも取得可能な情報であるため、隠そうとしても無駄です。 _ブロックチェーンに秘密はありません_。 +アカウントの残高を読み取ります。 誰でも他の人のアカウント残高を取得できることに注意してください。 この情報はどのノードでも入手可能であるため、隠そうとしても無駄です。 _ブロックチェーンに秘密はありません。_ ### トークンの転送 {#transfer-tokens} ```solidity /** - * @dev See {IERC20-transfer}. + * @dev {IERC20-transfer}を参照してください。 * - * Requirements: + * 要件: * - * - `recipient` cannot be the zero address. - * - the caller must have a balance of at least `amount`. + * - `recipient`はゼロアドレスであってはなりません。 + * - 呼び出し元は少なくとも`amount`の残高を持っている必要があります。 */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { ``` -`transfer`関数は、送信者のアカウントから別のアカウントにトークンを転送するために呼び出します。 戻り値がブール値になっていますが、この値は常に**true**を返すことに注意してください。 転送が失敗した場合、コントラクトは、呼び出しを取り消します。 +`transfer`関数は、送信者のアカウントから別のアカウントにトークンを転送するために呼び出します。 戻り値がブール値ですが、その値は常に**true**であることに注意してください。 転送が失敗した場合、コントラクトは呼び出しをリバートします。   @@ -424,44 +416,44 @@ contract ERC20 is Context, IERC20 { } ``` -`_transfer`関数は、実際の作業を行います。 これはprivate関数であり、他のコントラクト関数からのみ呼び出せます。 慣例として、private関数は状態変数と同様に、`_`のように命名されます。 +`_transfer`関数が実際の作業を行います。 これはprivate関数であり、他のコントラクト関数からのみ呼び出せます。 慣例として、private関数は状態変数と同様に`_`と命名されます。 -通常、Solidityでは、メッセージ送信者に`msg.sender`を使用します。 しかし、[OpenGSN](http://opengsn.org/)では使えません。 イーサ(ETH)無しのトークンのトランザクションを許可したい場合は、`_msgSender()`を使用しなければなりません。 通常のトランザクションでは、`msg.sender`を返しますが、イーサ(ETH)無しのトランザクションの場合は、メッセージを中継したコントラクトではなく、元の署名者を返します。 +通常、Solidityでは、メッセージ送信者に`msg.sender`を使用します。 しかし、それでは[OpenGSN](http://opengsn.org/)が機能しません。 トークンでetherレスのトランザクションを許可したい場合は、`_msgSender()`を使用する必要があります。 通常のトランザクションでは`msg.sender`を返しますが、etherレスのトランザクションの場合は、メッセージを中継したコントラクトではなく、元の署名者を返します。 -### 割当量(allowance)関連の関数 {#allowance-functions} +### 割当量関数 {#allowance-functions} -割当量機能を実装した関数には、`allowance`、`approve`、 `transferFrom`、`_approve`があります。 さらに、OpenZeppelin実装には、基本的な標準に加えてセキュリティを向上させる `increaseAllowance`や`decreaseAllowance`などの複数の機能が含まれます。 +これらは割当量機能を実装する関数です: `allowance`、`approve`、`transferFrom`、`_approve`。 さらに、OpenZeppelin実装は、セキュリティを向上させる機能である`increaseAllowance`と`decreaseAllowance`を含むように、基本的な標準を超えています。 #### allowance関数 {#allowance} ```solidity /** - * @dev See {IERC20-allowance}. + * @dev {IERC20-allowance}を参照してください。 */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } ``` -`allowance`関数を使用すると、誰でも割当量を確認することができます。 +`allowance`関数を使用すると、誰でも任意の割当量を確認することができます。 #### approve関数 {#approve} ```solidity /** - * @dev See {IERC20-approve}. + * @dev {IERC20-approve}を参照してください。 * - * Requirements: + * 要件: * - * - `spender` cannot be the zero address. + * - `spender`はゼロアドレスであってはなりません。 */ function approve(address spender, uint256 amount) public virtual override returns (bool) { ``` -この関数は、割当量を作成するときに呼び出します。 上述の`transfer`関数と似ています。 +この関数は、割当量を作成するときに呼び出します。 上記の`transfer`関数と似ています。 -- この関数は、単に実際に作業を行うinternal関数(この場合は`_approve`)を呼び出します。 -- この関数は、成功した場合は`true`を返し、失敗した場合は取り消します。 +- この関数は、単に実際に作業を行う内部関数(この場合は`_approve`)を呼び出します。 +- この関数は、成功した場合は`true`を返し、失敗した場合はリバートします。   @@ -471,25 +463,25 @@ contract ERC20 is Context, IERC20 { } ``` -internal関数を使用して、状態変更が起こる場所の数を最小限にしています。 状態を変更する_すべての_関数には、潜在的なセキュリティリスクがあり、セキュリティ監査が必要です。 このような方法で、間違いを犯す可能性を下げています。 +内部関数を使用して、状態変更が起こる場所の数を最小限にしています。 状態を変更する_すべての_関数には潜在的なセキュリティリスクがあり、セキュリティ監査が必要です。 この方法で、間違いを犯す可能性を下げています。 #### transferFrom関数 {#transferFrom} -これは、使用者(spender)が割当量(allowance)を使用するために呼び出す関数です。 これには、使う量の転送操作と、その量を割当量(allowance)から減らす操作の、2つの操作が必要になります。 +これは、使用者(`spender`)が割当量を使用するために呼び出す関数です。 これには、使用される量を転送し、その量だけ割当量を減らすという2つの操作が必要です。 ```solidity /** - * @dev See {IERC20-transferFrom}. + * @dev {IERC20-transferFrom}を参照してください。 * - * Emits an {Approval} event indicating the updated allowance. これは - * EIPでは必要ありません。 {ERC20}の最初にある注意事項を参照してください。 + * 更新された割当量を示す{Approval}イベントを発行します。これは + * EIPでは要求されていません。{ERC20}の冒頭の注記を参照してください。 * - * Requirements: + * 要件: * - * - `sender` and `recipient` cannot be the zero address. - * - `sender` must have a balance of at least `amount`. - * - the caller must have allowance for ``sender``'s tokens of at least - * `amount`. + * - `sender`と`recipient`はゼロアドレスであってはなりません。 + * - `sender`は少なくとも`amount`の残高を持っている必要があります。 + * - 呼び出し元は、`sender`のトークンに対して少なくとも + * `amount`の割当量を持っている必要があります。 */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { @@ -498,7 +490,8 @@ internal関数を使用して、状態変更が起こる場所の数を最小限   -`a.sub(b, "message")`関数の呼び出しでは、次の2つのことを行います。 まず、`a-b`を計算します。これが新しい割当量になります。 次に、この結果が負の数になっていないかをチェックします。 負になっている場合、提供されているメッセージを表示して、呼び出しが取り消されます。 呼び出しが取り消されると、呼び出し中に実行されたすべての処理が無効になるため、`_transfer`を元に戻す必要がないことに注意してください。 +`a.sub(b, "message")`関数の呼び出しでは、次の2つのことを行います。 まず、`a-b`を計算します。これが新しい割当量になります。 +次に、この結果が負の数になっていないかをチェックします。 負になっている場合、提供されているメッセージを表示して呼び出しがリバートされます。 呼び出しがリバートされると、その呼び出し中に以前に実行されたすべての処理は無視されるため、`_transfer`を元に戻す必要はありません。 ```solidity _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, @@ -507,50 +500,49 @@ internal関数を使用して、状態変更が起こる場所の数を最小限 } ``` -#### OpenZeppelinによる安全性の向上 {#openzeppelin-safety-additions} +#### OpenZeppelinの安全追加機能 {#openzeppelin-safety-additions} -ゼロ以外の割当量を別のゼロ以外の値に設定することには、リスクが伴います。自分が制御できるのは自分のトランザクションの順序のみであり、他のユーザーのトランザクションの順序を制御することはできないからです。 アリスという初心者ユーザーと、ビルという誠実さに欠けるユーザーがいるとします。 アリスは、ビルが提供しているサービスを購入することにしました。購入には5トークンの費用がかかるため、アリスは5トークンの割当量(allowance)をビルに付与しました。 +ゼロ以外の割当量を別のゼロ以外の値に設定することにはリスクが伴います。なぜなら、自分が制御できるのは自分のトランザクションの順序のみであり、他のユーザーのトランザクションの順序は制御できないからです。 経験の浅いアリスと、不誠実なビルという2人のユーザーがいるとします。 アリスはビルからサービスを受けたいと考えており、その費用は5トークンだと思っています。そこで、彼女はビルに5トークンの割当量を与えます。 -その後、ビルの設定した価格が何らかの理由で10トークンに上がりました。 アリスは依然としてそのサービスの購入を希望しており、ビルへの割当量を10に設定したトランザクションを送信しました。 ビルは、トランザクションプールでこの新しいトランザクションを確認した瞬間に、より早くマイニングされるようにかなり高いガス代を設定した、アリスの5トークンを使うトランザクションを送信します。 この方法で、ビルは5トークンを使います。その後、アリスが送信した新しい割当量がマイニングされたら、さらに10トークンを使います。こうして、アリスが承認するつもりだった量を超える、合計15トークンを使えることになります。 この手法は、[フロントランニング](https://consensysdiligence.github.io/smart-contract-best-practices/attacks/#front-running)と呼ばれます。 +その後、何かが変わり、ビルの価格は10トークンに上がりました。 まだサービスを受けたいアリスは、ビルの割当量を10に設定するトランザクションを送信します。 ビルはこの新しいトランザクションをトランザクションプールで確認した瞬間に、アリスの5トークンを使うトランザクションを送信し、より早くマイニングされるように非常に高いガス価格を設定します。 この方法で、ビルは最初に5トークンを使い、アリスの新しい割当量がマイニングされたら、さらに10トークンを使って合計15トークンを得ることができます。これはアリスが承認するつもりだった額よりも多いです。 このテクニックは[フロントランニング](https://consensysdiligence.github.io/smart-contract-best-practices/attacks/#front-running)と呼ばれます。 -| アリスのトランザクション | アリスのノンス | ビルのトランザクション | ビルのノンス | ビルの割当量 | ビルのアリスからの総収入 | -| ----------------- | ------- | ----------------------------- | ------ | ------ | ------------ | -| approve(Bill, 5) | 10 | | | 5 | 0 | -| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | -| approve(Bill, 10) | 11 | | | 10 | 5 | -| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 15 | +| アリスのトランザクション | アリスのノンス | ビルのトランザクション | ビルのノンス | ビルの割当量 | ビルのアリスからの総収入 | +| ------------------------------------ | ------- | ------------------------------------------------ | ------ | ------ | ------------ | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | +| approve(Bill, 10) | 11 | | | 10 | 5 | +| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 15 | -この問題を回避するには、割当量を特定の量に変更できる2つの関数(`increaseAllowance`と`decreaseAllowance`)を使用します。 これらの関数を使用すれば、ビルがすでに5トークンを使用していた場合、その後に使用できるのは残りの5トークンのみとなります。 タイミングに応じて、次のような2つの動作が考えられますが、いずれの場合でもビルが取得するのは10トークンのみです。 +この問題を回避するには、2つの関数(`increaseAllowance`と`decreaseAllowance`)を使用して、割当量を特定の量だけ変更します。 これにより、ビルがすでに5トークンを使用していた場合でも、その後に使用できるのは追加の5トークンのみとなります。 タイミングに応じて、次のような2つの動作が考えられますが、いずれの場合でもビルが取得するのは10トークンのみです。 A: -| アリスのトランザクション | アリスのノンス | ビルのトランザクション | ビルのノンス | ビルの割当量 | ビルのアリスからの総収入 | -| -------------------------- | -------:| ---------------------------- | ------:| -------:| ------------ | -| approve(Bill, 5) | 10 | | | 5 | 0 | -| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | -| increaseAllowance(Bill, 5) | 11 | | | 0+5 = 5 | 5 | -| | | transferFrom(Alice, Bill, 5) | 10,124 | 0 | 10 | +| アリスのトランザクション | アリスのノンス | ビルのトランザクション | ビルのノンス | ビルの割当量 | ビルのアリスからの総収入 | +| --------------------------------------------- | ------: | ----------------------------------------------- | -----: | ------: | ------------ | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | +| increaseAllowance(Bill, 5) | 11 | | | 0+5 = 5 | 5 | +| | | transferFrom(Alice, Bill, 5) | 10,124 | 0 | 10 | B: -| アリスのトランザクション | アリスのノンス | ビルのトランザクション | ビルのノンス | ビルの割当量 | ビルのアリスからの総収入 | -| -------------------------- | -------:| ----------------------------- | ------:| --------:| ------------:| -| approve(Bill, 5) | 10 | | | 5 | 0 | -| increaseAllowance(Bill, 5) | 11 | | | 5+5 = 10 | 0 | -| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 10 | +| アリスのトランザクション | アリスのノンス | ビルのトランザクション | ビルのノンス | ビルの割当量 | ビルのアリスからの総収入 | +| --------------------------------------------- | ------: | ------------------------------------------------ | -----: | -------: | -----------: | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| increaseAllowance(Bill, 5) | 11 | | | 5+5 = 10 | 0 | +| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 10 | ```solidity /** - * @dev Atomically increases the allowance granted to `spender` by the caller. + * @dev 呼び出し元によって付与された`spender`への割当量をアトミックに増加させます。 * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. + * これは{approve}の代替手段であり、{IERC20-approve}で説明されている問題を緩和するために使用できます。 * - * Emits an {Approval} event indicating the updated allowance. + * 更新された割当量を示す{Approval}イベントを発行します。 * - * Requirements: + * 要件: * - * - `spender` cannot be the zero address. + * - `spender`はゼロアドレスであってはなりません。 */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); @@ -558,23 +550,22 @@ B: } ``` -`a.add(b)`関数は、安全な加算です。 万が一、`a`+`b`>=`2^256`のような計算が行われても、通常の加算で発生してしまうオーバー(アンダー)フローが発生しません。 +`a.add(b)`関数は、安全な加算です。 万が一`a`+`b`>=`2^256`となっても、通常の加算のようにラップアラウンドしません。 ```solidity /** - * @dev Atomically decreases the allowance granted to `spender` by the caller. + * @dev 呼び出し元によって付与された`spender`への割当量をアトミックに減少させます。 * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. + * これは{approve}の代替手段であり、{IERC20-approve}で説明されている問題を緩和するために使用できます。 * - * Emits an {Approval} event indicating the updated allowance. + * 更新された割当量を示す{Approval}イベントを発行します。 * - * Requirements: + * 要件: * - * - `spender` cannot be the zero address. - * - `spender` must have allowance for the caller of at least - * `subtractedValue`. + * - `spender`はゼロアドレスであってはなりません。 + * - `spender`は呼び出し元に対して、少なくとも + * `subtractedValue`の割当量を持っている必要があります。 */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, @@ -587,27 +578,27 @@ B: 次の4つの関数(`_transfer`、`_mint`、`_burn`、`_approve`)は、実際の処理を行います。 -#### \_transfer関数 {#_transfer} +#### _transfer関数 {#_transfer} ```solidity /** - * @dev Moves tokens `amount` from `sender` to `recipient`. + * @dev トークン`amount`を`sender`から`recipient`に移動させます。 * - * This is internal function is equivalent to {transfer}, and can be used to - * e.g., implement automatic token fees, slashing mechanisms, etc. + * この内部関数は{transfer}と同等であり、 + * 例えば、自動的なトークン手数料やスラッシングメカニズムなどを実装するために使用できます。 * - * Emits a {Transfer} event. + * {Transfer}イベントを発行します。 * - * Requirements: + * 要件: * - * - `sender` cannot be the zero address. - * - `recipient` cannot be the zero address. - * - `sender` must have a balance of at least `amount`. + * - `sender`はゼロアドレスであってはなりません。 + * - `recipient`はゼロアドレスであってはなりません。 + * - `sender`は少なくとも`amount`の残高を持っている必要があります。 */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { ``` -`_transfer`関数は、トークンをあるアカウントから別のアカウントへ転送します。 この関数は、(送信者自身のアカウントから転送する)`transfer`と、(割当量を使用するために他のユーザーのアカウントから転送する)`transferFrom`の両方から呼び出されます。 +この`_transfer`関数は、トークンをあるアカウントから別のアカウントへ転送します。 この関数は、`transfer`(送信者自身のアカウントからの転送)と`transferFrom`(割当量を使用して他のユーザーのアカウントから転送)の両方から呼び出されます。   @@ -628,11 +619,11 @@ B: このコントラクトを使用するには2つの方法があります。 1. 自分のコードのテンプレートとして使う -1. [このコントラクトを継承](https://www.bitdegree.org/learn/solidity-inheritance)し、変更する必要がある関数のみをオーバーライドする +2. [コントラクトから継承](https://www.bitdegree.org/learn/solidity-inheritance)し、修正が必要な関数のみをオーバーライドする -OpenZeppelin ERC-20のコードはすでに監査を受けており、安全であることが知られているため、2つ目の方法をお勧めします。 継承を使用すると、変更した関数が明らかになります。他のユーザーは、変更された特定の関数を監査するだけで、そのコントラクトを信頼することができます。 +OpenZeppelinのERC-20コードはすでに監査を受けており、安全であることが示されているため、2つ目の方法がはるかに優れています。 継承を使用すると、変更した関数が明らかになり、人々はコントラクトを信頼するためにそれらの特定の関数のみを監査すればよくなります。 -多くの場合、トークンの所有者が変わるたびに関数を実行すると便利です。 ただし、`_transfer`は非常に重要な関数ですが、安全でない書き込みをしてしまう可能性があるため(下記参照)、オーバーライドしないことをお勧めします。 この解決策として、`_beforeTokenTransfer`という[フック関数](https://wikipedia.org/wiki/Hooking)があります。 この関数をオーバライドすれば、転送のたびに呼び出されるようになります。 +多くの場合、トークンの所有者が変わるたびに関数を実行すると便利です。 しかし、`_transfer`は非常に重要な関数であり、安全でない書き方をしてしまう可能性があるため(下記参照)、オーバーライドしないことをお勧めします。 解決策は、[フック関数](https://wikipedia.org/wiki/Hooking)である`_beforeTokenTransfer`です。 この関数をオーバーライドすれば、転送のたびに呼び出されるようになります。   @@ -641,7 +632,7 @@ OpenZeppelin ERC-20のコードはすでに監査を受けており、安全で _balances[recipient] = _balances[recipient].add(amount); ``` -この2行で、実際に転送を行っています。 この2行の間に**何もない**ことと、転送する量を送信者から減算してから、その量を受取人に加算していることに注意してください。 この2行の間に別のコントラクトへの呼び出しがある場合、このコントラクトで不正を行うために使用される可能性があるため、このことは非常に重要になります。 こうすることで、転送がアトミックになる(他の操作に割り込まれないようになる)ため、転送の途中で何かが発生することはなくなります。 +この2行で、実際に転送を行っています。 この2行の間には**何も**なく、受取人に加算する前に送信者から転送額を減算していることに注意してください。 この2行の間に別のコントラクトへの呼び出しがある場合、このコントラクトで不正を行うために使用される可能性があるため、このことは非常に重要になります。 こうすることで、転送がアトミックになり、その途中で何も起こらなくなります。   @@ -650,23 +641,25 @@ OpenZeppelin ERC-20のコードはすでに監査を受けており、安全で } ``` -最後に、`Transfer`イベントを発行します。 イベントは、スマートコントラクトからアクセスできません。しかし、ブロックチェーンの外で実行されているコードは、イベントをリッスンして対応することができます。 例えば、ウォレットで所有者がより多くのトークンを得た時期を追跡できます。 +最後に、`Transfer`イベントを発行します。 イベントはスマートコントラクトからアクセスできませんが、ブロックチェーンの外で実行されているコードは、イベントをリッスンして対応することができます。 例えば、ウォレットは所有者がより多くのトークンを得た時期を追跡できます。 -#### \_mint関数と\_burn関数 {#_mint-and-_burn} +#### _mint関数と_burn関数 {#_mint-and-_burn} -この2つの関数(`_mint`と`_burn`)は、トークンの総供給量を変更します。 これらはinternalであり、このコントラクト内にこれらを呼び出す関数はありません。そのため、コントラクトを継承し、新しいトークンのミントや既存のトークンのバーンを実行する条件を決定する独自のロジックを追加する場合にのみ役立ちます。 +この2つの関数(`_mint`と`_burn`)は、トークンの総供給量を変更します。 +これらは内部関数であり、このコントラクト内にこれらを呼び出す関数はありません。そのため、コントラクトを継承し、どのような条件下で新しいトークンをミントし、既存のトークンをバーンするかを決定する独自のロジックを追加する場合にのみ役立ちます。 -**注:** すべてのERC-20トークンには、トークン管理を規定している独自のビジネスロジックがあります。 例えば、固定した供給量のコントラクトでは、コンストラクタ内で `_mint`のみを呼び出す可能性があり、`_burn`を呼び出すことはありません。 トークンを販売するコントラクトは、支払いが行われたタイミングで`_mint`を呼び出し、天井知らずのインフレを避けるために、ある時点で`_burn`を呼び出すことが考えられます。 +**注:** すべてのERC-20トークンには、トークン管理を規定する独自のビジネスロジックがあります。 +例えば、固定供給量のコントラクトでは、コンストラクタ内で`_mint`のみを呼び出し、`_burn`を呼び出すことはありません。 トークンを販売するコントラクトは、支払いが行われたタイミングで`_mint`を呼び出し、おそらく、天井知らずのインフレを避けるためにある時点で`_burn`を呼び出します。 ```solidity - /** @dev Creates `amount` tokens and assigns them to `account`, increasing - * the total supply. + /** @dev `amount`のトークンを作成して`account`に割り当て、 + * 総供給量を増やします。 * - * Emits a {Transfer} event with `from` set to the zero address. + * `from`がゼロアドレスに設定された{Transfer}イベントを発行します。 * - * Requirements: + * 要件: * - * - `to` cannot be the zero address. + * - `to`はゼロアドレスであってはなりません。 */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); @@ -677,21 +670,21 @@ OpenZeppelin ERC-20のコードはすでに監査を受けており、安全で } ``` -トークンの総額が変更された場合は、`_totalSupply`を必ずアップデートしてください。 +トークンの総数が変更された場合は、`_totalSupply`を必ずアップデートしてください。   -``` +```solidity /** - * @dev Destroys `amount` tokens from `account`, reducing the - * total supply. + * @dev `account`から`amount`のトークンを破棄し、 + * 総供給量を減らします。 * - * Emits a {Transfer} event with `to` set to the zero address. + * `to`がゼロアドレスに設定された{Transfer}イベントを発行します。 * - * Requirements: + * 要件: * - * - `account` cannot be the zero address. - * - `account` must have at least `amount` tokens. + * - `account`はゼロアドレスであってはなりません。 + * - `account`は少なくとも`amount`のトークンを持っている必要があります。 */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); @@ -706,23 +699,23 @@ OpenZeppelin ERC-20のコードはすでに監査を受けており、安全で `_burn`関数は、方向が逆であることを除き`_mint`とほぼ同じです。 -#### \_approve関数 {#_approve} +#### _approve関数 {#_approve} -これは、実際の割当量(allowance)を指定する関数です。 所有者は自身の現在の残高よりも高い割当量を指定できることに注意してください。 残高は転送時にチェックされるため、割当量の作成時の残高と異なっていても問題ありません。 +これは、実際に割当量を指定する関数です。 所有者は自身の現在の残高よりも高い割当量を指定できることに注意してください。 残高は転送時にチェックされ、割当量の作成時の残高と異なる可能性があるため、これは問題ありません。 ```solidity /** - * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. + * @dev `owner`のトークンに対する`spender`の割当量を`amount`に設定します。 * - * This internal function is equivalent to `approve`, and can be used to - * e.g., set automatic allowances for certain subsystems, etc. + * この内部関数は`approve`と同等であり、 + * 例えば、特定のサブシステムに対する自動的な割当量などを設定するために使用できます。 * - * Emits an {Approval} event. + * {Approval}イベントを発行します。 * - * Requirements: + * 要件: * - * - `owner` cannot be the zero address. - * - `spender` cannot be the zero address. + * - `owner`はゼロアドレスであってはなりません。 + * - `spender`はゼロアドレスであってはなりません。 */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); @@ -733,7 +726,7 @@ OpenZeppelin ERC-20のコードはすでに監査を受けており、安全で   -`Approval`イベントを発行します。 アプリケーションがどのように書かれているかによって異なりますが、使用者(spender)のコントラクトには、所有者またはこれらのイベントをリッスンしているサーバーのいずれかによって承認(Approval)が通知されます。 +`Approval`イベントを発行します。 アプリケーションがどのように書かれているかによって異なりますが、使用者(`spender`)のコントラクトには、所有者またはこれらのイベントをリッスンしているサーバーのいずれかによって承認が通知されます。 ```solidity emit Approval(owner, spender, amount); @@ -741,57 +734,61 @@ OpenZeppelin ERC-20のコードはすでに監査を受けており、安全で ``` -### 小数変数の変更 {#modify-the-decimals-variable} +### decimals変数の変更 {#modify-the-decimals-variable} ```solidity /** - * @dev Sets {decimals} to a value other than the default one of 18. + * @dev {decimals}をデフォルト値の18以外の値に設定します。 * - * WARNING: This function should only be called from the constructor. Most - * applications that interact with token contracts will not expect - * {decimals} to ever change, and may work incorrectly if it does. + * 警告: この関数はコンストラクタからのみ呼び出すべきです。ほとんどの + * トークンコントラクトと対話するアプリケーションは、 + * {decimals}が変更されることを想定しておらず、変更されると誤動作する可能性があります。 */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } ``` -この関数は、`_decimals`変数を変更します。`_decimals`変数は、量(金額)の解釈方法をユーザーインターフェースに伝えるのに使用されます。 コンストラクタから呼び出される必要があります。 その後のどの時点においても、この関数を呼び出すと不正になります。アプリケーションは、このような処理をするようには設計されていません。 +この関数は`_decimals`変数を変更します。この変数は、量の解釈方法をユーザーインターフェースに伝えるのに使用されます。 +コンストラクタから呼び出すべきです。 その後のどの時点においてもこの関数を呼び出すと不正になり、アプリケーションはこのような処理をするようには設計されていません。 ### フック {#hooks} ```solidity /** - * @dev Hook that is called before any transfer of tokens. これには + * @dev トークンのあらゆる転送の前に呼び出されるフック。これには * ミントとバーンが含まれます。 * - * Calling conditions: + * 呼び出し条件: * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * will be to transferred to `to`. - * - when `from` is zero, `amount` tokens will be minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens will be burned. - * - `from` and `to` are never both zero. + * - `from`と`to`が両方ともゼロでない場合、`from`のトークンの`amount`が + * `to`に転送されます。 + * - `from`がゼロの場合、`amount`のトークンが`to`のためにミントされます。 + * - `to`がゼロの場合、`from`のトークンの`amount`がバーンされます。 + * - `from`と`to`が両方ともゼロになることはありません。 * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + * フックについてさらに学ぶには、xref:ROOT:extending-contracts.adoc#using-hooks[フックの使用]を参照してください。 */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } ``` -このフック関数は、転送中に呼び出されます。 空になっていますが、何かを実行するのにこの関数が必要な場合は、オーバーライドしてください。 +このフック関数は、転送中に呼び出されます。 ここでは空になっていますが、何かを実行するのにこの関数が必要な場合は、オーバーライドしてください。 -## まとめ {#conclusion} +## 結論 {#conclusion} 確認のため、このコントラクトの最も重要な点を以下にまとめています(個人的な意見のため、他者にとって重要な点とは異なる場合があります) 。 -- _ブロックチェーンに秘密はありません_。 スマートコントラクトがアクセスできる情報は、世界中で利用可能であることを意味します。 -- 自分のトランザクションの順序は自分で制御できますが、他のユーザーのトランザクションが発生するタイミングは制御できません。 これが、割当量(allowance)の変更に伴うリスクになります。変更により、使用者(spender)が変更前と変更後の両方の割当量を使用できてしまうためです。 -- `uint256`型の値がオーバー(アンダー)フローします。 つまり、例えば_0-1=2^256-1_です。 これが望ましい動作ではない場合、プログラムで確認する必要があります(または、それを行うSafeMathライブラリを使用します)。 この仕様は、[Solidity 0.8.0](https://docs.soliditylang.org/en/breaking/080-breaking-changes.html)で変更されていることに注意してください。 -- 監査を容易にするため、特定の場所で特定の型のすべての状態変更を行います。 例えば、`_approve`が、`approve`、`transferFrom`、`increaseAllowance`、`decreaseAllowance`などによって呼び出されるのは、この理由です。 -- (`_transfer`で見られるように)状態変更は、処理の途中で他の操作に割り込まれることがないアトミックである必要があります。 これは状態変更中に、一貫性のない状態が存在するためです。 例えば、送信者の残高からトークンを差し引いた時点から、受取人の残高にそのトークンを加えるまでの間は、存在すべき数よりも少ない数のトークンが存在することになります。 この2つの処理の間に別の操作(特に、異なるコントラクトの呼び出しなど)がある場合、このトークンの状態が悪用される可能性があります。 +- ブロックチェーンには秘密はありません。 スマートコントラクトがアクセスできる情報は、全世界で利用可能です。 +- 自分のトランザクションの順序は自分で制御できますが、他のユーザーのトランザクションが発生するタイミングは制御できません。 これが、割当量の変更が危険となりうる理由です。変更により、使用者(`spender`)が両方の割当量の合計を使用できてしまうためです。 +- `uint256`型の値はラップアラウンドします。 言い換えると、_0-1=2^256-1_となります。 これが望ましい動作ではない場合、プログラムで確認する必要があります(または、それを行うSafeMathライブラリを使用します)。 これは[Solidity 0.8.0](https://docs.soliditylang.org/en/breaking/080-breaking-changes.html)で変更されたことに注意してください。 +- 監査を容易にするため、特定の場所で特定の型のすべての状態変更を行います。 + これが、例えば`approve`、`transferFrom`、`increaseAllowance`、`decreaseAllowance`によって呼び出される`_approve`が存在する理由です。 +- 状態変更は、(`_transfer`で見られるように)処理の途中で他のアクションに割り込まれることがないアトミックである必要があります。 これは状態変更中に、一貫性のない状態が存在するためです。 例えば、送信者の残高から差し引いた時点から、受取人の残高に加えるまでの間は、存在するべき数よりも少ないトークンが存在することになります。 この2つの処理の間に別の操作(特に、異なるコントラクトの呼び出しなど)がある場合、このトークンの状態が悪用される可能性があります。 + +ここまで、OpenZeppelin ERC-20コントラクトがどのように書かれているか、特に、より安全に記述する方法を学びました。是非自分でも安全なコントラクトとアプリケーションを作成してみてください。 -ここまで、OpenZeppelin ERC-20コントラクトがどのように書かれているかについて見てきました。特に、より安全に記述する方法を学びました。是非自分でも安全なコントラクトとアプリケーションを作成してみてください。 +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). diff --git a/public/content/translations/ja/developers/tutorials/erc20-with-safety-rails/index.md b/public/content/translations/ja/developers/tutorials/erc20-with-safety-rails/index.md index ed97fc06bfd..26fabbd4847 100644 --- a/public/content/translations/ja/developers/tutorials/erc20-with-safety-rails/index.md +++ b/public/content/translations/ja/developers/tutorials/erc20-with-safety-rails/index.md @@ -1,10 +1,9 @@ --- -title: ERC-20の安全策 -description: つまらないミスをするのを避ける方法 +title: "安全策を備えたERC-20" +description: "ユーザーのうっかりミスを防ぐ方法" author: Ori Pomerantz lang: ja -tags: - - "ERC-20" +tags: [ "ERC-20" ] skill: beginner published: 2022-08-15 --- @@ -13,48 +12,51 @@ published: 2022-08-15 イーサリアムの素晴らしい点の1つとして、トランザクションを変更したり取り消したりできる中央機関が存在しないことがあります。 反対に、イーサリアムでは、ユーザーの間違いや不正なトランザクションを取り消す権限を持つ中央機関が存在しないことがデメリットになりえます。 この記事では、[ERC-20](/developers/docs/standards/tokens/erc-20/)トークンでユーザーがしてしまうよくあるミスや、そのミスを防ぐトークンの作成方法について説明します。また、中央機関に権限を与えることについても説明します (例えば、アカウントの凍結など) 。 -注意: [OpenZeppelin ERC-20トークンコントラクト](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20)を使いますが、このコントラクト自体について詳しくは説明しません。 ERC-20トークンコントラクトの詳細については、[こちら](/developers/tutorials/erc20-annotated-code)をご覧ください。 +この記事では[OpenZeppelin ERC-20トークンコントラクト](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20)を使用しますが、その詳細については説明しませんのでご注意ください。 この情報については、[こちら](/developers/tutorials/erc20-annotated-code)をご覧ください。 -全てのソースコードを表示したい場合は、次のようにします。 +完全なソースコードを確認したい場合は、以下を参照してください。 1. [Remix IDE](https://remix.ethereum.org/)を開きます。 -2. クローンGitHubアイコン (![clone github icon](icon-clone.png)) をクリックします。 +2. GitHubのクローンアイコン(![GitHubのクローンアイコン](icon-clone.png))をクリックします。 3. GitHubリポジトリ`https://github.com/qbzzt/20220815-erc20-safety-rails`をクローンします。 -4. 「**contracts > erc20-safety-rails.sol**」を開きます。 +4. **contracts > erc20-safety-rails.sol**を開きます。 ## ERC-20コントラクトの作成 {#creating-an-erc-20-contract} -安全策を講じるための機能を追加する前に、ERC-20コントラクトが必要になります。 この記事では、[OpenZeppelin Contracts Wizard](https://docs.openzeppelin.com/contracts/5.x/wizard)を使って加えます。 もう一つブラウザで開いて、次の手順に従ってください。 +安全策を講じるための機能を追加する前に、ERC-20コントラクトが必要になります。 この記事では、[OpenZeppelin Contracts Wizard](https://docs.openzeppelin.com/contracts/5.x/wizard)を使用します。 別のブラウザで開き、以下の手順に従ってください。 + +1. **ERC20**を選択します。 -1. **ERC20**を選びます。 2. 次の設定値を入力します。 | パラメータ | 値 | | -------------- | ---------------- | | 名前 | SafetyRailsToken | - | Symbol | SAFE | + | 記号 | SAFE | | Premint | 1000 | | 機能 | なし | | Access Control | Ownable | | Upgradability | なし | -3. 上にスクロールして (Remixを使う場合は) **Open in Remix**をクリックしてください。別の環境を使う場合は、**ダウンロード**をクリックしてください。 ここでは、Remixを使用していることとします。他の環境を使用する場合は、適宜変更してください。 -4. これで完全なERC-20コントラクトがあります。 「`.deps` > `npm`」でインポートしたコードを展開して確認できます。 -5. コントラクトをコンパイル、デプロイ、そして操作してERC-20 コントラクトとして機能していることを確認します。 Remixの使用方法を学びたいならば、[このチュートリアルが役立ちます](https://remix.ethereum.org/?#activate=udapp,solidity,LearnEth)。 +3. 上にスクロールして、(Remixの場合は) **Open in Remix** を、別の環境を使用する場合は **Download** をクリックします。 ここでは、Remixを使用していることとします。他の環境を使用する場合は、適宜変更してください。 + +4. これで完全に機能するERC-20コントラクトができました。 `.deps` > `npm` を展開すると、インポートされたコードを確認できます。 -## よくあるミス {#common-mistakes} +5. コントラクトをコンパイル、デプロイ、そして操作して、ERC-20コントラクトとして機能していることを確認します。 Remixの使い方を学ぶ必要がある場合は、[このチュートリアル](https://remix.ethereum.org/?#activate=udapp,solidity,LearnEth)を使用してください。 -### ミスのタイプ {#the-mistakes} +## よくある間違い {#common-mistakes} -ユーザーは、間違ったアドレスへトークンを送信してしまうことがあります。 なぜ間違って送ってしまった理由を知ることはできませんが、よく発生するミスのタイプで頻繁に検出できる次のものがあります。 +### 間違い {#the-mistakes} -1. トークンをコントラクト自身のアドレスに送信する。 例えば、[OptimismのOPトークン](https://optimism.mirror.xyz/qvd0WfuLKnePm1Gxb9dpGchPf5uDz5NSMEFdgirDS4c)では、[12万](https://optimistic.etherscan.io/address/0x4200000000000000000000000000000000000042#tokentxns)を超えるOPトークンが2か月もしないうちに累積していることがわかります。 これは、人々の膨大な資産がただ単に失われていることを表しています。 +ユーザーは、間違ったアドレスへトークンを送信してしまうことがあります。 ユーザーが何を意図していたかを知ることはできませんが、頻繁に発生し、かつ検出しやすいエラーには2つのタイプがあります。 -2. トークンを[外部所有アカウント](/developers/docs/accounts/#externally-owned-accounts-and-key-pairs)や[スマートコントラクト](/developers/docs/smart-contracts)に相当しない空アドレスへ送信する。 このミスがどのくらいの頻度で発生するかについての統計はありません。[1件のインシデントで2千万トークンを失っているものもあります](https://gov.optimism.io/t/message-to-optimism-community-from-Wintermute/2595)。 +1. トークンをコントラクト自身のアドレスに送信する。 例えば、[OptimismのOPトークン](https://optimism.mirror.xyz/qvd0WfuLKnePm1Gxb9dpGchPf5uDz5NSMEFdgirDS4c)は、2か月足らずで[120,000](https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000042)以上のOPトークンを蓄積してしまいました。 これは、おそらく人々が失ってしまったであろう、かなりの額の資産に相当します。 -### 送金を防止する {#preventing-transfers} +2. トークンを空のアドレス、つまり[外部所有アカウント](/developers/docs/accounts/#externally-owned-accounts-and-key-pairs)や[スマートコントラクト](/developers/docs/smart-contracts)ではないアドレスに送信してしまうこと。 これがどのくらいの頻度で発生するかについての統計はありませんが、[あるインシデントでは20,000,000トークンが失われる可能性がありました](https://gov.optimism.io/t/message-to-optimism-community-from-wintermute/2595)。 -OpenZeppelinのERC-20コントラクトには、[`_beforeTokenTransfer`というフック](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol#L364-L368)があり、トークンを送金する前に呼び出されます。 デフォルトでは、このフックは何も行いません。しかし、独自の機能をフックに掛けることで、問題がある場合に元に戻すなどのチェックが可能です。 +### 送金の防止 {#preventing-transfers} + +OpenZeppelinのERC-20コントラクトには、トークンが送金される前に呼び出される[`_beforeTokenTransfer`というフック](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol#L364-L368)が含まれています。 デフォルトでは、このフックは何も行いませんが、問題がある場合にトランザクションをリバートするチェックなど、独自の機能を実装するために利用できます。 このフックを使用するには、コンストラクタの後に次の関数を加えます。 @@ -67,42 +69,42 @@ OpenZeppelinのERC-20コントラクトには、[`_beforeTokenTransfer`という } ``` -Solidityにあまり詳しくない人ならば、次の関数の箇所は馴染みがないかもしれません。 +Solidityにあまり詳しくない方には、この関数の一部の要素は目新しいかもしれません。 ```solidity internal virtual ``` -上記の`virtual`キーワードでは、`ERC20`から機能を継承し、関数をオーバーライドして、他のコントラクトも同様にこのコントラクトの機能を継承して、この関数をオーバーライドできるようにしています。 +`virtual`キーワードは、私たちが`ERC20`から機能を継承してこの関数をオーバーライドしたのと同じように、他のコントラクトが私たちのコントラクトから継承してこの関数をオーバーライドできることを意味します。 ```solidity override(ERC20) ``` -ERC20トークンの`_beforeTokenTransfer`の定義を[オーバーライド](https://docs.soliditylang.org/en/v0.8.15/contracts.html#function-overriding)することを明示的に指定しなければなりません。 一般的なセキュリティの観点において、暗黙的な定義よりも明示的な定義の方がはるかに良いとされています。記述されていれば、それが実行されることを忘れないからです。 オーバーライドするスーパークラスの `_beforeTokenTransfer`を指定しなければならないのも同様の理由です。 +`_beforeTokenTransfer`のERC20トークン定義を[オーバーライド](https://docs.soliditylang.org/en/v0.8.15/contracts.html#function-overriding)していることを明示的に指定する必要があります。 一般的に、セキュリティの観点からは、暗黙的な定義よりも明示的な定義の方がはるかに優れています。目の前にあれば、何かをしたことを忘れることはありません。 これが、どのスーパークラスの`_beforeTokenTransfer`をオーバーライドしているかを指定する必要がある理由でもあります。 ```solidity super._beforeTokenTransfer(from, to, amount); ``` -上記は、継承しているコントラクトから継承元のコントラクトの `_beforeTokenTransfer`関数を呼び出しています。 この場合は、`ERC20`のみであり、`Ownable`にはこのフックがありません。 現時点では、`ERC20._beforeTokenTransfer`は何も行いません。コントラクトはデプロイ後に変更できないため、再デプロイによって将来に機能が追加された場合に備えて呼び出しています。 +この行は、継承元のコントラクトのうち、`_beforeTokenTransfer`関数を持つものの関数を呼び出します。 この場合、それは`ERC20`のみです。`Ownable`にはこのフックはありません。 現在`ERC20._beforeTokenTransfer`は何も行いませんが、将来機能が追加された場合に備えて呼び出します (コントラクトはデプロイ後に変更できないため、その場合はコントラクトを再デプロイすることになります)。 ### 要件のコーディング {#coding-the-requirements} -次の要件を関数に対して加えたいと思います。 +この関数に以下の要件を追加します。 -- `to`アドレスをERC-20コントラクト自体のアドレスである`address(this)`と等しくできないようにすること。 -- `to`アドレスを空にすることができないこと。また、次のいずれかであること。 - - 外部所有アカウント (EOA) 。 アドレスがEOAであるかどうかを直接確認することはできませんが、アドレスのETH残高を確認することはできます。 EOAは、たとえ使用されなくなったとしても、ほとんどの場合、残高が残っています。これは、最後のweiまで使うのは困難だからです。 - - スマートコントラクト。 アドレスがスマートコントラクトであるかどうかのテストは少し大変です。 [`EXTCODESIZE`](https://www.evm.codes/#3b)という外部コードの長さをチェックするオペコードがありますが、Solidityでは直接使用することはできません。 これには、[Yul](https://docs.soliditylang.org/en/v0.8.15/yul.html)というEVMアセンブリを使う必要があります。 Solidityから使用できる他の値 ([ `
.code`および `
.codehash`](https://docs.soliditylang.org/en/v0.8.15/units-and-global-variables.html#members-of-address-types)) もありますが、コストがそれよりも高くなります。 +- `to`アドレスは、ERC-20コントラクト自体のアドレスである`address(this)`であってはならない。 +- `to`アドレスは空であってはならず、以下のいずれかでなければならない。 + - 外部所有アカウント (EOA)。 アドレスがEOAであるかどうかを直接確認することはできませんが、アドレスのETH残高は確認できます。 EOAは、使用されなくなった後でもほとんどの場合、残高が残っています。最後のweiまで使い切るのは困難だからです。 + - スマートコントラクト。 アドレスがスマートコントラクトであるかどうかのテストは、少し難しくなります。 外部コード長をチェックする[`EXTCODESIZE`](https://www.evm.codes/#3b)というオペコードがありますが、Solidityで直接利用することはできません。 そのためには、EVMアセンブリである[Yul](https://docs.soliditylang.org/en/v0.8.15/yul.html)を使用する必要があります。 Solidityから使用できる他の値 ([`
.code` や `
.codehash`](https://docs.soliditylang.org/en/v0.8.15/units-and-global-variables.html#members-of-address-types)) もありますが、それらはより多くのコストがかかります。 -新しいコードを1行ずつ見てみましょう。 +新しいコードを1行ずつ見ていきましょう。 ```solidity - require(to != address(this), "Can't send tokens to the contract address"); + require(to != address(this), "コントラクトアドレスにトークンを送信することはできません"); ``` -これが最初の要件です。`to`と`this(address)`が等しくないことを確認しています。 +これが最初の要件で、`to`と`address(this)`が同じでないことを確認します。 ```solidity bool isToContract; @@ -111,53 +113,53 @@ ERC20トークンの`_beforeTokenTransfer`の定義を[オーバーライド](ht } ``` -上記は、アドレスがコントラクトかどうかを確認する方法です。 Yulから出力を直接受け取ることはできません。そのため、代わりに結果を保持する変数を定義しています (この場合は `isToContract`) 。 Yulでは、すべてのオペコードが関数として動作します。 したがって、最初に[`EXTCODESIZE`](https://www.evm.codes/#3b)を呼び出してコントラクトサイズを取得し、次に[`GT`](https://www.evm.codes/#11)でゼロでないことを確認します (符号なし整数を扱っているため、当然、負の値にすることはできません) 。 その後、結果を`isToContract`に書き込んでいます。 +このようにして、アドレスがコントラクトであるかどうかを確認します。 Yulから直接出力を受け取ることはできないので、代わりに結果を保持する変数(この場合は`isToContract`)を定義します。 Yulは、すべてのオペコードが関数としてみなされる仕組みになっています。 そこで、まず[`EXTCODESIZE`](https://www.evm.codes/#3b)を呼び出してコントラクトのサイズを取得し、次に[`GT`](https://www.evm.codes/#11)を使ってそれがゼロでないことを確認します(符号なし整数を扱っているので、もちろん負になることはありません)。 そして、その結果を`isToContract`に書き込みます。 ```solidity - require(to.balance != 0 || isToContract, "Can't send tokens to an empty address"); + require(to.balance != 0 || isToContract, "空のアドレスにトークンを送信することはできません"); ``` -最後に、空アドレスのチェックをしています。 +そして最後に、空のアドレスに対する実際のチェックを行います。 ## 管理者アクセス {#admin-access} -間違いを取り消せる管理者がいると、便利なことがあります。 悪用される可能性を減らすには、管理者を[マルチシグ](https://blog.logrocket.com/security-choices-multi-signature-wallets/)にして、各アクションに対する複数人の同意を必要にします。 この記事では、次の2つの管理機能を持つものとします。 +間違いを取り消せる管理者がいると、便利なことがあります。 悪用の可能性を減らすため、この管理者を[マルチシグ](https://blog.logrocket.com/security-choices-multi-signature-wallets/)にして、あるアクションに対して複数の人が合意しなければならないようにすることができます。 この記事では、2つの管理機能について説明します。 -1. アカウントの凍結と解凍。 これは、アカウントが侵害された可能性がある場合などに役立ちます。 +1. アカウントの凍結と凍結解除。 これは、例えば、アカウントが侵害された可能性がある場合に便利です。 2. アセットのクリーンアップ。 - 時には、詐欺師が正当であると思わせるために、本物のトークンコントラクトに偽物のトークンを送信することがあります。 [こちら](https://optimistic.etherscan.io/token/0x2348b1a1228ddcd2db668c3d30207c3e1852fbbe?a=0x4200000000000000000000000000000000000042)に、その例があります。 正当なERC-20コントラクトは、[0x4200....0042](https://optimistic.etherscan.io/address/0x4200000000000000000000000000000000000042)です。 装っているスキャムは、[0x234....bbe](https://optimistic.etherscan.io/address/0x2348b1a1228ddcd2db668c3d30207c3e1852fbbe)です。 + 詐欺師が正当性を装うために、本物のトークンコントラクトに偽のトークンを送ることがあります。 例えば、[こちら](https://optimism.blockscout.com/token/0x2348B1a1228DDCd2dB668c3d30207c3E1852fBbe?tab=holders)をご覧ください。 正規のERC-20コントラクトは [0x4200....0042](https://optimism.blockscout.com/token/0x4200000000000000000000000000000000000042) です。 それになりすました詐欺コントラクトは [0x234....bbe](https://optimism.blockscout.com/token/0x2348B1a1228DDCd2dB668c3d30207c3E1852fBbe) です。 - また、正当なERC-20トークンを誤ってコントラクト自体に送信してしまう可能性もあります。これが、アセットのクリーンアップ方法が必要になるもう1つの理由です。 + また、ユーザーが正規のERC-20トークンを誤って私たちのコントラクトに送ってしまう可能性もあります。これも、それらを取り出す方法が必要となるもう一つの理由です。 -OpenZeppelinでは、管理者アクセスを可能にする次の2つのメカニズムを提供しています。 +OpenZeppelinは、管理者アクセスを可能にする2つのメカニズムを提供しています。 -- [`Ownable`](https://docs.openzeppelin.com/contracts/5.x/access-control#ownership-and-ownable)コントラクトでは、所有者は1人です。 `onlyOwner` [modifier](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm)のある関数は、その所有者のみしか呼び出せません。 所有者は、所有権を他の人に譲渡することも、完全に放棄することもできます。 通常、それ以外のアカウントの権限は変わりません。 -- [`AccessControl`](https://docs.openzeppelin.com/contracts/5.x/access-control#role-based-access-control)コントラクトでは、[ロールベースのアクセス制御 (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control)機能があります。 +- [`Ownable`](https://docs.openzeppelin.com/contracts/5.x/access-control#ownership-and-ownable)コントラクトには、単一の所有者がいます。 `onlyOwner` [修飾子](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm)を持つ関数は、その所有者のみが呼び出すことができます。 所有者は、所有権を他の誰かに譲渡したり、完全に放棄したりすることができます。 他のすべてのアカウントの権限は、通常は同一です。 +- [`AccessControl`](https://docs.openzeppelin.com/contracts/5.x/access-control#role-based-access-control)コントラクトには、[ロールベースのアクセス制御(RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control)があります。 -この記事では、簡潔にするために`Ownable`を使っています。 +この記事では、簡潔にするために`Ownable`を使用します。 -### コントラクトの凍結および解凍 {#freezing-and-thawing-contracts} +### コントラクトの凍結と凍結解除 {#freezing-and-thawing-contracts} -コントラクトの凍結と解凍において、次のいくつかの変更が必要になります。 +コントラクトの凍結と凍結解除には、いくつかの変更が必要です。 -- どのアドレスが凍結されているかを追跡するために、アドレスを[ブール値](https://en.wikipedia.org/wiki/Boolean_data_type)で[マッピング](https://www.tutorialspoint.com/solidity/solidity_mappings.htm)します。 すべての値の初期値はゼロです。これは、ブール値でfalseとして解釈されます。 デフォルトでは、アカウントを凍結しないため、このようにしています。 +- どのアドレスが凍結されているかを追跡するための、アドレスから[ブール値](https://en.wikipedia.org/wiki/Boolean_data_type)への[マッピング](https://www.tutorialspoint.com/solidity/solidity_mappings.htm)。 すべての値は最初はゼロで、ブール値の場合はfalseと解釈されます。 デフォルトではアカウントは凍結されていないので、これは望ましい動作です。 ```solidity mapping(address => bool) public frozenAccounts; ``` -- アカウントが凍結または解除されたときに、関係者に対して[イベント](https://www.tutorialspoint.com/solidity/solidity_events.htm)で通知します。 技術的観点では、アカウントの凍結および解除におけるアクションでは、イベントは必要ありません。しかし、オフチェーンのコードで、これらのイベントをリッスンして何が起こっているかわかると便利です。 関係者に対して何かが発生したときに、スマートコントラクトでイベントを発行することは、良いマナーであるとされています。 +- アカウントが凍結または凍結解除されたときに関係者に通知するための[イベント](https://www.tutorialspoint.com/solidity/solidity_events.htm)。 技術的には、これらのアクションにイベントは必須ではありませんが、オフチェーンのコードがこれらのイベントをリッスンして何が起こっているかを知るのに役立ちます。 他の誰かに関連する可能性のあることが起こったときに、スマートコントラクトがイベントを発行することは、良いマナーとされています。 - どのタイミングでアカウントの凍結または解除されたかを検索できるように、イベントにインデックスを付けています。 + イベントにはインデックスが付けられるため、あるアカウントが凍結または凍結解除されたすべての回を検索できるようになります。 ```solidity - // When accounts are frozen or unfrozen + // アカウントが凍結または凍結解除されたとき event AccountFrozen(address indexed _addr); event AccountThawed(address indexed _addr); ``` -- アカウントを凍結および解凍するための関数。 これらの2つの関数は、ほぼ同一であるため凍結する関数についてのみ説明します。 +- アカウントを凍結および凍結解除するための関数。 これらの2つの関数はほぼ同一なので、ここでは凍結関数についてのみ説明します。 ```solidity function freezeAccount(address addr) @@ -165,27 +167,27 @@ OpenZeppelinでは、管理者アクセスを可能にする次の2つのメカ onlyOwner ``` - [`public`](https://www.tutorialspoint.com/solidity/solidity_contracts.htm)が付けられた関数では、他のスマートコントラクトまたはトランザクションから直接呼び出すことができます。 + [`public`](https://www.tutorialspoint.com/solidity/solidity_contracts.htm)とマークされた関数は、他のスマートコントラクトから、またはトランザクションによって直接呼び出すことができます。 ```solidity { - require(!frozenAccounts[addr], "Account already frozen"); + require(!frozenAccounts[addr], "アカウントはすでに凍結されています"); frozenAccounts[addr] = true; emit AccountFrozen(addr); } // freezeAccount ``` - アカウントがすでに凍結されている場合は、処理を取消します。 それ以外の場合は、凍結してイベントを`emit`します。 + アカウントがすでに凍結されている場合は、リバートします。 そうでなければ、アカウントを凍結し、イベントを`emit`します。 -- `_beforeTokenTransfer`を凍結されたアカウントから資金が移動されないよう変更します。 凍結されたアカウントへの送金は、引き続き可能であることに注意してください。 +- 凍結されたアカウントから資金が移動されないように`_beforeTokenTransfer`を変更します。 凍結されたアカウントへの送金は、引き続き可能であることに注意してください。 ```solidity - require(!frozenAccounts[from], "The account is frozen"); + require(!frozenAccounts[from], "アカウントは凍結されています"); ``` ### アセットのクリーンアップ {#asset-cleanup} -コントラクト自体が保持しているERC-20トークンを解放するには、それに属しているトークンコントラクトの関数である[`transfer`](https://eips.ethereum.org/EIPS/eip-20#transfer)または[`approve`](https://eips.ethereum.org/EIPS/eip-20#approve)を呼び出す必要があります。 この場合、Allowanceで無駄にガスを消費するのはもったいないため、直接送金 (transfer) の方がよいでしょう。 +このコントラクトが保有するERC-20トークンを解放するには、それらが属するトークンコントラクトの[`transfer`](https://eips.ethereum.org/EIPS/eip-20#transfer)または[`approve`](https://eips.ethereum.org/EIPS/eip-20#approve)のいずれかの関数を呼び出す必要があります。 この場合、Allowanceでガスを無駄にする意味はないので、直接送金した方が良いでしょう。 ```solidity function cleanupERC20( @@ -198,7 +200,7 @@ OpenZeppelinでは、管理者アクセスを可能にする次の2つのメカ IERC20 token = IERC20(erc20); ``` -これは、アドレスがトークンを受け取った場合に、コントラクトにオブジェクトを作成するための構文です。 ERC20トークンがソースコード (4行目を参照) の一部として定義されており、OpenZeppelinのERC-20コントラクトのインターフェースである[IERC20の定義](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol)がそのファイルに含まれているためこれが可能です。 +これは、アドレスを受け取ったときにコントラクトのオブジェクトを作成するための構文です。 これが可能なのは、ソースコードの一部としてERC20トークンの定義があり(4行目を参照)、そのファイルにはOpenZeppelinのERC-20コントラクトのインターフェースである[IERC20の定義](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol)が含まれているためです。 ```solidity uint balance = token.balanceOf(address(this)); @@ -206,8 +208,10 @@ OpenZeppelinでは、管理者アクセスを可能にする次の2つのメカ } ``` -上記は、すべてのトークンをクリーンアップする関数です。 このプロセスを自動化することで、ユーザーから手動で残高を取得するよりも効率化できます。 +これはクリーンアップ関数なので、トークンを残さないようにします。 ユーザーから手動で残高を取得する代わりに、プロセスを自動化した方が良いでしょう。 + +## 結論 {#conclusion} -## まとめ {#conclusion} +これは完璧な解決策ではありません。「ユーザーの間違い」によって発生する問題に完璧な解決策はないのです。 しかし、この種のチェックを使用することで、少なくともいくつかの間違いを防ぐことができます。 アカウントを凍結する機能は危険を伴いますが、ハッカーから盗まれた資金を奪うことで、特定のハッキングによる被害を限定するために使用できます。 -「ユーザーの間違い」によって発生する問題に完璧な解決策はないため、これらは完全な解決策ではありません。 しかしながら、この記事のようなチェックをすることで、少なくともいくつかのミスを防止できます。 アカウントの凍結機能は危険を伴うものの、ハッカーが資金を盗むことを防ぎ、ハッキングによる被害を特定の範囲内におさめるために使うことができます。 +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). diff --git a/public/content/translations/ja/developers/tutorials/ethereum-for-web2-auth/index.md b/public/content/translations/ja/developers/tutorials/ethereum-for-web2-auth/index.md new file mode 100644 index 00000000000..d5bb7ed4da2 --- /dev/null +++ b/public/content/translations/ja/developers/tutorials/ethereum-for-web2-auth/index.md @@ -0,0 +1,886 @@ +--- +title: "Web2認証にイーサリアムを使用する" +description: "このチュートリアルを読むことで、デベロッパーはイーサリアムログイン (Web3) をSAMLログインと統合できるようになります。SAMLは、Web2でシングルサインオンやその他の関連サービスを提供するために使用される標準です。 これにより、Web2リソースへのアクセスをイーサリアム署名を通じて認証できるようになり、ユーザー属性はアテステーションから取得されます。" +author: Ori Pomerantz +tags: [ "web2", "認証", "eas" ] +skill: beginner +lang: ja +published: 2025-04-30 +--- + +## はじめに + +[SAML](https://www.onelogin.com/learn/saml)は、Web2で[IDプロバイダー (IdP)](https://en.wikipedia.org/wiki/Identity_provider#SAML_identity_provider) が[サービスプロバイダー (SP)](https://en.wikipedia.org/wiki/Service_provider_\(SAML\))にユーザー情報を提供するために使用される標準です。 + +このチュートリアルでは、イーサリアム署名をSAMLと統合し、まだイーサリアムをネイティブにサポートしていないWeb2サービスに対して、ユーザーがイーサリアムウォレットを使用して認証できるようにする方法を学びます。 + +このチュートリアルは、2つの異なる読者を対象に書かれていることに注意してください。 + +- イーサリアムを理解していて、SAMLを学ぶ必要があるイーサリアム関係者 +- SAMLとWeb2認証を理解していて、イーサリアムを学ぶ必要があるWeb2関係者 + +そのため、すでにご存知の入門的な内容が多く含まれています。 適宜読み飛ばしてください。 + +### イーサリアム関係者向けのSAML + +SAMLは中央集権型のプロトコルです。 サービスプロバイダー (SP) は、IDプロバイダー (IdP) との間、またはそのIdPの証明書に署名した[証明書認証局](https://www.ssl.com/article/what-is-a-certificate-authority-ca/)との間に、既存の信頼関係がある場合にのみ、IdPからのアサーション (「これは私のユーザーJohnで、A、B、Cを行う権限を持つべきである」など) を受け入れます。 + +たとえば、SPは企業に旅行サービスを提供する旅行代理店、IdPは企業の社内ウェブサイトであるとします。 従業員が出張の予約をする際、旅行代理店は、実際に旅行の予約を許可する前に、従業員を会社の認証に送ります。 + +![SAMLプロセスのステップバイステップ](./fig-01-saml.png) + +これが、ブラウザ、SP、IdPの3つのエンティティがアクセスを交渉する方法です。 SPは、ブラウザを使用しているユーザーについて事前に何も知る必要はなく、IdPを信頼するだけで済みます。 + +### SAML関係者向けのイーサリアム + +イーサリアムは分散型システムです。 + +![イーサリアムのログオン](./fig-02-eth-logon.png) + +ユーザーは秘密鍵を持っています (通常はブラウザ拡張機能に保存されています)。 秘密鍵から公開鍵を導出し、そこから20バイトのアドレスを導出できます。 ユーザーがシステムにログインする必要がある場合、ノンス (1回限りの値) を持つメッセージに署名するよう要求されます。 サーバーは、署名がそのアドレスによって作成されたことを検証できます。 + +![アテステーションから追加データを取得](./fig-03-eas-data.png) + +署名はイーサリアムアドレスを検証するだけです。 他のユーザー属性を取得するには、通常[アテステーション](https://attest.org/)を使用します。 アテステーションには通常、以下のフィールドがあります。 + +- **証明者**、アテステーションを行ったアドレス +- **受取人**、アテステーションが適用されるアドレス +- **データ**、名前や権限など、証明されるデータ +- **スキーマ**、データの解釈に使用されるスキーマのID。 + +イーサリアムの分散型の性質により、どのユーザーでもアテステーションを作成できます。 どのアテステーションが信頼できるかを判断するには、証明者のIDが重要です。 + +## セットアップ + +最初のステップは、SAML SPとSAML IdPが互いに通信できるようにすることです。 + +1. ソフトウェアをダウンロードします。 この記事のサンプルソフトウェアは、[github](https://github.com/qbzzt/250420-saml-ethereum)にあります。 異なるステージは異なるブランチに保存されています。このステージでは `saml-only` を使用します。 + + ```sh + git clone https://github.com/qbzzt/250420-saml-ethereum -b saml-only + cd 250420-saml-ethereum + pnpm install + ``` + +2. 自己署名証明書でキーを作成します。 これは、キーがそれ自体の証明書認証局であることを意味し、サービスプロバイダーに手動でインポートする必要があります。 詳細については、[OpenSSLのドキュメント](https://docs.openssl.org/master/man1/openssl-req/)を参照してください。 + + ```sh + mkdir keys + cd keys + openssl req -new -x509 -days 365 -nodes -sha256 -out saml-sp.crt -keyout saml-sp.pem -subj /CN=sp/ + openssl req -new -x509 -days 365 -nodes -sha256 -out saml-idp.crt -keyout saml-idp.pem -subj /CN=idp/ + cd .. + ``` + +3. サーバー (SPとIdPの両方) を起動します。 + + ```sh + pnpm start + ``` + +4. URL [http://localhost:3000/](http://localhost:3000/)でSPにアクセスし、ボタンをクリックしてIdP (ポート3001) にリダイレクトします。 + +5. IdPにメールアドレスを入力し、**サービスプロバイダーにログイン**をクリックします。 サービスプロバイダー (ポート3000) にリダイレクトされ、メールアドレスで認識されていることを確認します。 + +### 詳細な説明 + +ステップバイステップで起こることは次のとおりです。 + +![イーサリアムを使用しない通常のSAMLログオン](./fig-04-saml-no-eth.png) + +#### src/config.mts + +このファイルには、IDプロバイダーとサービスプロバイダー両方の設定が含まれています。 通常、これら2つは異なるエンティティですが、ここでは簡潔にするためにコードを共有します。 + +```typescript +const fs = await import("fs") + +const protocol="http" +``` + +今のところテストだけなので、HTTPを使用して問題ありません。 + +```typescript +export const spCert = fs.readFileSync("keys/saml-sp.crt").toString() +export const idpCert = fs.readFileSync("keys/saml-idp.crt").toString() +``` + +通常は両方のコンポーネントで利用可能な公開鍵 (直接信頼されるか、信頼された証明書認証局によって署名される) を読み取ります。 + +```typescript +export const spPort = 3000 +export const spHostname = "localhost" +export const spDir = "sp" + +export const idpPort = 3001 +export const idpHostname = "localhost" +export const idpDir = "idp" + +export const spUrl = `${protocol}://${spHostname}:${spPort}/${spDir}` +export const idpUrl = `${protocol}://${idpHostname}:${idpPort}/${idpDir}` +``` + +両コンポーネントのURLです。 + +```typescript +export const spPublicData = { +``` + +サービスプロバイダーの公開データです。 + +```typescript + entityID: `${spUrl}/metadata`, +``` + +慣例的に、SAMLでは`entityID`はエンティティのメタデータが利用可能なURLです。 このメタデータはここの公開データに対応しますが、XML形式である点が異なります。 + +```typescript + wantAssertionsSigned: true, + authnRequestsSigned: false, + signingCert: spCert, + allowCreate: true, + assertionConsumerService: [{ + Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + Location: `${spUrl}/assertion`, + }] + } +``` + +私たちの目的にとって最も重要な定義は `assertionConsumerServer` です。 これは、サービスプロバイダーに何かをアサートする (例えば、「この情報を送信するユーザーは somebody@example.com です」) ためには、URL `http://localhost:3000/sp/assertion` に [HTTP POST](https://www.w3schools.com/tags/ref_httpmethods.asp) を使用する必要があることを意味します。 + +```typescript +export const idpPublicData = { + entityID: `${idpUrl}/metadata`, + signingCert: idpCert, + wantAuthnRequestsSigned: false, + singleSignOnService: [{ + Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", + Location: `${idpUrl}/login` + }], + singleLogoutService: [{ + Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", + Location: `${idpUrl}/logout` + }], + } +``` + +IDプロバイダーの公開データも同様です。 これは、ユーザーをログインさせるには `http://localhost:3001/idp/login` にPOSTし、ユーザーをログアウトさせるには `http://localhost:3001/idp/logout` にPOSTすることを指定しています。 + +#### src/sp.mts + +これはサービスプロバイダーを実装するコードです。 + +```typescript +import * as config from "./config.mts" +const fs = await import("fs") +const saml = await import("samlify") +``` + +SAMLを実装するために [`samlify`](https://www.npmjs.com/package/samlify) ライブラリを使用します。 + +```typescript +import * as validator from "@authenio/samlify-node-xmllint" +saml.setSchemaValidator(validator) +``` + +`samlify` ライブラリは、XMLが正しいこと、期待される公開鍵で署名されていることなどを検証するパッケージを必要とします。 この目的のために [`@authenio/samlify-node-xmllint`](https://www.npmjs.com/package/@authenio/samlify-node-xmllint) を使用します。 + +```typescript +const express = (await import("express")).default +const spRouter = express.Router() +const app = express() +``` + +[`express`](https://expressjs.com/)の [`Router`](https://expressjs.com/en/5x/api.html#router)は、ウェブサイト内にマウントできる「ミニウェブサイト」です。 この場合、すべてのサービスプロバイダーの定義をグループ化するために使用します。 + +```typescript +const spPrivateKey = fs.readFileSync("keys/saml-sp.pem").toString() + +const sp = saml.ServiceProvider({ + privateKey: spPrivateKey, + ...config.spPublicData +}) +``` + +サービスプロバイダー自身の表現は、すべての公開データと、情報に署名するために使用する秘密鍵です。 + +```typescript +const idp = saml.IdentityProvider(config.idpPublicData); +``` + +公開データには、サービスプロバイダーがIDプロバイダーについて知る必要があるすべてのものが含まれています。 + +```typescript +spRouter.get(`/metadata`, + (req, res) => res.header("Content-Type", "text/xml").send(sp.getMetadata()) +) +``` + +他のSAMLコンポーネントとの相互運用性を確保するため、サービスプロバイダーとIDプロバイダーは、公開データ (メタデータと呼ばれる) をXML形式で `/metadata` にて利用可能にする必要があります。 + +```typescript +spRouter.post(`/assertion`, +``` + +これは、ブラウザが自身を識別するためにアクセスするページです。 アサーションにはユーザー識別子 (ここではメールアドレスを使用) が含まれ、追加の属性を含めることもできます。 これは、上記のシーケンス図のステップ7のハンドラです。 + +```typescript + async (req, res) => { + // console.log(`SAML response:\n${Buffer.from(req.body.SAMLResponse, 'base64').toString('utf-8')}`) +``` + +コメントアウトされたコマンドを使用して、アサーションで提供されるXMLデータを確認できます。 これは[base64でエンコード](https://en.wikipedia.org/wiki/Base64)されています。 + +```typescript + try { + const loginResponse = await sp.parseLoginResponse(idp, 'post', req); +``` + +IDサーバーからのログインリクエストを解析します。 + +```typescript + res.send(` + + +

Hello ${loginResponse.extract.nameID}

+ + + `) + res.send(); +``` + +ユーザーにログインが成功したことを示すために、HTMLレスポンスを送信します。 + +```typescript + } catch (err) { + console.error('Error processing SAML response:', err); + res.status(400).send('SAML authentication failed'); + } + } +) +``` + +失敗した場合は、ユーザーに通知します。 + +```typescript +spRouter.get('/login', +``` + +ブラウザがこのページを取得しようとするときに、ログインリクエストを作成します。 これは、上記のシーケンス図のステップ1のハンドラです。 + +```typescript + async (req, res) => { + const loginRequest = await sp.createLoginRequest(idp, "post") +``` + +ログインリクエストをPOSTするための情報を取得します。 + +```typescript + res.send(` + + + +``` + +このページは、フォーム (下記参照) を自動的に送信します。 これにより、ユーザーはリダイレクトされるために何もする必要がありません。 これは、上記のシーケンス図のステップ2です。 + +```typescript +
+``` + +`loginRequest.entityEndpoint` (IDプロバイダーエンドポイントのURL) にPOSTします。 + +```typescript + +``` + +入力名は `loginRequest.type` (`SAMLRequest`) です。 そのフィールドのコンテンツは `loginRequest.context` で、これもbase64エンコードされたXMLです。 + +```typescript +
+ + + `) + } +) + +app.use(express.urlencoded({extended: true})) +``` + +[このミドルウェア](https://expressjs.com/en/5x/api.html#express.urlencoded) は [HTTPリクエスト](https://www.tutorialspoint.com/http/http_requests.htm)のボディを読み取ります。 ほとんどのリクエストはそれを必要としないため、Expressはデフォルトでそれを無視します。 POSTはボディを使用するため、これが必要です。 + +```typescript +app.use(`/${config.spDir}`, spRouter) +``` + +サービスプロバイダーディレクトリ (`/sp`) にルーターをマウントします。 + +```typescript +app.get("/", (req, res) => { + res.send(` + + + + + + `) +}) +``` + +ブラウザがルートディレクトリを取得しようとした場合は、ログインページへのリンクを提供します。 + +```typescript +app.listen(config.spPort, () => { + console.log(`service provider is running on http://${config.spHostname}:${config.spPort}`) +}) +``` + +このExpressアプリケーションで `spPort` をリッスンします。 + +#### src/idp.mts + +これはIDプロバイダーです。 これはサービスプロバイダーと非常によく似ており、以下の説明は異なる部分についてです。 + +```typescript +const xmlParser = new (await import("fast-xml-parser")).XMLParser( + { + ignoreAttributes: false, // Preserve attributes + attributeNamePrefix: "@_", // Prefix for attributes + } +) +``` + +サービスプロバイダーから受け取ったXMLリクエストを読み取り、理解する必要があります。 + +```typescript +const getLoginPage = requestId => ` +``` + +この関数は、上記のシーケンス図のステップ4で返される、自動送信フォーム付きのページを作成します。 + +```typescript + + + ログインページ + + +

ログインページ

+
+ + メールアドレス: +
+ +``` + +サービスプロバイダーに送信するフィールドは2つあります。 + +1. 応答する `requestId`。 +2. ユーザー識別子 (ここではユーザーが提供するメールアドレスを使用)。 + +```typescript +
+ + + +const idpRouter = express.Router() + +idpRouter.post("/loginSubmitted", async (req, res) => { + const loginResponse = await idp.createLoginResponse( +``` + +これは、上記のシーケンス図のステップ5のハンドラです。 [`idp.createLoginResponse`](https://github.com/tngan/samlify/blob/master/src/entity-idp.ts#L73-L125)はログインレスポンスを作成します。 + +```typescript + sp, + { + authnContextClassRef: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport', + audience: sp.entityID, +``` + +オーディエンスはサービスプロバイダーです。 + +```typescript + extract: { + request: { + id: req.body.requestId + } + }, +``` + +リクエストから抽出された情報です。 リクエストで重要なパラメータはrequestIdで、これによりサービスプロバイダーはリクエストとそのレスポンスを一致させることができます。 + +```typescript + signingKey: { privateKey: idpPrivateKey, publicKey: config.idpCert } // Ensure signing +``` + +レスポンスに署名するためのデータを持つために `signingKey` が必要です。 サービスプロバイダーは署名されていないリクエストを信頼しません。 + +```typescript + }, + "post", + { + email: req.body.email +``` + +これは、サービスプロバイダーに送り返すユーザー情報を持つフィールドです。 + +```typescript + } + ); + + res.send(` + + + + +
+ +
+ + + `) +}) +``` + +ここでも、自動送信フォームを使用します。 これは、上記のシーケンス図のステップ6です。 + +```typescript + +// ログインリクエスト用のIdPエンドポイント +idpRouter.post(`/login`, +``` + +これはサービスプロバイダーからログインリクエストを受け取るエンドポイントです。 これは、上記のシーケンス図のステップ3のハンドラです。 + +```typescript + async (req, res) => { + try { + // parseLoginRequestが動作しなかったための回避策 + // const loginRequest = await idp.parseLoginRequest(sp, 'post', req) + const samlRequest = xmlParser.parse(Buffer.from(req.body.SAMLRequest, 'base64').toString('utf-8')) + res.send(getLoginPage(samlRequest["samlp:AuthnRequest"]["@_ID"])) +``` + +[`idp.parseLoginRequest`](https://github.com/tngan/samlify/blob/master/src/entity-idp.ts#L127-L144)を使用して認証リクエストのIDを読み取ることができるはずです。 しかし、動作させることができず、それに多くの時間を費やす価値もなかったので、[汎用XMLパーサー](https://www.npmjs.com/package/fast-xml-parser)を使用します。 必要な情報は、XMLのトップレベルにある `` タグ内の `ID` 属性です。 + +## イーサリアム署名の使用 + +ユーザーIDをサービスプロバイダーに送信できるようになったので、次のステップは信頼できる方法でユーザーIDを取得することです。 Viemを使用すると、ウォレットにユーザーアドレスを尋ねるだけで済みますが、これはブラウザに情報を要求することを意味します。 私たちはブラウザを制御していないため、そこから得られる応答を自動的に信頼することはできません。 + +代わりに、IdPはブラウザに署名するための文字列を送信します。 ブラウザのウォレットがこの文字列に署名すれば、それが本当にそのアドレスであること (つまり、そのアドレスに対応する秘密鍵を知っていること) を意味します。 + +これを実際に確認するには、既存のIdPとSPを停止し、次のコマンドを実行します。 + +```sh +git checkout eth-signatures +pnpm install +pnpm start +``` + +次に、[SP](http://localhost:3000)にアクセスし、指示に従ってください。 + +この時点では、イーサリアムアドレスからメールアドレスを取得する方法がわからないため、代わりに `<イーサリアムアドレス>@bad.email.address` をSPに報告します。 + +### 詳細な説明 + +変更点は、前の図のステップ4-5にあります。 + +![イーサリアム署名付きSAML](./fig-05-saml-w-signature.png) + +変更したファイルは `idp.mts` だけです。 以下が変更された部分です。 + +```typescript +import { v4 as uuidv4 } from 'uuid' +import { verifyMessage } from 'viem' +``` + +これら2つの追加ライブラリが必要です。 [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce)値を作成するために [`uuid`](https://www.npmjs.com/package/uuid) を使用します。 値自体は問題ではなく、一度しか使用されないという事実が重要です。 + +[`viem`](https://viem.sh/) ライブラリを使用すると、イーサリアムの定義を使用できます。 ここでは、署名が実際に有効であることを検証するためにこれが必要です。 + +```typescript +const loginPrompt = "サービスプロバイダーにアクセスするには、このノンスに署名してください: " +``` + +ウォレットは、ユーザーにメッセージに署名する許可を求めます。 ノンスだけのメッセージはユーザーを混乱させる可能性があるため、このプロンプトを含めます。 + +```typescript +// ここにrequestIDを保持する +let nonces = {} +``` + +応答するためには、リクエスト情報が必要です。 リクエストと一緒に送信し (ステップ4)、それを受け取る (ステップ5) こともできます。 しかし、潜在的に敵対的なユーザーの制御下にあるブラウザから得られる情報は信頼できません。 したがって、ノンスをキーとしてここに保存する方が良いでしょう。 + +簡潔さのために、ここでは変数としてこれを行っていることに注意してください。 ただし、これにはいくつかの欠点があります。 + +- サービス拒否攻撃に対して脆弱です。 悪意のあるユーザーが複数回ログオンを試み、メモリを使い果たす可能性があります。 +- IdPプロセスを再起動する必要がある場合、既存の値を失います。 +- 各プロセスが独自の変数を持つため、複数のプロセス間で負荷分散を行うことはできません。 + +本番システムでは、データベースを使用し、何らかの有効期限メカニズムを実装します。 + +```typescript +const getSignaturePage = requestId => { + const nonce = uuidv4() + nonces[nonce] = requestId +``` + +ノンスを作成し、後で使用するために `requestId` を保存します。 + +```typescript + return ` + + + + + +

署名してください

+ +
+ + + +` +} +``` + +残りは標準のHTMLです。 + +```typescript +idpRouter.get("/signature/:nonce/:account/:signature", async (req, res) => { +``` + +これはシーケンス図のステップ5のハンドラです。 + +```typescript + const requestId = nonces[req.params.nonce] + if (requestId === undefined) { + res.send("Bad nonce") + return ; + } + + nonces[req.params.nonce] = undefined +``` + +リクエストIDを取得し、再利用されないように `nonces` からノンスを削除します。 + +```typescript + try { +``` + +署名が無効になる方法が非常に多いため、これを `try ...` でラップします。 catch`ブロックで、スローされたエラーをキャッチします。 + +```typescript + const validSignature = await verifyMessage({ + address: req.params.account, + message: `${loginPrompt}${req.params.nonce}`, + signature: req.params.signature + }) +``` + +シーケンス図のステップ5.5を実装するには、[`verifyMessage`](https://viem.sh/docs/actions/public/verifyMessage#verifymessage) を使用します。 + +```typescript + if (!validSignature) + throw("Bad signature") + } catch (err) { + res.send("Error:" + err) + return ; + } +``` + +ハンドラの残りの部分は、1つの小さな変更を除いて、以前に `/loginSubmitted` ハンドラで行ったことと同等です。 + +```typescript + const loginResponse = await idp.createLoginResponse( + . + . + . + { + email: req.params.account + "@bad.email.address" + } + ); +``` + +実際のメールアドレスは持っていないので (次のセクションで取得します)、今のところはイーサリアムアドレスを返し、それがメールアドレスではないことを明確にマークします。 + +```typescript +// ログインリクエスト用のIdPエンドポイント +idpRouter.post(`/login`, + async (req, res) => { + try { + // parseLoginRequestが動作しなかったための回避策 + // const loginRequest = await idp.parseLoginRequest(sp, 'post', req) + const samlRequest = xmlParser.parse(Buffer.from(req.body.SAMLRequest, 'base64').toString('utf-8')) + res.send(getSignaturePage(samlRequest["samlp:AuthnRequest"]["@_ID"])) + } catch (err) { + console.error('SAMLレスポンスの処理中にエラーが発生しました:', err); + res.status(400).send('SAML認証に失敗しました'); + } + } +) +``` + +ステップ3のハンドラでは、`getLoginPage` の代わりに `getSignaturePage` を使用します。 + +## メールアドレスの取得 + +次のステップは、サービスプロバイダーによって要求された識別子であるメールアドレスを取得することです。 そのためには、[Ethereum Attestation Service (EAS)](https://attest.org/) を使用します。 + +アテステーションを取得する最も簡単な方法は、[GraphQL API](https://docs.attest.org/docs/developer-tools/api)を使用することです。 このクエリを使用します。 + +``` +query GetAttestationsByRecipient { + attestations( + where: { + recipient: { equals: "${getAddress(ethAddr)}" } + schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" } + } + take: 1 + ) { + data + id + attester + } +} +``` + +この[`schemaId`](https://optimism.easscan.org/schema/view/0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977)には、メールアドレスのみが含まれます。 このクエリは、このスキーマのアテステーションを要求します。 アテステーションのサブジェクトは `recipient` と呼ばれます。 これは常にイーサリアムアドレスです。 + +警告:ここでアテステーションを取得する方法には、2つのセキュリティ上の問題があります。 + +- 中央集権的なコンポーネントであるAPIエンドポイント `https://optimism.easscan.org/graphql` にアクセスしています。 `id`属性を取得し、オンチェーンでルックアップしてアテステーションが本物であることを確認できますが、APIエンドポイントは、アテステーションについて通知しないことで、依然としてアテステーションを検閲できます。 + + この問題は解決不可能ではありません。独自のGraphQLエンドポイントを実行し、チェーンログからアテステーションを取得できますが、私たちの目的には過剰です。 + +- 私たちは証明者のIDを見ていません。 誰でも偽の情報を私たちに与えることができます。 実際の環境では、信頼できる証明者のセットを持ち、彼らのアテステーションのみを参照します。 + +これを実際に確認するには、既存のIdPとSPを停止し、次のコマンドを実行します。 + +```sh +git checkout email-address +pnpm install +pnpm start +``` + +次に、メールアドレスを入力します。 これを行うには2つの方法があります。 + +- 秘密鍵を使用してウォレットをインポートし、テスト用の秘密鍵 `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80` を使用します。 + +- 自分のメールアドレスにアテステーションを追加します。 + + 1. アテステーションエクスプローラーの[スキーマ](https://optimism.easscan.org/schema/view/0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977)に移動します。 + + 2. **スキーマで証明**をクリックします。 + + 3. 受信者としてイーサリアムアドレスを入力し、メールアドレスとしてメールアドレスを入力し、**オンチェーン**を選択します。 次に、**アテステーションを作成**をクリックします。 + + 4. ウォレットでトランザクションを承認します。 ガスを支払うために、[Optimism Blockchain](https://app.optimism.io/bridge/deposit)にETHが必要です。 + +どちらの場合も、これを行った後、[http://localhost:3000](http://localhost:3000) にアクセスして指示に従ってください。 テスト用の秘密鍵をインポートした場合、受け取るメールは `test_addr_0@example.com` です。 独自のアドレスを使用した場合は、証明した内容になります。 + +### 詳細な説明 + +![イーサリアムアドレスからメールアドレスへの変換](./fig-06-saml-sig-n-email.png) + +新しいステップはGraphQL通信、ステップ5.6と5.7です。 + +再度、`idp.mts`の変更部分を示します。 + +```typescript +import { GraphQLClient } from 'graphql-request' +import { SchemaEncoder } from '@ethereum-attestation-service/eas-sdk' +``` + +必要なライブラリをインポートします。 + +```typescript +const graphqlEndpointUrl = "https://optimism.easscan.org/graphql" +``` + +[各ブロックチェーンに個別のエンドポイント](https://docs.attest.org/docs/developer-tools/api)があります。 + +```typescript +const graphqlClient = new GraphQLClient(graphqlEndpointUrl, { fetch }) +``` + +エンドポイントのクエリに使用できる新しい`GraphQLClient`クライアントを作成します。 + +```typescript +const graphqlSchema = 'string emailAddress' +const graphqlEncoder = new SchemaEncoder(graphqlSchema) +``` + +GraphQLは、バイトを持つ不透明なデータオブジェクトのみを提供します。 それを理解するためにはスキーマが必要です。 + +```typescript +const ethereumAddressToEmail = async ethAddr => { +``` + +イーサリアムアドレスからメールアドレスを取得する関数です。 + +```typescript + const query = ` + query GetAttestationsByRecipient { +``` + +これはGraphQLクエリです。 + +```typescript + アテステーション( +``` + +アテステーションを探しています。 + +```typescript + where: { + recipient: { equals: "${getAddress(ethAddr)}" } + schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" } + } +``` + +必要なアテステーションは、私たちのスキーマにあり、受信者が`getAddress(ethAddr)`であるものです。 [`getAddress`](https://viem.sh/docs/utilities/getAddress#getaddress)関数は、アドレスが正しい[チェックサム](https://github.com/ethereum/ercs/blob/master/ERCS/erc-55.md)を持つことを保証します。 GraphQLは大文字と小文字を区別するため、これは必要です。 「0xBAD060A7」、「0xBad060A7」、および「0xbad060a7」は異なる値です。 + +```typescript + take: 1 +``` + +見つかったアテステーションの数に関係なく、最初のものだけが必要です。 + +```typescript + ) { + data + id + attester + } + }` +``` + +受け取りたいフィールドです。 + +- `attester`: アテステーションを送信したアドレス。 通常、これはアテステーションを信頼するかどうかを決定するために使用されます。 +- `id`: アテステーションID。 この値を使用して、[アテステーションをオンチェーンで読み取り](https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000021?tab=read_proxy&source_address=0x4E0275Ea5a89e7a3c1B58411379D1a0eDdc5b088#0xa3112a64)、GraphQLクエリからの情報が正しいことを確認できます。 +- `data`: スキーマデータ (この場合はメールアドレス)。 + +```typescript + const queryResult = await graphqlClient.request(query) + + if (queryResult.attestations.length == 0) + return "no_address@available.is" +``` + +アテステーションがない場合は、明らかに不正であるが、サービスプロバイダーには有効に見える値を返します。 + +```typescript + const attestationDataFields = graphqlEncoder.decodeData(queryResult.attestations[0].data) + return attestationDataFields[0].value.value +} +``` + +値がある場合は、`decodeData`を使用してデータをデコードします。 提供されるメタデータは不要で、値自体のみが必要です。 + +```typescript + const loginResponse = await idp.createLoginResponse( + sp, + { + . + . + . + }, + "post", + { + email: await ethereumAddressToEmail(req.params.account) + } + ); +``` + +新しい関数を使用してメールアドレスを取得します。 + +## 分散化については? + +この構成では、イーサリアムとメールアドレスのマッピングに信頼できる証明者に依存している限り、ユーザーは他人になりすますことはできません。 しかし、私たちのIDプロバイダーは依然として中央集権的なコンポーネントです。 IDプロバイダーの秘密鍵を持っている人は誰でも、サービスプロバイダーに偽の情報を送信できます。 + +[マルチパーティ計算 (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation) を使用した解決策があるかもしれません。 将来のチュートリアルでそれについて書きたいと思っています。 + +## まとめ + +イーサリアム署名のようなログオン標準の採用は、鶏が先か卵が先かの問題に直面します。 サービスプロバイダーは、可能な限り広い市場にアピールしたいと考えています。 ユーザーは、ログオン標準のサポートを心配することなく、サービスにアクセスできることを望んでいます。 +イーサリアムIdPなどのアダプターを作成することは、このハードルを乗り越えるのに役立ちます。 + +[私の他の作品はこちらでご覧いただけます](https://cryptodocguy.pro/). diff --git a/public/content/translations/ja/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md b/public/content/translations/ja/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md index 119b5ca92b0..7e34b96fcf2 100644 --- a/public/content/translations/ja/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md +++ b/public/content/translations/ja/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md @@ -1,13 +1,8 @@ --- -title: イーサリアム開発入門 +title: "イーサリアム開発入門" description: "この文書は、はじめてイーサリアム開発を行う初心者用のガイドです。 APIエンドポイントの立ち上げ、コマンドライン・リクエストの作成、さらにweb3スクリプトの作成までをステップごとに説明します。 ブロックチェーンの開発経験は必要ありません!" author: "Elan Halpern" -tags: - - "JavaScript" - - "ethers.js" - - "ノード" - - "クエリ" - - "Alchemy" +tags: [ "JavaScript", "ethers.js", "ノード", "クエリ", "Alchemy" ] skill: beginner lang: ja published: 2020-10-30 @@ -15,46 +10,46 @@ source: Medium sourceUrl: https://medium.com/alchemy-api/getting-started-with-ethereum-development-using-alchemy-c3d6a45c567f --- -![イーサリアムおよび Alchemyのロゴ](./ethereum-alchemy.png) +![イーサリアムとAlchemyのロゴ](./ethereum-alchemy.png) -この記事は、はじめてイーサリアム開発を行う初心者向けのガイドです。 このチュートリアルでは、[Alchemy](https://alchemyapi.io/)を使用します。Alchemyは、何百万人ものユーザーを持つ代表的なブロックチェーン開発者向けプラットフォームで、最も人気が高いブロックチェーンアプリ( Maker、0x、MyEtherWallet、Dharma、Kyberなど)のうち7割がAlchemyを使用しています。 Alchemyを使用するとイーサリアムチェーン上でAPIエンドポイントにアクセスできるため、トランザクションの読み書きが可能になります。 +この記事は、はじめてイーサリアム開発を行う初心者向けのガイドです。 このチュートリアルでは、[Alchemy](https://alchemyapi.io/)を使用します。これは、Maker、0x、MyEtherWallet、Dharma、Kyberといったトップクラスのブロックチェーンアプリの70%に採用され、数百万人のユーザーを支える、業界をリードするブロックチェーン開発者プラットフォームです。 Alchemyを使用するとイーサリアムチェーン上でAPIエンドポイントにアクセスできるため、トランザクションの読み書きが可能になります。 -このチュートリアルでは、Alchemyにサインアップする方法から、最初のweb3 スクリプトを作成するまでを学習します。 ブロックチェーンの開発経験は必要ありません! +Alchemyへのサインアップから、最初のWeb3スクリプト作成までご案内します! ブロックチェーンの開発経験は必要ありません! ## 1. 無料のAlchemyアカウントにサインアップする {#sign-up-for-a-free-alchemy-account} -Alchemyのアカウントを作成するのは簡単です。 [こちら](https://auth.alchemyapi.io/signup)から無料でサインアップしてください。 +Alchemyのアカウント作成は簡単です。[こちらから無料でサインアップしてください](https://auth.alchemy.com/)。 -## 2. Alchemy アプリを作成する {#create-an-alchemy-app} +## 2. Alchemyアプリを作成する {#create-an-alchemy-app} イーサリアムチェーンと通信し、Alchemy製品を使用するには、あなたのリクエストを認証するためのAPIキーが必要になります。 -APIキーは、[ダッシュボード](http://dashboard.alchemyapi.io/)で作成できます。 新規キーを作成するには、以下の手順で「Create App」に移動します。 +APIキーは[ダッシュボードから作成](https://dashboard.alchemy.com/)できます。 新しいキーを作成するには、以下のように「Create App」に移動してください: -ダッシュボードの表示を許可していただいた[_ShapeShift_](https://shapeshift.com/) _に感謝します!_ +_ダッシュボードの表示を許可していただいた[_ShapeShift_](https://shapeshift.com/)に、心より感謝申し上げます!_ -![Alchemyダッシュボード](./alchemy-dashboard.png) +![Alchemyのダッシュボード](./alchemy-dashboard.png) -「Create App」の下にある詳細情報に記入して、新規キーを取得してください。 ここでは、あなたが以前に作成したアプリや、あなたのチームが作成したアプリも確認できます。 どのアプリについても、「View Key(キーを表示)」をクリックすると既存のキーを取得できます。 +「Create App」の下にある詳細情報に記入して、新しいキーを取得してください。 ここでは、あなたが以前に作成したアプリや、あなたのチームが作成したアプリも確認できます。 どのアプリについても、「View Key」をクリックすると既存のキーを取得できます。 -![Alchemyのスクリーンショットでアプリを作成する](./create-app.png) +![Alchemyでアプリを作成するスクリーンショット](./create-app.png) -あるいは、カーソルを「Apps(アプリ)」の部分に移動させ、希望するアプリを選択する方法でも既存のAPIキーを取得することができます。 ここでは、「View Key(キーを表示)」できる他、「Edit App(アプリを編集)」して、特定のドメインをホワイトリストに追加したり、開発者ツールを参照したり、アナリティクスを確認することができます。 +「Apps」にカーソルを合わせていずれかを選択することでも、既存のAPIキーを取得できます。 ここでは「View Key」の表示に加え、「Edit App」で特定のドメインをホワイトリストに登録したり、複数の開発者ツールを閲覧したり、アナリティクスを確認したりすることができます。 -![APIキーの取得方法をユーザーに表示するGIF画像](./pull-api-keys.gif) +![ユーザーへのAPIキー取得方法を示すGIF](./pull-api-keys.gif) -## 3. コマンドラインでリクエストを作成する {#make-a-request-from-the-command-line} +## 3. コマンドラインからリクエストを行う {#make-a-request-from-the-command-line} -JSON-RPCとcurlを使用して、Alchemy経由でイーサリアムブロックチェーンとのやり取りを行います。 +JSON-RPCとcurlを使用して、Alchemy経由でイーサリアムブロックチェーンとやり取りをします。 -マニュアルでリクエストを作成する場合は、`JSON-RPC`の`POST`リクエストを使ってやりとりすることをお勧めします。 `Content-Type: application/json`のヘッダーと、クエリの`POST`本文に、以下のフィールドを入力してください: +手動リクエストの場合は、`POST`リクエストを介して`JSON-RPC`とやり取りすることをお勧めします。 `Content-Type: application/json`ヘッダーと、以下のフィールドを含むクエリを`POST`ボディとして渡すだけです: -- `jsonrpc`:JSON-RPC のバージョン - 現在対応しているのは バージョン`2.0` のみです。 -- `method`:ETH APIメソッド。 [APIリファレンスを参照してください。](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc) +- `jsonrpc`: JSON-RPCのバージョン — 現在は`2.0`のみがサポートされています。 +- `method`: ETH APIメソッド。 [APIリファレンスを参照](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc) - `params`: メソッドに渡すパラメータのリストです。 -- `id`: このリクエストのIDです。 この値は応答によって返されるため、どのリクエストに対する応答なのかを追跡できます。 +- `id`: リクエストのIDです。 この値はレスポンスによって返されるため、どのリクエストに対するレスポンスなのかを追跡できます。 -以下の例は、コマンドラインから現在のガス代の情報を取得するコードです。 +以下は、コマンドラインから現在のガス価格を取得するために実行できる一例です: ```bash curl https://eth-mainnet.alchemyapi.io/v2/demo \ @@ -63,37 +58,37 @@ curl https://eth-mainnet.alchemyapi.io/v2/demo \ -d '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":73}' ``` -_**注意:** [https://eth-mainnet.alchemyapi.io/v2/demo](https://eth-mainnet.alchemyapi.io/jsonrpc/demo)は、`https://eth-mainnet.alchemyapi.io/v2/**your-api-key` など、あなた自身のAPIキーと置き換えてください。_ +_**注:** [https://eth-mainnet.alchemyapi.io/v2/demo](https://eth-mainnet.alchemyapi.io/jsonrpc/demo) を、ご自身のAPIキー `https://eth-mainnet.alchemyapi.io/v2/**your-api-key` に置き換えてください。_ -**出力:** +**結果:** ```json { "id": 73,"jsonrpc": "2.0","result": "0x09184e72a000" // 10000000000000 } ``` -## 4. Web3 クライアントを設定する {#set-up-your-web3-client} +## 4. Web3クライアントのセットアップ {#set-up-your-web3-client} -**すでにクライアントをインストール済みの場合は、** 現在のノードプロバイダーのURLを、APIキーを含むAlchemyのURL( `"https://eth-mainnet.alchemyapi.io/v2/your-api-key"`など)に変更します。 +**既存のクライアントをお持ちの場合:** 現在のノードプロバイダーのURLを、ご自身のAPIキーを含むAlchemyのURL `“https://eth-mainnet.alchemyapi.io/v2/your-api-key\"` に変更してください。 -**_注意:_** 以下のスクリプトは、 コマンドラインで実行するのではなく、**ノードコンテキスト**または**ファイルに保存した形で**実行する必要があります。 Nodeまたはnpmがインストールされていない場合は、[Mac用設定ガイド](https://app.gitbook.com/@alchemyapi/s/alchemy/guides/alchemy-for-macs) をご覧ください。 +**_注:_** 以下のスクリプトは、コマンドラインから実行するのではなく、**nodeコンテキスト**で実行するか、**ファイルに保存する**必要があります。 まだNodeまたはnpmをインストールしていない場合は、こちらの[Mac用クイックセットアップガイド](https://app.gitbook.com/@alchemyapi/s/alchemy/guides/alchemy-for-macs)をご覧ください。 -Alchemyと統合可能な[Web3ライブラリ](https://docs.alchemyapi.io/guides/getting-started#other-web3-libraries)は無数に存在しますが、このチュートリアルでは、Alchemyとシームレスに動作するように構築・設定されたweb3.jsの完全互換版である[Alchemy Web3](https://docs.alchemy.com/reference/api-overview)をお勧めします。 Alchemy Web3は、自動リトライや WebScoket に対する充実したサポートなどの利点を持っています。 +Alchemyと統合できる[Web3ライブラリ](https://docs.alchemyapi.io/guides/getting-started#other-web3-libraries)はたくさんありますが、web3.jsのドロップインリプレースメントとして、Alchemyとシームレスに動作するように構築・設定された[Alchemy Web3](https://docs.alchemy.com/reference/api-overview)の使用をお勧めします。 これにより、自動リトライや堅牢なWebSocketサポートなど、複数の利点が得られます。 -Alchemy Web3.jsをインストールするには、 **プロジェクトディレクトリに移動して**、以下を実行します。 +AlchemyWeb3.jsをインストールするには、**プロジェクトディレクトリに移動し**、以下を実行してください: -**Yarnの場合:** +**Yarnの場合:** ``` yarn add @alch/alchemy-web3 ``` -**NPMの場合:** +**NPMの場合:** ``` npm install @alch/alchemy-web3 ``` -Alchemyのノードインフラとやり取りするには、Node.jsで実行するか、JavaScriptファイルに以下の行を追加します: +Alchemyのノードインフラストラクチャとやり取りするには、NodeJSで実行するか、このコードをJavaScriptファイルに追加してください: ```js const { createAlchemyWeb3 } = require("@alch/alchemy-web3") @@ -102,26 +97,26 @@ const web3 = createAlchemyWeb3( ) ``` -## 5. はじめてのWeb3スクリプトを作成しましょう! {#write-your-first-web3-script} +## 5. 最初のWeb3スクリプトを書きましょう! {#write-your-first-web3-script} -それではさっそく、実際にweb3のプログラミングを始めましょう。まずは、イーサリアム・メインネットにおける最新のブロック番号を出力する簡単なスクリプトを作成します。 +それでは、少しWeb3プログラミングを試してみましょう。イーサリアムメインネットから最新のブロック番号を出力する簡単なスクリプトを作成します。 -**1. すでに実行していない場合、ターミナルで新規のプロジェクトディレクトリを作成し、cdコマンドで移動します。** +\*\*1. **まだ作成していない場合は、ターミナルで新しいプロジェクトディレクトリを作成し、そこにcdで移動してください:** ``` mkdir web3-example cd web3-example ``` -**2. まだ実行していない場合、Alchemy web3(または任意の web3)の依存関係をプロジェクトにインストールします。** +\*\*2. **まだインストールしていない場合は、Alchemy Web3(または任意のWeb3)の依存関係をプロジェクトにインストールしてください:** ``` npm install @alch/alchemy-web3 ``` -**3. `index.js`という名称のファイルを作成し、以下の内容を追加します:** +\*\*3. **`index.js`という名前のファイルを作成し、次の内容を追加してください:** -> 最終的には、`demo`をあなたのAlchemy HTTP API keyに置き換える必要があります。 +> 最終的には`demo`をご自身のAlchemy HTTP APIキーに置き換える必要があります。 ```js async function main() { @@ -133,22 +128,22 @@ async function main() { main() ``` -非同期関数についてよく理解していない場合は、 この[Mediumの記事](https://medium.com/better-programming/understanding-async-await-in-javascript-1d81bb079b2c)を参照してください。 +async(非同期処理)に慣れていませんか? こちらの[Mediumの投稿](https://medium.com/better-programming/understanding-async-await-in-javascript-1d81bb079b2c)をご確認ください。 -**4. ノードを使用して、ターミナルで実行します。** +\*\*4. **nodeを使ってターミナルで実行します** ``` node index.js ``` -**5. コンソールに、最新のブロック番号が出力されるはずです。** +\*\*5. **コンソールに最新のブロック番号が出力されているはずです!** ``` The latest block number is 11043912 ``` -**よくできました! おめでとうございます! Alchemyを使用した最初のweb3スクリプトが完成しました 🎉** +**素晴らしい!** おめでとうございます! **これでAlchemyを使って最初のWeb3スクリプトが書けました 🎉** -次は何を学べば良いのかわからない場合は、 [「ハローワールド・スマートコントラクトガイド」](https://docs.alchemyapi.io/tutorials/hello-world-smart-contract)を使って、はじめてのスマートコントラクトのデプロイとSolidityプログラミングに挑戦するか、[ダッシュボード・デモアプリ](https://docs.alchemyapi.io/tutorials/demo-app)でダッシュボードに関するあなたの知識をテストしてみましょう! +次に何をすればいいかわからないですか? [Hello Worldスマートコントラクトガイド](https://www.alchemy.com/docs/hello-world-smart-contract)で最初のスマートコントラクトをデプロイしてSolidityプログラミングを試すか、[ダッシュボードデモアプリ](https://docs.alchemyapi.io/tutorials/demo-app)でダッシュボードの知識を試してみてください! -_[Alchemyに無料登録し](https://auth.alchemyapi.io/signup)、[ドキュメンテーション](https://docs.alchemyapi.io/)を確認してください。また、[Twitter](https://twitter.com/AlchemyPlatform)_をフォローして、最新ニュースをチェックしてください。 +_[Alchemyに無料でサインアップ](https://auth.alchemy.com/)し、[ドキュメント](https://www.alchemy.com/docs/)をチェックして、最新ニュースは[Twitter](https://twitter.com/AlchemyPlatform)でフォローしてください。_ diff --git a/public/content/translations/ja/developers/tutorials/guide-to-smart-contract-security-tools/index.md b/public/content/translations/ja/developers/tutorials/guide-to-smart-contract-security-tools/index.md index 9a1ae545f7a..58df2c54541 100644 --- a/public/content/translations/ja/developers/tutorials/guide-to-smart-contract-security-tools/index.md +++ b/public/content/translations/ja/developers/tutorials/guide-to-smart-contract-security-tools/index.md @@ -1,105 +1,102 @@ --- -title: スマートコントラクト関連セキュリティツールのガイド -description: テストおよびプログラム分析に関する3種類のテクニックの概要 +title: "スマートコントラクトセキュリティツールのガイド" +description: "3つの異なるテストおよびプログラム分析手法の概要" author: "Trailofbits" lang: ja -tags: - - "Solidity" - - "スマートコントラクト" - - "セキュリティ" +tags: [ "Solidity", "スマート契約", "セキュリティ" ] skill: intermediate published: 2020-09-07 -source: セキュアなコントラクトの開発 +source: Building secure contracts sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis --- -このチュートリアルでは、以下の3種類のテスト/プログラム分析テクニックを取り上げます: +ここでは、3つの特徴的なテストおよびプログラム分析の手法を使用します。 -- **[Slither](/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/)を用いた静的解析**では、プログラムのすべてのパスにつき、様々なプログラム表示(例:制御フローグラフ)を通じて同時に近似値を求め、分析します。 -- **[Echidna](/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/)を用いたファジング**では、疑似乱数に基づいて生成したトランザクションを用いてコードを実行します。 ファザーは、特定のプロパティに違反する一連のトランザクションを見つけようとします。 -- **[Manticore](/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/)を用いたシンボリック実行**は、各実行パスを数式に変換した上で、制約条件をチェックできるフォーマルな検証テクニックです。 +- **[Slither](/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/)による静的解析。** プログラムのすべてのパスが、異なるプログラム表示(例:制御フローグラフ)を通じて、同時に近似、分析されます。 +- **[Echidna](/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/)によるファジング。** コードは、トランザクションの疑似ランダム生成によって実行されます。 ファザーは、特定のプロパティに違反する一連のトランザクションを見つけようとします。 +- **[Manticore](/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/)によるシンボリック実行。** 各実行パスを数式に変換し、その上で制約をチェックできる形式的検証テクニックです。 -上記の手法はそれぞれの利点と欠点を持つため、[特定の用途](#determining-security-properties)に合わせて選択すべきです。 +各テクニックには長所と短所があり、[特定のケース](#determining-security-properties)で役立ちます: -| 手法 | ツール | 用途 | 速度 | バグを見落とす可能性 | 偽陽性の可能性 | -| -------- | --------- | ------------------------ | --- | ---------- | ------- | -| 静的解析 | Slither | CLI およびスクリプト | 数秒 | 中程度 | 低い | -| ファジング | Echidna | Solidityのプロパティ | 数分間 | 低い | なし | -| シンボリック実行 | Manticore | Solidity のプロパティおよび スクリプト | 数時間 | なし* | なし | +| テクニック | ツール | 使用法 | 速度 | 見逃されるバグ | 誤検知 | +| -------- | --------- | -------------------- | --- | ------- | --- | +| 静的解析 | Slither | CLIとスクリプト | 数秒 | 中 | 低 | +| ファジング | Echidna | Solidityのプロパティ | 数分間 | 低 | なし | +| シンボリック実行 | Manticore | Solidityのプロパティとスクリプト | 数時間 | なし\* | なし | -* すべてのパスをタイムアウトなしで検証した場合 +- すべてのパスがタイムアウトなしで探索された場合 -**Slither**は、コントラクトを数秒間で分析できますが、 静的解析は誤検知を伴う場合があり、複雑なチェック作業(例:算術演算の検査)にはあまり適していません。 Slitherは、APIを通じてプッシュボタンによりビルトインの検知器にアクセスするか、ユーザー定義のチェック作業用のAPIを通じて実行します。 +**Slither**は数秒でコントラクトを分析しますが、静的解析は誤検知につながる可能性があり、複雑なチェック(算術チェックなど)にはあまり適していません。 Slitherは、API経由で実行し、組み込みの検出器にプッシュボタンでアクセスするか、ユーザー定義のチェックを行います。 -**Echidna**では、検出に数分間を要しますが、偽陽性は検出しません。 Echidnaでは、Solidityで作成され、ユーザーが提供したセキュリティ属性をチェックします。 ただし、ランダムな検索に基づくため、すべてのバグを検出するとは限りません。 +**Echidna**は、実行に数分を要しますが、生成するのは真陽性のみです。 Echidnaは、ユーザーが提供した、Solidityで記述されたセキュリティプロパティをチェックします。 ランダムな探索に基づいているため、バグを見逃す可能性があります。 -**Manticore**は、「最も徹底的な」分析を行います。 Echidnaの場合と同様に、ユーザーが提供した属性を検証します。 検出時間はさらに長くなりますが、特定のプロパティを検証でき、偽陽性を検出することもありません。 +**Manticore**は、"最もヘビー級"の分析を実行します。 Echidnaと同様に、Manticoreはユーザーが提供したプロパティを検証します。 実行にはより時間がかかりますが、プロパティの有効性を証明でき、誤検知を報告しません。 -## 推奨するワークフロー {#suggested-workflow} +## 推奨ワークフロー {#suggested-workflow} -まず、Slitherに内蔵されている検出機能で開始し、現時点において単純なバグが存在せず、今後も紛れ込む可能性がないことを確認しましょう。 Slither を使って、継承、変数の依存関係、および構造的な問題についてチェックします。 コードベースの規模が拡大するのに伴い、Echidnaを使って、ステートマシンにおけるより複雑なプロパティをテストします。 また、上書きされる機能に対する保護などのSolidityでは提供されない保護については、Slitherを使ってカスタムチェックを作成してください。 最後にManticoreを使用して、セキュリティに関する最重要のプロパティ(例:算術演算)のみに対象を絞った検証を行います。 +まずSlitherの組み込み検出器から始めて、単純なバグが現在存在しないこと、そして後で入り込まないことを確認します。 Slitherを使用して、継承、変数の依存関係、構造上の問題に関連するプロパティをチェックします。 コードベースが大きくなるにつれて、Echidnaを使用してステートマシンのより複雑なプロパティをテストします。 Solidityでは利用できない保護(関数のオーバーライドに対する保護など)のために、Slitherを再検討してカスタムチェックを開発します。 最後に、Manticoreを使用して、重要なセキュリティプロパティ(算術演算など)の的を絞った検証を実行します。 -- SlitherのCLIを使って、よくある問題を検出する -- Echidnaを使って、スマートコントラクトにおける高度なセキュリティ関連プロパティをテストする -- Slitherを使って、カスタムの静的解析を作成する -- 最重要なセキュリティ属性について詳細な保証が必要になったら、Manticoreを使用する +- SlitherのCLIを使用して、一般的な問題を検出する +- Echidnaを使用して、コントラクトの高レベルのセキュリティプロパティをテストする +- Slitherを使用して、カスタムの静的チェックを作成する +- 重要なセキュリティプロパティの詳細な保証が必要になったら、Manticoreを使用する -**単体テストに関する注意事項**: 高品質のソフトウェア開発には、単体テストが必須です。 一方で、セキュリティ欠陥を検出する上で、単体テストは最善の方法とは言えません。 通常、単体テストはコードの望ましい動作を確認するため(つまり、通常の環境において予想通りに動作するかどうか)に用いられますが、セキュリティ上の欠陥は、デベロッパが見落としていた周縁的なケースで発生する場合が多いのです。 スマートコントラクトを対象とする数十件のセキュリティレビューを調査した結果、[単体テストのカバレッジは、クライアントのコード上で特定されたセキュリティ上の欠陥の数や重大性には影響を与えていないことが分かりました](https://blog.trailofbits.com/2019/08/08/246-findings-from-our-smart-contract-audits-an-executive-summary/)。 +**単体テストに関する注記**。 高品質のソフトウェアを構築するには、単体テストが必要です。 しかし、これらのテクニックはセキュリティ上の欠陥を見つけるのに最適というわけではありません。 単体テストは通常、コードの正常な動作(つまり、通常のコンテキストで期待どおりにコードが機能すること)をテストするために使用されますが、セキュリティ上の欠陥は、開発者が考慮しなかったエッジケースに存在する傾向があります。 数十件のスマートコントラクトのセキュリティレビューに関する我々の調査では、クライアントのコードで発見されたセキュリティ上の欠陥の数や重大度に対して、[単体テストのカバレッジは影響を与えませんでした](https://blog.trailofbits.com/2019/08/08/246-findings-from-our-smart-contract-audits-an-executive-summary/)。 -## 対象とするセキュリティ属性を決定する {#determining-security-properties} +## セキュリティプロパティの決定 {#determining-security-properties} -コードを効果的にテスト、検証するには、まず、対象とすべき分野を特定する必要があります。 セキュリティのためのリソースには限りがありますから、コードベースにおける脆弱な部分や高価値の部分に注力して、この取り組みを最適化することが重要です。 これには、脅威モデリングの手法を用いるとよいでしょう。 以下の事項をレビューしてください: +コードを効果的にテストおよび検証するには、注意が必要な領域を特定する必要があります。 セキュリティに費やすリソースは限られているため、労力を最適化するには、コードベースの脆弱な部分や価値の高い部分を特定することが重要です。 脅威モデリングが役立ちます。 以下をレビューすることを検討してください: -- [迅速リスク評価](https://infosec.mozilla.org/guidelines/risk/rapid_risk_assessment.html)(短時間に完了しなければならない場合に推奨するアプローチです) -- [データ中心システムにおける脅威モデル作成ガイド](https://csrc.nist.gov/publications/detail/sp/800-154/draft)(別名:NIST 800-154) -- [Shostackスレッド・モデリング](https://www.amazon.com/Threat-Modeling-Designing-Adam-Shostack/dp/1118809998) -- [STRIDE](https://wikipedia.org/wiki/STRIDE_(security)) / [DREAD](https://wikipedia.org/wiki/DREAD_(risk_assessment_model)) +- [迅速なリスク評価](https://infosec.mozilla.org/guidelines/risk/rapid_risk_assessment.html) (時間がない場合に推奨されるアプローチ) +- [データ中心システム脅威モデリングガイド](https://csrc.nist.gov/pubs/sp/800/154/ipd)(別名NIST 800-154) +- [Shostack脅威モデリング](https://www.amazon.com/Threat-Modeling-Designing-Adam-Shostack/dp/1118809998) +- [STRIDE](https://wikipedia.org/wiki/STRIDE_\(security\)) / [DREAD](https://wikipedia.org/wiki/DREAD_\(risk_assessment_model\)) - [PASTA](https://wikipedia.org/wiki/Threat_model#P.A.S.T.A.) -- [アサーションを活用する](https://blog.regehr.org/archives/1091) +- [アサーションの使用](https://blog.regehr.org/archives/1091) -### 構成要素 {#components} +### コンポーネント {#components} -どの事項をチェックしたいのかを把握しておくと、適切なツール選択に役立ちます。 +何をチェックしたいかを知ることも、適切なツールを選ぶのに役立ちます。 -スマートコントラクトにおいて問題となる可能性が高い分野としては、以下が挙げられます: +スマートコントラクトに頻繁に関連する幅広い分野には、以下が含まれます: -- **状態マシン** ほとんどのコントラクトは、状態マシンとして表示することが可能です。 ですから、(1)無効な状態に達していないか、(2)状態が有効かつ到達しうるか、および(3)コントラクトをトラップする状態が存在しないか、についてチェックするとよいでしょう。 +- **ステートマシン。** ほとんどのコントラクトはステートマシンとして表現できます。 (1) 無効なステートに到達できないこと、(2) ステートが有効である場合に到達可能であること、(3) コントラクトをトラップするステートがないこと、を確認することを検討してください。 - - 状態マシンの仕様をテストするのに適しているのは、EchidnaとManticoreです。 + - EchidnaとManticoreは、ステートマシンの仕様をテストするのに適したツールです。 -- **アクセス管理。** あなたのシステムに特権ユーザー(例:所有者、管理者など)が含まれる場合、(1)各ユーザーが、権限を持つアクションのみ実行可能であること、および(2)権限レベルがより上のユーザーによるアクションをブロックできる下位ユーザーが存在しないこと、を確認してください。 +- **アクセス制御。** システムに特権ユーザー(例:オーナー、コントローラーなど)がいる場合 (1) 各ユーザーが許可されたアクションのみを実行できること、(2) より特権的なユーザーからのアクションをブロックできるユーザーがいないこと、を保証する必要があります。 - - アクセス管理については、Slither、Echidna、およびManticoreのいずれも適切なチェックを実行できます。 例えばSlitherでは、ホワイトリストに登録された関数のうち、onlyOwner修飾子を持たない関数のみをチェックすることができます。 一方、EchidnaおよびManticoreは、コントラクトが特定のステートに達した場合のみに権限が付与される場合など、より複雑なアクセス管理を確認するのに適しています。 + - Slither、Echidna、Manticoreは、正しいアクセス制御をチェックできます。 例えばSlitherは、`onlyOwner`修飾子を欠く関数がホワイトリスト登録済みのものだけであることをチェックできます。 EchidnaとManticoreは、コントラクトが特定のステートに達した場合にのみ許可が与えられるなど、より複雑なアクセス制御に役立ちます。 -- **算術演算。** 算術演算が正しく実行されるかを確認することは、非常に重要です。 オーバーフロー/アンダーフローを防止するには、`SafeMath`を常に利用することを推奨しますが、同時に、端数処理に関する問題やコントラクトをトラップしてしまうような欠陥など、その他の演算上の欠陥についても確認する必要があります。 +- **算術演算。** 算術演算の健全性をチェックすることは非常に重要です。 あらゆる場所で`SafeMath`を使用することは、オーバーフロー/アンダーフローを防ぐための良い一歩ですが、丸め誤差の問題やコントラクトをトラップする欠陥など、他の算術的な欠陥も考慮する必要があります。 - - この分野では、Manticoreを使用するのがよいでしょう。 当該の算術演算がSMTソルバーの対象範囲外である場合は、Echidnaを選択してもよいです。 + - ここではManticoreが最良の選択です。 算術がSMTソルバーの範囲外である場合は、Echidnaを使用できます。 -- **継承の正確性。** Solidityで作成したコントラクトは、複数の継承に大きく依存しています。 シャドーイング関数において`super`コールが含まれていない場合や、C3 linearizationの順序の誤解釈などのミスは、容易に発生する可能性があります。 +- **継承の正確性。** Solidityコントラクトは多重継承に大きく依存しています。 `super`呼び出しを欠いたシャドウイング関数や、誤って解釈されたC3線形化の順序などの間違いは、簡単に発生し得ます。 - - これらの問題を検出するには、Slitherが最適です。 + - Slitherは、これらの問題を確実に検出するためのツールです。 -- **外部とのやりとり。** コントラクトは相互にやりとりを行いますが、外部のコントラクトの中には信用すべきでないものもあります。 例えば、あなたのコントラクトが外部オラクルに依存する場合、利用可能なオラクルの半分が汚染されていれば、あなたのコントラクトはセキュアであるとは言えないでしょう。 +- **外部とのやりとり。** コントラクトは相互にやりとりしますが、一部の外部コントラクトは信頼すべきではありません。 例えば、コントラクトが外部のオラクルに依存している場合、利用可能なオラクルの半分が侵害されても、そのコントラクトは安全なままでしょうか? - - コントラクトにおける外部とのやりとりをテストするには、ManticoreおよびEchidnaが最適です。 Manticoreには、外部のコントラクトをスタブするためのメカニズムが内蔵されています。 + - ManticoreとEchidnaは、コントラクトと外部とのやりとりをテストするための最良の選択です。 Manticoreには、外部コントラクトをスタブするための組み込みメカニズムがあります。 -- **規格適合性。** イーサリアムの標準(例:ERC-20)には、これまで様々な設計ミスが含まれていました。 ですから、作成するコントラクトの規格上の制約に十分注意してください。 - - Slither、Echidna、およびManticoreはいずれも、特定の規格に対する非遵守を検知するのに役立ちます。 +- **標準準拠。** イーサリアムの標準(例:ERC20)には、その設計に欠陥があった歴史があります。 構築の基盤となる標準の制限に注意してください。 + - Slither、Echidna、Manticoreは、特定の標準からの逸脱を検出するのに役立ちます。 -### ツール選択の早見表 {#tool-selection-cheatsheet} +### ツール選択のチートシート {#tool-selection-cheatsheet} -| 構成要素 | ツール | 具体例 | -| -------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 状態マシン | Echidna、Manticore | | -| アクセス管理 | Slither、Echidna、Manticore | [Slither エクササイズ2](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise2.md)、[Echidna エクササイズ2](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-2.md) | -| 算術演算 | Manticore、Echidna | [Echidna エクササイズ1](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-1.md)、[Manticore エクササイズ1~3](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore/exercises) | -| 継承の正確さ | Slither | [Slither エクササイズ1](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise1.md) | -| 外部とのやりとり | Manticore、Echidna | | -| 規格の遵守 | Slither、Echidna、Manticore | [`slither-erc`](https://github.com/crytic/slither/wiki/ERC-Conformance) | +| コンポーネント | ツール | 実例: | +| -------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ステートマシン | Echidna、Manticore | | +| アクセス制御 | Slither、Echidna、Manticore | [Slither 演習 2](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise2.md)、[Echidna 演習 2](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-2.md) | +| 算術演算 | Manticore、Echidna | [Echidna 演習 1](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-1.md)、[Manticore 演習 1~3](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore/exercises) | +| 継承の正確性 | Slither | [Slither 演習 1](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise1.md) | +| 外部とのやりとり | Manticore、Echidna | | +| 標準準拠 | Slither、Echidna、Manticore | [`slither-erc`](https://github.com/crytic/slither/wiki/ERC-Conformance) | -コントラクトの目的に応じて他の分野についてもチェックする必要がありますが、あらゆるスマートコントラクトにおいて、上記の大まかな注力分野から確認することをお勧めします。 +目標によっては他の領域もチェックする必要がありますが、これらの大まかな重点領域は、あらゆるスマートコントラクトシステムにとって良い出発点となります。 -本サイトのパブリック監査には、検証/テスト済みのプロパティの具体例が含まれています。 実際のセキュリティ関連プロパティについてレビューしたい場合は、以下のレポートの`Automated Testing and Verification(自動テスト/検証)`セクションを参照してください。 +我々の公開監査には、検証またはテストされたプロパティの例が含まれています。 実際のセキュリティプロパティをレビューするには、以下のレポートの`Automated Testing and Verification`のセクションを読むことを検討してください: - [0x](https://github.com/trailofbits/publications/blob/master/reviews/0x-protocol.pdf) - [Balancer](https://github.com/trailofbits/publications/blob/master/reviews/BalancerCore.pdf) diff --git a/public/content/translations/ja/developers/tutorials/hello-world-smart-contract-fullstack/index.md b/public/content/translations/ja/developers/tutorials/hello-world-smart-contract-fullstack/index.md index e57b51fa783..b5025cc984c 100644 --- a/public/content/translations/ja/developers/tutorials/hello-world-smart-contract-fullstack/index.md +++ b/public/content/translations/ja/developers/tutorials/hello-world-smart-contract-fullstack/index.md @@ -1,87 +1,90 @@ --- -title: 初心者向けのHello Worldスマートコントラクト - フルスタック -description: イーサリアムでの簡単なスマートコントラクトの作成とデプロイに関する入門チュートリアル +title: "初心者向けのHello Worldスマートコントラクト - フルスタック" +description: "イーサリアムでの簡単なスマートコントラクトの作成とデプロイに関する入門チュートリアル。" author: "nstrike2" tags: - - "Solidity" - - "Hardhat" - - "Alchemy" - - "スマートコントラクト" - - "デプロイ" - - "ブロックエクスプローラ" - - "フロントエンド" - - "トランザクション" + [ + "Solidity", + "hardhat", + "Alchemy", + "スマート契約", + "デプロイ", + "ブロックエクスプローラー", + "フロントエンド", + "トランザクション" + ] skill: beginner lang: ja published: 2021-10-25 --- -このガイドはブロックチェーンの開発の初心者で、どこから始めたらよいか分からなかったり、スマートコントラクトのデプロイやインタラクト方法について分からない方向けのものです。 これから一緒に、Goerliテストネットワーク上で簡単なスマートコントラクトを作成してデプロイする方法を順を追ってたどりましょう。その際、[MetaMask](https://metamask.io)、[Solidity](https://docs.soliditylang.org/en/v0.8.0/)、[Hardhat](https://hardhat.org)と[Alchemy](https://alchemyapi.io/eth)を使用します。 +このガイドは、ブロックチェーン開発の初心者で、どこから始めればよいか、スマートコントラクトのデプロイやインタラクトの方法がわからない方を対象としています。 [MetaMask](https://metamask.io)、[Solidity](https://docs.soliditylang.org/en/v0.8.0/)、[Hardhat](https://hardhat.org)、[Alchemy](https://alchemy.com/eth) を使って、Goerliテストネットワークでシンプルなスマートコントラクトを作成し、デプロイする手順を説明します。 -このチュートリアルを完了するためにはAlchemyのアカウントが必要です。 [無料でアカウント登録する](https://www.alchemy.com/). +このチュートリアルを完了するには、Alchemyのアカウントが必要です。 [無料アカウントにサインアップ](https://www.alchemy.com/) -質問がある場合は、いつでもお気軽に[Alchemy Discord](https://discord.gg/gWuC7zB)でお問い合わせください。 +ご不明な点がありましたら、[Alchemy Discord](https://discord.gg/gWuC7zB) までお気軽にお問い合わせください。 -## パート1: Hardhatを利用してスマートコントラクトを作りデプロイする {#part-1} +## パート1 - Hardhatを使用してスマートコントラクトを作成・デプロイする {#part-1} -### イーサリアムネットワークに接続する {#connect-to-the-ethereum-network} +### Ethereumネットワークに接続する {#connect-to-the-ethereum-network} -イーサリアムチェーンにリクエストを行う方法はたくさんあります。 簡略化のため、ここではAlchemyの無料アカウントを使用します。このブロックチェーンのデベロッパープラットフォームとAPIにより、独自のノードを実行することなく、イーサリアムチェーンとの通信が可能になります。 Alchemyには、スマートコントラクトのデプロイメントにおいて内部で何が起こっているのかを把握するためにこのチュートリアルで利用する、監視と分析のためのデベロッパーツールも備わっています。 +イーサリアムチェーンにリクエストを行う方法はたくさんあります。 簡潔にするため、ブロックチェーン開発者プラットフォームであり、APIでもあるAlchemyの無料アカウントを使用します。これにより、自分でノードを実行することなく、Ethereumチェーンと通信できます。 Alchemyには、モニタリングと分析のための開発者ツールもあります。このチュートリアルでは、これらのツールを利用して、スマートコントラクトのデプロイで内部的に何が起こっているかを理解します。 -### アプリのAPIキーの作成 {#create-your-app-and-api-key} +### アプリとAPIキーを作成する {#create-your-app-and-api-key} -Alchemyのアカウントを作成した後、アプリを作成することでAPIキーを生成することができます。 これにより、Goerliテストネットへのリクエストが可能になります。 テストネットに詳しくない場合は、[Alchemyのネットワークの選択ガイド](https://docs.alchemyapi.io/guides/choosing-a-network)をお読みください。 +Alchemyのアカウントを作成したら、アプリを作成してAPIキーを生成できます。 これにより、Goerliテストネットにリクエストを送信できるようになります。 テストネットに馴染みがない場合は、[Alchemyのネットワーク選択ガイド](https://www.alchemy.com/docs/choosing-a-web3-network) をお読みください。 -Alchemyダッシュボード上にあるナビゲーションバーで**Apps**ドロップダウンがあります。そこで、**Create App**をクリックします。 +Alchemyのダッシュボードで、ナビゲーションバーにある **Apps** ドロップダウンを見つけ、**Create App** をクリックします。 -![Hello WorldのCreate App](./hello-world-create-app.png) +![Hello worldアプリ作成](./hello-world-create-app.png) -アプリに「_Hello World_」という名前を付けて、短い説明を書きます。 環境は、**Staging**を選択します。ネットワークは、**Goerli**を選択します。 +アプリに「_Hello World_」という名前を付け、簡単な説明を記述します。 環境として **Staging** を、ネットワークとして **Goerli** を選択します。 -![Hello WorldのCreate App画面](./create-app-view-hello-world.png) +![アプリ作成ビュー hello world](./create-app-view-hello-world.png) -_注意: 必ず**Goerli**を選択してください。そうしないと、このチュートリアルどおり行きません。_ +_注: 必ず **Goerli** を選択してください。選択しないと、このチュートリアルは機能しません。_ -**Create app**をクリックしてください。 アプリが下の表に表示されます。 +**Create app** をクリックします。 作成したアプリが下の表に表示されます。 -### イーサリアムアカウントの作成 {#create-an-ethereum-account} +### Ethereumアカウントを作成する {#create-an-ethereum-account} -トランザクションの送受信には、イーサリアムアカウントが必要です。 ここでは、MetaMaskを使います。MataMaskは、ユーザーがイーサリアムのアカウントアドレスを管理できるブラウザーの仮想ウォレットです。 +トランザクションを送受信するには、Ethereumアカウントが必要です。 ここでは、ユーザーがEthereumアカウントアドレスを管理できる、ブラウザ上の仮想ウォレットであるMetaMaskを使用します。 -Metamaskのアカウントは[こちら](https://metamask.io/download)から無料でダウンロード、作成できます。 アカウントを作成後、またはすでにアカウントをお持ちの場合は(実際に支払いが発生しないように)右上の「Goerli Test Network」に切り替えてください。 +MetaMaskアカウントは、[こちら](https://metamask.io/download)から無料でダウンロードして作成できます。 アカウントを作成するとき、またはすでにアカウントをお持ちの場合は、右上の「Goerli Test Network」に必ず切り替えてください (実在の通貨を扱わないようにするため)。 -### ステップ4: フォーセットからイーサリアムを追加する {#step-4-add-ether-from-a-faucet} +### ステップ4: フォーセットからイーサを追加する {#step-4-add-ether-from-a-faucet} -テストネットワークにスマートコントラクトをデプロイするには、偽のETHが複数必要になります。 GoerliネットワークでETHを取得するには、Goerliフォーセットに移動し、あなたのGoerliのアカウントアドレスを入力します。 Goerliフォーセットは最近、不安定になることがあります。試せるオプションのリストは、[テストネットワークのページ](/developers/docs/networks/#goerli)を参照してください。 +スマートコントラクトをテストネットワークにデプロイするには、偽のETHが必要です。 GoerliネットワークでETHを取得するには、Goerliフォーセットにアクセスし、Goerliアカウントアドレスを入力します。 Goerliフォーセットは最近、少し信頼性が低い場合があります。試せるオプションの一覧については、[テストネットワークのページ](/developers/docs/networks/#goerli) を参照してください: -_注意: ネットワークの混雑状況によっては、時間がかかる場合があります。_ +_注: ネットワークの混雑により、しばらく時間がかかる場合があります。_ +`` ### ステップ5: 残高を確認する {#step-5-check-your-balance} -あなたのウォレットにETHがあることをダブルチェックし、[eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)リクエストを[Alchemyのコンポーザーツール](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D)を使って出してみましょう。 リクエストすると、ウォレット内のETHの量が返却されます。 詳細については、[Alchemyの短いチュートリアルにあるコンポーザーツールの使用方法](https://youtu.be/r6sjRxBZJuU)をご覧ください。 +ウォレットにETHが入っていることを再確認するために、[Alchemyのcomposerツール](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D) を使って [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) リクエストを実行してみましょう。 このリクエストをすると、ウォレット内のETHの額が返されます。 詳細については、[composerツールの使用方法に関するAlchemyの短いチュートリアル](https://youtu.be/r6sjRxBZJuU) をご覧ください。 -MetaMaskアカウントのアドレスを入力し、**Send Request**をクリックします。 以下のコードスニペットのようなレスポンスが来ます。 +MetaMaskアカウントのアドレスを入力し、**Send Request** をクリックします。 以下のようなコードスニペットのレスポンスが表示されます。 ```json { "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" } ``` -> _注意: この結果の単位はweiであり、ETHではありません。 weiはETHの最小単位として使われています。_ +> _注: この結果はETHではなく、wei単位です。_ _Weiはetherの最小単位として使用されます。_ -ご安心ください。 私たちの偽物のお金はすべてそこにあります。 +ふう! 私たちの偽物のお金はすべてそこにあります。 ### ステップ6: プロジェクトを初期化する {#step-6-initialize-our-project} -まず、プロジェクトのフォルダを作成する必要があります。 コマンドラインに移動し、次のように入力します。 +まず、プロジェクト用のフォルダを作成する必要があります。 コマンドラインに移動し、次のように入力します。 ``` mkdir hello-world cd hello-world ``` -プロジェクトフォルダ内に入ったら、`npm init`を使用してプロジェクトを初期化します。 +プロジェクトフォルダに入ったので、`npm init`を使用してプロジェクトを初期化します。 -> npmをまだインストールしていない場合は、[こちら](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm)の手順に従いNode.jsとnpmをインストールします。 +> まだnpmがインストールされていない場合は、[Node.jsとnpmをインストールするための指示](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm)に従ってください。 このチュートリアルでは、初期化における質問にどのように答えるかには重点を置いていません。 参考までに、私たちは次のように行いました。 @@ -111,29 +114,29 @@ About to write to /Users/.../.../.../hello-world/package.json: } ``` -package.jsonを承認すれば完了です。 +package.jsonを承認すれば完了です! -### ステップ7: Hardhatのダウンロード {#step-7-download-hardhat} +### ステップ7: Hardhatをダウンロードする {#step-7-download-hardhat} Hardhatは、イーサリアムのソフトウェアをコンパイル、デプロイ、テスト、デバッグするための開発環境です。 デベロッパーがライブチェーンにデプロイする前に、スマートコントラクトや分散型アプリケーション(Dapp)をローカルに構築する際に役立ちます。 -先ほど作成した`hello-world`プロジェクト内で、以下を実行します。 +`hello-world`プロジェクト内で次を実行します。 ``` npm install --save-dev hardhat ``` -[インストール手順](https://hardhat.org/getting-started/#overview)の詳細については、こちらのページをご覧ください。 +[インストール手順](https://hardhat.org/getting-started/#overview)の詳細については、このページをご覧ください。 ### ステップ8: Hardhatプロジェクトを作成する {#step-8-create-hardhat-project} -先ほど作成した`hello-world`プロジェクトフォルダ内で、以下を実行します。 +`hello-world`プロジェクトフォルダ内で、以下を実行します: ``` npx hardhat ``` -ウェルカムメッセージと、次に何をするのかを選択できるオプションが表示されます。 「Create an empty hardhat.config.js」を選択します。 +ウェルカムメッセージと、次に何をするのかを選択できるオプションが表示されます。 「create an empty hardhat.config.js」を選択してください。 ``` 888 888 888 888 888 @@ -153,57 +156,57 @@ Create a sample project Quit ``` -これで、プロジェクト内に`hardhat.config.js`ファイルが生成されます。 プロジェクトの設定を明記するのにチュートリアルの後半でこれを使用します。 +これにより、プロジェクトに `hardhat.config.js` ファイルが生成されます。 チュートリアルの後半で、これを使用してプロジェクトのセットアップを指定します。 ### ステップ9: プロジェクトフォルダを追加する {#step-9-add-project-folders} -プロジェクトを整理するために、2つの新しいフォルダを作成します。 コマンドラインで、`hello-world`プロジェクトのルートディレクトリに移動し、次のように入力します。 +プロジェクトを整理するために、2つの新しいフォルダを作成しましょう。 コマンドラインで、`hello-world` プロジェクトのルートディレクトリに移動し、次のように入力します: ``` mkdir contracts mkdir scripts ``` -- `contracts/`は、Hello Worldスマートコントラクトのコードファイルを格納する場所です。 -- `scripts/`は、コントラクトをデプロイして対話するスクリプトを保持する場所です。 +- `contracts/` には、hello worldスマートコントラクトのコードファイルを保存します +- `scripts/` には、コントラクトをデプロイして対話するためのスクリプトを保存します ### ステップ10: コントラクトを作成する {#step-10-write-our-contract} -一体いつになったらコードを書くのだろうと疑問をお持ちではないでしょうか 。 まさに、その時です! +「いつになったらコードを書くのだろう?」と思っているかもしれませんね。 その時が来ました! -あなたのお気に入りのエディターでhello-worldプロジェクトを開きます。 スマートコントラクトは、最も一般的にはSolidityで書かれています。そのため、Solidityでスマートコントラクトを作成します。 +お好きなエディタでhello-worldプロジェクトを開いてください。 スマートコントラクトは、最も一般的にはSolidityで記述されており、今回もSolidityを使ってスマートコントラクトを作成します。 -1. `contracts`フォルダに移動し、`HelloWorld.sol`という名前の新規ファイルを作成します。 -2. 以下は、このチュートリアルで使用するHello Worldスマートコントラクトのサンプルです。 以下の内容を`HelloWorld.sol`ファイルにコピーします。 +1. `contracts` フォルダに移動し、`HelloWorld.sol` という名前の新しいファイルを作成します。 +2. 以下は、このチュートリアルで使用するHello Worldスマートコントラクトのサンプルです。 以下の内容を `HelloWorld.sol` ファイルにコピーしてください。 -_注意: 必ずコメントを読み、このコントラクトの処理内容を理解してください。_ +_注: このコントラクトが何をするのかを理解するために、必ずコメントをお読みください。_ ``` -// Specifies the version of Solidity, using semantic versioning. -// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +// Solidityのバージョンをセマンティックバージョニングで指定します。 +// 詳細: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma pragma solidity >=0.7.3; -// Defines a contract named `HelloWorld`. -// A contract is a collection of functions and data (its state). Once deployed, a contract resides at a specific address on the Ethereum blockchain. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +// `HelloWorld` という名前のコントラクトを定義します。 +// コントラクトは関数とデータ (その状態) の集合です。一度デプロイされると、コントラクトはEthereumブロックチェーン上の特定のアドレスに存在します。詳細: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html contract HelloWorld { - //Emitted when update function is called - //Smart contract events are a way for your contract to communicate that something happened on the blockchain to your app front-end, which can be 'listening' for certain events and take action when they happen. + // update関数が呼び出されたときに発行されます + //スマートコントラクトのイベントは、ブロックチェーン上で何かが起こったことをコントラクトがアプリのフロントエンドに伝える方法です。フロントエンドは特定のイベントを「リッスン」し、それが起こったときに行動を起こすことができます。 event UpdatedMessages(string oldStr, string newStr); - // Declares a state variable `message` of type `string`. - // State variables are variables whose values are permanently stored in contract storage. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value. + // `string` 型の状態変数 `message` を宣言します。 + // 状態変数は、その値がコントラクトのストレージに永続的に保存される変数です。`public` キーワードにより、変数はコントラクトの外部からアクセス可能になり、他のコントラクトやクライアントが値をアクセスするために呼び出せる関数が作成されます。 string public message; - // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation. - // Constructors are used to initialize the contract's data. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + // 多くのクラスベースのオブジェクト指向言語と同様に、コンストラクタはコントラクト作成時に一度だけ実行される特別な関数です。 + // コンストラクタはコントラクトのデータを初期化するために使用されます。詳細:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors constructor(string memory initMessage) { - // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable). + // 文字列引数 `initMessage` を受け取り、その値をコントラクトの `message` ストレージ変数に設定します)。 message = initMessage; } - // A public function that accepts a string argument and updates the `message` storage variable. + // 文字列引数を受け取り、 `message` ストレージ変数を更新する公開関数です。 function update(string memory newMessage) public { string memory oldMsg = message; message = newMessage; @@ -212,15 +215,15 @@ contract HelloWorld { } ``` -これは、作成時にメッセージを保存する基本的なスマートコントラクトです。 `update`関数を呼び出すことで更新できます。 +これは、作成時にメッセージを保存する基本的なスマートコントラクトです。 `update` 関数を呼び出すことで更新できます。 ### ステップ11: MetaMaskとAlchemyをプロジェクトに接続する {#step-11-connect-metamask-alchemy-to-your-project} -ここまでで、MetaMaskウォレットとAlchemyアカウントを作成し、スマートコントラクトも作成しました。次はこの3つを接続しましょう。 +MetaMaskウォレットとAlchemyアカウントを作成し、スマートコントラクトも作成しました。次はこの3つを接続しましょう。 -ウォレットから送信されるすべてのトランザクションには、固有の秘密鍵を使用した署名が必要です。 この許可をプログラムに与えるために、秘密鍵を環境ファイルに安全に格納する作業を行います。 AlchemyのAPIキーもここに保存します。 +ウォレットから送信されるすべてのトランザクションには、一意の秘密鍵を使用した署名が必要です。 プログラムにこの許可を与えるために、秘密鍵を環境ファイルに安全に保存することができます。 AlchemyのAPIキーもここに保存します。 -> トランザクションの送信の詳細については、[こちらのチュートリアル](https://docs.alchemyapi.io/alchemy/tutorials/sending-transactions-using-web3-and-alchemy)のweb3使ったトランザクションの送信に関する内容をご覧ください。 +> トランザクションの送信についてさらに詳しく知るには、web3を使用したトランザクションの送信に関する[このチュートリアル](https://www.alchemy.com/docs/hello-world-smart-contract#step-11-connect-metamask--alchemy-to-your-project) を確認してください。 まず、プロジェクトディレクトリにdotenvパッケージをインストールします。 @@ -228,31 +231,31 @@ contract HelloWorld { npm install dotenv --save ``` -次に、プロジェクトのルートディレクトリに`.env`ファイルを作成します。 MetaMask秘密鍵とHTTP Alchemy API URLをファイルに加えます。 +次に、プロジェクトのルートディレクトリに `.env` ファイルを作成します。 そのファイルに、MetaMaskの秘密鍵とHTTP Alchemy APIのURLを追加します。 -環境ファイルの名前は、必ず`.env`にしてください。そうしないと環境ファイルとして認識されません。 +環境ファイルは `.env` という名前にする必要があります。そうしないと、環境ファイルとして認識されません。 -`process.env`や`.env-custom`などの名前を付けないでください。 +`process.env` や `.env-custom` など、他の名前にしないでください。 -- 秘密鍵をエクスポートするには、[こちらの手順](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key)に従ってください。 -- HTTP Alchemy APIのURLを取得するには、以下を参照してください。 +- 秘密鍵をエクスポートするための[これらの手順](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key)に従ってください +- HTTP Alchemy API URLを取得するには、以下を参照してください ![](./get-alchemy-api-key.gif) -`.env`ファイルは次のようになります。 +`.env`は次のようになります: ``` API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" PRIVATE_KEY = "your-metamask-private-key" ``` -これらの変数を実際にコードに接続するために、ステップ13でこれらの変数を`hardhat.config.js`ファイル内で参照します。 +これらをコードに実際に接続するために、ステップ13で`hardhat.config.js`ファイル内のこれらの変数を参照します。 ### ステップ12: Ethers.jsをインストールする {#step-12-install-ethersjs} -Ethers.jsは、よりユーザーフレンドリーなメソッドで[標準のJSON-RPCメソッド](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc)をラップすることにより、イーサリアムとの対話やリクエストを簡単に行うためのライブラリです。 +Ethers.jsは、[標準的なJSON-RPCメソッド](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc)をよりユーザーフレンドリーなメソッドでラップすることにより、Ethereumとのインタラクションやリクエストを容易にするライブラリです。 -Hardhatを使用すると、追加のツールと拡張機能のための[プラグイン](https://hardhat.org/plugins/)を統合できます。 コントラクトのデプロイでは、[Ethersプラグイン](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers)を利用します。 +Hardhatでは、追加のツールや拡張機能のために[プラグイン](https://hardhat.org/plugins/)を統合することができます。 コントラクトのデプロイには[Ethersプラグイン](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers)を利用します。 プロジェクトのホームディレクトリで以下を実行します。 @@ -260,11 +263,11 @@ Hardhatを使用すると、追加のツールと拡張機能のための[プラ npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0" ``` -### ステップ13: hardhat.config.jsをアップデートする {#step-13-update-hardhat-configjs} +### ステップ13: hardhat.config.jsを更新する {#step-13-update-hardhat-configjs} -ここまでで、いくつかの依存関係とプラグインを追加しました。次に、`hardhat.config.js`を更新して、プロジェクトがそれらすべてについて認識できるようにする必要があります。 +ここまでで、いくつかの依存関係とプラグインを追加しました。次に、プロジェクトがそれらすべてを認識できるように、`hardhat.config.js`を更新する必要があります。 -`hardhat.config.js`を以下のように更新します。 +`hardhat.config.js`を次のように更新します: ```javascript /** @@ -291,7 +294,7 @@ module.exports = { ### ステップ14: コントラクトをコンパイルする {#step-14-compile-our-contract} -ここまででしっかりと動作していることを確認するため、コントラクトをコンパイルしてみましょう。 `compile`タスクは、組み込みのHardhatタスクの1つです。 +ここまでの作業がうまくいっていることを確認するために、コントラクトをコンパイルしてみましょう。 `compile`タスクは、組み込みのHardhatタスクの1つです。 コマンドラインで以下を実行します。 @@ -299,19 +302,19 @@ module.exports = { npx hardhat compile ``` -`SPDX license identifier not provided in source file`という警告が表示される場合がありますが、心配する必要はありません。警告が表示されないのがベストですが、 表示された場合は、いつでも[Alchemy discord](https://discord.gg/u72VCg3)でメッセージを送信できます。 +`SPDX license identifier not provided in source file` という警告が表示されるかもしれませんが、心配する必要はありません。それ以外はすべて問題ないはずです! うまくいかない場合は、いつでも[Alchemy Discord](https://discord.gg/u72VCg3)でメッセージを送ることができます。 -### ステップ15: デプロイスクリプトを書く {#step-15-write-our-deploy-script} +### ステップ15: デプロイスクリプトを作成する {#step-15-write-our-deploy-script} コントラクトの作成と設定ファイルの作成が完了したら、いよいよコントラクトのデプロイのためのスクリプトを作成します。 -`scripts/`フォルダに移動して、`deploy.js`という名前のファイルを新規に作成し、以下の内容を追加します。 +`scripts/`フォルダに移動して`deploy.js`という名前の新しいファイルを作成し、次の内容を追加します: ```javascript async function main() { const HelloWorld = await ethers.getContractFactory("HelloWorld") - // Start deployment, returning a promise that resolves to a contract object + // デプロイを開始し、コントラクトオブジェクトに解決されるpromiseを返す const hello_world = await HelloWorld.deploy("Hello World!") console.log("Contract deployed to address:", hello_world.address) } @@ -324,23 +327,23 @@ main() }) ``` -Hardhatがコードの各行で行っている驚くべき内容については、Hardhatの[コントラクトチュートリアル](https://hardhat.org/tutorial/testing-contracts.html#writing-tests)で説明されています。以下では、その説明を採用しています。 +Hardhatは、[コントラクトのチュートリアル](https://hardhat.org/tutorial/testing-contracts.html#writing-tests)で、これらのコードの各行が何をするかを非常にうまく説明しています。ここではその説明を採用しました。 ```javascript const HelloWorld = await ethers.getContractFactory("HelloWorld") ``` -ethers.jsの`ContractFactory`は新しいスマートコントラクトをデプロイするための抽象化であり、ここでの`HelloWorld`はhello worldコントラクトのインスタンスのための[ファクトリ](https://en.wikipedia.org/wiki/Factory_(object-oriented_programming))です。 `hardhat-ethers`プラグインを使用する場合、`ContractFactory`および`Contract`インスタンスはデフォルトで最初の署名者 (所有者) に接続されます。 +ethers.jsの `ContractFactory` は、新しいスマートコントラクトをデプロイするために使用される抽象化です。したがって、ここでの `HelloWorld` は、私たちのhello worldコントラクトのインスタンスのための[ファクトリ](https://en.wikipedia.org/wiki/Factory_\(object-oriented_programming\))です。 `hardhat-ethers`プラグインを使用する場合、`ContractFactory`と`Contract`のインスタンスは、デフォルトで最初の署名者 (所有者) に接続されます。 ```javascript const hello_world = await HelloWorld.deploy() ``` -`ContractFactory`で`deploy()`を呼び出すとデプロイメントが開始され、`Contract`オブジェクトに解決すべき`Promise`が返されます。 これは、スマートコントラクトの各関数に対するメソッドを持つオブジェクトです。 +`ContractFactory`で`deploy()`を呼び出すとデプロイが開始され、`Contract`オブジェクトに解決される`Promise`が返されます。 これは、スマートコントラクトの各関数に対するメソッドを持つオブジェクトです。 ### ステップ16: コントラクトをデプロイする {#step-16-deploy-our-contract} -ようやく、スマートコントラクトをデプロイする準備が整いました。 コマンドラインに移動し、以下を実行します。 +ようやく、スマートコントラクトをデプロイする準備が整いました。 コマンドラインに移動し、次を実行します: ```bash npx hardhat run scripts/deploy.js --network goerli @@ -349,34 +352,34 @@ npx hardhat run scripts/deploy.js --network goerli 次のような画面が表示されるはずです。 ```bash -Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 +コントラクトがデプロイされたアドレス: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 ``` -**このアドレスを保存してください**。 このアドレスをチュートリアルの後半で使用します。 +**このアドレスを保存してください**。 このアドレスはチュートリアルの後半で使用します。 -[Goerli etherscan](https://goerli.etherscan.io)に移動し、コントラクトアドレスを検索すると、コントラクトが正常にデプロイされていることを確認できるはずです。 トランザクションは以下のようなものになります。 +[Goerli etherscan](https://goerli.etherscan.io) にアクセスしてコントラクトアドレスを検索すると、正常にデプロイされたことを確認できるはずです。 トランザクションは以下のようなものになります。 ![](./etherscan-contract.png) -`From`アドレスはMetaMaskアカウントのアドレスと一致し、`To`アドレスは「**Contract Creation**」と表示されます。 トランザクション内容をクリックすると、`To`フィールドにコントラクトアドレスが表示されます. +`From`アドレスはMetaMaskアカウントのアドレスと一致し、`To`アドレスには**Contract Creation**と表示されます。 トランザクションをクリックすると、`To`フィールドにコントラクトアドレスが表示されます。 ![](./etherscan-transaction.png) -おめでとうございます! イーサリアムのテストネットにスマートコントラクトをデプロイできました. +おめでとうございます! Ethereumテストネットにスマートコントラクトをデプロイできました。 -内部で何が起こっているのかを理解するために、[Alchemyダッシュボード](https://dashboard.alchemyapi.io/explorer)のExplorerタブに移動してみましょう。 Alchemyのアプリが複数ある場合は、必ずアプリでフィルタリングし、「**Hello World**」を選択してください。 +内部で何が起こっているのかを理解するために、[Alchemyダッシュボード](https://dashboard.alchemy.com/explorer)のExplorerタブに移動してみましょう。 複数のAlchemyアプリをお持ちの場合は、必ずアプリでフィルタリングし、**Hello World**を選択してください。 ![](./hello-world-explorer.png) -ここでは、`.deploy()`関数を呼び出した際に、HardhatもしくはEthersが内部で行ったJSON-RPCメソッドを見ることができます。 ここで2つの重要なメソッドがあります。まずは、[`eth_sendRawTransaction`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction)です。これは、Goerliチェーンにコントラクトを書き込むリクエストです。次に[`eth_getTransactionByHash`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_gettransactionbyhash)は、ハッシュを指定してトランザクションに関する情報を読み取るリクエストです。 トランザクションの送信の詳細については、[こちら](/developers/tutorials/sending-transactions-using-web3-and-alchemy/)のチュートリアルにあるWeb3を使用したトランザクションの送信をご覧ください。 +ここでは、`.deploy()`関数を呼び出した際に、Hardhat/Ethersが内部で行ったいくつかのJSON-RPCメソッドを見ることができます。 ここでの2つの重要なメソッドは、コントラクトをGoerliチェーンに書き込むリクエストである [`eth_sendRawTransaction`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction) と、ハッシュが与えられたトランザクションに関する情報を読み取るリクエストである [`eth_getTransactionByHash`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_gettransactionbyhash) です。 トランザクションの送信についてさらに詳しく知るには、[Web3を使用したトランザクション送信に関するチュートリアル](/developers/tutorials/sending-transactions-using-web3-and-alchemy/)を確認してください。 -## パート2: スマートコントラクトとのやり取り {#part-2-interact-with-your-smart-contract} +## パート2: スマートコントラクトとインタラクトする {#part-2-interact-with-your-smart-contract} スマートコントラクトをGoerliネットワークに正常にデプロイできました。それでは、スマートコントラクトとやり取りする方法について学びましょう。 -### interact.jsファイルの作成 {#create-a-interactjs-file} +### interact.jsファイルを作成する {#create-a-interactjs-file} -このファイルに、やり取りするスクリプトを記述します。 パート1でインストールしたEthers.jsライブラリを使用します。 +このファイルに、インタラクトするスクリプトを記述します。 パート1でインストールしたEthers.jsライブラリを使用します。 `scripts/`フォルダ内に、`interact.js`という名前の新しいファイルを作成し、次のコードを追加します。 @@ -388,9 +391,9 @@ const PRIVATE_KEY = process.env.PRIVATE_KEY const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS ``` -### .envファイルの更新 {#update-your-env-file} +### .envファイルを更新する {#update-your-env-file} -新しい環境変数を使用します。そのため、[以前に作成した](#step-11-connect-metamask-&-alchemy-to-your-project)`.env`ファイルに定義する必要があります。 +新しい環境変数を使用するため、[以前に作成した](#step-11-connect-metamask-&-alchemy-to-your-project)`.env`ファイルに定義する必要があります。 Alchemyの`API_KEY`とスマートコントラクトがデプロイされている`CONTRACT_ADDRESS`の定義を加える必要があります。 @@ -407,14 +410,14 @@ CONTRACT_ADDRESS = "0x" ### コントラクトABIを取得する {#grab-your-contract-ABI} -コントラクト[ABI(アプリケーションバイナリインターフェイス)](/glossary/#abi)は、スマートコントラクトと対話するためのインターフェイスです。 Hardhatは自動的にABIを生成して、`HelloWorld.json`ファイルに保存します。 ABIを使うには、`interact.js`ファイルに次のコードを追加して、コンテンツをパースする必要があります。 +コントラクトの[ABI (アプリケーションバイナリインターフェイス)](/glossary/#abi)は、スマートコントラクトとインタラクトするためのインターフェイスです。 Hardhatは自動的にABIを生成して、`HelloWorld.json`ファイルに保存します。 ABIを使うには、`interact.js`ファイルに次のコードを追加して、コンテンツをパースする必要があります。 ```javascript // interact.js const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json") ``` -ABIを表示したい場合は、次のコードを追加することでコンソールに出力できます: +ABIを確認したい場合は、コンソールに出力できます: ```javascript console.log(JSON.stringify(contract.abi)) @@ -428,13 +431,13 @@ npx hardhat run scripts/interact.js ### コントラクトのインスタンスを作成する {#create-an-instance-of-your-contract} -コントラクトを操作するには、コード内にコントラクトのインスタンスを作成する必要があります。 Ethers.jsでこれを行うには、次の3つのコンセプトを機能させる必要があります。 +コントラクトを操作するには、コード内にコントラクトのインスタンスを作成する必要があります。 Ethers.jsでこれを行うには、次の3つのコンセプトを扱う必要があります。 1. Provider - ブロックチェーンへの読み取りおよび書き込みアクセスを提供するノードプロバイダです。 -2. Signer - トランザクションに署名するイーサリアムアカウントを表します。 +2. Signer - トランザクションに署名するEthereumアカウントを表します。 3. Contract - オンチェーンにデプロイされた特定のコントラクトを表すEthers.jsのオブジェクトです。 -前の手順で取得したコントラクABIを使って、コントラクトのインスタンスを作成します。 +前の手順で取得したコントラクトABIを使って、コントラクトのインスタンスを作成します。 ```javascript // interact.js @@ -458,11 +461,11 @@ const helloWorldContract = new ethers.Contract( Provider、Signer、Contractの詳細については、[ethers.jsドキュメント](https://docs.ethers.io/v5/)をご覧ください。 -### initメッセージの読み取り {#read-the-init-message} +### initメッセージを読み込む {#read-the-init-message} -`initMessage = "Hello world!"`を使用してコントラクトをデプロイしたことを思い出せますでしょうか? ここでは、スマートコントラクトに保存されているメッセージを読み取り、コンソールに出力します。 +`initMessage = "Hello world!"`を使用してコントラクトをデプロイしたことを覚えていますか? ここでは、スマートコントラクトに保存されているメッセージを読み取り、コンソールに出力します。 -JavaScriptでは、ネットワークとのやり取りで非同期関数を使います。 非同期関数の詳細については、[この記事の中ほど](https://blog.bitsrc.io/Understanding-asynchronous-javascript-the-event-loop-74cd408419ff)をご覧ください。 +JavaScriptでは、ネットワークとのインタラクトで非同期関数を使います。 非同期関数についてさらに詳しく知るには、[このmediumの記事](https://blog.bitsrc.io/understanding-asynchronous-javascript-the-event-loop-74cd408419ff)をお読みください。 以下のコードを使用して、スマートコントラクトの`message`関数を呼び出し、initメッセージを読み取ります。 @@ -478,19 +481,19 @@ async function main() { main() ``` -ターミナルで`npx hardware run scripts/interact.js`を入力してファイルを実行すると、次のレスポンスが表示されるはずです。 +ターミナルで `npx hardhat run scripts/interact.js` を使用してファイルを実行すると、次のようなレスポンスが表示されます。 ``` The message is: Hello world! ``` -おめでとうございます! イーサリアムブロックチェーンからスマートコントラクトのデータを正常に読み取ることができました。 +おめでとうございます! Ethereumブロックチェーンからスマートコントラクトのデータを正常に読み取ることができました。お見事! -### メッセージの更新 {#update-the-message} +### メッセージを更新する {#update-the-message} -メッセージを読み取るだけでなく、`update`関数を使ってスマートコントラクトに保存されたメッセージを更新することもできます。 かなりイケてますよね? +メッセージを読み取るだけでなく、`update`関数を使ってスマートコントラクトに保存されたメッセージを更新することもできます。 かなりクールでしょう? -メッセージを更新するには、インスタンス化されたコントラクトのオブジェクトで`update`関数を直接呼び出します。 +メッセージを更新するには、インスタンス化されたContractオブジェクトで`update`関数を直接呼び出します。 ```javascript // interact.js @@ -508,13 +511,13 @@ async function main() { main() ``` -11行目で、返されたトランザクションのオブジェクトに対して `.wait()`を呼び出していることに注目してください。 これにより、スクリプトが関数を終了する前に、トランザクションがブロックチェーン上でマイニングされるまで待機することを確実にします。 `.wait()`を呼び出しを含めなかった場合、スクリプトは、コントラクト内で更新された`message`の値を表示しないことがあります。 +11行目で、返されたトランザクションオブジェクトに対して `.wait()` を呼び出していることに注目してください。 これにより、スクリプトが関数を終了する前に、トランザクションがブロックチェーン上でマイニングされるまで待機することが保証されます。 `.wait()` の呼び出しを含めなかった場合、スクリプトは、コントラクト内で更新された `message` の値を表示しないことがあります。 -### 新しいメッセージの読み取り {#read-the-new-message} +### 新しいメッセージを読み込む {#read-the-new-message} -[前の手順](#read-the-init-message)を繰り返して、更新された`message`の値を読み取ることができるのに違いありません。 その新しい値を出力するために必要となる変更を、少し考えてみましょう! +[前の手順](#read-the-init-message)を繰り返して、更新された `message` の値を読み取ることができるはずです。 少し時間を取って、新しい値を表示するために必要な変更を加えられるか試してみましょう! -ヒントが必要ですか?この時点で、あなたの`interact.js`ファイルは次のようになるはずです。 +ヒントが必要な場合は、この時点で`interact.js`ファイルがどのようになるかを示します。 ```javascript // interact.js @@ -556,7 +559,7 @@ async function main() { main() ``` -このスクリプトを実行するだけで、古いメッセージ、更新ステータス、および新しいメッセージがコンソールに出力されるのを確認できるはずです。 +このスクリプトを実行するだけで、古いメッセージ、更新ステータス、および新しいメッセージがターミナルに出力されるのを確認できるはずです。 `npx hardhat run scripts/interact.js --network goerli` @@ -566,29 +569,29 @@ Updating the message... The new message is: This is the new message. ``` -このスクリプトの実行中、新しいメッセージが読み込まれる前に、 `Updating the message...`のステップの読み込みにしばらく時間がかかることに気づくかもしれません。 これはマイニングプロセスによるものです。マイニング中のトランザクションの追跡に興味があるならば、[Alchemy mempool](https://dashboard.alchemyapi.io/mempool)にアクセスしてトランザクションのステータスを確認できます。 トランザクションがドロップされた場合は、[Goerli Etherscan](https://goerli.etherscan.io)を確認してトランザクションのハッシュを検索することもできます。 +このスクリプトの実行中、新しいメッセージが読み込まれる前に、 `Updating the message...` のステップの読み込みにしばらく時間がかかることに気づくかもしれません。 これはマイニングプロセスによるものです。マイニング中のトランザクションの追跡に興味があるならば、[Alchemyメンプール](https://dashboard.alchemyapi.io/mempool)にアクセスしてトランザクションのステータスを確認できます。 トランザクションがドロップされた場合は、[Goerli Etherscan](https://goerli.etherscan.io) を確認してトランザクションハッシュを検索することも役立ちます。 ## パート3: スマートコントラクトをEtherscanに公開する {#part-3-publish-your-smart-contract-to-etherscan} -あなたは、スマートコントラクトに命を吹き込むことに大変な努力をしました。それでは、その努力を世界に共有しましょう! +あなたは、スマートコントラクトに命を吹き込むために大変な努力をしました。さあ、その成果を世界に共有しましょう! -Etherscanでスマートコントラクトを検証すると、誰でもソースコードを表示して、あなたのスマートコントラクトとやり取りできるようになります。 さあ、始めましょう! +Etherscanでスマートコントラクトを検証すると、誰でもソースコードを表示して、あなたのスマートコントラクトとインタラクトできるようになります。 さあ、始めましょう! ### ステップ1: EtherscanアカウントでAPIキーを生成する {#step-1-generate-an-api-key-on-your-etherscan-account} EtherscanのAPIキーは、公開しようとしているスマートコントラクトを所有していることを確認するために必要になります。 -Etherscanアカウントをお持ちでない場合は、[アカウントの登録](https://etherscan.io/register)をしてください。 +Etherscanアカウントをお持ちでない場合は、[アカウントにサインアップ](https://etherscan.io/register)してください。 -ログインしたら、ナビゲーションバーでユーザー名を見つけ、その上にマウスを移動して、「**My profile**」ボタンを選択します。 +ログインしたら、ナビゲーションバーでユーザー名を見つけ、その上にカーソルを合わせて **My profile** ボタンを選択します。 -プロフィールページにサイドナビゲーションバーが表示されます。 サイドナビゲーションバーで、**API Keys**を選択します。 次に、「Add」ボタンを押して新しいAPIキーを作成し、アプリに**hello-world**という名前を付けて、「**Create New API**」ボタンを押します。 +プロフィールページにサイドナビゲーションバーが表示されます。 サイドナビゲーションバーで、**API Keys**を選択します。 次に、「Add」ボタンを押して新しいAPIキーを作成し、アプリに **hello-world** という名前を付けて、「**Create New API Key**」ボタンを押します。 新しいAPIキーがAPIキーテーブルに表示されるはずです。 APIキーをクリップボードにコピーします。 次に、EtherscanのAPIキーを`.env`ファイルに加える必要があります。 -そうすると、`.env`ファイルは次のようになります。 +追加した後、`.env`ファイルは次のようになります。 ```javascript API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" @@ -598,17 +601,17 @@ CONTRACT_ADDRESS = "your-contract-address" ETHERSCAN_API_KEY = "your-etherscan-key" ``` -### Hardhatにデプロイされたスマートコントラクト {#hardhat-deployed-smart-contracts} +### Hardhatでデプロイされたスマートコントラクト {#hardhat-deployed-smart-contracts} -#### hardhat-etherscanのインストール {#install-hardhat-etherscan} +#### hardhat-etherscanをインストールする {#install-hardhat-etherscan} -あなたのコントラクトをEtherscanへ公開するのは、Hardhatを使って簡単にできます。 はじめに、まず`hardhat-etherscan`プラグインをインストールしてください。 `hardhat-etherscan`は、スマートコントラクトのソースコードとEtherscan上のABIを自動的に検証します。 インストールするには、`hello-world`ディレクトリで次のコマンドを実行します。 +Hardhatを使ってコントラクトをEtherscanへ公開するのは簡単です。 まず、`hardhat-etherscan`プラグインをインストールする必要があります。 `hardhat-etherscan`は、スマートコントラクトのソースコードとEtherscan上のABIを自動的に検証します。 インストールするには、`hello-world`ディレクトリで次のコマンドを実行します。 ```text npm install --save-dev @nomiclabs/hardhat-etherscan ``` -インストールをしたら、`hardhat.config.js`の先頭に次のステートメントを含んだEtherscan構成オプションを追加します。 +インストールしたら、`hardhat.config.js`の先頭に次のステートメントを含め、Etherscanの構成オプションを追加します。 ```javascript // hardhat.config.js @@ -630,14 +633,14 @@ module.exports = { }, }, etherscan: { - // Your API key for Etherscan - // Obtain one at https://etherscan.io/ + // Etherscan用のAPIキー + // https://etherscan.io/ で取得 apiKey: ETHERSCAN_API_KEY, }, } ``` -#### Etherscan上でスマートコントラクトを検証する {#verify-your-smart-contract-on-etherscan} +#### Etherscanでスマートコントラクトを検証する {#verify-your-smart-contract-on-etherscan} すべてのファイルが保存され、すべての`.env`変数が正しく構成されていることを確認してください。 @@ -647,9 +650,9 @@ module.exports = { npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!' ``` -`DEPLOYED_CONTRACT_ADDRESS`がGoerliテストネットワーク上にデプロイされたスマートコントラクトのアドレスであることを確認してください。 また、最後の引数 (`'Hello World!'`) は、 [パート1のデプロイ手順](#write-our-deploy-script)で使われたの文字列値と同じでなければなりません。 +`DEPLOYED_CONTRACT_ADDRESS`がGoerliテストネットワーク上にデプロイされたスマートコントラクトのアドレスであることを確認してください。 また、最後の引数(`'Hello World!'`)は、[パート1のデプロイスクリプト作成手順](#write-our-deploy-script)で使用した文字列値と同じでなければなりません。 -順調に行けば、コンソールに次のメッセージが表示されます。 +すべてが順調に進めば、ターミナルに次のメッセージが表示されます。 ```text Successfully submitted source code for contract @@ -661,69 +664,69 @@ Successfully verified contract HelloWorld on Etherscan. https://goerli.etherscan.io/address/#contracts ``` -おめでとうございます! これで、あなたのスマートコントラクトコードは、Etherscan上にあります。 +おめでとうございます! これで、あなたのスマートコントラクトコードはEtherscan上にあります。 -### Etherscanであなたのスマートコントラクトを確認する {#check-out-your-smart-contract-on-etherscan} +### Etherscanであなたのスマートコントラクトを確認しましょう! {#check-out-your-smart-contract-on-etherscan} -コンソールに表示されているリンクに移動すると、Etherscanで公開されているスマートコントラクトコードとABIが表示されます。 +ターミナルに表示されているリンクに移動すると、Etherscanで公開されているスマートコントラクトコードとABIが表示されます。 -**ヤッホー!栄冠を手にしました。 これで、誰でもスマートコントラクトを呼び出したり、書き込んだりできるようになりました。 次にあなたが何を構築するか楽しみにしています。** +**ヤッター!やりましたね! これで、誰でもスマートコントラクトを呼び出したり、書き込んだりできるようになりました。 次にあなたが何を構築するか楽しみにしています。** -## パート4: フロントエンドとスマートコントラクトの統合 {#part-4-integrating-your-smart-contract-with-the-frontend} +## パート4 - スマートコントラクトをフロントエンドと統合する {#part-4-integrating-your-smart-contract-with-the-frontend} このチュートリアルを終えると、次の方法がわかるようになります。 - MetaMaskウォレットをdappに接続する -- [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) APIを使用してスマートコントラクトからデータを読み取る。 -- MetaMaskを使用してイーサリアムトランザクションに署名する +- [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) APIを使用してスマートコントラクトからデータを読み取る +- MetaMaskを使用してEthereumトランザクションに署名する -このdappでは、フロントエンドフレームワークで[React](https://reactjs.org/)を使っていますが、Web3の機能をプロジェクトに導入することに焦点を当てているので、Reactの基本を説明することに多くの時間を費やさないことに注意してください。 +このdappでは、フロントエンドフレームワークとして[React](https://react.dev/)を使用します。ただし、このプロジェクトではWeb3機能を導入することに主に焦点を当てているため、Reactの基礎を詳しく説明する時間はあまりかけない点に注意してください。 -前提条件として、Reactについて初心者レベルの理解をしている必要があります。 知らなければ、公式の[React入門チュートリアル](https://reactjs.org/tutorial/tutorial.html)を読むことをお勧めします。 +前提条件として、Reactについて初心者レベルの理解をしている必要があります。 そうでない場合は、公式の[React入門チュートリアル](https://react.dev/learn)を完了することをお勧めします。 -### スターターファイルのクローン {#clone-the-starter-files} +### スターターファイルをクローンする {#clone-the-starter-files} -まず、このプロジェクトの開始ファイルを取得するために[「hello-world-part-four」GitHubリポジトリ](https://github.com/alchemyplatform/hello-world-part-four-tutorial)に行き、このリポジトリのクローンをローカルマシンに作成します。 +まず、[hello-world-part-four GitHubリポジトリ](https://github.com/alchemyplatform/hello-world-part-four-tutorial)にアクセスして、このプロジェクトのスターターファイルを取得し、このリポジトリをローカルマシンにクローンします。 -クローンしたリポジトリをローカルで開きます。 `starter-files`と`completed`の2つのフォルダが含まれています。 +クローンしたリポジトリをローカルで開きます。 `starter-files`と`completed`の2つのフォルダが含まれていることに注意してください。 -- `starter-files` - **このディレクトリで作業します**。UIをイーサリアムウォレットおよび[パート3](#part-3)でEtherscanに公開したスマートコントラクトに接続します。 +- `starter-files` - **このディレクトリで作業します**。UIをEthereumウォレットおよび[パート3](#part-3)でEtherscanに公開したスマートコントラクトに接続します。 - `completed`には、チュートリアル全体が完了したものが入っています。行き詰まった場合にのみ、参考として使ってください。 次に、`starter-files`のコピーをお気に入りのコードエディタで開き、`src`フォルダに移動します。 -これから作成するすべてのコードは、`src`フォルダに保存されます。 `HelloWorld.js`コンポーネントと JavaScriptファイルである`util/interact.js`を編集して、プロジェクトにWeb3の機能を追加していきます。 +私たちが書くコードはすべて`src`フォルダの下に置かれます。 `HelloWorld.js`コンポーネントと`util/interact.js`JavaScriptファイルを編集して、プロジェクトにWeb3機能を追加します。 -### スターターファイルの確認 {#check-out-the-starter-files} +### スターターファイルを確認する {#check-out-the-starter-files} コーディングを開始する前に、スターターファイルで提供されるものを探索してみましょう。 -#### Reactプロジェクトの実行 {#get-your-react-project-running} +#### Reactプロジェクトを実行する {#get-your-react-project-running} まずは、ブラウザでReactプロジェクトを実行しましょう。 Reactの素晴らしいところは、一度ブラウザでプロジェクトを実行すると、保存した変更がブラウザでも同時に更新されることです。 -プロジェクトを実行するには、次のようにターミナルで`starter-files`フォルダのルートディレクトリに移動し、`npm install`を実行してプロジェクトの依存関係をインストールします。 +プロジェクトを実行するには、`starter-files`フォルダのルートディレクトリに移動し、ターミナルで`npm install`を実行してプロジェクトの依存関係をインストールします。 ```bash cd starter-files npm install ``` -インストールが完了したら、ターミナルで`npm start`を実行します。 +インストールが完了したら、ターミナルで`npm start`を実行します: ```bash npm start ``` -これにより、ブラウザで[http://localhost:3000/](http://localhost:3000/)を開くと、プロジェクトのフロントエンドが表示されます。 これは、1つのフィールド \(スマートコントラクトに保存されているメッセージを更新する場所\) である「Connect Wallet」ボタン、および「Update」ボタンで構成されています。 +これにより、ブラウザで[http://localhost:3000/](http://localhost:3000/)が開かれ、プロジェクトのフロントエンドが表示されます。 これは、1つのフィールド \(スマートコントラクトに保存されているメッセージを更新する場所\)、「Connect Wallet」ボタン、および「Update」ボタンで構成されています。 -どちらのボタンをクリックしても、機能しないことがわかります。この機能をプログラムする必要があるためです。 +どちらのボタンをクリックしても機能しないことに気づくでしょう。これは、まだその機能をプログラムする必要があるためです。 #### `HelloWorld.js`コンポーネント {#the-helloworld-js-component} エディタの`src`フォルダに戻り、`HelloWorld.js`ファイルを開きましょう。 このファイルには、これから作業を進めていく主要なReactコンポーネントが含まれています。すべての内容を理解することが非常に重要です。 -このファイルの先頭には、いくつかの重要なステートメントがあるこに気が付くでしょう。Reactライブラリ、useEffectフックとuseStateフック、`./util/interact.js`のいくつかのアイテムなど、プロジェクトを実行するために必要になります (これらについては、すぐに詳しく説明します!) 。また、Alchemyのロゴがあります +このファイルの先頭には、プロジェクトを実行するために必要な、Reactライブラリ、useEffectフックとuseStateフック、`./util/interact.js`からのいくつかのアイテム (これらについては、すぐに詳しく説明します!)、そしてAlchemyのロゴなど、いくつかのimport文があることに気づくでしょう。 ```javascript // HelloWorld.js @@ -753,14 +756,14 @@ const [message, setMessage] = useState("No connection to the network.") const [newMessage, setNewMessage] = useState("") ``` -それぞれの変数は以下の用途で使われます。 +それぞれの変数は以下の内容を表します。 - `walletAddress` - ユーザーのウォレットアドレスを格納する文字列 -- `status` - ユーザーにdappの操作方法を案内する補助メッセージを文字列として格納する +- `status`- ユーザーにdappのインタラクト方法を案内する補助メッセージを格納する文字列 - `message` - スマートコントラクトの現在のメッセージを格納する文字列 - `newMessage` - スマートコントラクトに書き込まれる新しいメッセージを格納する文字列 -ステート変数の後に、`useEffect` 、`addSmartContractListener`、 `addWalletListener`、 `connectWalletPressed`、`onUpdatePressed`の未実装の5つの関数があります。 次に、それらが何をするのかを説明します。 +ステート変数の後に、`useEffect`、`addSmartContractListener`、`addWalletListener`、`connectWalletPressed`、`onUpdatePressed`の5つの未実装の関数があります。 次に、それらが何をするのかを説明します。 ```javascript // HelloWorld.js @@ -787,10 +790,10 @@ const onUpdatePressed = async () => { } ``` -- [`useEffect`](https://reactjs.org/docs/hooks-effect.html)- コンポーネントがレンダリングされた後に呼び出されるReactフックです。 空の配列`[]`のプロップが渡されているため \(4行目を参照\)、コンポーネントの_最初_のレンダリングでのみ呼び出されます。 ここでは、スマートコントラクトに保存されている現在のメッセージのロード、スマートコントラクトとウォレットリスナーの呼び出し、ウォレットが既に接続されているかどうかを反映してUIを更新するのに使います。 +- [`useEffect`](https://legacy.reactjs.org/docs/hooks-effect.html) - これは、コンポーネントがレンダリングされた後に呼び出されるReactフックです。 空の配列`[]`のプロップが渡されているため \(4行目を参照\)、コンポーネントの_最初の_レンダリングでのみ呼び出されます。 ここでは、スマートコントラクトに保存されている現在のメッセージをロードし、スマートコントラクトとウォレットリスナーを呼び出し、ウォレットが既に接続されているかどうかを反映してUIを更新します。 - `addSmartContractListener` - この関数では、HelloWorldコントラクトの`UpdatedMessages`イベントを監視し、スマートコントラクトでメッセージが変更されたときにUIを更新するリスナーを設定します。 - `addWalletListener` - この関数では、ユーザーがウォレットを切断したときやアドレスを切り替えたときなど、ユーザーのMetaMaskウォレットのステートの変化を検出するリスナーを設定します。 -- `connectWalletPressed` - この関数は、ユーザーのMetaMaskウォレットをdappに接続するのに呼び出されます。 +- `connectWalletPressed`- この関数は、ユーザーのMetaMaskウォレットをdappに接続するのに呼び出されます。 - `onUpdatePressed` - この関数は、ユーザーがスマートコントラクトに保存されているメッセージを更新したいときに呼び出されます。 このファイルの終盤には、コンポーネントのUIがあります。 @@ -830,32 +833,33 @@ return ( - - + + + ) ``` このコードを注意深く読むと、さまざまなステート変数がUIのどの場所で使用されているかがわかります。 -- 6~12行目では、ユーザーのウォレットが接続されている場合 \(すなわち、`walletAddress.length > 0`\)、 ID「walletButton;」に省略されたユーザーの`walletAddress`がボタンに表示されます。 それ以外の場合は、単に「Connect Wallet」と表示されます。 +- 6~12行目では、ユーザーのウォレットが接続されている場合 \(すなわち`walletAddress.length > 0`\)、ID「walletButton」のボタンに省略されたユーザーの`walletAddress`が表示されます。それ以外の場合は、単に「Connect Wallet」と表示されます。 - 17行目では、`message`文字列でキャプチャされたスマートコントラクトに保存されている現在のメッセージを表示します。 -- 23~26行目では、テキストフィールドの入力が変化したときに[制御コンポーネント](https://reactjs.org/docs/forms.html#controlled-components)を使用して `newMessage`ステート変数を更新します。 +- 23~26行目では、[制御されたコンポーネント](https://legacy.reactjs.org/docs/forms.html#controlled-components)を使用して、テキストフィールドの入力が変化したときに`newMessage`ステート変数を更新します。 -ステート変数に加えて、IDが`publishButton`と`walletButton`である、それぞれがクリックされると`connectWalletPressed`および`onUpdatePressed`関数が呼び出されることがわります。 +ステート変数に加えて、IDが`publishButton`と`walletButton`であるボタンがそれぞれクリックされると、`connectWalletPressed`および`onUpdatePressed`関数が呼び出されることがわかります。 最後に、この`HelloWorld.js`コンポーネントがどこに加えられるかについて説明します。 -他のすべてのコンポーネントのコンテナとして機能する、Reactのメインコンポーネントである`App.js`ファイルを表示すると、`HelloWorld.js`コンポーネントが7行目に挿入されていることが分かります。 +`App.js`ファイルは、他のすべてのコンポーネントのコンテナとして機能するReactのメインコンポーネントですが、このファイルを表示すると、`HelloWorld.js`コンポーネントが7行目に挿入されていることが分かります。 -最後の最後となりますが、提供されているもう1つのファイル、`interact.js`ファイルを確認してみましょう。 +最後に、提供されているもう1つのファイル、`interact.js`ファイルを確認してみましょう。 #### `interact.js`ファイル {#the-interact-js-file} -[M-V-C](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)のパラダイムを実践したいので、dappのロジック、データ、ルールを管理するすべての関数を含んだファイルを分割し、これらの関数をフロントエンド \(`HelloWorld.js`コンポーネント\) にエクスポートできるようにします。 +[M-V-C](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)パラダイムに従うため、dappのロジック、データ、ルールを管理するすべての関数を含む個別のファイルを作成し、それらの関数をフロントエンド \(私たちの`HelloWorld.js`コンポーネント\) にエクスポートできるようにします。 -👆🏽まさにこれが`interact.js`ファイルの目的です。 +👆🏽まさにこれが`interact.js`ファイルの目的です! -`src`ディレクトリの`util`フォルダに移動すると、`interact.js`というファイルが含まれていることがわかります。これには、スマートコントラクトとのやり取り、ウォレット関数と変数が含まれています。 +`src`ディレクトリの`util`フォルダに移動すると、`interact.js`というファイルが含まれていることがわかります。これには、すべてのスマートコントラクトとのインタラクション、ウォレット関数、変数が含まれています。 ```javascript // interact.js @@ -875,35 +879,35 @@ export const updateMessage = async (message) => {} `helloWorldContract`オブジェクトの後の4つの未実装の関数は、次のことを行います。 -- `loadCurrentMessage` - この関数は、スマートコントラクトに保存されている現在のメッセージをロードするロジックを扱います。 [Alchemy Web3 API](https://github.com/alchemyplatform/alchemy-web3)を使ってHello Worldスマートコントラクトの_read_の呼び出しを行います。 -- `connectWallet` - この関数は、私たちのdappをユーザーのMetaMaskに接続します。 -- `getCurrentWalletConnected` - この関数は、ページの読み込み時にイーサリアムアカウントが既にdappに接続されているかどうかを確認し、それに応じてUIを更新します。 -- `updateMessage` - この関数は、スマートコントラクトに保存されているメッセージを更新します。 Hello Worldスマートコントラクトで_write_の呼び出しが行われるため、ユーザーのMetaMaskウォレットでは、メッセージを更新するためにイーサリアムトランザクションに署名する必要があります。 +- `loadCurrentMessage` - この関数は、スマートコントラクトに保存されている現在のメッセージをロードするロジックを扱います。 [Alchemy Web3 API](https://github.com/alchemyplatform/alchemy-web3)を使用して、Hello Worldスマートコントラクトへの_読み取り_呼び出しを行います。 +- `connectWallet` - この関数は、ユーザーのMetaMaskをdappに接続します。 +- `getCurrentWalletConnected` - この関数は、ページの読み込み時にEthereumアカウントが既にdappに接続されているかどうかを確認し、それに応じてUIを更新します。 +- `updateMessage` - この関数は、スマートコントラクトに保存されているメッセージを更新します。 Hello Worldスマートコントラクトで_書き込み_呼び出しが行われるため、ユーザーのMetaMaskウォレットでは、メッセージを更新するためにEthereumトランザクションに署名する必要があります。 何をするか理解したので、スマートコントラクトから読み取る方法を解明していきましょう。 -### ステップ3: スマートコントラクトからの読み込み {#step-3-read-from-your-smart-contract} +### ステップ3: スマートコントラクトから読み取る {#step-3-read-from-your-smart-contract} スマートコントラクトから読み取るには、次の設定を正しく行う必要があります。 -- イーサリアムチェーンへのAPI接続 -- あなたのスマートコントラクトがロードされたインスタンス +- EthereumチェーンへのAPI接続 +- スマートコントラクトのロードされたインスタンス - スマートコントラクトの関数を呼び出す関数 - スマートコントラクトから読み取っているデータが変更されたときの更新を監視するリスナー -手順がたくさんあるように感じますが、心配しないでください! それぞれの方法を1つずつ説明していきます。 :\) +手順がたくさんあるように感じますが、心配しないでください! それぞれの方法を1つずつ説明していきます。 :​) -#### イーサリアムチェーンへのAPI接続を確立する {#establish-an-api-connection-to-the-ethereum-chain} +#### EthereumチェーンへのAPI接続を確立する {#establish-an-api-connection-to-the-ethereum-chain} -チュートリアルのパート2で私たちは、[Alchemy Web3キーを使ってスマートコントラクトから読み込みました](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract/interacting-with-a-smart-contract#step-1-install-web3-library)。 チェーンから読み取るには、あなたのdappでAlchemy Web3キーがまた必要になります。 +このチュートリアルのパート2で、[Alchemy Web3キーを使用してスマートコントラクトから読み取った](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract/interacting-with-a-smart-contract#step-1-install-web3-library)ことを覚えていますか? チェーンから読み取るには、dappでAlchemy Web3キーも必要になります。 -もしなければ、最初に、 `starter-files`のルートディレクトリへ移動して、次のコマンドをコンソールで実行して[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3)をインストールしてください。 +まだインストールしていない場合は、まず`starter-files`のルートディレクトリに移動し、ターミナルで次のコマンドを実行して[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3)をインストールしてください。 ```text npm install @alch/alchemy-web3 ``` -[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3)は、[Web3.js](https://docs.web3js.org/)のラッパーであり、強化されたAPIメソッドや重要なメリットを提供し、Web3デベロッパーの負担を軽減します。 最小限の設定で使えるように設計されているので、アプリですぐに使用可能です。 +[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3)は[Web3.js](https://docs.web3js.org/)のラッパーであり、強化されたAPIメソッドやその他の重要なメリットを提供し、web3開発者としての作業を容易にします。 最小限の設定で使えるように設計されているので、アプリですぐに使用可能です。 次に、[dotenv](https://www.npmjs.com/package/dotenv)パッケージをプロジェクトディレクトリにインストールします。これにより、APIキーを取得した後に安全な場所に保管できるようになります。 @@ -911,15 +915,15 @@ npm install @alch/alchemy-web3 npm install dotenv --save ``` -dappでは、HTTP API キーの代わりに**Websockets APIキー**を使用します。これにより、スマートコントラクトに保存されたメッセージが変更されたときに検出するリスナーを設定できます。 +dappでは、HTTP APIキーの代わりに**Websockets APIキーを使用します**。これにより、スマートコントラクトに保存されたメッセージが変更されたときに検出するリスナーを設定できます。 -APIキーを取得したら、ルートディレクトリに `.env`ファイルを作成し、Alchemy Websocketsの URLを.envファイルに加えます。 `.env`ファイルは次のようになります。 +APIキーを取得したら、ルートディレクトリに `.env`ファイルを作成し、Alchemy Websocketsの URLをそのファイルに追加します。 その後、`.env`ファイルは次のようになります。 ```javascript REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/ ``` -これで、私たちのdappにAlchemy Web3エンドポイントを設定する準備が整いました。 `util`フォルダー内に入っている`interact.js`に戻り、ファイルの先頭に次のコードを加えてください。 +これで、dappにAlchemy Web3エンドポイントを設定する準備が整いました。 `util`フォルダー内に入っている`interact.js`に戻り、ファイルの先頭に次のコードを加えてください。 ```javascript // interact.js @@ -932,23 +936,23 @@ const web3 = createAlchemyWeb3(alchemyKey) //export const helloWorldContract; ``` -上記のコードでは、まず`.env`ファイルから Alchemyキーをインポートし、次に`alchemyKey`を`createAlchemyWeb3`に渡してAlchemy Web3エンドポイントへ確立しています。 +上記のコードでは、まず`.env`ファイルから Alchemyキーをインポートし、次に`alchemyKey`を`createAlchemyWeb3`に渡してAlchemy Web3エンドポイントを確立しています。 エンドポイントの準備できたので、スマートコントラクトをロードするときです! #### Hello Worldスマートコントラクトをロードする {#loading-your-hello-world-smart-contract} -Hello Worldスマートコントラクトをロードするには、そのコントラクトアドレスとABIが必要です。[このチュートリアルのパート3](/developers/tutorials/hello-world-smart-contract-fullstack/#part-3-publish-your-smart-contract-to-etherscan-part-3-publish-your-smart-contract-to-etherscan)を終了していれば、両方ともEtherscanから入手できます。 +Hello Worldスマートコントラクトをロードするには、そのコントラクトアドレスとABIが必要です。これらは、[このチュートリアルのパート3](/developers/tutorials/hello-world-smart-contract-fullstack/#part-3-publish-your-smart-contract-to-etherscan-part-3-publish-your-smart-contract-to-etherscan)を完了していれば、Etherscanで見つけることができます。 -#### EtherscanからコントラクトABIを入手する方法 {#how-to-get-your-contract-abi-from-etherscan} +#### EtherscanからコントラクトABIを取得する方法 {#how-to-get-your-contract-abi-from-etherscan} -このチュートリアルのパート3を飛ばした場合は、アドレス[0x6f3f635A9762B47954229Ea479b4541eAF402A6A](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code)にあるHelloWorldコントラクトを使ってください。 ABIは、[こちら](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code)にあります。 +このチュートリアルのパート3を飛ばした場合は、アドレス[0x6f3f635A9762B47954229Ea479b4541eAF402A6A](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code)のHelloWorldコントラクトを使用できます。 そのABIは[こちら](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code)で見つけることができます。 -コントラクトのABIは、コントラクトが呼び出す関数を指定し、関数が確実に意図しているフォーマットでデータを返すようにするために必要です。 コントラクトABIをコピーしたら、それを`contract-abi.json`という名前のJSONファイルとして`src`ディレクトリに保存しましょう。 +コントラクトのABIは、コントラクトが呼び出す関数を指定し、関数が期待するフォーマットでデータを確実に返すようにするために必要です。 コントラクトABIをコピーしたら、それを`contract-abi.json`という名前のJSONファイルとして`src`ディレクトリに保存しましょう。 contract-abi.jsonは、srcフォルダーに格納されている必要があります。 -コントラクトアドレス、ABI、Alchemy Web3エンドポイントを用意することで、[コントラクトメソッド](https://docs.web3js.org/api/web3-eth-contract/class/Contract)を使ってスマートコントラクトのインスタンスをロードすることができます。 コントラクトABIを`interact.js`ファイルにインポートし、コントラクトアドレスを加えます。 +コントラクトアドレス、ABI、Alchemy Web3エンドポイントを用意したので、[contractメソッド](https://docs.web3js.org/api/web3-eth-contract/class/Contract)を使ってスマートコントラクトのインスタンスをロードすることができます。 コントラクトABIを`interact.js`ファイルにインポートし、コントラクトアドレスを加えます。 ```javascript // interact.js @@ -986,21 +990,19 @@ export const helloWorldContract = new web3.eth.Contract( ) ``` -私たちのコントラクトがロードされたので、`loadCurrentMessage`関数を実装できます! +コントラクトがロードされたので、`loadCurrentMessage`関数を実装できます! #### `interact.js`ファイルに`loadCurrentMessage`を実装する {#implementing-loadCurrentMessage-in-your-interact-js-file} -これは非常にシンプルな関数です。 私たちのコントラクトから読み取るのに、単純な非同期のWeb3の呼び出しを作成します。 この関数では、スマートコントラクトに保存されているメッセージを返します。 +これは非常にシンプルな関数です。 コントラクトから読み取るために、単純な非同期のweb3呼び出しを作成します。 この関数では、スマートコントラクトに保存されているメッセージを返します。 -`interact.js`ファイルの `loadCurrentMessage`を次のように更新してください。 +// interact.jsexport const loadCurrentMessage = async () => { +const message = await helloWorldContract.methods.message().call() +return message +} ```javascript -// interact.js - -export const loadCurrentMessage = async () => { - const message = await helloWorldContract.methods.message().call() - return message -} +`interact.js`ファイルの `loadCurrentMessage`を次のように更新してください。 ``` このスマートコントラクトをUIに表示したいので、`HelloWorld.js`コンポーネントの `useEffect`関数を次のように更新します。 @@ -1015,48 +1017,48 @@ useEffect(async () => { }, []) ``` -`loadCurrentMessage`は、コンポーネントの最初のレンダリングで1回だけ呼び出されることに注目してください。 この後、`addSmartContractListener`を実装して、スマートコントラクト内のメッセージが変更された後にUIを自動的に更新できるようにします。 +注: `loadCurrentMessage`は、コンポーネントの最初のレンダリング時に1回だけ呼び出されるようにします。 この後、`addSmartContractListener`を実装して、スマートコントラクト内のメッセージが変更された後にUIを自動的に更新できるようにします。 -リスナーについて詳しく説明する前に、これまでの内容を確認してみましょう! `HelloWorld.js`ファイルと`interact.js`ファイルを保存し、[http://localhost: 3000/](http://localhost:3000/)へアクセスしてください。 +リスナーについて詳しく説明する前に、これまでの内容を確認してみましょう! `HelloWorld.js`ファイルと`interact.js`ファイルを保存し、[http://localhost:3000/](http://localhost:3000/)にアクセスしてください。 -現在、「ネットワークに接続されていません」というメッセージが表示されなくなっていることがわかります。 代わりに、スマート コントラクトに保存されているメッセージが反映されます。 カッコイイ! +現在、「ネットワークに接続されていません」というメッセージが表示されなくなっていることがわかります。 代わりに、スマートコントラクトに保存されているメッセージが反映されます。 カッコイイ! -#### 今や、UIにスマートコントラクトに保存されたメッセージが反映されるようになりました。 {#your-UI-should-now-reflect-the-message-stored-in-the-smart-contract} +#### UIにスマートコントラクトに保存されたメッセージが反映されるはずです {#your-UI-should-now-reflect-the-message-stored-in-the-smart-contract} それでは、リスナーについて説明していきます。 #### `addSmartContractListener`を実装する {#implement-addsmartcontractlistener} -[このチュートリアルのパート1](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract#step-10-write-our-contract)で記述した`HelloWorld.sol`ファイルについて振り返ると、`UpdatedMessages`というスマートコントラクトのイベントがあったと思います。このイベントは、スマートコントラクトの`update`関数が呼び出された後に発行されます \(9 行目と27行目を参照\)。 +このチュートリアルシリーズの[パート1で作成した`HelloWorld.sol`ファイル](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract#step-10-write-our-contract)を思い出すと、スマートコントラクトの`update`関数が呼び出された後に`UpdatedMessages`というスマートコントラクトイベントが発行されることを思い出すでしょう(9行目と27行目を参照)。 ```javascript // HelloWorld.sol -// Specifies the version of Solidity, using semantic versioning. -// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +// Solidityのバージョンをセマンティックバージョニングで指定します。 +// 詳細: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma pragma solidity ^0.7.3; -// Defines a contract named `HelloWorld`. -// A contract is a collection of functions and data (its state). Once deployed, a contract resides at a specific address on the Ethereum blockchain. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +// `HelloWorld` という名前のコントラクトを定義します。 +// コントラクトは関数とデータ (その状態) の集合です。一度デプロイされると、コントラクトはEthereumブロックチェーン上の特定のアドレスに存在します。詳細: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html contract HelloWorld { - //Emitted when update function is called - //Smart contract events are a way for your contract to communicate that something happened on the blockchain to your app front-end, which can be 'listening' for certain events and take action when they happen. + // update関数が呼び出されたときに発行されます + //スマートコントラクトのイベントは、ブロックチェーン上で何かが起こったことをコントラクトがアプリのフロントエンドに伝える方法です。フロントエンドは特定のイベントを「リッスン」し、それが起こったときに行動を起こすことができます。 event UpdatedMessages(string oldStr, string newStr); - // Declares a state variable `message` of type `string`. - // State variables are variables whose values are permanently stored in contract storage. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value. + // `string` 型の状態変数 `message` を宣言します。 + // 状態変数は、その値がコントラクトのストレージに永続的に保存される変数です。`public` キーワードにより、変数はコントラクトの外部からアクセス可能になり、他のコントラクトやクライアントが値をアクセスするために呼び出せる関数が作成されます。 string public message; - // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation. - // Constructors are used to initialize the contract's data. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + // 多くのクラスベースのオブジェクト指向言語と同様に、コンストラクタはコントラクト作成時に一度だけ実行される特別な関数です。 + // コンストラクタはコントラクトのデータを初期化するために使用されます。詳細:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors constructor(string memory initMessage) { - // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable). + // 文字列引数 `initMessage` を受け取り、その値をコントラクトの `message` ストレージ変数に設定します)。 message = initMessage; } - // A public function that accepts a string argument and updates the `message` storage variable. + // 文字列引数を受け取り、 `message` ストレージ変数を更新する公開関数です。 function update(string memory newMessage) public { string memory oldMsg = message; message = newMessage; @@ -1065,9 +1067,9 @@ contract HelloWorld { } ``` -スマートコントラクトイベントは、ブロックチェーンで何かが起こったこと \(すなわち、_イベント_の発生 \) をフロントエンドアプリケーションに伝える方法です。特定のイベントを「リスニング」して、それが起きた時にアクションを実行します。 +スマートコントラクトイベントは、ブロックチェーンで何かが起こったこと \(すなわち、_イベント_の発生\) をフロントエンドアプリケーションに伝える方法です。フロントエンドは特定のイベントを「リスニング」して、それが起きた時にアクションを実行します。 -具体的には、`addSmartContractListener`関数がHello Worldスマートコントラクトの`UpdatedMessages`イベントをリッスンしており、新しいメッセージを表示するようにUIの更新をします。 +`addSmartContractListener`関数は、具体的にはHello Worldスマートコントラクトの`UpdatedMessages`イベントをリッスンし、新しいメッセージを表示するようにUIを更新します。 `addSmartContractListener`を次のように変更します。 @@ -1081,7 +1083,7 @@ function addSmartContractListener() { } else { setMessage(data.returnValues[1]) setNewMessage("") - setStatus("🎉 Your message has been updated!") + setStatus("🎉 メッセージが更新されました!") } }) } @@ -1089,10 +1091,10 @@ function addSmartContractListener() { リスナーがイベントを検出したときに何が起こるかを詳しく解説します。 -- イベントの発行時にエラーが発生した場合、そのエラーは`status`ステート変数を介してUIに反映する。 -- それ以外の場合は、返された`data`オブジェクトを使う。 `data.returnValues`は、配列にある最初のエレメントがインデックスの0に格納されている配列です。配列の最初のエレメントには前のメッセージが格納され、2番目のエレメントには更新されたメッセージが格納されます。 つまり、イベントが成功すると、`message`文字列を更新されたメッセージに設定し、`newMessage`文字列をクリアし、`status`ステート変数を更新します。 これにより、新しいメッセージがスマートコントラクトに公開されたことを反映しています。 +- イベントの発行時にエラーが発生した場合、そのエラーは`status`ステート変数を介してUIに反映されます。 +- それ以外の場合は、返された`data`オブジェクトを使います。 `data.returnValues`は、0からインデックス付けされた配列で、配列の最初の要素には前のメッセージが格納され、2番目の要素には更新されたメッセージが格納されます。 つまり、イベントが成功すると、`message`文字列を更新されたメッセージに設定し、`newMessage`文字列をクリアし、`status`ステート変数を更新して、新しいメッセージがスマートコントラクトに公開されたことを反映させます。 -最後に、`useEffect`関数でリスナーを呼び出して、`HelloWorld.js`コンポーネントの最初のレンダリング時にリスナーが初期化されるようにしましょう。 あなたの`useEffect`関数全体は、次のようになります。 +最後に、`useEffect`関数でリスナーを呼び出して、`HelloWorld.js`コンポーネントの最初のレンダリング時にリスナーが初期化されるようにしましょう。 全体として、`useEffect`関数は次のようになります。 ```javascript // HelloWorld.js @@ -1104,43 +1106,43 @@ useEffect(async () => { }, []) ``` -スマートコントラクトから読み取れるようになったので、スマートコントラクトに書き込む方法も理解できるとなおよいでしょう! ただし、dappに書き込むには、まずイーサリアムウォレットをdappに接続する必要があります。 +スマートコントラクトから読み取れるようになったので、スマートコントラクトに書き込む方法も理解できると素晴らしいですね! ただし、dappに書き込むには、まずEthereumウォレットをdappに接続する必要があります。 -それでは、次にイーサリアムウォレット \(MetaMask\) を設定し、それをdappに接続することに取り組んでいきましょう! +それでは、次にEthereumウォレット \(MetaMask\) を設定し、それをdappに接続することに取り組みましょう! -### ステップ4: イーサリアムウォレットのセットアップ {#step-4-set-up-your-ethereum-wallet} +### ステップ4: Ethereumウォレットを設定する {#step-4-set-up-your-ethereum-wallet} -イーサリアムチェーンに何かを書き込むには、ユーザーは仮想ウォレットの秘密鍵を使ってトランザクションに署名しなければなりません。 このチュートリアルでは、イーサリアムアカウントアドレスの管理に使用されるブラウザの仮想ウォレットである[MetaMask](https://metamask.io/)を使用します。これにより、エンドユーザーは、このランザクションの署名がとても簡単になります。 +Ethereumチェーンに何かを書き込むには、ユーザーは仮想ウォレットの秘密鍵を使ってトランザクションに署名しなければなりません。 このチュートリアルでは、Ethereumアカウントアドレスの管理に使用されるブラウザの仮想ウォレットである[MetaMask](https://metamask.io/)を使用します。これにより、エンドユーザーは、このトランザクションの署名が非常に簡単になります。 -イーサリアムのトランザクションの仕組みの詳細については、イーサリアム・ファウンデーションの[こちらのページ](/developers/docs/transactions/)をご覧ください。 +イーサリアム上のトランザクションの仕組みについてさらに詳しく知りたい場合は、イーサリアム・ファウンデーションの[こちらのページ](/developers/docs/transactions/)をご覧ください。 -#### MetaMaskをダウンロード {#download-metamask} +#### MetaMaskをダウンロードする {#download-metamask} -Metamaskのアカウントは[こちら](https://metamask.io/download)から無料でダウンロード、作成できます。 アカウントを作成後、またはすでにアカウントをお持ちの場合は\( 実際に支払いが発生しないように \)右上の「Goerli Test Network」に切り替えてください。 +MetaMaskアカウントは、[こちら](https://metamask.io/download)から無料でダウンロードして作成できます。 アカウントを作成するとき、またはすでにアカウントをお持ちの場合は、右上の「Goerli Test Network」に必ず切り替えてください \(実在の通貨を扱わないようにするため\)。 -#### フォーセットからイーサ(ETH)を追加 {#add-ether-from-a-faucet} +#### フォーセットからetherを追加する {#add-ether-from-a-faucet} -イーサリアムブロックチェーンでトランザクションに署名するには、偽のETHが必要になります。 ETHを取得するには、 [FaucETH](https://fauceth.komputing.org)にアクセスしてGoerliアカウントアドレスを入力し、「Request funds」をクリックしてください。 そしてドロップダウンで「Ethereum Testnet Goerli」を選択し、最後に「Request funds」ボタンを再度クリックします。 MetamaskアカウントにETHが表示されるはずです。 +Ethereumブロックチェーンでトランザクションに署名するには、偽のEthが必要です。 Ethを取得するには、[FaucETH](https://fauceth.komputing.org)にアクセスしてGoerliアカウントアドレスを入力し、「Request funds」をクリックし、ドロップダウンで「Ethereum Testnet Goerli」を選択し、最後に「Request funds」ボタンを再度クリックします。 MetamaskアカウントにETHが表示されるはずです。 -#### 残高の確認 {#check-your-balance} +#### 残高を確認する {#check-your-balance} -残高を再確認するために、[Alchemyのコンポーザーツール](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D)を使用して[eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)をリクエストしてみましょう。 このリクエストをすると、ウォレット内のETHの額が返されます。 Metamaskアカウントアドレスを入力して「Send Request」をクリックすると、次のようなレスポンスが表示されます。 +残高を確認するために、[Alchemyのcomposerツール](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D)を使用して[eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)リクエストを行いましょう。 このリクエストをすると、ウォレット内のETHの額が返されます。 MetaMaskアカウントアドレスを入力して「Send Request」をクリックすると、次のようなレスポンスが表示されます。 ```text {"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"} ``` -**注:** この結果の単位は、ETHではなくweiです。 weiはETHの最小単位として使われています。 weiからETHへ変換すると、1 eth = 10¹⁸ weiになります。 つまり、0xde0b6b3a7640000を10進数に変換すると、1\*10¹⁸となり、1 ETHに相当します。 +**注:** この結果はethではなくwei単位です。 weiはETHの最小単位として使われています。 weiからETHへ変換すると、1 eth = 10¹⁸ weiになります。 つまり、0xde0b6b3a7640000を10進数に変換すると、1\*10¹⁸となり、1 ETHに相当します。 -ご安心ください。 これで、偽のお金を手に入れました。 🤑 +ふう! これで、偽のお金を手に入れました。 🤑 -### ステップ5: メタマスクをUIへ接続する {#step-5-connect-metamask-to-your-UI} +### ステップ5: MetaMaskをUIに接続する {#step-5-connect-metamask-to-your-UI} MetaMaskウォレットが設定されたので、分散型アプリケーション(Dapp)を接続しましょう。 #### `connectWallet`関数 {#the-connectWallet-function} -`interact.js`ファイルの`connectWallet`関数を実装します。この関数は、`HelloWorld.js`コンポーネントで呼び出します。 +`interact.js`ファイルで`connectWallet`関数を実装します。この関数は、`HelloWorld.js`コンポーネントで呼び出します。 `connectWallet`を次のように変更しましょう。 @@ -1154,7 +1156,7 @@ export const connectWallet = async () => { method: "eth_requestAccounts", }) const obj = { - status: "👆🏽 Write a message in the text-field above.", + status: "👆🏽 上のテキストフィールドにメッセージを書き込んでください。", address: addressArray[0], } return obj @@ -1172,8 +1174,7 @@ export const connectWallet = async () => {

{" "} 🦊 - You must install MetaMask, a virtual Ethereum wallet, in your - browser. + ブラウザに仮想EthereumウォレットであるMetaMaskをインストールする必要があります。

@@ -1187,20 +1188,20 @@ export const connectWallet = async () => { まず、ブラウザで`window.ethereum`が有効になっているかどうかをチェックしています。 -`window.ethereum`は、MetaMaskおよび他のウォレットプロバイダーによって挿入されるグローバルAPIであり、ウェブサイトがユーザーのイーサリアムアカウントを要求できるようにするものです。 承認されると、ユーザーが接続しているブロックチェーンからデータを読み取ったり、メッセージやトランザクションへの署名をユーザーに提案したりできるようになります。 詳細については、[MetaMaskのドキュメント](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents)を参照してください。 +`window.ethereum`は、MetaMaskやその他のウォレットプロバイダーによって挿入されるグローバルAPIで、WebサイトがユーザーのEthereumアカウントを要求できるようにするものです。 承認されると、ユーザーが接続しているブロックチェーンからデータを読み取ったり、メッセージやトランザクションへの署名をユーザーに提案したりできるようになります。 詳細については[MetaMaskのドキュメント](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents)をご覧ください。 -`window.ethereum`が_存在しない_場合は、MeTaMaskがインストールされていないことを意味します。 その結果、空の文字列に設定された、返される`address`と、ユーザーがMetaMaskをインストールする必要があることを伝える`status`JSXオブジェクトが入ったJSONオブジェクトが返されます。 +`window.ethereum`が_存在しない_場合、それはMetaMaskがインストールされていないことを意味します。 これにより、返される`address`が空の文字列で、`status` JSXオブジェクトがユーザーにMetaMaskをインストールするよう促すJSONオブジェクトが返されます。 -`window.ethereum`が_存在_する場合、興味深いことが起こります。 +さて、`window.ethereum`が_存在する_場合、ここからが面白くなります。 -try/catch ループを使用して、[`window.ethereum.request({ method: "eth_requestAccounts" });`](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts)を呼び出すことでMetaMaskに接続しようとしています。 この関数を呼び出すと、ブラウザでMetaMaskが開き、ユーザーはウォレットを分散型アプリケーション(Dapp)に接続するように求められます。 +try/catchループを使用して、[`window.ethereum.request({ method: "eth_requestAccounts" });`](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts)を呼び出してMetaMaskへの接続を試みます。 この関数を呼び出すと、ブラウザでMetaMaskが開き、ユーザーはウォレットを分散型アプリケーション(Dapp)に接続するように求められます。 -- ユーザーが接続を選んだ場合、`method: "eth_requestAccounts"`は、分散型アプリケーション(Dapp)に接続されているすべてのユーザーのアカウントアドレスを含む配列を返します。 `connectWallet`関数は、配列内の_最初の_`address`と\(9 行目参照\)、ユーザーにスマートコントラクトにメッセージを書き込むように促す`status`メッセージが入ったJSONオブジェクトを返します。 -- ユーザーが接続を拒否した場合、JSONオブジェクトには、返される`address`に入る空の文字列と、ユーザーが接続を拒否したことを示す`status`メッセージが入ることになります。 +- ユーザーが接続を選んだ場合、`method: "eth_requestAccounts"`は、dappに接続されているすべてのユーザーのアカウントアドレスを含む配列を返します。 まとめると、`connectWallet`関数は、この配列の_最初の_`address`(9行目参照)と、ユーザーにスマートコントラクトへのメッセージを書き込むよう促す`status`メッセージを含むJSONオブジェクトを返します。 +- ユーザーが接続を拒否した場合、JSONオブジェクトには返される`address`の空文字列と、ユーザーが接続を拒否したことを示す`status`メッセージが含まれます。 これで、`connectWallet`関数を作成できたので、次のステップでは、この関数を`HelloWorld.js`コンポーネントに呼び出します。 -#### `connect Wallet`関数を`Hello World.js`UIコンポーネントに加える {#add-the-connectWallet-function-to-your-HelloWorld-js-ui-component} +#### `connectWallet`関数を`HelloWorld.js`UIコンポーネントに追加する {#add-the-connectWallet-function-to-your-HelloWorld-js-ui-component} `HelloWorld.js`にある `connectWalletPressed`関数に移動し、次のように更新します。 @@ -1216,19 +1217,19 @@ const connectWalletPressed = async () => { `interact.js`ファイルによって、機能の大部分が`HelloWorld.js`コンポーネントからどのように抽象化されているかに注目してください。 これは、モデルビューコントローラ(M-V-C)パラダイムに準拠しているためです。 -`connectWalletPressed`では、単にインポートされた`connectWallet`関数のawait呼び出しを行っています。さらに、そのレスポンスを使用し、`status`と`walletAddress`変数を状態フックを介して更新しています。 +`connectWalletPressed`では、インポートした`connectWallet`関数をawaitで呼び出し、そのレスポンスを使って状態フックを介して`status`と`walletAddress`変数を更新します。 それでは、両方のファイル \(`HelloWorld.js`と`interact.js`\) を保存して、これまでのUIをテストしてみましょう。 -[http://localhost:3000/](http://localhost:3000/)でブラウザを開き、ページ右上にある「Connect Wallet」ボタンを押します。 +[http://localhost:3000/](http://localhost:3000/)ページでブラウザを開き、ページ右上にある「Connect Wallet」ボタンを押します。 MetaMaskがインストールされている場合は、ウォレットを分散型アプリケーション(Dapp)に接続するように求められます。 接続リクエストを承認します。 -ウォレットボタンに、接続した自分のアドレスが表示されているはずです。 やりましたね🔥 +ウォレットボタンに、接続した自分のアドレスが表示されているはずです! やった!🔥 -次に、ページを更新してみてください。変ですね。 ウォレットボタンによって、すでに接続しているにもかかわらずMetaMaskに接続するよう求められます。 +次に、ページを再読み込みしてみてください... これは奇妙です。 ウォレットボタンによって、すでに接続しているにもかかわらずMetaMaskに接続するよう求められます。 -しかし、恐れるに足りません。 `getCurrentWalletConnected`を実装することで、簡単にこれを修正できます。この関数は、アドレスが分散型アプリケーション(Dapp) にすでに接続されているかどうかを確認し、それに応じてUIを更新します。 +しかし、恐れることはありません! これを簡単に修正できます(分かりましたか?) `getCurrentWalletConnected`を実装することで、アドレスがすでにdappに接続されているかどうかを確認し、それに応じてUIを更新できます! #### `getCurrentWalletConnected`関数 {#the-getcurrentwalletconnected-function} @@ -1246,12 +1247,12 @@ export const getCurrentWalletConnected = async () => { if (addressArray.length > 0) { return { address: addressArray[0], - status: "👆🏽 Write a message in the text-field above.", + status: "👆🏽 上のテキストフィールドにメッセージを書き込んでください。", } } else { return { address: "", - status: "🦊 Connect to MetaMask using the top right button.", + status: "🦊 右上のボタンを使ってMetaMaskに接続してください。", } } } catch (err) { @@ -1268,8 +1269,7 @@ export const getCurrentWalletConnected = async () => {

{" "} 🦊 - You must install MetaMask, a virtual Ethereum wallet, in your - browser. + ブラウザに仮想EthereumウォレットであるMetaMaskをインストールする必要があります。

@@ -1279,9 +1279,9 @@ export const getCurrentWalletConnected = async () => { } ``` -このコードは、前述の`connectWallet`関数に_非常に_似ています。 +このコードは、前のステップで作成した`connectWallet`関数に非常に似ています。 -主な違いとしては、ユーザーがウォレットに接続するためにMetaMaskを開く`eth_requestAccounts`メソッドを呼び出す代わりに、 ここでは`eth_accounts`メソッドを呼び出しています。これは、現在、分散型アプリケーション(Dapp)に接続されているMetaMaskのアドレスを含む配列を単に返すだけです。 +主な違いは、ユーザーがウォレットを接続するためにMetaMaskを開く`eth_requestAccounts`メソッドを呼び出す代わりに、ここでは`eth_accounts`メソッドを呼び出している点です。これは、現在dappに接続されているMetaMaskのアドレスを含む配列を単に返すだけです。 この関数を動作させるため、`HelloWorld.js`コンポーネントの`useEffect`関数で呼び出しましょう。 @@ -1299,13 +1299,13 @@ useEffect(async () => { }, []) ``` -`walletAddress`状態変数と`status`状態変数を更新するのに、呼び出した`getCurrentWalletConnected`のレスポンスを使用していることに注目してください。 +`walletAddress`と`status`の状態変数を更新するのに、`getCurrentWalletConnected`の呼び出しのレスポンスを使用していることに注目してください。 このコードを加えたら、ブラウザウィンドウを更新してみてください。 素晴らしい! リフレッシュ後も、ボタンには接続されていることが示されており、接続されたウォレットのアドレスのプレビューが表示されているはずです。 -#### `addWalletListener`の実装 {#implement-addwalletlistener} +#### `addWalletListener`を実装する {#implement-addwalletlistener} 分散型アプリケーション(Dapp)ウォレットの設定の最終ステップは、ウォレットリスナーを実装することです。これにより、ユーザーが接続を切断したり、アカウントを切り替えたりした場合など、ウォレットの状態が変更されたときにUIが更新されます。 @@ -1319,10 +1319,10 @@ function addWalletListener() { window.ethereum.on("accountsChanged", (accounts) => { if (accounts.length > 0) { setWallet(accounts[0]) - setStatus("👆🏽 Write a message in the text-field above.") + setStatus("👆🏽 上のテキストフィールドにメッセージを書き込んでください。") } else { setWallet("") - setStatus("🦊 Connect to MetaMask using the top right button.") + setStatus("🦊 右上のボタンを使ってMetaMaskに接続してください。") } }) } else { @@ -1330,7 +1330,7 @@ function addWalletListener() {

{" "} 🦊 - You must install MetaMask, a virtual Ethereum wallet, in your browser. + ブラウザに仮想EthereumウォレットであるMetaMaskをインストールする必要があります。

) @@ -1338,11 +1338,11 @@ function addWalletListener() { } ``` -この時点で何が起こっているかを理解するのに私たちの助けは必要ないと思いますが、完璧な理解を目指しているので簡単に説明します。 +この時点で何が起こっているかを理解するのに私たちの助けは必要ないと思いますが、念のため、簡単に説明します。 -- まず、ブラウザで`window.ethereum`が有効になっているか\(すなわち MetaMaskがインストールされているか\)を関数がチェックしています。 - - 有効になっていない場合、ユーザーにMetaMaskのインストールを求めるJSX文字列を`status`状態変数に設定します。 - - 有効になっている場合、MetaMaskウォレットの状態変更をリッスンしている3行目の`window.ethereum.on("accountsChanged")`リスナーを設定します。この状態変更には、ユーザーが追加のアカウントを分散型アプリケーション(Dapp)に接続した場合、アカウントを切り替えた場合、アカウントを切断した場合が含まれます。 少なくとも1つのアカウントが接続されていれば、`accounts`配列の最初のアカウントがリスナーから返されたときに、`walletAddress`状態変数が更新されます。 それ以外の場合は、`walletAddress`に空の文字列が設定されます。 +- まず、この関数は`window.ethereum`が有効になっているか(つまり、MetaMaskがインストールされているか)をチェックします。 + - 有効でない場合、`status`状態変数を、ユーザーにMetaMaskのインストールを促すJSX文字列に設定するだけです。 + - 有効になっている場合、3行目のリスナー`window.ethereum.on("accountsChanged")`を設定します。これはMetaMaskウォレットの状態変更をリッスンします。これには、ユーザーがdappに追加のアカウントを接続した場合、アカウントを切り替えた場合、アカウントを切断した場合が含まれます。 少なくとも1つのアカウントが接続されていれば、`walletAddress`状態変数は、リスナーから返された`accounts`配列の最初のアカウントとして更新されます。 それ以外の場合、`walletAddress`には空の文字列が設定されます。 最後に、`useEffect`関数で次のように呼び出す必要があります。 @@ -1364,21 +1364,21 @@ useEffect(async () => { 完成です! ウォレットのすべての機能をプログラミングしました。 次は最後のタスクです。スマートコントラクトに保存されているメッセージを更新します。 -### ステップ6: `updateMessage`関数の実装する {#step-6-implement-the-updateMessage-function} +### ステップ6: `updateMessage`関数を実装する {#step-6-implement-the-updateMessage-function} -友よ!最終段階にたどり着きました。 `interact.js`ファイルの`updateMessage`で、次のことを実行します。 +さあ、最終段階にたどり着きました。 `interact.js`ファイルの`updateMessage`で、次のことを実行します。 -1. スマートコンタクトに公開したいメッセージが有効であることを確認する。 +1. スマートコントラクトに公開したいメッセージが有効であることを確認する。 2. MetaMaskを使用してトランザクションに署名する 3. `HelloWorld.js`フロントエンドコンポーネントでこの関数を呼び出す。 -これには、それほど時間を要しません。dappを完成させましょう! +これには、それほど時間はかかりません。dappを完成させましょう! #### 入力エラー処理 {#input-error-handling} 当然ながら、関数の開始時に何らかの入力エラー処理を行うことは理にかなっています。 -MetaMaskエクステンションがインストールされていない場合や接続されているウォレットがない場合 \(つまり、渡された `address`が空の文字列\) 、 `message`は空の文字列になります。 次のエラー処理を`updateMessage`に追加しましょう。 +MetaMaskエクステンションがインストールされていない場合、接続されているウォレットがない場合 \(つまり、渡された `address`が空の文字列の場合\) 、または`message`が空の文字列の場合は、関数が早期にリターンするようにします。 次のエラー処理を`updateMessage`に追加しましょう。 ```javascript // interact.js @@ -1387,13 +1387,13 @@ export const updateMessage = async (address, message) => { if (!window.ethereum || address === null) { return { status: - "💡 Connect your MetaMask wallet to update the message on the blockchain.", + "💡 ブロックチェーン上のメッセージを更新するには、MetaMaskウォレットを接続してください。", } } if (message.trim() === "") { return { - status: "❌ Your message cannot be an empty string.", + status: "❌ メッセージを空の文字列にすることはできません。", } } } @@ -1401,21 +1401,21 @@ export const updateMessage = async (address, message) => { 入力エラーを適切に処理できるようなりました。それでは、MetaMaskを介してトランザクションに署名をします。 -#### トランザクションへ署名する {#signing-our-transaction} +#### トランザクションに署名する {#signing-our-transaction} -従来のWeb3イーサリアムトランザクションにすでに慣れている場合は、次に記述するコードは非常に馴染みのあるものになるでしょう。 入力エラー処理コードの下に、次の`updateMessage`を加えます。 +従来のweb3 Ethereumトランザクションにすでに慣れている場合は、次に記述するコードは非常に馴染みのあるものになるでしょう。 入力エラー処理コードの下に、次の`updateMessage`を加えます。 ```javascript // interact.js -//set up transaction parameters +//トランザクションパラメータを設定する const transactionParameters = { - to: contractAddress, // Required except during contract publications. - from: address, // must match user's active address. + to: contractAddress, // コントラクト公開時以外は必須。 + from: address, // ユーザーのアクティブなアドレスと一致する必要がある。 data: helloWorldContract.methods.update(message).encodeABI(), } -//sign the transaction +//トランザクションに署名する try { const txHash = await window.ethereum.request({ method: "eth_sendTransaction", @@ -1426,11 +1426,10 @@ try { ✅{" "} - View the status of your transaction on Etherscan! + Etherscanでトランザクションのステータスを表示してください!
- ℹ️ Once the transaction is verified by the network, the message will be - updated automatically. + ℹ️ トランザクションがネットワークによって検証されると、メッセージは自動的に更新されます。
), } @@ -1443,45 +1442,45 @@ try { 何をしているか、説明していきましょう。 まず、次のようにトランザクションパラメータを設定します。 -- `to`に受取人のアドレス\(スマートコントラクト\)を設定します 。 -- `from`では、関数に渡した`address`変数であるトランザクションの署名者を指定します。 -- `data`には、Hello Worldスマートコントラクトの `update`メソッドへの呼び出しが含まれており、`message`文字列変数を入力として受け取っています。 +- `to`は受信者アドレス(スマートコントラクト)を指定します +- `from`はトランザクションの署名者を指定します。これは関数に渡した`address`変数です。 +- `data`には、Hello Worldスマートコントラクトの`update`メソッドへの呼び出しが含まれており、`message`文字列変数を入力として受け取っています。 -次に、`window.ethereum.request`をawaitで呼び出して、MetaMaskにトランザクションの署名を依頼します。 11行目と12行目で、ethメソッド `eth_sendTransaction`を指定し、`transactionParameters`を渡していることに注目してください。 +次に、`window.ethereum.request`をawaitで呼び出して、MetaMaskにトランザクションの署名を依頼します。 11行目と12行目で、ethメソッド`eth_sendTransaction`を指定し、`transactionParameters`を渡していることに注目してください。 この時点で、ブラウザでMetaMaskが開かれ、ユーザーにトランザクションの署名または拒否を求めます。 -- トランザクションが成功した場合、この関数は、Etherscanでトランザクションについての詳細を確認するようユーザーに求める`status`JSX文字列が入ったJSONオブジェクトを返します。 +- トランザクションが成功した場合、この関数は、`status` JSX文字列がEtherscanでトランザクションについての詳細を確認するようユーザーに促すJSONオブジェクトを返します。 - トランザクションが失敗した場合、この関数は、エラーメッセージを伝える`status`文字列が入ったJSONオブジェクトを返します。 -全体では、`updateMessage`関数は次のようになります。 +全体として、`updateMessage`関数は次のようになります。 ```javascript // interact.js export const updateMessage = async (address, message) => { - //input error handling + //入力エラー処理 if (!window.ethereum || address === null) { return { status: - "💡 Connect your MetaMask wallet to update the message on the blockchain.", + "💡 ブロックチェーン上のメッセージを更新するには、MetaMaskウォレットを接続してください。", } } if (message.trim() === "") { return { - status: "❌ Your message cannot be an empty string.", + status: "❌ メッセージを空の文字列にすることはできません。", } } - //set up transaction parameters + //トランザクションパラメータを設定する const transactionParameters = { - to: contractAddress, // Required except during contract publications. - from: address, // must match user's active address. + to: contractAddress, // コントラクト公開時以外は必須。 + from: address, // ユーザーのアクティブなアドレスと一致する必要がある。 data: helloWorldContract.methods.update(message).encodeABI(), } - //sign the transaction + //トランザクションに署名する try { const txHash = await window.ethereum.request({ method: "eth_sendTransaction", @@ -1492,11 +1491,10 @@ export const updateMessage = async (address, message) => { ✅{" "} - View the status of your transaction on Etherscan! + Etherscanでトランザクションのステータスを表示してください!
- ℹ️ Once the transaction is verified by the network, the message will - be updated automatically. + ℹ️ トランザクションがネットワークによって検証されると、メッセージは自動的に更新されます。
), } @@ -1523,18 +1521,18 @@ const onUpdatePressed = async () => { } ``` -とても綺麗ででシンプルです。 そして、なんということでしょう。 dappの完成です。 +とてもクリーンでシンプルです。 そして、なんと... dappの完成です!!! -**更新**ボタンを試してみてください! +**Update**ボタンを試してみてください! -### 自分自身でカスタムdappを作る {#make-your-own-custom-dapp} +### 独自のカスタムdappを作成する {#make-your-own-custom-dapp} -おめでとう!あなたは、このチュートリアルを最後までやりきりました! おさらいすると、以下の方法を学びました。 +おめでとうございます!チュートリアルの最後までたどり着きました! おさらいすると、以下の方法を学びました。 - MetaMaskウォレットをdappプロジェクトに接続する -- [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) APIを使用してスマートコントラクトからデータを読み取る。 -- MetaMaskを使用してイーサリアムトランザクションに署名する +- [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) APIを使用してスマートコントラクトからデータを読み取る +- MetaMaskを使用してEthereumトランザクションに署名する -これで、このチュートリアルのスキルを応用して独自のカスタムdappプロジェクトを構築するための準備が整いました。 何かご質問がございましたら、[Alchemy Discord](https://discord.gg/gWuC7zB)でいつでもお気軽にお問い合わせください。 🧙‍♂️ +これで、このチュートリアルのスキルを応用して独自のカスタムdappプロジェクトを構築するための準備が整いました。 いつものように、ご質問があれば、[Alchemy Discord](https://discord.gg/gWuC7zB)でお気軽にお問い合わせください。 🧙‍♂️ -このチュートリアルを通して体験したことやフィードバックがあれば、Twitter[@alchemyplatform](https://twitter.com/AlchemyPlatform)でタグ付けしてお知らせください。 +このチュートリアルを完了したら、Twitterで[@alchemyplatform](https://twitter.com/AlchemyPlatform)にタグ付けして、感想やフィードバックをお知らせください! diff --git a/public/content/translations/ja/developers/tutorials/hello-world-smart-contract/index.md b/public/content/translations/ja/developers/tutorials/hello-world-smart-contract/index.md index 7cef5459340..8130177db6e 100644 --- a/public/content/translations/ja/developers/tutorials/hello-world-smart-contract/index.md +++ b/public/content/translations/ja/developers/tutorials/hello-world-smart-contract/index.md @@ -1,73 +1,62 @@ --- -title: 初心者向けのHello Worldスマートコントラクト -description: イーサリアムでの簡単なスマートコントラクトの作成とデプロイに関する入門チュートリアル +title: "初心者向けのHello Worldスマートコントラクト" +description: "イーサリアムでの簡単なスマートコントラクトの作成とデプロイに関する入門チュートリアル。" author: "elanh" -tags: - - "Solidity" - - "Hardhat" - - "alchemy" - - "スマートコントラクト" - - "デプロイ" +tags: [ "Solidity", "hardhat", "Alchemy", "スマート契約", "デプロイ" ] skill: beginner lang: ja published: 2021-03-31 --- -このチュートリアルは、ブロックチェーンの開発が初めてで、どこから始めたらよいのか分からない場合や、 スマートコントラクトをデプロイしてやり取りする方法を理解したいだけの場合に、最適なガイドとなります。 このチュートリアルでは、仮想ウォレット([MetaMask](https://metamask.io/))、[Solidity](https://docs.soliditylang.org/en/v0.8.0/)、[Hardhat](https://hardhat.org/)、[Alchemy](https://alchemyapi.io/eth)を使用して、Goerliテストネットワーク上で簡単なスマートコントラクトを作成してデプロイする方法を順を追って説明します(現時点でしっかりと理解できていなくても、心配はご無用です。後ほど説明します)。 +このガイドは、ブロックチェーンの開発が初めてでどこから始めたらよいのか分からない方や、スマートコントラクトをデプロイして対話する方法を理解したいだけの方に最適です。 このチュートリアルでは、仮想ウォレット([MetaMask](https://metamask.io/))、[Solidity](https://docs.soliditylang.org/en/v0.8.0/)、[Hardhat](https://hardhat.org/)、[Alchemy](https://www.alchemy.com/)を使用して、Sepoliaテストネットワーク上で簡単なスマートコントラクトを作成してデプロイする方法を順を追って説明します(現時点でしっかりと理解できていなくても、心配はご無用です。後ほど説明します)。 -> **警告** -> -> 🚧 非推奨の通知 -> -> このガイドでは、Goerliテストネットワークをスマートコントラクトの作成とデプロイに使用しています。 ただし、イーサリアム・ファウンデーションにより、[Goerliが間もなく廃止予定](https://www.alchemy.com/blog/goerli-faucet-deprecation)であることが発表されました。 -> -> このチュートリアルでは、[Sepolia](https://www.alchemy.com/overviews/sepolia-testnet)および[Sepoliaフォーセット](https://sepoliafaucet.com/)の利用を推奨します。 +このチュートリアルの[パート2](https://docs.alchemy.com/docs/interacting-with-a-smart-contract)では、デプロイ後のスマートコントラクトとの対話方法について、[パート3](https://www.alchemy.com/docs/submitting-your-smart-contract-to-etherscan)ではEtherscanで公開する方法について説明します。 -このチュートリアルの[パート2](https://docs.alchemy.com/docs/interacting-with-a-smart-contract)では、ここでデプロイしたスマートコントラクトとやり取りする方法について説明します。[パート3](https://docs.alchemy.com/docs/submitting-your-smart-contract-to-etherscan)では、そのスマートコントラクトをEtherscanで公開する方法について説明します。 - -質問がある場合は、いつでも[Alchemy Discord](https://discord.gg/gWuC7zB)でお問い合わせください。 +ご不明な点がございましたら、[Alchemy Discord](https://discord.gg/gWuC7zB)までお気軽にお問い合わせください! ## ステップ1: イーサリアムネットワークに接続する {#step-1} -イーサリアムチェーンにリクエストを行う方法はたくさんあります。 簡略化のため、ここではAlchemyの無料アカウントを使用します。これは独自のノードを実行することなく、イーサリアムチェーンとの通信を可能にするブロックチェーンのデベロッパープラットフォームとAPIです。 このプラットフォームには、スマートコントラクトのデプロイメントにおいて内部で何が起こっているのかを把握するためにこのチュートリアルで利用する、監視と分析のためのデベロッパーツールも備わっています。 Alchemyのアカウントをお持ちでない場合は、[こちら](https://dashboard.alchemyapi.io/signup)から無料で登録できます。 +イーサリアムチェーンにリクエストを行う方法はたくさんあります。 簡略化のため、ここではAlchemyの無料アカウントを使用します。これは独自のノードを実行することなく、イーサリアムチェーンとの通信を可能にするブロックチェーンのデベロッパープラットフォームとAPIです。 このプラットフォームには、スマートコントラクトのデプロイメントにおいて内部で何が起こっているのかを把握するためにこのチュートリアルで利用する、監視と分析のためのデベロッパーツールも備わっています。 Alchemyアカウントをお持ちでない場合は、[こちらから無料で登録](https://dashboard.alchemy.com/signup)できます。 -## ステップ2: アプリ(およびAPI キー)を作成する {#step-2} +## ステップ2: アプリ(およびAPIキー)を作成する {#step-2} -Alchemyのアカウントを作成すると、アプリを作成することでAPIキーを生成できるようになります。 これにより、Goerliテストネットワークへのリクエストが可能になります。 テストネットに詳しくない場合は、[こちらのページ](/developers/docs/networks/)をご覧ください。 +Alchemyのアカウントを作成した後、アプリを作成することでAPIキーを生成することができます。 これにより、Sepoliaテストネットワークへのリクエストが可能になります。 テストネットに詳しくない場合は、[こちらのページ](/developers/docs/networks/)をご覧ください。 -1. ナビゲーションバーの「Apps」にマウスを合わせて、「Create App」をクリックし、Alchemyダッシュボードの「Create App」ページに移動してください。 +1. Alchemyダッシュボードのナビゲーションバーで「Select an app」を選択し、「Create new app」をクリックして、「Create new app」ページに移動します。 -![Hello WorldのCreate App](./hello-world-create-app.png) +![Hello worldアプリ作成](./hello-world-create-app.png) -2. アプリに「Hello World」という名前を付け、簡単な説明を記述し、環境に「Staging」を選択(アプリのブックキーピングに使用)し、ネットワークに「Goerli」を選択します。 +2. アプリに「Hello World」という名前を付け、簡単な説明を提示し、「Infra & Tooling」などのユースケースを選択します。 次に、「Ethereum」を検索してネットワークを選択します。 -![Hello WorldのCreate App画面](./create-app-view-hello-world.png) +![アプリ作成ビュー hello world](./create-app-view-hello-world.png) -3. 「Create app」をクリックして完了です。 アプリが下の表に表示されます。 +3. 「Next」をクリックして続行し、次に「Create app」をクリックすれば完了です! ナビゲーションバーのドロップダウンメニューにアプリが表示され、APIキーをコピーできるようになります。 ## ステップ3: イーサリアムアカウント(アドレス)を作成する {#step-3} -トランザクションの送受信には、イーサリアムアカウントが必要です。 このチュートリアルでは、イーサリアムアカウントアドレスを管理するためにブラウザの仮想ウォレットであるMetamaskを使用します。 [トランザクション](/developers/docs/transactions/)の詳細。 +トランザクションの送受信には、イーサリアムアカウントが必要です。 このチュートリアルでは、イーサリアムアカウントアドレスを管理するためにブラウザの仮想ウォレットであるMetamaskを使用します。 [トランザクション](/developers/docs/transactions/)に関する詳細はこちら。 + +MetaMaskは[こちら](https://metamask.io/download)からダウンロードして、無料でイーサリアムアカウントを作成できます。 アカウントを作成するとき、またはすでにアカウントをお持ちの場合は、(実際のお金を使わないように)ネットワークのドロップダウンメニューを使用して「Sepolia」テストネットワークに切り替えてください。 -Metamaskのアカウントは[こちら](https://metamask.io/download)から無料でダウンロード、作成できます。 アカウントを作成後、またはすでにアカウントをお持ちの場合は(実際に支払いが発生しないように)右上の「Goerli Test Network」に切り替えてください。 +Sepoliaがリストに表示されない場合は、メニューから「高度な設定」に進み、下にスクロールして「テストネットワークを表示」をオンに切り替えます。 ネットワーク選択メニューで、「カスタム」タブを選択してテストネットのリストを見つけ、「Sepolia」を選択します。 -![MetaMask Ropstenの例](./metamask-ropsten-example.png) +![metamask sepolia の例](./metamask-sepolia-example.png) -## ステップ4: フォーセットからイーサ(ETH)を追加する {#step-4} +## ステップ4: フォーセットからイーサを追加する {#step-4} -テストネットワークにスマートコントラクトをデプロイするには、偽のETHが必要になります。 ETHを取得するには、[Goerliフォーセット](https://goerlifaucet.com/)にアクセスし、Alchemyアカウントでログインしてウォレットアドレスを入力し、「Send Me ETH」をクリックしてください。 ネットワークトラフィックのために偽のETHを受け取るのに時間がかかる場合があります。 (この記事の執筆時点では、30分ほどかかりました。) MetaMaskアカウントにETHが表示されるはずです! +テストネットワークにスマートコントラクトをデプロイするには、偽のEthが必要になります。 Sepolia ETHを入手するには、[Sepoliaネットワーク詳細](/developers/docs/networks/#sepolia)にアクセスして、さまざまなフォーセットのリストを表示します。 1つが機能しない場合は、別のものを試してください。枯渇している場合があります。 ネットワークのトラフィックにより、偽のETHの受信に時間がかかる場合があります。 その後すぐに、MetaMaskアカウントにETHが表示されるはずです! ## ステップ5: 残高を確認する {#step-5} -残高を再確認するために、[eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)を[Alchemyのコンポーザーツール](https://composer.alchemyapi.io?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D)を使用してリクエストしてみましょう。 このリクエストをすると、ウォレット内のETHの額が返されます。 MetaMaskアカウントアドレスを入力して「Send Request」をクリックすると、次のようなレスポンスが表示されます。 +残高があることを再確認するために、[Alchemyのcomposerツール](https://sandbox.alchemy.com/?network=ETH_SEPOLIA&method=eth_getBalance&body.id=1&body.jsonrpc=2.0&body.method=eth_getBalance&body.params%5B0%5D=&body.params%5B1%5D=latest)を使用して[eth_getBalance](/developers/docs/apis/json-rpc/#eth_getbalance)リクエストを作成しましょう。 このリクエストをすると、ウォレット内のETHの額が返されます。 MetaMaskアカウントアドレスを入力して「Send Request」をクリックすると、次のようなレスポンスが表示されます。 ```json { "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" } ``` -> **注:** この結果の単位は、ETHではなくweiです。 weiはETHの最小単位として使われています。 weiからETHへ変換すると、1 eth = 1018 weiになります。 つまり、0x2B5E3AF16B1880000を10進数に変換すると、5\*10¹⁸となり、5 ETHに相当します。 +> \*\*注:\*\*この結果の単位はETHではなくweiです。 weiはETHの最小単位として使われています。 weiからETHへの変換は、1 eth = 1018 weiです。 したがって、0x2B5E3AF16B1880000を10進数に変換すると5\*10¹⁸になり、これは5 ETHに相当します。 > -> ご安心ください。 偽のお金はすべてそこにあります。 +> ふう! 偽のお金がすべて揃いました。 ## ステップ6: プロジェクトを初期化する {#step-6} @@ -78,18 +67,18 @@ mkdir hello-world cd hello-world ``` -プロジェクトフォルダ内に入ったら、`npm init`を使用してプロジェクトを初期化します。 まだnpmがインストールされていない場合は、[こちらの手順](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm)に従ってください(Node.jsも必要となりますので、こちらもダウンロードしてください。) +プロジェクトフォルダに入ったので、`npm init`を使用してプロジェクトを初期化します。 npmをまだインストールしていない場合は、[これらの手順](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm)に従ってください(Node.jsも必要なので、それもダウンロードしてください!)。 ``` npm init ``` -インストール時の質問についてはどのように回答してもかまいませんが、参考までに以前行った回答を以下に示します。 +インストールの質問にどう答えるかは重要ではありませんが、参考までに私たちの回答方法を次に示します。 ``` package name: (hello-world) version: (1.0.0) -description: hello world smart contract +description: hello world スマートコントラクト entry point: (index.js) test command: git repository: @@ -101,7 +90,7 @@ About to write to /Users/.../.../.../hello-world/package.json: { "name": "hello-world", "version": "1.0.0", - "description": "hello world smart contract", + "description": "hello world スマートコントラクト", "main": "index.js", "scripts": { "test": "echo \\"Error: no test specified\\" && exit 1" @@ -111,19 +100,19 @@ About to write to /Users/.../.../.../hello-world/package.json: } ``` -package.jsonを承認すれば完了です。 +package.jsonを承認すれば完了です! ## ステップ7: [Hardhat](https://hardhat.org/getting-started/#overview)をダウンロードする {#step-7} Hardhatは、イーサリアムのソフトウェアをコンパイル、デプロイ、テスト、デバッグするための開発環境です。 デベロッパーがライブチェーンにデプロイする前に、スマートコントラクトや分散型アプリケーション(Dapp)をローカルに構築する際に役立ちます。 -先ほど作成した`hello-world`プロジェクト内で、以下を実行します。 +`hello-world`プロジェクト内で次を実行します。 ``` npm install --save-dev hardhat ``` -[インストール手順](https://hardhat.org/getting-started/#overview)の詳細については、こちらのページをご覧ください。 +[インストール手順](https://hardhat.org/getting-started/#overview)の詳細については、このページをご覧ください。 ## ステップ8: Hardhatプロジェクトを作成する {#step-8} @@ -133,7 +122,7 @@ npm install --save-dev hardhat npx hardhat ``` -ウェルカムメッセージと、次に何をするのかを選択できるオプションが表示されます。 「Create an empty hardhat.config.js」を選択します。 +ウェルカムメッセージと、次に何をするのかを選択できるオプションが表示されます。 「create an empty hardhat.config.js」を選択してください。 ``` 888 888 888 888 888 @@ -153,7 +142,7 @@ Create a sample project Quit ``` -`hardhat.config.js`ファイルが生成されます。このファイルでプロジェクトのすべての設定を行います(ステップ13で行います)。 +これにより `hardhat.config.js` ファイルが生成されます。ここでプロジェクトのすべてのセットアップを指定します(ステップ13)。 ## ステップ9: プロジェクトフォルダを追加する {#step-9} @@ -164,55 +153,55 @@ mkdir contracts mkdir scripts ``` -- `contracts/`は、Hello Worldスマートコントラクトのコードファイルを格納する場所です。 -- `scripts/`は、コントラクトをデプロイして対話するスクリプトを保持する場所です。 +- `contracts/` には、hello worldスマートコントラクトのコードファイルを保存します +- `scripts/` には、コントラクトをデプロイして対話するためのスクリプトを保存します ## ステップ10: コントラクトを作成する {#step-10} -一体いつになったらコードを書くのだろうと疑問をお持ちではないでしょうか 。 このステップ10でコードを書いていきましょう。 +一体いつになったらコードを書くのだろう、と思っているかもしれませんね。 さあ、このステップ10でコードを書き始めましょう。 -お気に入りのエディタでhello-worldプロジェクトを開きます(通常は[VScode](https://code.visualstudio.com/)を使用しています)。 スマートコントラクトは、Solidityと呼ばれる言語で記述されています。HelloWorld.solスマートコントラクトの作成にこの言語を使用します。 +お気に入りのエディタでhello-worldプロジェクトを開きます([VSCode](https://code.visualstudio.com/)がおすすめです)。 スマートコントラクトはSolidityという言語で書かれており、これを使ってHelloWorld.solスマートコントラクトを作成します。‌ -1. 「contracts」フォルダに移動し、HelloWorld.solという名前の新規ファイルを作成します。 -2. 以下は、このチュートリアルで使用するイーサリアム・ファウンデーションのHello Worldスマートコントラクトのサンプルです。 以下の内容をコピーして、HelloWorld.solファイルに貼り付けます。コメントを読んで、このコントラクトが何を行うのかを理解してください。 +1. 「contracts」フォルダに移動し、HelloWorld.solという名前の新規ファイルを作成します。 +2. 以下は、このチュートリアルで使用するイーサリアム・ファウンデーションのHello Worldスマートコントラクトのサンプルです。 以下の内容をHelloWorld.solファイルにコピー&ペーストし、コメントを読んでこのコントラクトが何をするのかを理解してください。 ```solidity -// Specifies the version of Solidity, using semantic versioning. -// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +// セマンティックバージョニングを使用して、Solidityのバージョンを指定します。 +// 詳細はこちら: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma pragma solidity ^0.7.0; -// Defines a contract named `HelloWorld`. -// A contract is a collection of functions and data (its state). Once deployed, a contract resides at a specific address on the Ethereum blockchain. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +// `HelloWorld`という名前のコントラクトを定義します。 +// コントラクトは関数とデータ(その状態)の集合です。デプロイされると、コントラクトはイーサリアムのブロックチェーン上の特定のアドレスに配置されます。詳細はこちら: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html contract HelloWorld { - // Declares a state variable `message` of type `string`. - // State variables are variables whose values are permanently stored in contract storage. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value. + // `string`型の状態変数`message`を宣言します。 + // 状態変数は、その値がコントラクトストレージに永続的に保存される変数です。`public`キーワードは、変数をコントラクトの外部からアクセス可能にし、他のコントラクトやクライアントがその値をアクセスするために呼び出せる関数を作成します。 string public message; - // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation. - // Constructors are used to initialize the contract's data. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + // 多くのクラスベースのオブジェクト指向言語と同様に、コンストラクタはコントラクト作成時にのみ実行される特別な関数です。 + // コンストラクタは、コントラクトのデータを初期化するために使用されます。詳細はこちら:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors constructor(string memory initMessage) { - // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable). + // 文字列引数`initMessage`を受け入れ、その値をコントラクトの`message`ストレージ変数に設定します。 message = initMessage; } - // A public function that accepts a string argument and updates the `message` storage variable. + // 文字列引数を受け入れ、`message`ストレージ変数を更新する公開関数です。 function update(string memory newMessage) public { message = newMessage; } } ``` -これは、作成時にメッセージを保存し、`update`関数を呼び出すことで更新できる非常にシンプルなスマートコントラクトです。 +これは、作成時にメッセージを保存し、`update`関数を呼び出すことで更新できる、非常にシンプルなスマートコントラクトです。 ## ステップ11: MetaMaskとAlchemyをプロジェクトに接続する {#step-11} -ここまでで、MetaMaskウォレットとAlchemyアカウントを作成し、スマートコントラクトも作成しました。次はこの3つを接続しましょう。 +MetaMaskウォレットとAlchemyアカウントを作成し、スマートコントラクトも作成しました。次はこの3つを接続しましょう。 -仮想ウォレットから送信されるすべてのトランザクションには、固有の秘密鍵を使用した署名が必要です。 この権限をプログラムに提供するため、秘密鍵(およびAlchemy APIキー)を環境ファイルに安全に保存できます。 +仮想ウォレットから送信されるすべてのトランザクションには、固有の秘密鍵を使用した署名が必要です。 この許可をプログラムに与えるために、秘密鍵(とAlchemyのAPIキー)を環境ファイルに安全に格納する作業を行います。 -> トランザクションの送信の詳細については、web3を使用したトランザクションの送信に関する[こちらのチュートリアル](/developers/tutorials/sending-transactions-using-web3-and-alchemy/)をご覧ください。 +> トランザクションの送信について詳しく知るには、web3を使用したトランザクション送信に関する[このチュートリアル](/developers/tutorials/sending-transactions-using-web3-and-alchemy/)をご覧ください。 まず、プロジェクトディレクトリにdotenvパッケージをインストールします。 @@ -220,37 +209,37 @@ contract HelloWorld { npm install dotenv --save ``` -次に、 `.env`ファイルをプロジェクトのルートディレクトリに作成し、そのファイルにMetamaskの秘密鍵とHTTP Alchemy APIのURLを追加します。 +次に、プロジェクトのルートディレクトリに`.env`ファイルを作成し、MetaMaskの秘密鍵とHTTP Alchemy API URLを追加します。 -- 秘密鍵をエクスポートするには、[こちらの手順](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key)に従ってください。 -- HTTP Alchemy APIのURLを取得するには、以下を参照してください。 +- [これらの手順](https://support.metamask.io/configure/accounts/how-to-export-an-accounts-private-key/)に従って秘密鍵をエクスポートします +- HTTP Alchemy API URLを取得するには、以下を参照してください -![Alchemy APIキーの取得](./get-alchemy-api-key.gif) +![alchemy apiキーの取得](./get-alchemy-api-key.png) -Alchemy APIのURLをコピーします。 +Alchemy API URLをコピーする -`.env`ファイルは次のようになります。 +`.env`は次のようになります: ``` -API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" +API_URL = "https://eth-sepolia.g.alchemy.com/v2/your-api-key" PRIVATE_KEY = "your-metamask-private-key" ``` -これらの変数を実際にコードに接続するために、ステップ13でこれらの変数を`hardhat.config.js`ファイル内で参照します。 +これらをコードに実際に接続するために、ステップ13で`hardhat.config.js`ファイル内のこれらの変数を参照します。 -.envファイルをコミットしないでください! .envファイルを誰かと共有したり公開したりしないようにしてください。秘密が漏洩する可能性があります。 バージョン管理ツールを使用している場合は、.envgitignoreファイルに追加します。 +.envはコミットしないでください! .envは決して他人と共有したり、公開したりしないように注意してください。共有することで、あなたのアカウント情報が漏洩する可能性があります。 バージョンを管理する場合は、.envgitignoreファイルに追加してください。 ## ステップ12: Ethers.jsをインストールする {#step-12-install-ethersjs} -Ethers.jsは、よりユーザーフレンドリーなメソッドで[標準のJSON-RPCメソッド](/developers/docs/apis/json-rpc/)をラップすることにより、イーサリアムとの対話やリクエストを簡単にするライブラリです。 +Ethers.jsは、[標準のJSON-RPCメソッド](/developers/docs/apis/json-rpc/)をよりユーザーフレンドリーなメソッドでラップすることで、イーサリアムとの対話やリクエストを容易にするライブラリです。 -Hardhatは、追加のツールと拡張機能のための[プラグイン](https://hardhat.org/plugins/)の統合を非常に簡単にしてくれます。 コントラクトのデプロイメントに[Ethersプラグイン](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers)を利用します([Ethers.js](https://github.com/ethers-io/ethers.js/)には、複数の非常にクリーンなコントラクトのデプロイメント方法があります)。 +Hardhatでは、追加のツールや拡張機能のための[プラグイン](https://hardhat.org/plugins/)を非常に簡単に統合できます。 コントラクトのデプロイには[Ethersプラグイン](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers)を活用します([Ethers.js](https://github.com/ethers-io/ethers.js/)には非常にクリーンなコントラクトデプロイメントメソッドがあります)。 プロジェクトのホームディレクトリで以下を実行します。 @@ -258,13 +247,13 @@ Hardhatは、追加のツールと拡張機能のための[プラグイン](http npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0" ``` -次のステップの`hardhat.config.js`でもEthers(.js)が必要になります。 +次のステップで、`hardhat.config.js`にethersもrequireします。 ## ステップ13: hardhat.config.jsを更新する {#step-13-update-hardhatconfigjs} -ここまでで、いくつかの依存関係とプラグインを追加しました。次に、`hardhat.config.js`を更新して、プロジェクトがそれらすべてについて認識できるようにする必要があります。 +ここまでで、いくつかの依存関係とプラグインを追加しました。次に、プロジェクトがそれらすべてを認識できるように、`hardhat.config.js`を更新する必要があります。 -`hardhat.config.js`を以下のように更新します。 +`hardhat.config.js`を次のように更新します: ``` require('dotenv').config(); @@ -277,10 +266,10 @@ const { API_URL, PRIVATE_KEY } = process.env; */ module.exports = { solidity: "0.7.3", - defaultNetwork: "goerli", + defaultNetwork: "sepolia", networks: { hardhat: {}, - goerli: { + sepolia: { url: API_URL, accounts: [`0x${PRIVATE_KEY}`] } @@ -290,7 +279,7 @@ module.exports = { ## ステップ14: コントラクトをコンパイルする {#step-14-compile-our-contracts} -ここまででしっかりと動作していることを確認するため、コントラクトをコンパイルしてみましょう。 `compile`タスクは、組み込みのHardhatタスクの1つです。 +ここまでの作業がうまくいっていることを確認するために、コントラクトをコンパイルしてみましょう。 `compile`タスクは、組み込みのHardhatタスクの1つです。 コマンドラインで以下を実行します。 @@ -298,19 +287,19 @@ module.exports = { npx hardhat compile ``` -`SPDX license identifier not provided in source file`という警告が表示される場合がありますが、心配する必要はありません。警告が表示されないのがベストですが、 表示された場合は、いつでも[Alchemy discord](https://discord.gg/u72VCg3)でメッセージを送信できます。 +`SPDX license identifier not provided in source file`という警告が表示されるかもしれませんが、心配する必要はありません。それ以外は問題ないはずです! うまくいかない場合は、いつでも[Alchemy Discord](https://discord.gg/u72VCg3)でメッセージを送ることができます。 -## ステップ15: デプロイスクリプトを書く {#step-15-write-our-deploy-scripts} +## ステップ15: デプロイスクリプトを作成する {#step-15-write-our-deploy-scripts} コントラクトの作成と設定ファイルの作成が完了したら、いよいよコントラクトのデプロイのためのスクリプトを作成します。 -`scripts/`フォルダに移動して、`deploy.js`という名前のファイルを新規に作成し、以下の内容を追加します。 +`scripts/`フォルダに移動して`deploy.js`という名前の新しいファイルを作成し、次の内容を追加します: ``` async function main() { const HelloWorld = await ethers.getContractFactory("HelloWorld"); - // Start deployment, returning a promise that resolves to a contract object + // デプロイを開始し、コントラクトオブジェクトに解決されるpromiseを返します const hello_world = await HelloWorld.deploy("Hello World!"); console.log("Contract deployed to address:", hello_world.address);} @@ -322,48 +311,49 @@ main() }); ``` -Hardhatがコードの各行で行っている驚くべき内容については、Hardhatの[コントラクトチュートリアル](https://hardhat.org/tutorial/testing-contracts.html#writing-tests)で説明されています。以下では、その説明を採用しています。 +Hardhatは、[コントラクトのチュートリアル](https://hardhat.org/tutorial/testing-contracts.html#writing-tests)で、これらのコードの各行が何をするかを非常にうまく説明しています。ここではその説明を採用しました。 ``` const HelloWorld = await ethers.getContractFactory("HelloWorld"); ``` -ethers.jsの`ContractFactory`は新しいスマートコントラクトをデプロイするための抽象化であり、ここでの`HelloWorld`はhello worldコントラクトのインスタンスのためのファクトリです。 `hardhat-ethers`プラグインを使用する場合、`ContractFactory`および`Contract`インスタンスはデフォルトで最初の署名者に接続されます。 +ethers.jsの`ContractFactory`は、新しいスマートコントラクトをデプロイするために使用される抽象化です。したがって、ここでの`HelloWorld`は、私たちのhello worldコントラクトのインスタンスのためのファクトリです。 `hardhat-ethers`プラグインを使用する場合、`ContractFactory`および`Contract`インスタンスはデフォルトで最初の署名者に接続されます。 ``` const hello_world = await HelloWorld.deploy(); ``` -`ContractFactory`で`deploy()`を呼び出すとデプロイメントが開始され、`Contract`に解決すべき`Promise`が返されます。 これは、スマートコントラクトの各関数に対するメソッドを持つオブジェクトです。 +`ContractFactory`で`deploy()`を呼び出すとデプロイメントが開始され、`Contract`に解決される`Promise`が返されます。 これは、スマートコントラクトの各関数に対するメソッドを持つオブジェクトです。 ## ステップ16: コントラクトをデプロイする {#step-16-deploy-our-contract} -ようやく、スマートコントラクトをデプロイする準備が整いました。 コマンドラインに移動し、以下を実行します。 +ようやく、スマートコントラクトをデプロイする準備が整いました。 コマンドラインに移動し、次を実行します: ``` -npx hardhat run scripts/deploy.js --network goerli +npx hardhat run scripts/deploy.js --network sepolia ``` 次のような画面が表示されるはずです。 ``` -Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 +コントラクトがデプロイされたアドレス: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 ``` -[Goerli etherscan](https://goerli.etherscan.io/)に移動し、コントラクトアドレスを検索すると、コントラクトが正常にデプロイされていることを確認できるはずです。 トランザクションは以下のようなものになります。 +[Sepolia etherscan](https://sepolia.etherscan.io/)にアクセスし、コントラクトアドレスを検索すると、正常にデプロイされたことが確認できるはずです。 トランザクションは以下のようなものになります。 -![EtherscanのContract](./etherscan-contract.png) +![etherscanコントラクト](./etherscan-contract.png) -`From`アドレスはMetaMaskアカウントアドレスと一致する必要があります。Toアドレスには「Contract Creation」と表示されますが、トランザクションをクリックすると、`To`フィールドにコントラクトアドレスが表示されます。 +`From`アドレスはMetaMaskアカウントのアドレスと一致し、`To`アドレスは「Contract Creation」と表示されますが、トランザクションをクリックすると`To`フィールドにコントラクトアドレスが表示されます。 -![EtherscanのTransaction](./etherscan-transaction.png) +![etherscanトランザクション](./etherscan-transaction.png) -おめでとうございます! イーサリアムチェーンにスマートコントラクトをデプロイできました 🎉 +おめでとうございます! イーサリアムチェーンにスマートコントラクトをデプロイできました 🎉 -内部で何が起こっているのかを理解するために、[Alchemyダッシュボード](https://dashboard.alchemyapi.io/explorer)のExplorerタブに移動してみましょう。 Alchemyのアプリが複数ある場合は、必ずアプリでフィルタリングし、「Hello World」を選択してください。 ![Hello WorldのExplorer](./hello-world-explorer.png) +内部で何が起こっているのかを理解するために、[Alchemyダッシュボード](https://dashboard.alchemyapi.io/explorer)のExplorerタブに移動してみましょう。 Alchemyアプリが複数ある場合は、必ずアプリでフィルタリングし、「Hello World」を選択してください。 +![hello worldエクスプローラー](./hello-world-explorer.png) -ここでは、`.deploy()`関数を呼び出した際に、Hardhat/Ethersが内部で行ったJSON-RPCの呼び出しを見ることができます。 ここで呼び出している2つの重要なJSON-RPCは、実際にGoerliチェーン上でコントラクトを書き込むリクエストの[`eth_sendRawTransaction`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction)と、(トランザクションの際の典型的なパターンである)ハッシュを与えられているトランザクションに関する情報を読み取るリクエストの[`eth_getTransactionByHash`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_gettransactionbyhash)です。 トランザクションの送信の詳細については、こちらのチュートリアルの[Web3を使用したトランザクションの送信](/developers/tutorials/sending-transactions-using-web3-and-alchemy/)をご覧ください。 +ここでは、`.deploy()`関数を呼び出したときにHardhat/Ethersが内部で行ったいくつかのJSON-RPCコールを確認できます。 ここで注目すべき重要なコールは2つあります。1つは、コントラクトをSepoliaチェーンに実際に書き込むためのリクエストである[`eth_sendRawTransaction`](https://www.alchemy.com/docs/node/abstract/abstract-api-endpoints/eth-send-raw-transaction)で、もう1つはハッシュが与えられたトランザクションに関する情報を読み取るためのリクエストである[`eth_getTransactionByHash`](https://www.alchemy.com/docs/node/abstract/abstract-api-endpoints/eth-get-transaction-by-hash)です(これはトランザクションにおける典型的なパターンです)。 トランザクションの送信についてさらに詳しく知りたい場合は、[Web3を使用したトランザクションの送信](/developers/tutorials/sending-transactions-using-web3-and-alchemy/)に関するチュートリアルをご覧ください。 -こちらのチュートリアルのパート1は以上となります。パート2では初期メッセージの更新による[スマートコントラクトとの実際のやり取り](https://docs.alchemyapi.io/alchemy/tutorials/hello-world-smart-contract#part-2-interact-with-your-smart-contract)を、パート3では[Etherscanへのスマートコントラクトの公開](https://docs.alchemyapi.io/alchemy/tutorials/hello-world-smart-contract#optional-part-3-publish-your-smart-contract-to-etherscan)を行い、やり取りする方法を学びます。 +このチュートリアルのパート1は以上です。パート2では、最初のメッセージを更新して[スマートコントラクトと実際に対話](https://www.alchemy.com/docs/interacting-with-a-smart-contract)し、パート3では[スマートコントラクトをEtherscanに公開](https://www.alchemy.com/docs/submitting-your-smart-contract-to-etherscan)して、誰もがその対話方法を知ることができるようにします。 -**Alchemyの詳細については、 [ウェブサイト](https://alchemyapi.io/eth)をご覧ください。 アップデートを見逃したくない場合は、 [こちら](https://www.alchemyapi.io/newsletter)でニュースレターを購読してください。 [Twitter](https://twitter.com/alchemyplatform)もあわせてフォローし、[Discord](https://discord.com/invite/u72VCg3)**にもご参加ください。 +**Alchemyについてもっと知りたいですか? 私たちの[ウェブサイト](https://www.alchemy.com/eth)をご覧ください。 アップデートを見逃したくないですか? [こちら](https://www.alchemy.com/newsletter)でニュースレターに登録してください! 私たちの[Discord](https://discord.gg/u72VCg3)にもぜひご参加ください。** diff --git a/public/content/translations/ja/developers/tutorials/how-to-implement-an-erc721-market/index.md b/public/content/translations/ja/developers/tutorials/how-to-implement-an-erc721-market/index.md index b77db2aa191..2ebda7147f7 100644 --- a/public/content/translations/ja/developers/tutorials/how-to-implement-an-erc721-market/index.md +++ b/public/content/translations/ja/developers/tutorials/how-to-implement-an-erc721-market/index.md @@ -1,12 +1,8 @@ --- -title: ERC-721マーケットを実装する方法 -description: 分散型のクラシファイドボード(掲示板)に、トークン化されたアイテムを出品する方法 +title: "ERC-721マーケットを実装する方法" +description: "分散型のクラシファイドボード(掲示板)に、トークン化されたアイテムを出品する方法" author: "Alberto Cuesta Cañada" -tags: - - "スマートコントラクト" - - "erc-721" - - "Solidity" - - "トークン" +tags: [ "スマート契約", "ERC-721", "Solidity", "トークン" ] skill: intermediate lang: ja published: 2020-03-19 @@ -22,17 +18,17 @@ Gumtree、Ebay、Craigslistといったインターネット掲示板が登場 ブロックチェーン技術は、この掲示板市場をさらに変革するものです。以下では、その方法について紹介します。 -## マネタイゼーション(収益化) {#monetization} +## 収益化 {#monetization} パブリックブロックチェーンにおける掲示板のビジネスモデルは、Ebayなどの企業のビジネスモデルとは異なります。 -まず第一に、 [脱中心化](/developers/docs/web2-vs-web3/)されている点を指摘すべきでしょう。 既存の掲示板プラットフォームは、自社サーバーを運用する必要があります。 しかし、分散型プラットフォームの運営はユーザーが実行するため、プラットフォーム所有者はコア・プラットフォームを稼働させるコストを負担する必要がありません。 +まず、[分散化という観点](/developers/docs/web2-vs-web3/)があります。 既存の掲示板プラットフォームは、自社サーバーを運用する必要があります。 しかし、分散型プラットフォームの運営はユーザーが実行するため、プラットフォーム所有者はコア・プラットフォームを稼働させるコストを負担する必要がありません。 -次に、プラットフォームにアクセスする機能を提供するウェブサイトまたはインターフェイスといったフロントエンドがあります。 フロントエンドには、いくつかのオプションがあります。 プラットフォーム所有者は、ユーザーのアクセス権限を制限し、インターフェイスを有料で使わせることができます。 一方で、プラットフォーム所有者がアクセス権限を開放し(人々に力を!)、ユーザーが自由にインターフェイスを開発できるようにすることもできます。 あるいは、これら2つの極端なアプローチの中間を選択することもできます。 +次に、プラットフォームにアクセスする機能を提供するウェブサイトまたはインターフェイスといったフロントエンドがあります。 フロントエンドには、いくつかのオプションがあります。 プラットフォーム所有者は、ユーザーのアクセス権限を制限し、インターフェイスを有料で使わせることができます。 プラットフォームの所有者は、アクセスを開放することも決定できます (Power to the People!) そして、誰でもプラットフォームへのインターフェースを構築できるようにします。 あるいは、これら2つの極端なアプローチの中間を選択することもできます。 -_私よりもビジネス感覚に優れている方は、これをどのように収益化すべきかについてよくご存じでしょう。 分散型のクラシファイドについて私が言えるのは、既存の掲示板プラットフォームとは異なっており、おそらく収益化が可能だということです。_ +_私よりも先見の明のあるビジネスリーダーは、これを収益化する方法を知っているでしょう。 私にわかるのは、これが現状とは異なり、おそらく利益になるだろうということだけです。_ -掲示板プラットフォームについては、自動化や支払い方法の視点からも考えることができます。 一部の商品は、[トークン化を非常に効果的に実行でき](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com) 、掲示板での取引に適しています。 トークン化されたアセットは、ブロックチェーン上で簡単に移転できます。 また、ブロックチェーンでは、非常に複雑な支払い方法も簡単に実装できます。 +掲示板プラットフォームについては、自動化や支払い方法の視点からも考えることができます。 一部のものは、非常に[効果的にトークン化](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com)され、クラシファイドボードで取引できます。 トークン化されたアセットは、ブロックチェーン上で簡単に移転できます。 また、ブロックチェーンでは、非常に複雑な支払い方法も簡単に実装できます。 私はここに、ビジネスチャンスがあると感じているわけです。 トランザクションごとに複雑な支払経路を設定しつつ、運用コストが発生しない掲示板を、簡単に実装できるからです。 私は、ブロックチェーン・コミュニティから必ず、これを活用するアイディアが生まれてくると考えています。 @@ -40,9 +36,9 @@ _私よりもビジネス感覚に優れている方は、これをどのよう ## 実装 {#implementation} -私たちは少し前に、具体的なビジネスケースに基づく実装やその他のリソースを提供する [オープンソースのリポジトリ](https://github.com/HQ20/contracts?ref=hackernoon.com)を立ち上げました。ぜひアクセスしてください。 +少し前に、ビジネスケースのサンプル実装やその他の特典を含む[オープンソースリポジトリ](https://github.com/HQ20/contracts?ref=hackernoon.com)を開始しました。ぜひご覧ください。 -この [イーサリアム・クラシファイド掲示板](https://github.com/HQ20/contracts/tree/master/contracts/classifieds?ref=hackernoon.com) のコードもレポジトリから入手できますので、自由に活用してください。 ただし、このコードは監査を経ていないため、お金を伴う取引を開始する前に、各自でデューデリジェンスを行う必要があります。 +この[Ethereumクラシファイドボード](https://github.com/HQ20/contracts/tree/master/contracts/classifieds?ref=hackernoon.com)のコードはそこにあります。どうぞご自由にお使いください。 ただし、このコードは監査を経ていないため、お金を伴う取引を開始する前に、各自でデューデリジェンスを行う必要があります。 掲示板における基本的な機能はシンプルなものです。 掲示板におけるすべての投稿は、いくつかのフィールドを含む構造体に過ぎません: @@ -67,9 +63,9 @@ mapping(uint256 => Trade) public trades; 次に、どのようなアイテムを扱う必要があり、取引の支払いにはどの通貨を使うかについて考える必要があります。 -取扱アイテムについては、[ERC-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol?ref=hackernoon.com)インターフェイスを実装すればよいです。このインターフェイスは、[デジタル資産と最も相性がよいのは事実ですが](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com)、現実に存在するアイテムをブロックチェーン上で表現するための手段に過ぎません。 この掲示板用の ERC-721コントラクトはコンストラクタで指定するため、掲示板に含まれるアセットは前もってトークン化しておく必要があります。 +アイテムについては、[ERC-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol?ref=hackernoon.com)インターフェースを実装することだけを求めます。これは現実世界のアイテムをブロックチェーンで表現する方法にすぎませんが、[デジタルアセットで最も効果的に機能します](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com)。 この掲示板用の ERC-721コントラクトはコンストラクタで指定するため、掲示板に含まれるアセットは前もってトークン化しておく必要があります。 -支払い機能についても、ほぼ同じです。 大部分のブロックチェーン・プロジェクトでは、独自の [ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol?ref=hackernoon.com)暗号通貨を定義しています。 DAIのように、著名な暗号通貨を採用する場合もあります。 このクラシファイド掲示板では、コンストラクタにおいて通貨を指定する必要があるだけです。 簡単ですね。 +支払い機能についても、ほぼ同じです。 ほとんどのブロックチェーンプロジェクトは、独自の[ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol?ref=hackernoon.com)暗号通貨を定義しています。 DAIのように、著名な暗号通貨を採用する場合もあります。 このクラシファイド掲示板では、コンストラクタにおいて通貨を指定する必要があるだけです。 簡単ですね。 ```solidity constructor ( @@ -127,18 +123,18 @@ function cancelTrade(uint256 _trade) Trade memory trade = trades[_trade]; require( msg.sender == trade.poster, - "Trade can be cancelled only by poster." + "取引は投稿者のみがキャンセルできます。" ); - require(trade.status == "Open", "Trade is not Open."); + require(trade.status == "Open", "取引はオープン状態ではありません。"); itemToken.transferFrom(address(this), trade.poster, trade.item); trades[_trade].status = "Cancelled"; emit TradeStatusChange(_trade, "Cancelled"); } ``` -以上です! これで、実装が完了しました。 この掲示板もそうですが、ビジネスコンセプトをコードで表現すると、驚くほど簡潔であることが少なくありません。 私たちのレポジトリから、[完成したコントラクト](https://github.com/HQ20/contracts/blob/master/contracts/classifieds/Classifieds.sol)を入手できます。 +以上です! これで、実装が完了しました。 この掲示板もそうですが、ビジネスコンセプトをコードで表現すると、驚くほど簡潔であることが少なくありません。 完全なコントラクトは[私たちのリポジトリ](https://github.com/HQ20/contracts/blob/master/contracts/classifieds/Classifieds.sol)で確認してください。 -## まとめ {#conclusion} +## 結論 {#conclusion} クラシファイド掲示板は、インターネットの普及に伴い爆発的に規模を拡大した一般的な市場構成であり、非常に人気のあるビジネスモデルを大手数社が独占している状態です。 @@ -146,4 +142,4 @@ function cancelTrade(uint256 _trade) この記事は、実際の掲示板ビジネスと、技術的な実装との間にあるギャップを埋めるための試みです。 この記事に記載された知識を基に、適切なスキルを持つユーザーであれば、掲示板ビジネスのビジョンと実装ロードマップを生み出すことができるでしょう。 -いつものように、面白いプロジェクトを進行中で助言が必要な場合は、[ぜひご連絡ください](https://albertocuesta.es/)! いつでも喜んでお手伝いします。 +いつものように、面白いプロジェクトを進行中で助言が必要な場合は、ぜひ[ご連絡ください](https://albertocuesta.es/)! いつでも喜んでお手伝いします。 diff --git a/public/content/translations/ja/developers/tutorials/how-to-mint-an-nft/index.md b/public/content/translations/ja/developers/tutorials/how-to-mint-an-nft/index.md index ef610e4373a..dab2badc296 100644 --- a/public/content/translations/ja/developers/tutorials/how-to-mint-an-nft/index.md +++ b/public/content/translations/ja/developers/tutorials/how-to-mint-an-nft/index.md @@ -1,28 +1,26 @@ --- -title: NFTのミント方法(NFTチュートリアルシリーズの2/3) -description: このチュートリアルでは、スマートコントラクトとWeb3を使用してEthereumブロックチェーン上でNFTをミントする方法を説明します。 +title: "NFTのミント方法(NFTチュートリアルシリーズの2/3)" +description: "このチュートリアルでは、スマートコントラクトとWeb3を使用してイーサリアムブロックチェーン上でNFTをミントする方法を説明します。" author: "Sumi Mudgil" -tags: - - "ERC-721" - - "alchemy" - - "Solidity" - - "スマートコントラクト" +tags: [ "ERC-721", "Alchemy", "Solidity", "スマート契約" ] skill: beginner lang: ja published: 2021-04-22 --- -[Beeple](https://www.nytimes.com/2021/03/11/arts/design/nft-auction-christies-beeple.html): $69,000,000 [3LAU](https://www.forbes.com/sites/abrambrown/2021/03/03/3lau-nft-nonfungible-tokens-justin-blau/?sh=5f72ef64643b): $11,000,000 [Grimes](https://www.theguardian.com/music/2021/mar/02/grimes-sells-digital-art-collection-non-fungible-tokens): $6,000,000 +[Beeple](https://www.nytimes.com/2021/03/11/arts/design/nft-auction-christies-beeple.html): 6900万ドル +[3LAU](https://www.forbes.com/sites/abrambrown/2021/03/03/3lau-nft-nonfungible-tokens-justin-blau/?sh=5f72ef64643b): 1100万ドル +[Grimes](https://www.theguardian.com/music/2021/mar/02/grimes-sells-digital-art-collection-non-fungible-tokens): 600万ドル -いずれもAlchemyの強力なAPIを使ってNFTをミントしています。 このチュートリアルでは、\<10分以内でNFTをミントする方法を説明します。 +いずれもAlchemyの強力なAPIを使ってNFTをミントしています。 このチュートリアルでは、\<10分で同じことを行う方法を説明します。 -「NFTのミント」とは、ブロックチェーン上にERC-721トークンのユニークなインスタンスを公開することです。 [NFTチュートリアルシリーズのパート1](/developers/tutorials/how-to-write-and-deploy-an-nft/)で作成したスマートコントラクトを使って、Web3のスキルを駆使し、NFTをミントしてみましょう。 このチュートリアルが終わるころには、あなた(とウォレット) の望むがままにNFTをミントできるようになります。 +「NFTのミント」とは、ブロックチェーン上にERC-721トークンのユニークなインスタンスを公開することです。 この[NFTチュートリアルシリーズのパート1](/developers/tutorials/how-to-write-and-deploy-an-nft/)で作成したスマートコントラクトを使って、Web3のスキルを駆使し、NFTをミントしてみましょう。 このチュートリアルが終わるころには、あなた(とウォレット)が望むだけNFTをミントできるようになります。 さあ、始めましょう。 ## ステップ1: Web3をインストールする {#install-web3} -NFTスマートコントラクトの作成に関する最初のチュートリアルに沿って進めている場合、すでにEthers.jsを使用していることと思います。 Web3はEthersと同様、イーサリアムブロックチェーンへのリクエストを簡単に作成するために使用されるライブラリです。 このチュートリアルでは、自動再試行と堅牢なWebSocketサポートを提供する拡張Web3ライブラリ、[Alchemy Web3](https://docs.alchemyapi.io/alchemy/documentation/alchemy-web3)を使用します。 +NFTスマートコントラクトの作成に関する最初のチュートリアルに沿って進めている場合、すでにEthers.jsを使用した経験があるはずです。 Web3はEthersと同様、イーサリアムブロックチェーンへのリクエストを簡単に作成するために使用されるライブラリです。 このチュートリアルでは、自動再試行と堅牢なWebSocketサポートを提供する拡張Web3ライブラリである[Alchemy Web3](https://docs.alchemyapi.io/alchemy/documentation/alchemy-web3)を使用します。 プロジェクトのホームディレクトリで以下を実行します。 @@ -32,7 +30,7 @@ npm install @alch/alchemy-web3 ## ステップ2: `mint-nft.js`ファイルを作成する {#create-mintnftjs} -scriptsディレクトリ内に`mint-nft.js`ファイルを作成し、以下のコード行を追加します。 +`scripts`ディレクトリ内に`mint-nft.js`ファイルを作成し、以下のコード行を追加してください。 ```js require("dotenv").config() @@ -43,19 +41,19 @@ const web3 = createAlchemyWeb3(API_URL) ## ステップ3: コントラクトABIを取得する {#contract-abi} -コントラクトABI(アプリケーションバイナリインターフェイス)は、スマートコントラクトと対話するためのインターフェイスです。 コントラクトABIの詳細については、[こちら](https://docs.alchemyapi.io/alchemy/guides/eth_getlogs#what-are-ab-is)をご覧ください。 Hardhatは自動的にABIを生成して、`MyNFT.json`ファイルに保存します。 このABIを使用するには、`mint-nft.js`に次のコードを追加して、ABIをパースする必要があります。 +コントラクトABI (Application Binary Interface) は、スマートコントラクトと対話するためのインターフェイスです。 コントラクトABIの詳細は[こちら](https://docs.alchemyapi.io/alchemy/guides/eth_getlogs#what-are-ab-is)をご覧ください。 Hardhatは自動的にABIを生成し、`MyNFT.json`ファイルに保存します。 これを使用するには、`mint-nft.js`ファイルに以下のコード行を追加してコンテンツを解析する必要があります。 ```js const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json") ``` -ABIを表示したい場合は、次のコードを追加することでコンソールに出力できます: +ABIを確認したい場合は、コンソールに出力できます: ```js console.log(JSON.stringify(contract.abi)) ``` -`mint-nft.js`を実行し、コンソールに出力されたABIを表示するには、ターミナルに移動して次のコードを実行します。 +`mint-nft.js`を実行し、コンソールに出力されたABIを確認するには、ターミナルに移動して以下を実行します。 ```js node scripts/mint-nft.js @@ -63,61 +61,61 @@ node scripts/mint-nft.js ## ステップ4: IPFSを使用してNFTのメタデータを設定する {#config-meta} -パート1のチュートリアルを思い出してください。スマートコントラクトの`mintNFT`関数は、NFTのメタデータを記述したJSONドキュメントで解決すべきtokenURIパラメータを取り込みます。その結果、NFTが生成され、名前、記述、画像、その他の属性などの設定可能なプロパティを実装することができます。 +パート1のチュートリアルで触れたように、`mintNFT`スマートコントラクト関数は、`tokenURI`パラメータを受け取ります。このパラメータは、NFTのメタデータを記述したJSONドキュメントに解決される必要があります。このメタデータこそがNFTに命を吹き込み、名前、説明、画像、その他の属性などの設定可能なプロパティを持たせることを可能にするのです。 -> _Interplanetary File System(IPFS)は、分散型ファイルシステムでデータを格納、共有するための分散プロトコルであり、ピアツーピアネットワークです。_ +> _Interplanetary File System (IPFS) は、分散ファイルシステムでデータを保存・共有するための、分散型プロトコルでありピアツーピアネットワークです。_ -私たちは、NFTアセットとメタデータを保存してNFTを真に分散化するために、便利なIPFS APIとツールキットであるPinataを使用します。 Pinataアカウントをお持ちでない場合は、[こちら](https://app.pinata.cloud)から無料アカウントにサインアップし、メールアドレスの認証手順を完了してください。 +NFTが真に分散化されるように、便利なIPFS APIおよびツールキットであるPinataを使用して、NFTのアセットとメタデータを保存します。 Pinataのアカウントをお持ちでない場合は、[こちら](https://app.pinata.cloud)から無料アカウントにサインアップし、メールアドレスの認証手続きを完了してください。 -アカウント作成後の手順: +アカウントを作成したら: -- 「Files」ページに移動し、ページの左上にある青色の「Upload」ボタンをクリックします。 +- 「Files」ページに移動し、ページの左上にある青い「Upload」ボタンをクリックします。 -- Pinataに画像をアップロードします。これがNFTの画像アセットになります。 アセットに好きな名前を付けてください。 +- 画像をPinataにアップロードします。これがあなたのNFTの画像アセットになります。 アセットには自由に名前を付けてかまいません。 -- アップロード後、「Files」ページのテーブルにファイル情報が表示されます。 また、CID列も表示されます。 隣のコピーボタンをクリックするとCIDをコピーできます。 アップロードは`https://gateway.pinata.cloud/ipfs/`で確認できます。 例えば、IPFSで使われている画像は、[こちら](https://gateway.pinata.cloud/ipfs/QmZdd5KYdCFApWn7eTZJ1qgJu18urJrP9Yh1TZcZrZxxB5)です。 +- アップロード後、「Files」ページのテーブルにファイル情報が表示されます。 CID列も表示されます。 隣にあるコピーボタンをクリックすると、CIDをコピーできます。 アップロードしたファイルは `https://gateway.pinata.cloud/ipfs/` で確認できます。 例えば、私たちが使用した画像はIPFS上の[こちら](https://gateway.pinata.cloud/ipfs/QmZdd5KYdCFApWn7eTZJ1qgJu18urJrP9Yh1TZcZrZxxB5)にあります。 -視覚型学習者のために、上記の手順を要約します。 +視覚的に学習したい方のために、上記の手順を以下にまとめます。 ![Pinataに画像をアップロードする方法](./instructionsPinata.gif) -次に、Pinataにもう1件ドキュメントをアップロードしますが、 その前にドキュメントを作成する必要があります。 +さて、Pinataにもう一つドキュメントをアップロードします。 しかしその前に、それを作成する必要があります。 -ルートディレクトリに`nft-metadata.json`というファイルを新規作成し、以下のjsonコードを追加してください。 +ルートディレクトリに `nft-metadata.json` という新しいファイルを作成し、以下のJSONコードを追加してください。 ```json { "attributes": [ { - "trait_type": "Breed", - "value": "Maltipoo" + "trait_type": "品種", + "value": "マルプー" }, { - "trait_type": "Eye color", - "value": "Mocha" + "trait_type": "目の色", + "value": "モカ" } ], - "description": "The world's most adorable and sensitive pup.", + "description": "世界で最も愛らしくて繊細な子犬です。", "image": "ipfs://QmWmvTJmJU3pozR9ZHFmQC2DNDwi2XJtf3QGyYiiagFSWb", - "name": "Ramses" + "name": "ラムセス" } ``` -json内のデータは自由に変更できます。 属性セクションを削除または追加できます。 最も重要なことは、画像フィールドがIPFS画像の位置を指していることを確認することです。そうしないと、NFTに(とても可愛い)犬の写真が含まれます。 +JSON内のデータは自由に変更してかまいません。 `attributes`セクションは削除したり、追加したりできます。 最も重要なのは、`image`フィールドがあなたのIPFS画像の場所を指していることを確認することです。さもなければ、あなたのNFTには(とてもかわいい!) 犬の写真が含まれてしまいます。 -JSONファイルの編集が終わったら、保存して、画像のアップロードと同じ手順でPinataにアップロードしてください。 +JSONファイルの編集が終わったら保存し、画像をアップロードしたのと同じ手順でPinataにアップロードしてください。 -![nft-metadata.jsonを Pinataにアップロードする方法](./uploadPinata.gif) +![nft-metadata.jsonをPinataにアップロードする方法](./uploadPinata.gif) ## ステップ5: コントラクトのインスタンスを作成する {#instance-contract} -ここで、私たちのコントラクトと対話するには、コード内でインスタンスを作成する必要があります インスタンスの作成には、コントラクトアドレスが必要になります。コントラクトをデプロイする際に使用したアドレスを検索することで、デプロイメントまたは[Etherscan](https://sepolia.etherscan.io/)から取得できます。 +さて、コントラクトとやりとりするには、コード内でそのインスタンスを作成する必要があります。 そのためにはコントラクトアドレスが必要ですが、これはデプロイメントから取得するか、コントラクトのデプロイに使用したアドレスを[Blockscout](https://eth-sepolia.blockscout.com/)で検索することで取得できます。 -![Etherscanでコントラクトアドレスを表示する](./view-contract-etherscan.png) +![Etherscanでコントラクトアドレスを表示](./view-contract-etherscan.png) -上記の例では、コントラクトのアドレスは、0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778です。 +上記の例では、コントラクトアドレスは`0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778`です。 -次に、Web3の[コントラクトメソッド](https://docs.web3js.org/api/web3-eth-contract/class/Contract)を活用して、ABIとアドレスを使用したコントラクトを作成します。 `mint-nft.js`ファイルに以下を追加します。 +次に、ABIとアドレスを使い、Web3の[contractメソッド](https://docs.web3js.org/api/web3-eth-contract/class/Contract)でコントラクトを作成します。 `mint-nft.js`ファイルに、以下を追加します。 ```js const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778" @@ -125,39 +123,39 @@ const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778" const nftContract = new web3.eth.Contract(contract.abi, contractAddress) ``` -## ステップ6: `.env`ファイルをアップデートする {#update-env} +## ステップ6: `.env`ファイルを更新する {#update-env} -それでは、イーサリアムチェーンにトランザクションを作成して送信するために、公開されているイーサリアムのアカウントアドレスを使用してアカウントのノンスを取得します(以下で説明します)。 +さて、イーサリアムチェーンにトランザクションを作成して送信するために、あなたの公開イーサリアムアカウントアドレスを使用してアカウントのノンス(後述)を取得します。 -`.env`ファイルにあなたの公開鍵を追加します。チュートリアルのパート1を完了している場合、`.env`ファイルは次のようになっているはずです。 +あなたの公開鍵を`.env`ファイルに追加してください。チュートリアルのパート1を完了している場合、`.env`ファイルは次のようになっているはずです: ```js -API_URL = "https://eth-sepolia.g.alchemy.com/v2/your-api-key" -PRIVATE_KEY = "your-private-account-address" -PUBLIC_KEY = "your-public-account-address" +API_URL = "https://eth-sepolia.g.alchemy.com/v2/あなたのAPIキー" +PRIVATE_KEY = "あなたのプライベートアカウントアドレス" +PUBLIC_KEY = "あなたのパブリックアカウントアドレス" ``` ## ステップ7: トランザクションを作成する {#create-txn} -まず、`mintNFT(tokenData)`という名前の関数を定義し、次のようにトランザクションを作成してみましょう。 +まず、`mintNFT(tokenData)`という名前の関数を定義し、以下のようにトランザクションを作成します。 -1. _PRIVATE_KEY_と_PUBLIC_KEY_を`.env`ファイルから取得します。 +1. `.env`ファイルから_PRIVATE_KEY_と_PUBLIC_KEY_を取得します。 -1. 次に、アカウントのノンスを確認します。 ノンスの指定は、あなたのアドレスから送信されたトランザクションの数を追跡するために使用されます。これは、セキュリティ目的で、[リプレイ攻撃](https://docs.alchemyapi.io/resources/blockchain-glossary#account-nonce)を防ぐために必要となります。 あなたのアドレスから送信されたトランザクションの数を取得するには、 [getTransactionCount](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc#eth_gettransactioncount)を使用します。 +2. 次に、アカウントのノンスを確認する必要があります。 ノンスの仕様は、あなたのアドレスから送信されたトランザクション数を追跡するために使用されます。これは、セキュリティ上の目的と、[リプレイ攻撃](https://docs.alchemyapi.io/resources/blockchain-glossary#account-nonce)を防ぐために必要です。 あなたのアドレスから送信されたトランザクション数を取得するには、[getTransactionCount](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc#eth_gettransactioncount)を使用します。 -1. 最後に、以下の情報を使用してトランザクションを設定します。 +3. 最後に、以下の情報でトランザクションをセットアップします。 -- `'from': PUBLIC_KEY` — トランザクションの発信元は公開アドレス +- `'from': PUBLIC_KEY` — トランザクションの発信元である、私たちの公開アドレス -- `'to': contractAddress` — 対話し、トランザクションを送信したいコントラクト +- `'to': contractAddress` — やりとりを行い、トランザクションを送信したいコントラクト -- `'nonce': nonce` — アドレスから送信されたトランザクションの数を持つアカウントのノンス +- `'nonce': nonce` — 私たちのアドレスから送信されたトランザクション数を含むアカウントのノンス -- `'gas': estimatedGas` —トランザクションを完了するために必要な推定ガス +- `'gas': estimatedGas` — トランザクションを完了するために必要な推定ガス量 -- `'data': nftContract.methods.mintNFT(PUBLIC_KEY, md).encodeABI()` — このトランザクションで実行したい計算。今回の場合は、NFTを発行すること。 +- `'data': nftContract.methods.mintNFT(PUBLIC_KEY, md).encodeABI()` — このトランザクションで実行したい計算、この場合はNFTのミント -`mint-nft.js`ファイルは、現在このような状態になっているはずです。 +`mint-nft.js`ファイルは次のようになっているはずです: ```js require('dotenv').config(); @@ -175,7 +173,7 @@ PUBLIC_KEY = "your-public-account-address" async function mintNFT(tokenURI) { const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce - //the transaction + //トランザクション const tx = { 'from': PUBLIC_KEY, 'to': contractAddress, @@ -188,9 +186,9 @@ PUBLIC_KEY = "your-public-account-address" ## ステップ8: トランザクションに署名する {#sign-txn} -さて、トランザクションを作成したら、それを送信するために署名する必要があります。 ここで秘密鍵を使用します。 +トランザクションを作成したので、次に送信するために署名する必要があります。 ここで秘密鍵を使用します。 -`web3.eth.sendSignedTransaction`はトランザクションハッシュを提供することで、トランザクションがきちんとマイニングされ、ネットワークによってドロップされていないことを確認できます。 トランザクション署名のセクションにいくつかエラーチェックを追加することで、トランザクションが正常に完了したかどうかを確認できるようにしておきます。 +`web3.eth.sendSignedTransaction`はトランザクションハッシュを返します。これを使用して、トランザクションがマイニングされ、ネットワークによってドロップされなかったことを確認できます。 トランザクション署名のセクションでは、トランザクションが正常に実行されたかどうかを知るために、エラーチェックを追加していることにお気づきでしょう。 ```js require("dotenv").config() @@ -208,7 +206,7 @@ const nftContract = new web3.eth.Contract(contract.abi, contractAddress) async function mintNFT(tokenURI) { const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //get latest nonce - //the transaction + //トランザクション const tx = { from: PUBLIC_KEY, to: contractAddress, @@ -225,13 +223,13 @@ async function mintNFT(tokenURI) { function (err, hash) { if (!err) { console.log( - "The hash of your transaction is: ", + "トランザクションのハッシュ: ", hash, - "\nCheck Alchemy's Mempool to view the status of your transaction!" + "\nAlchemyのメンプールでトランザクションのステータスを確認してください!" ) } else { console.log( - "Something went wrong when submitting your transaction:", + "トランザクションの送信中に問題が発生しました:", err ) } @@ -239,24 +237,24 @@ async function mintNFT(tokenURI) { ) }) .catch((err) => { - console.log(" Promise failed:", err) + console.log(" Promiseが失敗しました:", err) }) } ``` -## ステップ9: `mintNFT`を呼び出し、ノード`mint-nft.js`を実行する {#call-mintnft-fn} +## ステップ9: `mintNFT`を呼び出し、`node mint-nft.js`を実行する {#call-mintnft-fn} -Pinataにアップロードした`metadata.json`を覚えているでしょうか。 Pinataからそのハッシュコードを取得し、`https://gateway.pinata.cloud/ipfs/`をパラメータとして、関数`mintNFT`に渡します。 +Pinataにアップロードした`metadata.json`を覚えていますか? Pinataからそのハッシュコードを取得し、`https://gateway.pinata.cloud/ipfs/`をパラメータとして`mintNFT`関数に渡します。 -ハッシュコードを取得する方法をこちらにご紹介します。 +ハッシュコードを取得する方法は次のとおりです。 -![Pinataでnftメタデータハッシュコードを取得する方法](./metadataPinata.gif)_Pinataでnftメタデータハッシュコードを取得する方法_ +![PinataでNFTメタデータのハッシュコードを取得する方法](./metadataPinata.gif)_PinataでNFTメタデータのハッシュコードを取得する方法_ -> 別のウィンドウで`https://gateway.pinata.cloud/ipfs/`を読み込んでみて、コピーしたハッシュコードが**metadata.json**にリンクしているか確認します。 以下のスクリーンショットと類似したページが表示されるはずです。 +> 別のウィンドウで `https://gateway.pinata.cloud/ipfs/` を読み込み、コピーしたハッシュコードがあなたの**metadata.json**にリンクしていることを再確認してください。 ページは下のスクリーンショットのようになっているはずです。 -![ページにjsonメタデータが表示されるはずです](./metadataJSON.png)_ページにjsonメタデータが表示されるはずです_ +![ページにJSONメタデータが表示されるはずです](./metadataJSON.png)_ページにJSONメタデータが表示されるはずです_ -最終的には、次のようなコードになっているはずです。 +すべてをまとめると、コードは次のようになります。 ```js require("dotenv").config() @@ -274,7 +272,7 @@ const nftContract = new web3.eth.Contract(contract.abi, contractAddress) async function mintNFT(tokenURI) { const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //get latest nonce - //the transaction + //トランザクション const tx = { from: PUBLIC_KEY, to: contractAddress, @@ -291,13 +289,13 @@ async function mintNFT(tokenURI) { function (err, hash) { if (!err) { console.log( - "The hash of your transaction is: ", + "トランザクションのハッシュ: ", hash, - "\nCheck Alchemy's Mempool to view the status of your transaction!" + "\nAlchemyのメンプールでトランザクションのステータスを確認してください!" ) } else { console.log( - "Something went wrong when submitting your transaction:", + "トランザクションの送信中に問題が発生しました:", err ) } @@ -305,25 +303,27 @@ async function mintNFT(tokenURI) { ) }) .catch((err) => { - console.log("Promise failed:", err) + console.log("Promiseが失敗しました:", err) }) } mintNFT("ipfs://QmYueiuRNmL4MiA2GwtVMm6ZagknXnSpQnB3z2gWbz36hP") ``` -次に、`node scripts/mint-nft.js`を実行してNFTをデプロイします。 数秒後にターミナル上に次のようなレスポンスが表示されるはずです。 +さて、`node scripts/mint-nft.js`を実行して、NFTをミントしましょう。 数秒後、ターミナルに次のような応答が表示されるはずです。 - The hash of your transaction is: 0x301791fdf492001fcd9d5e5b12f3aa1bbbea9a88ed24993a8ab2cdae2d06e1e8 + ``` + トランザクションのハッシュ: 0x301791fdf492001fcd9d5e5b12f3aa1bbbea9a88ed24993a8ab2cdae2d06e1e8 - Alchemyのメンプールをチェックし、あなたのトランザクションのステータスを確認しましょう。 + Alchemyのメンプールでトランザクションのステータスを確認してください! + ``` -[Alchemy mempool](https://dashboard.alchemyapi.io/mempool)にアクセスして、トランザクションのステータス(保留中、マイニング中、ネットワークによってドロップされたかどうか)を確認します。 トランザクションが削除された場合は、 [Sepolia Etherscan](https://sepolia.etherscan.io/)を確認してトランザクションハッシュを検索することもできます。 +次に、[Alchemyメンプール](https://dashboard.alchemyapi.io/mempool)にアクセスして、トランザクションのステータス(ペンディング、マイニング済み、またはネットワークによるドロップ)を確認します。 トランザクションがドロップされた場合は、[Blockscout](https://eth-sepolia.blockscout.com/)でトランザクションハッシュを検索して確認することも役立ちます。 -![EtherscanでNFTトランザクションハッシュを表示する](./view-nft-etherscan.png)_EtherscanでNFTトランザクションハッシュを表示します_ +![EtherscanでNFTトランザクションハッシュを表示](./view-nft-etherscan.png)_EtherscanでNFTトランザクションハッシュを表示_ -以上で完了です。 イーサリアムブロックチェーン上にデプロイしてNFTをミントしました。 +以上です。 これで、イーサリアムブロックチェーン上でNFTをデプロイし、ミントしました -`mint-nft.js`を使えば、あなたの心(ウォレット)が望むだけ、NFTをミントすることができます。 NFTのメタデータを記述した新しいtokenURIを必ず渡してください(この作業を怠ると、異なるIDを備えた同一のものを大量に作成してしまいます)。 +`mint-nft.js`を使えば、あなた(とウォレット)が望むだけNFTをミントできます。 必ずNFTのメタデータを記述した新しい`tokenURI`を渡すようにしてください(そうしないと、IDが違うだけで同一のものを大量に作ってしまうことになります)。 -ウォレットにNFTを表示する方法については、[パート3: ウォレットにNFTを表示する方法](/developers/tutorials/how-to-view-nft-in-metamask/)をご覧ください。 +おそらく、ウォレットで自分のNFTを披露したいと思うでしょう。そのために、ぜひ[パート3: ウォレットでNFTを表示する方法](/developers/tutorials/how-to-view-nft-in-metamask/)もチェックしてください。 diff --git a/public/content/translations/ja/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md b/public/content/translations/ja/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md index af2a8e5f252..715d54d0ea2 100644 --- a/public/content/translations/ja/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md +++ b/public/content/translations/ja/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md @@ -1,30 +1,26 @@ --- -title: Solidity で、スマートコントラクトのテスト用モックアップを作成する方法 -description: テストでは、コントラクトのモックアップを使用すべき理由 +title: "Solidity で、スマートコントラクトのテスト用モックアップを作成する方法" +description: "テストでは、コントラクトのモックアップを使用すべき理由" author: Markus Waas lang: ja -tags: - - "Solidity" - - "スマートコントラクト" - - "テスト" - - "モック" +tags: [ "Solidity", "スマート契約", "テスト", "モックアップ作成" ] skill: intermediate published: 2020-05-02 source: soliditydeveloper.com sourceUrl: https://soliditydeveloper.com/mocking-contracts --- -[モック・オブジェクト](https://wikipedia.org/wiki/Mock_object) は、オブジェクト指向プログラミングにおいて一般的なデザインパターンです。 「モック」という言葉は、古フランス語で「からかう」という意味を持つ「mocquer」が由来で、「本物を模倣する」という意味に発展しました。これこそ、プログラミングにで「モックアップ」を作成する作業です。 あなたが作成したスマートコントラクトについて「からかう」必要はありませんが、「モックアップ」の作成はできる限り必ず行ってください。 後々、楽になります。 +「[モックオブジェクト](https://wikipedia.org/wiki/Mock_object)」は、オブジェクト指向プログラミングにおいて一般的なデザインパターンです。 「モック」という言葉は、古フランス語で「からかう」という意味を持つ「mocquer」が由来で、「本物を模倣する」という意味に発展しました。これこそ、プログラミングにで「モックアップ」を作成する作業です。 あなたが作成したスマートコントラクトについて「からかう」必要はありませんが、「モックアップ」の作成はできる限り必ず行ってください。 後々、楽になります。 -## コントラクトのモックアップを使った単体テスト {#unit-testing-contracts-with-mocks} +## モックを使ったコントラクトの単体テスト {#unit-testing-contracts-with-mocks} -コントラクトのモックアップを作成するとは、究極的に、オリジナルとほぼ同様に動作するものの、デベロッパが簡単に管理できる第2のバージョンを作成することです。 複雑なコントラクトを開発すると、[コントラクト全体の一部のみを単体テストする](/developers/docs/smart-contracts/testing/)必要が発生する場合が少なくありません。 問題となるのは、この一部分をテストする上で、非常に具体的なコントラクトの状態を再現する必要があるものの、再現するのが難しい場合です。 +コントラクトのモックアップを作成するとは、究極的に、オリジナルとほぼ同様に動作するものの、デベロッパが簡単に管理できる第2のバージョンを作成することです。 複雑なコントラクトを扱うことになると、[コントラクトのごく一部だけを単体テスト](/developers/docs/smart-contracts/testing/)したい場合がよくあります。 問題となるのは、この一部分をテストする上で、非常に具体的なコントラクトの状態を再現する必要があるものの、再現するのが難しい場合です。 テストに求められる状態を再現するために、テストを設定するための複雑なロジックを作成する代わりに、モックアップを作成すればよいのです。 コントラクトのモックアップは、継承を使って簡単に作成できます。 オリジナル版を継承したモックアップのコントラクトを作成するだけです。 モックアップでは、機能をオーバーライドすることができます。 この点については、具体例で見てみましょう。 -## 例:プライベートのERC-20コントラクト {#example-private-erc20} +## 例: Private ERC20 {#example-private-erc20} -ここでは、当初にプライベート期間が設定された ERC-20 コントラクトを使って説明します。 トークン所有者はプライベートユーザーを管理でき、当初トークンを受け取ることができるのはプライベートユーザーのみになります。 設定した時間が経過した後は、すべてのユーザーがトークンを使用できるようになります。 参考までに、この例では新しいOpenZeppelinコントラクトv3に含まれている[`_beforeTokenTransfer`](https://docs.openzeppelin.com/contracts/5.x/extending-contracts#using-hooks)フックを使用しています。 +ここでは、当初にプライベート期間が設定された ERC-20 コントラクトを使って説明します。 トークン所有者はプライベートユーザーを管理でき、当初トークンを受け取ることができるのはプライベートユーザーのみになります。 設定した時間が経過した後は、すべてのユーザーがトークンを使用できるようになります。 ちなみに、新しいOpenZeppelin contracts v3の[`_beforeTokenTransfer`](https://docs.openzeppelin.com/contracts/5.x/extending-contracts#using-hooks)フックを使用しています。 ```solidity pragma solidity ^0.6.0; @@ -90,17 +86,17 @@ contract PrivateERC20Mock is PrivateERC20 { - `PrivateERC20Mock.sol: TypeError: Overriding function is missing "override" specifier.` - `PrivateERC20.sol: TypeError: Trying to override non-virtual function. Did you forget to add "virtual"?.` -Solidityの新しいバージョン(0.6)を使用しているため、オーバーライド可能な関数については`virtual`のキーワードを追加して、オーバーライド関数をオーバーライドする必要があります。 このため、両方の`isPublic`関数にこのキーワードを追加します。 +新しいSolidityバージョン0.6を使用しているため、オーバーライドされる関数には`virtual`キーワードを、オーバーライドする関数には`override`を追加する必要があります。 では、両方の`isPublic`関数にそれらを追加しましょう。 -これで、単体テストで`PrivateERC20Mock`を使えるようになりました。 プライベート利用期間中の動作をテストしたい場合は、`setIsPublic(false)`を使用し、パブリック利用期間については`setIsPublic(true)`を使ってテストしてください。 もちろん、[time helpers](https://docs.openzeppelin.com/test-helpers/0.5/api#increase)を使って、対象時間を変更することもできます。 しかし、すでにモックアップの有用性について理解できたでしょう。単に時間を進めるよりも、モックアップを利用した方が望ましいシナリオがすぐに思い浮かぶはずです。 +これで、単体テストで代わりに`PrivateERC20Mock`を使用できます。 プライベート利用期間中の動作をテストしたい場合は`setIsPublic(false)`を、同様にパブリック利用期間のテストには`setIsPublic(true)`を使用します。 もちろん、この例では[タイムヘルパー](https://docs.openzeppelin.com/test-helpers/0.5/api#increase)を使って、時間も同様に変更することもできます。 しかし、すでにモックアップの有用性について理解できたでしょう。単に時間を進めるよりも、モックアップを利用した方が望ましいシナリオがすぐに思い浮かぶはずです。 -## 複数のコントラクトに対してモックアップを作成する場合 {#mocking-many-contracts} +## 多数のコントラクトのモック {#mocking-many-contracts} -モックアップごとに新たなコントラクトを作成するのは煩雑な作業ですね。 これを避けたい場合は、[MockContract](https://github.com/gnosis/mock-contract)ライブラリを参照してください。 このライブラリを用いることで、コントラクトの動作をその場でオーバーライドし、変更することができます。 ただしこのライブラリは、別のコントラクトに対する呼び出しのみに対応していますので、今回は使用できません。 +モックアップごとに新たなコントラクトを作成するのは煩雑な作業ですね。 これが気になる場合は、[MockContract](https://github.com/gnosis/mock-contract)ライブラリをご覧ください。 これにより、コントラクトの動作をその場でオーバーライドして変更できます。 ただしこのライブラリは、別のコントラクトに対する呼び出しのみに対応していますので、今回は使用できません。 -## モックアップは、その他にも利点があります {#mocking-can-be-even-more-powerful} +## モックはさらに強力になりうる {#mocking-can-be-even-more-powerful} モックアップ作成のメリットは、他にもあります。 -- 関数の追加:特定の関数をオーバーライドする機能だけでなく、関数を追加できることも有益です。 トークンを対象とする場合のよい例としては、`ミント`機能を追加することで、ユーザーが新規トークンを無料で手に入れられるようにすることができます。 +- 関数の追加:特定の関数をオーバーライドする機能だけでなく、関数を追加できることも有益です。 トークンの良い例として、追加の`mint`関数を持たせることで、どのユーザーでも新しいトークンを無料で取得できるようにすることが挙げられます。 - テストネットでの使用:Dapp と共に、テストネット上でコントラクトをデプロイ、テストする際は、モックアップの活用を検討すべきです。 本当に必要な場合を除き、関数をオーバーライドすることは避けるべきです。 結局のところ、実際のロジックをテストする必要があるからです。 しかし例えば、コントラクトのステートを、新たにデプロイする必要なしで開始時点にリセットするだけのリセット機能は、有益な場合があるでしょう。 言うまでもなく、メインネット用のコントラクトにはこのようなリセット機能を含めてはなりません。 diff --git a/public/content/translations/ja/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md b/public/content/translations/ja/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md index e481f540dfb..177724c76a1 100644 --- a/public/content/translations/ja/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md +++ b/public/content/translations/ja/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md @@ -1,17 +1,12 @@ --- -title: スマートコントラクトのテストにEchidnaを使用する方法 -description: Echidnaを使用して、スマートコントラクトを自動でテストする方法 +title: "Echidnaを使用してスマートコントラクトをテストする方法" +description: "Echidnaを使用してスマートコントラクトを自動テストする方法" author: "Trailofbits" lang: ja -tags: - - "Solidity" - - "スマートコントラクト" - - "セキュリティ" - - "テスト" - - "ファジング" +tags: [ "Solidity", "スマート契約", "セキュリティ", "テスト", "ファジング" ] skill: advanced published: 2020-04-10 -source: セキュアなコントラクトの構築 +source: Building secure contracts sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/echidna --- @@ -19,53 +14,53 @@ sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/progr Echidnaは、Dockerまたはコンパイル済みのバイナリを使用してインストールします。 -### DockerからEchidnaをインストールする {#echidna-through-docker} +### DockerによるEchidna {#echidna-through-docker} ```bash docker pull trailofbits/eth-security-toolbox docker run -it -v "$PWD":/home/training trailofbits/eth-security-toolbox ``` -_最後のコマンドは、現在のディレクトリにアクセスできるdockerでeth-security-toolboxを実行します。 ホストからファイルを変更し、dockerからファイル上のツールを実行できます。_ +_最後のコマンドは、現在のディレクトリにアクセスできるDockerでeth-security-toolboxを実行します。 ホストからファイルを変更し、Dockerからファイル上のツールを実行できます。_ -dockerで、以下を実行します: +Docker内で次を実行します: ```bash solc-select 0.5.11 cd /home/training ``` -### バイナリの入手先 {#binary} +### バイナリ {#binary} [https://github.com/crytic/echidna/releases/tag/v1.4.0.0](https://github.com/crytic/echidna/releases/tag/v1.4.0.0) -## プロパティベースのファジングとは {#introduction-to-property-based-fuzzing} +## プロパティベースファジング入門 {#introduction-to-property-based-fuzzing} -Echidnaは、プロパティベースのファザーです。これについては、以前のブログ投稿([1](https://blog.trailofbits.com/2018/03/09/echidna-a-smart-fuzzer-for-ethereum/)、[2](https://blog.trailofbits.com/2018/05/03/state-machine-testing-with-echidna/)、[3](https://blog.trailofbits.com/2020/03/30/an-echidna-for-all-seasons/))を参照してください。 +Echidnaはプロパティベースのファザーであり、以前のブログ投稿([1](https://blog.trailofbits.com/2018/03/09/echidna-a-smart-fuzzer-for-ethereum/)、[2](https://blog.trailofbits.com/2018/05/03/state-machine-testing-with-echidna/)、[3](https://blog.trailofbits.com/2020/03/30/an-echidna-for-all-seasons/))で説明しています。 ### ファジング {#fuzzing} -[ファジング ](https://wikipedia.org/wiki/Fuzzing)は、セキュリティコミュニティでよく知られているテクニックです。 ファジングでは、プログラム内のバグを見つけるために、大小のランダムな入力を生成することを行います。 通常のソフトウェアを対象とするファザー([AFL](http://lcamtuf.coredump.cx/afl/)や[LibFuzzer](https://llvm.org/docs/LibFuzzer.html)など)は、効率的にバグを特定できるツールであると評価されています。 +[ファジング](https://wikipedia.org/wiki/Fuzzing)は、セキュリティコミュニティでよく知られているテクニックです。 プログラム内のバグを見つけるために、ある程度ランダムな入力を生成するものです。 従来のソフトウェア用のファザー([AFL](http://lcamtuf.coredump.cx/afl/)や[LibFuzzer](https://llvm.org/docs/LibFuzzer.html)など)は、バグを見つけるための効率的なツールとして知られています。 -入力をまったくランダムに生成するだけでなく、適切な入力を生成するための多くのテクニックや戦略を活用できます: +純粋にランダムな入力生成以外に、適切な入力を生成するための多くのテクニックや戦略があります。以下に例を挙げます。 -- 各実行から取得したフィードバックに基づき、入力を生成する。 例えば、新しく生成された入力値が新しいパスの発見を導いている場合、それに近い新しい入力値を生成することは理にかなっています。 -- 構造上の制約を考慮した入力を生成する。 例えば、入力にチェックサム付のヘッダーが含まれている場合、ファザーにチェックサムを検証する入力を生成させることも有益です。 -- 既知の入力に基づいて新たな入力を生成する。大規模な有効な入力のデータセットにアクセスできる場合、まったくランダムに生成するのではなく、それらに基づいて新たな入力を生成することができます。 この場合、参照するデータを_シード_と呼びます。 +- 各実行からフィードバックを取得し、それを使用して生成をガイドします。 例えば、新しく生成された入力が新しいパスの発見につながる場合、それに近い新しい入力を生成することは理にかなっています。 +- 構造的制約を尊重して入力を生成する。 例えば、入力にチェックサム付きのヘッダーが含まれている場合、ファザーにチェックサムを検証する入力を生成させるのは理にかなっています。 +- 既知の入力を使用して新しい入力を生成する:有効な入力の大規模なデータセットにアクセスできる場合、ファザーはゼロから生成を開始するのではなく、それらから新しい入力を生成できます。 これらは通常、_シード_と呼ばれます。 -### プロパティベースのファジング {#property-based-fuzzing} +### プロパティベースファジング {#property-based-fuzzing} -Echidnaは、プロパティに基づくファジングを実行するファザーであり、[QuickCheck](https://wikipedia.org/wiki/QuickCheck)の影響を強く受けたプログラムです。 クラッシュを監視する従来のファザーとは異なり、Echidnaでは、ユーザー定義の不変条件を壊そうとします。 +Echidnaは、[QuickCheck](https://wikipedia.org/wiki/QuickCheck)に強くインスパイアされたプロパティベースファジングという、ファザーの特定の種類に属します。 クラッシュを見つけようとする従来のファザーとは対照的に、Echidnaはユーザー定義の不変条件を破ろうとします。 -スマートコントラクトにおける不変条件とは、コントラクトにおいて不適切または無効な状態が発生しうるSolidityの関数を意味します。具体的には、以下が挙げられます: +スマートコントラクトにおける不変条件とは、コントラクトが到達しうる不正または無効な状態を表すSolidity関数であり、次のようなものが含まれます。 -- 不適切なアクセス制御:攻撃者がコントラクトの所有者になる場合。 -- 不適切な状態マシン:コントラクトの一次停止中に、トークンを送信できる。 -- 不適切な計算: ユーザーは残高をアンダーフローし無制限に無料トークンを取得できる。 +- 不正なアクセス制御:攻撃者がコントラクトの所有者になる。 +- 不正な状態マシン:コントラクトが一時停止している間にトークンを転送できる。 +- 不正な算術演算:ユーザーが残高をアンダーフローさせ、無制限の無料トークンを取得できる。 -### Echidnaを使って、プロパティをテストする {#testing-a-property-with-echidna} +### Echidnaでプロパティをテストする {#testing-a-property-with-echidna} -それでは、Echidnaを使ってスマートコントラクトをテストする方法を見てみましょう。 対象は、[ `token.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/token.sol)のスマートコントラクトです。 +Echidnaを使ってスマートコントラクトをテストする方法を見ていきましょう。 対象は、次のスマートコントラクト[`token.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/token.sol)です。 ```solidity contract Token{ @@ -83,26 +78,26 @@ contract Token{ } ``` -このトークンは、以下のプロパティを持つと想定します: +このトークンは、以下のプロパティを持つと想定します。 -- ユーザーは、最大1000トークンを所持できる -- このトークン(ERC-20トークンではない)は、送信不可である +- 誰でも最大1000トークンを保有できます +- このトークンは転送できません(ERC20トークンではありません) ### プロパティを記述する {#write-a-property} -Echidnaのプロパティは、Solidityの関数です。 プロパティは、以下の条件を満たす必要があります: +Echidnaのプロパティは、Solidityの関数です。 プロパティは、以下の条件を満たす必要があります。 - 引数を持たない -- 実行に成功した場合、 `true` を返す -- `echidna`で始まる名前を持つ +- 成功した場合に`true`を返す +- 名前が`echidna`で始まる -Echidnaは、以下を実行します: +Echidnaは、以下を実行します。 -- このプロパティをテストするためのランダムなトランザクションを自動で生成する。 -- プロパティが `false`またはエラーを返すすべてのトランザクションを報告する。 -- プロパティの呼び出しに伴う副作用を無視する(つまり、プロパティが状態変数を変更した場合、テスト後にこの変更を破棄する) +- プロパティをテストするために、任意のトランザクションを自動的に生成します。 +- プロパティが`false`を返すか、エラーをスローするトランザクションを報告します。 +- プロパティ呼び出し時の副作用を破棄する(つまり、プロパティが状態変数を変更した場合、テスト後にその変更は破棄されます) -以下のプロパティは、呼び出し元のユーザーが所持するトークンが1000以下であることを確認します。 +以下のプロパティは、呼び出し元が1000トークンを超えて保有していないことをチェックします。 ```solidity function echidna_balance_under_1000() public view returns(bool){ @@ -120,36 +115,36 @@ contract TestToken is Token{ } ``` -[`token.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/token.sol)は、プロパティを実装し、このトークンを継承します。 +[`token.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/token.sol)はプロパティを実装し、トークンを継承します。 -### コントラクトを開始する {#initiate-a-contract} +### コントラクトの初期化 {#initiate-a-contract} -Echidnaでは、引数なしの[コンストラクタ](/developers/docs/smart-contracts/anatomy/#constructor-functions)が必要です。 コントラクトにおいて特定の初期化が必要な場合、コンストラクタ上で実行する必要があります。 +Echidnaには、引数のない[コンストラクタ](/developers/docs/smart-contracts/anatomy/#constructor-functions)が必要です。 コントラクトに特定の初期化が必要な場合、コンストラクタで行う必要があります。 -Echidnaには、いくつかの特定のアドレスが含まれます: +Echidnaには、いくつかの特定のアドレスが含まれます。 -- `0x00a329c0648769A73afAc7F9381E08FB43dBEA72`:コンストラクタを呼び出すアドレスです。 -- `0x10000`、`0x20000`、`0x00a329C0648769a73afAC7F9381e08fb43DBEA70`:他の関数をランダムに呼び出すアドレスです。 +- `0x00a329c0648769A73afAc7F9381E08FB43dBEA72`はコンストラクタを呼び出します。 +- `0x10000`、`0x20000`、および`0x00a329C0648769a73afAC7F9381e08fb43DBEA70`は、他の関数をランダムに呼び出します。 -このチュートリアルでは特定の初期化を実行する必要がないため、コンストラクタは空になります。 +この例では特定の初期化は必要ないため、コンストラクタは空になります。 -### Echidnaを実行する {#run-echidna} +### Echidnaの実行 {#run-echidna} -以下のコードで、Echidnaを起動します: +Echidnaは次のように起動します。 ```bash echidna-test contract.sol ``` -contract.solに複数のコントラクトが含まれる場合、実行したいコントラクトを指定できます: +contract.solに複数のコントラクトが含まれる場合、対象を指定できます。 ```bash echidna-test contract.sol --contract MyContract ``` -### プロパティテストのまとめ {#summary-testing-a-property} +### まとめ:プロパティのテスト {#summary-testing-a-property} -以下は、このチュートリアルにおけるEchidnaの実行をまとめたものです。 +以下は、この例におけるEchidnaの実行をまとめたものです。 ```solidity contract TestToken is Token{ @@ -172,11 +167,12 @@ echidna_balance_under_1000: failed!💥 ... ``` -Echidnaは、 `backdoor`が呼び出された場合、このプロパティが侵害されることを確認しました。 +Echidnaは、`backdoor`が呼び出された場合にプロパティが違反することを発見しました。 -## ファジング中に呼び出す関数を絞り込む {#filtering-functions-to-call-during-a-fuzzing-campaign} +## ファジングキャンペーン中に呼び出す関数をフィルタリングする {#filtering-functions-to-call-during-a-fuzzing-campaign} -ファジングの対象となる関数を絞り込む方法を見ていきましょう。 以下のスマートコントラクトを対象とします: +ファジング対象となる関数をフィルタリングする方法を見ていきましょう。 +対象は、次のスマートコントラクトです。 ```solidity contract C { @@ -227,7 +223,9 @@ contract C { } ``` -この簡単な例は、状態変数を変更する特定のトランザクションのシーケンスをEchidnaに見つけさせるものです。 これは、ファザーにとって容易ではありません([Manticore](https://github.com/trailofbits/manticore)のようなシンボリック実行ツールを使用することをお勧めします)。 Echidnaで、以下のように検証を実行します: +この簡単な例は、状態変数を変更する特定のトランザクションのシーケンスをEchidnaに見つけさせるものです。 +これはファザーにとって困難です([Manticore](https://github.com/trailofbits/manticore)のようなシンボリック実行ツールの使用が推奨されます)。 +Echidnaを実行して、これを確認できます。 ```bash echidna-test multi.sol @@ -236,30 +234,32 @@ echidna_state4: passed! 🎉 Seed: -3684648582249875403 ``` -### 対象の関数を絞り込む {#filtering-functions} +### 関数のフィルタリング {#filtering-functions} -2つのリセット関数(`reset1`と`reset2`)がすべての状態変数を`false`に設定するため、Echidnaはこのコントラクトをテストするための正しいシーケンスを見つけられません。 しかしEchidnaでは、リセット関数をブラックリストに含めるか、 `f`、`g`、 `h`、および `i`の関数のみをホワイトリストに含める特別の機能が利用できます。 +2つのリセット関数(`reset1`と`reset2`)がすべての状態変数を`false`に設定するため、Echidnaがこのコントラクトをテストするための正しいシーケンスを見つけるのは困難です。 +しかし、Echidnaの特別な機能を使用して、リセット関数をブラックリストに登録するか、`f`、`g`、 +`h`、`i`関数のみをホワイトリストに登録することができます。 -関数をブラックリストに登録するには、設定ファイルを以下のように指定します: +関数をブラックリストに登録するには、この設定ファイルを使用します。 ```yaml filterBlacklist: true filterFunctions: ["reset1", "reset2"] ``` -関数を絞り込むもう一つの方法は、ホワイトリストに含まれる関数を列挙することです。 これには、設定ファイルを以下のように指定します: +関数をフィルタリングするもう1つのアプローチは、ホワイトリストに登録された関数をリストアップすることです。 そのためには、この設定ファイルを使用します。 ```yaml filterBlacklist: false filterFunctions: ["f", "g", "h", "i"] ``` -- `filterBlacklist`の初期値は`true`です。 -- 絞り込みは、名前のみ(パラメータなし)で実行されます。 `f()`と`f(uint256)`の両方が含まれる場合、`"f"`で絞り込むと両方の関数がヒットします。 +- `filterBlacklist`のデフォルト値は`true`です。 +- フィルタリングは、名前のみ(パラメータなし)で実行されます。 `f()`と`f(uint256)`の両方がある場合、フィルタ`"f"`は両方の関数にマッチします。 -### Echidnaを実行する {#run-echidna-1} +### Echidnaの実行 {#run-echidna-1} -設定ファイル `blacklist.yaml` に従ってEchidnaを実行するには、以下のようにします: +Echidnaを構成ファイル`blacklist.yaml`で実行するには、次のようにします。 ```bash echidna-test multi.sol --config blacklist.yaml @@ -272,11 +272,11 @@ echidna_state4: failed!💥 i() ``` -Echidnaは、プロパティをfalseにするトランザクションのシーケンスを瞬時に特定します。 +Echidnaは、プロパティを偽にするトランザクションのシーケンスをほぼ瞬時に見つけます。 -### 対象の関数を絞り込む作業のまとめ {#summary-filtering-functions} +### まとめ:関数のフィルタリング {#summary-filtering-functions} -Echidnaでは、ファジングで呼び出す機能を絞り込むために、ブラックリストあるいはホワイトリストの関数を使用します: +Echidnaは、ファジングキャンペーン中に呼び出す関数を、以下を使用してブラックリストまたはホワイトリストに登録できます。 ```yaml filterBlacklist: true @@ -288,11 +288,11 @@ echidna-test contract.sol --config config.yaml ... ``` -Echidnaは、`filterBlacklist`のブール値に基づき、`f1`、`f2`、および `f3`の関数をブラックリストに含めるか、これらの関数のみを呼び出してファジングを実行します。 +Echidnaは、`filterBlacklist`ブール値の値に応じて、`f1`、`f2`、`f3`をブラックリストに登録するか、これらのみを呼び出すファジングキャンペーンを開始します。 ## EchidnaでSolidityのアサーションをテストする方法 {#how-to-test-soliditys-assert-with-echidna} -次の短いチュートリアルでは、Echidnaを使って、コントラクトに含まれるアサーションをテストします。 以下のようなコントラクトを想定します: +この短いチュートリアルでは、Echidnaを使用してコントラクトのアサーションチェックをテストする方法を説明します。 以下のようなコントラクトを想定します。 ```solidity contract Incrementor { @@ -309,7 +309,7 @@ contract Incrementor { ### アサーションを記述する {#write-an-assertion} -引き算を実行した後に、`tmp`の値が`counter`の値以下であることを確認したいとします。 Echidnaのプロパティで記述することもできますが、`tmp`値をどこかに格納する必要があります。 これには、以下のようなアサーションを用いることができます: +その差を返した後に、`tmp`が`counter`以下であることを確認したいと思います。 Echidnaのプロパティを記述することもできますが、`tmp`の値をどこかに保存する必要があります。 代わりに、このようなアサーションを使用できます。 ```solidity contract Incrementor { @@ -324,15 +324,15 @@ contract Incrementor { } ``` -### Echidnaを実行する {#run-echidna-2} +### Echidnaの実行 {#run-echidna-2} -アサーションの失敗をテストできるようにするには、[Echidnaの設定ファイル](https://github.com/crytic/echidna/wiki/Config)として `config.yaml` を作成します: +アサーション失敗テストを有効にするには、[Echidna設定ファイル](https://github.com/crytic/echidna/wiki/Config) `config.yaml`を作成します。 ```yaml checkAsserts: true ``` -このコントラクトをEchidnaで実行すると、次のような期待通りの結果が得られます: +このコントラクトをEchidnaで実行すると、期待通りの結果が得られます。 ```bash echidna-test assert.sol --config config.yaml @@ -346,11 +346,11 @@ assertion in inc: failed!💥 Seed: 1806480648350826486 ``` -このように、Echidnaでは、アサーション違反の一部を`inc`関数で報告します。 1つの関数に対し複数のアサーションを含めることは可能ですが、どのアサーションが失敗したのか区別できなくなります。 +ご覧のとおり、Echidnaは`inc`関数でのアサーションの失敗を報告します。 1つの関数に複数のアサーションを追加することは可能ですが、Echidnaはどのアサーションが失敗したかを特定できません。 -### アサーションをいつ、どのように使用すべきか {#when-and-how-use-assertions} +### アサーションを使用する状況とその方法 {#when-and-how-use-assertions} -アサーションは、特にチェックしたい条件が`f`という特定の操作を適切に用いることと直接関係する場合に、プロパティを明示しない代替手段として用いることができます。 コードにアサーションを追加することで、このコードを実行した直後に強制的にチェックが実行されます。 +アサーションは、特にチェック対象の条件が何らかの操作`f`の正しい使用に直接関連する場合、明示的なプロパティの代替として使用できます。 コードの後にアサーションを追加すると、そのコードが実行された直後にチェックが強制的に行われます。 ```solidity function f(..) public { @@ -362,7 +362,7 @@ function f(..) public { ``` -反対に、Echidnaのプロパティを明示的に使用する場合、トランザクションがランダムに実行されるため、チェックをどの時点で強制的に実行させるかを決定しにくくなります。 この場合、以下のような回避策を用いることもできます: +対照的に、明示的なEchidnaプロパティを使用すると、トランザクションがランダムに実行され、いつチェックされるかを正確に強制する簡単な方法はありません。 この回避策を行うことは依然として可能です。 ```solidity function echidna_assert_after_f() public returns (bool) { @@ -371,22 +371,22 @@ function echidna_assert_after_f() public returns (bool) { } ``` -しかし、以下の問題点が残ります: +しかし、いくつかの問題があります。 -- `f`が`internal`あるいは`external`と宣言されている場合、違反になる。 -- どの引数を使って`f`を呼び出すべきかが不明確である。 -- `f`が元に戻された場合、このプロパティは違反になる。 +- `f`が`internal`または`external`として宣言されている場合は失敗します。 +- `f`を呼び出すのにどの引数を使用すべきかが不明確です。 +- `f`がリバートされると、プロパティは失敗します。 -全般的なアサーションの使用については、この[John Regehrの提案](https://blog.regehr.org/archives/1091)に従うことを推奨します: +一般に、アサーションの使用方法については、[John Regehr氏の推奨事項](https://blog.regehr.org/archives/1091)に従うことをお勧めします。 -- アサーションチェック中には、副作用を強制しない。 例:`assert(ChangeStateAndReturn() == 1)` -- 明らかなステートメントは、アサートしない。 例:`var`を`uint`と宣言している場合、`assert(var >= 0)`は必要ない。 +- アサーションチェック中に副作用を強制しないでください。 例:`assert(ChangeStateAndReturn() == 1)` +- 明らかなステートメントをアサートしないでください。 例えば、`var`が`uint`として宣言されている場合の`assert(var >= 0)`などです。 -最後に、`assert`の代わりに`require`を用いるのは**避けてください**。Echidnaではrequireを検出できません。(ただしこの場合でも、コントラクトは元に戻されます)。 +最後に、`assert`の代わりに`require`を**使用しないでください**。Echidnaはそれを検出できません(ただし、コントラクトはいずれにしてもリバートします)。 -### アサーションチェックのまとめ {#summary-assertion-checking} +### まとめ:アサーションチェック {#summary-assertion-checking} -以下は、この例におけるEchidnaの実行をまとめたものです: +以下は、この例におけるEchidnaの実行をまとめたものです。 ```solidity contract Incrementor { @@ -413,11 +413,11 @@ assertion in inc: failed!💥 Seed: 1806480648350826486 ``` -Echidnaは、`inc`のアサーションにつき、この関数が大きな引数で複数回呼び出された場合に違反となりうることを発見しました。 +Echidnaは、`inc`のアサーションが、この関数が大きな引数で複数回呼び出された場合に失敗する可能性があることを見つけました。 -## Echidnaコーパスを収集、修正する {#collecting-and-modifying-an-echidna-corpus} +## Echidnaコーパスの収集と変更 {#collecting-and-modifying-an-echidna-corpus} -次に、Echidnaを使ってトランザクションのコーパスを収集し、これを利用する方法について見ていきましょう。 対象は、[`magic.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/magic.sol)のスマートコントラクトです。 +Echidnaを使ってトランザクションのコーパスを収集し、利用する方法について見ていきましょう。 対象は、次のスマートコントラクト[`magic.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/magic.sol)です。 ```solidity contract C { @@ -437,7 +437,9 @@ contract C { } ``` -以下の短いコードは、状態変数を変更する特定の値をEchidnaに発見させます。 これは、ファザーにとって容易ではありません([Manticore](https://github.com/trailofbits/manticore)のようなシンボリック実行ツールを使用することをお勧めします)。 Echidnaで、以下のように検証を実行します: +この簡単な例では、状態変数を変更するために、Echidnaに特定の値を探索させます。 これはファザーにとって困難です +([Manticore](https://github.com/trailofbits/manticore)のようなシンボリック実行ツールの使用が推奨されます)。 +Echidnaを実行して、これを確認できます。 ```bash echidna-test magic.sol @@ -448,30 +450,31 @@ echidna_magic_values: passed! 🎉 Seed: 2221503356319272685 ``` -ただしEchidnaでは、ファジングの実行中もコーパスを収集することができます。 +しかし、このファジングキャンペーンの実行中にEchidnaを使用してコーパスを収集することは依然として可能です。 -### コーパスを収集する {#collecting-a-corpus} +### コーパスの収集 {#collecting-a-corpus} -コーパスを収集するには、まずコーパスのディレクトリを作成します: +コーパス収集を有効にするには、コーパスディレクトリを作成します。 ```bash mkdir corpus-magic ``` -さらに、[Echidnaの設定ファイル](https://github.com/crytic/echidna/wiki/Config)である `config.yaml` を作成します: +そして、[Echidna設定ファイル](https://github.com/crytic/echidna/wiki/Config)である`config.yaml`を作成します。 ```yaml coverage: true corpusDir: "corpus-magic" ``` -これで、ツールを実行しながら収集したコーパスをチェックできるようになりました: +これでツールを実行し、収集したコーパスを確認できます。 ```bash echidna-test magic.sol --config config.yaml ``` -この段階ではEchidnaはまだ適切なmagic値を特定できませんが、収集したコーパスを確認することはできます。 例えば、以下のようなファイルが収集されました: +Echidnaはまだ正しいマジック値を見つけることができませんが、収集したコーパスを見ることができます。 +例えば、これらのファイルの1つは次のとおりでした。 ```json [ @@ -516,17 +519,18 @@ echidna-test magic.sol --config config.yaml ] ``` -言うまでもなく、この入力はプロパティ違反をトリガーしません。 しかし、次のステップでこれを修正することができます。 +明らかに、この入力はプロパティの失敗をトリガーしません。 しかし、次のステップでは、そのためにこれを変更する方法を見ていきます。 -### コーパスをシードする {#seeding-a-corpus} +### コーパスのシーディング {#seeding-a-corpus} -Echidnaを`magic`関数に対応するように設定する必要があります。 この入力が適切なパラメータを使用できるように、コピーし、変更します。 +`magic`関数を扱うために、Echidnaには少し助けが必要です。 入力をコピーして変更し、適切な +パラメータを使用するようにします。 ```bash cp corpus/2712688662897926208.txt corpus/new.txt ``` -`magic(42,129,333,0)`を呼び出せるように`new.txt`を変更します。 その上で、Echidnaを再実行します: +`new.txt`を`magic(42,129,333,0)`を呼び出すように変更します。 これでEchidnaを再実行できます。 ```bash echidna-test magic.sol --config config.yaml @@ -542,11 +546,11 @@ Seed: -7293830866560616537 ``` -今回は、プロパティ違反がただちに検出されました。 +今回は、プロパティが即座に違反していることがわかりました。 -## ガス消費量が多いトンラザクションを見つける {#finding-transactions-with-high-gas-consumption} +## ガス消費量の多いトランザクションの発見 {#finding-transactions-with-high-gas-consumption} -ガス消費量が多いトランザクションを特定するために、Echidnaを使用する方法について見ていきましょう。 対象は、次のスマートコントラクトです: +Echidnaを使用してガス消費量が多いトランザクションを見つける方法を見ていきましょう。 対象は、次のスマートコントラクトです。 ```solidity contract C { @@ -571,9 +575,10 @@ contract C { } ``` -ここでは、`expensive`でガス消費量が高くなる可能性があります。 +ここで`expensive`は大きなガス消費量を持つ可能性があります。 -この時点では、Echidna上で常にテストすべきプロパティを設定する必要があり、`echidna_test`は常に`true`を返します。 Echidnaを実行して、これを確認します: +現在、Echidnaは常にテスト対象のプロパティを必要とします。ここでは`echidna_test`が常に`true`を返します。 +Echidnaを実行して、これを確認できます。 ``` echidna-test gas.sol @@ -583,24 +588,24 @@ echidna_test: passed! 🎉 Seed: 2320549945714142710 ``` -### ガス消費量を測定する {#measuring-gas-consumption} +### ガス消費量の測定 {#measuring-gas-consumption} -Ethidnaでガスの消費量を確認できるようにするには、 `config.yaml`を以下のように設定します: +Echidnaでガス消費を有効にするには、設定ファイル`config.yaml`を作成します。 ```yaml estimateGas: true ``` -この例では、結果を分かりやすくするために、次のようにトランザクションシーケンスのサイズを減らしています。 +この例では、結果を理解しやすくするために、トランザクションシーケンスのサイズも小さくします。 ```yaml seqLen: 2 estimateGas: true ``` -### Echidnaを実行する {#run-echidna-3} +### Echidnaの実行 {#run-echidna-3} -設定が完了したら、次のようにEchidnaを実行します: +設定ファイルが作成されたら、次のようにEchidnaを実行できます。 ```bash echidna-test gas.sol --config config.yaml @@ -617,12 +622,13 @@ Seed: -325611019680165325 ``` -- 表示されたガス量は、[HEVM](https://github.com/dapphub/dapptools/tree/master/src/hevm#hevm-)による見積もりです。 +- 表示されるガスは、[HEVM](https://github.com/dapphub/dapptools/tree/master/src/hevm#hevm-)によって提供される推定値です。 -### ガス量を削減する呼び出しを対象外にする {#filtering-out-gas-reducing-calls} +### ガスを削減する呼び出しの除外 {#filtering-out-gas-reducing-calls} -**ファジング実行時に呼び出す関数を絞り込む方法**のチュートリアルでは、特定の関数をテストの対象外にする方法を示しました。 -この作業は、ガス量を正確に見積もる上で非常に重要です。 以下の例を検討してみましょう: +上記の「**ファジングキャンペーン中に呼び出す関数のフィルタリング**」のチュートリアルでは、テストからいくつかの関数を削除する方法を示しています。 +これは、正確なガス見積もりを得るために重要となる場合があります。 +次の例を考えてみましょう。 ```solidity contract C { @@ -648,7 +654,7 @@ contract C { } ``` -Echidnaがすべての関数を呼び出せる場合、ガス代が高いトランザクションを見つけることは困難になるでしょう。 +Echidnaがすべての関数を呼び出せる場合、ガス代が高いトランザクションを簡単に見つけることはできません。 ``` echidna-test pushpop.sol --config config.yaml @@ -662,7 +668,8 @@ clear used a maximum of 35916 gas push used a maximum of 40839 gas ``` -なぜかと言えば、ガス代は`addrs`のサイズに依存しており、ランダムに呼び出した場合は配列がほぼ空になるためです。 このような場合、 `pop`と`clear`をブラックリストに追加することで、より正確な結果を得ることができます。 +これは、コストが`addrs`のサイズに依存し、ランダムな呼び出しでは配列がほとんど空のままになる傾向があるためです。 +しかし、`pop`と`clear`をブラックリストに登録すると、はるかに良い結果が得られます。 ```yaml filterBlacklist: true @@ -677,9 +684,9 @@ push used a maximum of 40839 gas check used a maximum of 1484472 gas ``` -### ガス消費量が高いトランザクションを見つける作業のまとめ {#summary-finding-transactions-with-high-gas-consumption} +### まとめ:ガス消費量の多いトランザクションの発見 {#summary-finding-transactions-with-high-gas-consumption} -Echidnaでは、`estimateGas`の設定オプションを使用してガス消費量の多いトランザクションを特定することができます: +Echidnaは、`estimateGas`設定オプションを使用して、ガス消費量の多いトランザクションを見つけることができます。 ```yaml estimateGas: true @@ -690,4 +697,4 @@ echidna-test contract.sol --config config.yaml ... ``` -Echidnaは、ファジングを実行した後、各関数ごとにガス消費量が最大となるシーケンスを報告します。 +ファジングキャンペーンが終了すると、Echidnaは各関数の最大ガス消費量を持つシーケンスを報告します。 diff --git a/public/content/translations/ja/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md b/public/content/translations/ja/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md index baa9cab81c5..7e10233e51b 100644 --- a/public/content/translations/ja/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md +++ b/public/content/translations/ja/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md @@ -1,17 +1,12 @@ --- -title: Manticoreを使ってスマートコントラクトのバグを特定する方法 -description: Manticoreを使って、自動でスマートコントラクト上のバグを特定する +title: "Manticoreを使ってスマートコントラクトのバグを特定する方法" +description: "Manticoreを使って、自動でスマートコントラクト上のバグを特定する方法" author: Trailofbits lang: ja -tags: - - "Solidity" - - "スマートコントラクト" - - "セキュリティ" - - "テスト" - - "フォーマルな検証" +tags: [ "Solidity", "スマート契約", "セキュリティ", "テスト", "形式的検証" ] skill: advanced published: 2020-01-13 -source: セキュアなコントラクトの構築 +source: Building secure contracts sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore --- @@ -19,25 +14,25 @@ sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/progr ## インストール {#installation} -Manticoreを使用するには、Python 3.6 が必要です。 pipでインストールすることも、Dockerを使用してインストールすることもできます。 +Manticoreには、Python 3.6以上が必要です。 pipでインストールすることも、Dockerを使用してインストールすることもできます。 -### DockerでManticoreをインストールする場合 {#manticore-through-docker} +### Docker経由のManticore {#manticore-through-docker} ```bash docker pull trailofbits/eth-security-toolbox docker run -it -v "$PWD":/home/training trailofbits/eth-security-toolbox ``` -_最後のコマンドは、現在のディレクトリにアクセスできるdockerでeth-security-toolboxを実行します。 ホストからファイルを変更し、dockerからファイル上のツールを実行することができます。_ +_最後のコマンドは、現在のディレクトリにアクセスできるDockerでeth-security-toolboxを実行します。 ホストからファイルを変更し、Dockerからファイル上のツールを実行できます。_ -Dockerで、以下を実行します: +Docker内で、以下を実行します。 ```bash solc-select 0.5.11 cd /home/trufflecon/ ``` -### pipでManticoreをインストールする場合 {#manticore-through-pip} +### pip経由のManticore {#manticore-through-pip} ```bash pip3 install --user manticore @@ -45,9 +40,9 @@ pip3 install --user manticore solc 0.5.11を推奨します。 -### スクリプトを実行する {#running-a-script} +### スクリプトの実行 {#running-a-script} -Python 3では、以下のPythonスクリプトを実行します: +Python 3でPythonスクリプトを実行するには: ```bash python3 script.py @@ -55,60 +50,60 @@ python3 script.py ## 動的シンボリック実行の概要 {#introduction-to-dynamic-symbolic-execution} -### 動的シンボリック実行とは何か {#dynamic-symbolic-execution-in-a-nutshell} +### 動的シンボリック実行の簡単な説明 {#dynamic-symbolic-execution-in-a-nutshell} -動的シンボリック実行(DSE)は、高度な意味認識に基づき状態空間を探索するプログラム解析手法です。 この手法は、`path predicates`と呼ばれる数式で表される「プログラム・パス」を発見するものです。 概念的には、この手法は2つのステップによりパス述語を操作します: +動的シンボリック実行(DSE)は、高度な意味認識に基づき状態空間を探索するプログラム解析手法です。 この手法は、`パス述語`と呼ばれる数式で表される「プログラムパス」の発見に基づいています。 概念的には、この手法は2つのステップでパス述語を操作します: -1. プログラムの入力に対する制約を参照して、プログラム・パスを構築します。 -2. プログラム・パスは、関連パスを実行させるプログラムの入力を生成します。 +1. パス述語は、プログラムの入力に対する制約を使用して構築されます。 +2. パス述語は、関連するパスを実行させるプログラム入力を生成するために使用されます。 -このアプローチでは、具体値で実行する際に、特定されたすべてのプログラムの状態をトリガーしうるという意味で誤検出が発生しません。 例えば、解析において整数のオーバーフローが特定された場合、このオーバーフローは確実に再現可能です。 +このアプローチでは、特定されたすべてのプログラム状態が具体的な実行中にトリガーされうるという意味で、誤検出(偽陽性)は発生しません。 例えば、解析で整数オーバーフローが発見された場合、その再現性が保証されます。 -### パス述語の具体例 {#path-predicate-example} +### パス述語の例 {#path-predicate-example} -DSEの仕組みを理解するために、以下の例を考えてみましょう。 +DSEがどのように機能するかを理解するために、次の例を考えてみましょう。 ```solidity function f(uint a){ if (a == 65) { - // A bug is present + // バグが存在します } } ``` -`f()`には2つのパスが含まれているため、DSEにより、2つの異なるパス述語が構築されます。 +`f()`には2つのパスが含まれているため、DSEは2つの異なるパス述語を構築します。 -- 第1のパス: `a == 65` -- 第2のパス: `Not (a == 65)` +- パス1: `a == 65` +- パス2: `Not (a == 65)` -それぞれのパス述語は、いわゆる[SMTソルバー](https://wikipedia.org/wiki/Satisfiability_modulo_theories)に投入できる数式であり、SMTソルバーはこの等式を解決しようとします。 `第1のパス`では、ソルバーは、このパスが`a = 65`で探索可能だと返します。 `第2のパス`では、ソルバーは、`a`に対し、65以外の任意の値を与えることができます(例: `a = 0`)。 +各パス述語は、いわゆる[SMTソルバー](https://wikipedia.org/wiki/Satisfiability_modulo_theories)に与えることができる数式であり、ソルバーはその方程式を解こうとします。 `パス1`では、ソルバーは `a = 65` でパスを探索できると応答します。 `パス2`では、ソルバーは `a` に65以外の任意の値、例えば `a = 0` を与えることができます。 -### プロパティを検証する {#verifying-properties} +### プロパティの検証 {#verifying-properties} -Manticoreでは、各パスの実行全体を完全に制御できます。 このため、ほぼすべての事項に対して任意の制限を加えることができます。 この制御を通じて、コントラクトのプロパティを作成することができます。 +Manticoreでは、各パスのすべての実行を完全に制御できます。 その結果、ほとんどすべてのものに任意の制約を追加できます。 この制御により、コントラクトに関するプロパティを作成できます。 -次の例を考えてみましょう: +次の例を考えてみましょう。 ```solidity function unsafe_add(uint a, uint b) returns(uint c){ - c = a + b; // no overflow protection + c = a + b; // オーバーフロー保護なし return c; } ``` -この関数を探索するには、1つのパスしか存在しません。 +この関数には、探索するパスが1つしかありません。 -- パス1: `c = a + b` +- パス1: `c = a + b` -Manticoreを使用することで、オーバーフローの有無を確認し、パス述語に制限を加えられます。 +Manticoreを使用すると、オーバーフローをチェックし、パス述語に制約を追加できます。 - `c = a + b AND (c < a OR c < b)` -上記の述語パスが実行可能となる`a`および`b`の値を見つけられる場合、オーバーフローが特定できたことになります。 例えば、ソルバーは、`a = 10 , b = MAXUINT256`という入力を生成できます。 +上記のパス述語が実行可能になるような `a` と `b` の評価を見つけることができれば、オーバーフローを発見したことになります。 例えば、ソルバーは `a = 10 , b = MAXUINT256` という入力を生成できます。 -次に、上記を修正したコードを見てみましょう: +修正版を考えてみましょう。 ```solidity function safe_add(uint a, uint b) returns(uint c){ @@ -119,17 +114,17 @@ function safe_add(uint a, uint b) returns(uint c){ } ``` -オーバーフローを確認するために数式は、以下のようになるでしょう: +オーバーフローチェックに関連する数式は次のようになります。 -- `c = a + b AND (c >= a) AND (c=>b) AND (c < a OR c < b)` +- `c = a + b AND (c >= a) AND (c >= b) AND (c < a OR c < b)` -この数式は解くことができません。言い換えれば、`safe_add`において、`c`の値が常に増加することを**証明**しています。 +この数式は解けません。言い換えれば、これは `safe_add` において `c` が常に増加することの**証明**です。 -このように、DSEはコード上の任意の制限について検証できるパワフルなツールです。 +したがって、DSEはコード上の任意の制約を検証できる強力なツールです。 -## Manticoreで実行する {#running-under-manticore} +## Manticoreでの実行 {#running-under-manticore} -以下に、Manticore APIを用いて、スマートコントラクトを探索する方法を紹介します。 対象となるスマートコントラクトは、[`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol)です。 +ここでは、Manticore APIを使ってスマートコントラクトを探索する方法を見ていきます。 対象は、次のスマートコントラクト [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol) です。 ```solidity pragma solidity >=0.4.24 <0.6.0; @@ -143,15 +138,15 @@ contract Simple { } ``` -### スタンドアロンの探索を実行する {#run-a-standalone-exploration} +### スタンドアロン探索の実行 {#run-a-standalone-exploration} -以下のコマンドから、スマートコントラクト上で直接Manticoreを実行できます(`project`は、Solidityファイルでもプロジェクトディレクトリでも構いません)。 +次のコマンドで、スマートコントラクト上で直接Manticoreを実行できます(`project`はSolidityファイル、またはプロジェクトディレクトリです): ```bash $ manticore project ``` -実行すると、以下のようなテストケースが出力されます(順番は異なる可能性があります): +次のようなテストケースの出力が得られます(順序は変わる可能性があります): ``` ... @@ -166,39 +161,40 @@ $ manticore project ... ``` -Manticoreは、追加情報が提供されない限り、新規のシンボリック・トランザクションを使って、コントラクト上で新規パスが発見されるまでコントラクトを探索します。 Manticoreでは、トランザクションが失敗した場合(例:状態が元に戻された後)は、新規のトランザクションを実行しません。 +追加情報がない場合、Manticoreはコントラクト上で新しいパスを探索しなくなるまで、新しいシンボリック +トランザクションでコントラクトを探索します。 Manticoreは、失敗したトランザクションの後(例: revertの後)には、新しいトランザクションを実行しません。 -Manticoreでは、 `mcore_*`ディレクトリに情報を出力します。 このディレクトリには、以下が含まれます: +Manticoreは、`mcore_*`ディレクトリに情報を出力します。 このディレクトリには、とりわけ次のものが含まれます。 -- `global.summary`:カバレッジとコンパイラに関する警告 -- `test_XXXXX.summary`:テストケースごとのカバレッジ、最後の命令、およびアカウント残高 -- `test_XXXXX.tx`:テストケースごとの詳細なトランザクションリスト +- `global.summary`: カバレッジとコンパイラの警告 +- `test_XXXXX.summary`: カバレッジ、最後の命令、テストケースごとのアカウント残高 +- `test_XXXXX.tx`: テストケースごとのトランザクションの詳細リスト -この例では、以下に該当する7つのテストケースが特定されました(ファイル名の順番は異なるかもしれません): +ここでは、Manticoreは7つのテストケースを見つけます。これらは以下に対応します(ファイル名の順序は変わる可能性があります): -| | トランザクション 0 | トランザクション 1 | トランザクション 2 | 結果 | -|:--------------------:|:----------:|:----------:| ---------- |:------:| -| **test_00000000.tx** | コントラクトの作成 | f(!=65) | f(!=65) | STOP | -| **test_00000001.tx** | コントラクトの作成 | フォールバック関数 | | REVERT | -| **test_00000002.tx** | コントラクトの作成 | | | RETURN | -| **test_00000003.tx** | コントラクトの作成 | f(65) | | REVERT | -| **test_00000004.tx** | コントラクトの作成 | f(!=65) | | STOP | -| **test_00000005.tx** | コントラクトの作成 | f(!=65) | f(65) | REVERT | -| **test_00000006.tx** | コントラクトの作成 | f(!=65) | フォールバック関数 | REVERT | +| | トランザクション0 | トランザクション1 | トランザクション2 | 結果 | +| :-------------------------------------------------------: | :-------: | :------------------------: | -------------------------- | :----: | +| **test_00000000.tx** | コントラクト作成 | f(!=65) | f(!=65) | STOP | +| **test_00000001.tx** | コントラクト作成 | フォールバック関数 | | REVERT | +| **test_00000002.tx** | コントラクト作成 | | | RETURN | +| **test_00000003.tx** | コントラクト作成 | f(65) | | REVERT | +| **test_00000004.tx** | コントラクト作成 | f(!=65) | | STOP | +| **test_00000005.tx** | コントラクト作成 | f(!=65) | f(65) | REVERT | +| **test_00000006.tx** | コントラクト作成 | f(!=65) | フォールバック関数 | REVERT | _探索サマリーにおける「f(!=65)」は、f が 65 以外の値で呼び出されたことを意味します。_ -ご覧のように、Manticoreでは、すべての成功した/元に戻されたトランザクションにつき、固有のテストケースを生成します。 +お気づきのように、Manticoreは成功したトランザクションやrevertされたトランザクションごとに、一意のテストケースを生成します。 -高速な探索を行いたい場合は、`--quick-mode`フラグを使用してください(バグ検出、ガス代計算等が省略されます)。 +高速なコード探索を行いたい場合は `--quick-mode` フラグを使用してください(バグ検出器、ガス計算などを無効にします)。 -### Manticore APIを使ってスマートコントラクトを操作する {#manipulate-a-smart-contract-through-the-api} +### APIによるスマートコントラクトの操作 {#manipulate-a-smart-contract-through-the-api} -このセクションでは、Manticore Python APIを使ってスマートコントラクトを操作する方法について詳しく説明します。 Pythonの拡張子である`*.py`を持つ新規ファイルを作成し、APIコマンド(基本知識については以下で説明します)をファイルに追加して必要なコードを書いてから、`$ python3 *.py`コマンドで実行します。 また、Pythonのコンソールから直接コマンドを実行することもできます。コンソールを起動する際のコマンドは、`$ python3`です。 +このセクションでは、Manticore Python APIを介してスマートコントラクトを操作する方法について詳しく説明します。 Pythonの拡張子である\*.pyを持つ新規ファイルを作成し、APIコマンド(基本知識については以下で説明します)をファイルに追加して必要なコードを書いてから、$ python3 \*.pyコマンドで実行します。 また、Pythonのコンソールから直接コマンドを実行することもできます。コンソールを起動する際のコマンドは、$ python3です。 -### アカウントを作成する {#creating-accounts} +### アカウントの作成 {#creating-accounts} -まず、以下のコマンドを持つ新規のブロックチェーンを立ち上げる必要があります。 +最初に行うべきことは、次のコマンドで新しいブロックチェーンを開始することです。 ```python from manticore.ethereum import ManticoreEVM @@ -206,13 +202,13 @@ from manticore.ethereum import ManticoreEVM m = ManticoreEVM() ``` -[m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account)により、コントラクトではないアカウントが作成されます。 +非コントラクトアカウントは [m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account) を使用して作成されます。 ```python user_account = m.create_account(balance=1000) ``` -Solidityで作成したコントラクトについては、[m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract)でデプロイします。 +Solidityコントラクトは [m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract) を使用してデプロイできます。 ```solidity source_code = ''' @@ -225,24 +221,24 @@ contract Simple { } } ''' -# Initiate the contract +# コントラクトを初期化する contract_account = m.solidity_create_contract(source_code, owner=user_account) ``` #### まとめ {#summary} -- [m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account)と[m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract)を使用して、ユーザーアカウントとコントラクトアカウントを作成できます。 +- [m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account) と [m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract) でユーザーアカウントとコントラクトアカウントを作成できます。 -### トランザクションを実行する {#executing-transactions} +### トランザクションの実行 {#executing-transactions} -Manticoreは、2種類のトランザクションに対応しています: +Manticoreは2種類のトランザクションをサポートしています。 -- 生トランザクション:すべての関数を探索します。 -- 名前付きトランザクション:1つの関数だけを探索します。 +- Rawトランザクション: すべての関数が探索されます +- 名前付きトランザクション: 1つの関数のみが探索されます -#### 生トランザクション {#raw-transaction} +#### Rawトランザクション {#raw-transaction} -生トランザクションは、[m.transaction](https://manticore.readthedocs.io/en/latest/evm.html?highlight=transaction#manticore.ethereum.ManticoreEVM.transaction)で実行されます。 +Rawトランザクションは [m.transaction](https://manticore.readthedocs.io/en/latest/evm.html?highlight=transaction#manticore.ethereum.ManticoreEVM.transaction) を使用して実行されます。 ```python m.transaction(caller=user_account, @@ -251,12 +247,12 @@ m.transaction(caller=user_account, value=value) ``` -呼び出し元、アドレス、データ、トランザクションの値は、具体値あるいはシンボリック値のどちらでも構いません: +トランザクションの呼び出し元、アドレス、データ、または値は、具象値またはシンボリック値のいずれかです。 -- [m.make_symbolic_value](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_value#manticore.ethereum.ManticoreEVM.make_symbolic_value)は、シンボリック値を生成します。 -- [m.make_symbolic_buffer(size)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_buffer#manticore.ethereum.ManticoreEVM.make_symbolic_buffer)は、シンボリックなバイト配列を生成します。 +- [m.make_symbolic_value](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_value#manticore.ethereum.ManticoreEVM.make_symbolic_value) はシンボリック値を生成します。 +- [m.make_symbolic_buffer(size)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_buffer#manticore.ethereum.ManticoreEVM.make_symbolic_buffer) はシンボリックバイト配列を生成します。 -以下の例を確認してください: +以下の例をご覧ください: ```python symbolic_value = m.make_symbolic_value() @@ -267,40 +263,41 @@ m.transaction(caller=user_account, value=symbolic_value) ``` -データがシンボリック値の場合、Manticoreは、トランザクションの実行時にコントラクトに含まれるすべての関数を探索します。 関数がどのように選択されるかを理解するには、[Hands on the Ethernaut CTF](https://blog.trailofbits.com/2017/11/06/hands-on-the-ethernaut-ctf/)の記事におけるフォールバック関数についての説明を参照してください。 +データがシンボリックの場合、Manticoreはトランザクションの実行中にコントラクトのすべての関数を探索します。 関数の選択がどのように機能するかを理解するには、[Hands on the Ethernaut CTF](https://blog.trailofbits.com/2017/11/06/hands-on-the-ethernaut-ctf/)の記事にあるフォールバック関数の説明を参照すると役立ちます。 #### 名前付きトランザクション {#named-transaction} -関数は、名前から実行できます。 `f(uint var)`につき、シンボリック値を用いて、user_accountから、0 etherで実行する場合、以下を使用します: +関数は名前で実行できます。 +`f(uint var)` をシンボリック値で、user_accountから、0 etherで実行するには、次のようにします。 ```python symbolic_var = m.make_symbolic_value() contract_account.f(symbolic_var, caller=user_account, value=0) ``` -トランザクションの`value`を指定しない場合、デフォルトでは0になります。 +トランザクションの `value` が指定されていない場合、デフォルトで0になります。 #### まとめ {#summary-1} -- トランザクションの引数は、具体値またはシンボリック値のどちらでもよい。 -- 生トランザクションは、すべての関数を探索する。 -- 関数は、名前で呼び出すことができる。 +- トランザクションの引数は具象値またはシンボリック値にできます +- Rawトランザクションはすべての関数を探索します +- 関数は名前で呼び出すことができます ### ワークスペース {#workspace} -`m.workspace`は、生成されたすべてのファイルにおける出力ディレクトリとして使用されるディレクトリです。 +`m.workspace` は、生成されたすべてのファイルの出力ディレクトリとして使用されるディレクトリです。 ```python print("Results are in {}".format(m.workspace)) ``` -### 探索を終了する {#terminate-the-exploration} +### 探索の終了 {#terminate-the-exploration} -探索を停止するには、[m.finalize()](https://manticore.readthedocs.io/en/latest/evm.html?highlight=finalize#manticore.ethereum.ManticoreEVM.finalize)を使用します。 このメソッドが呼び出された時点で、さらにトランザクションは送信されなくなり、Manticoreは探索済みの各パスにつきテストケースを生成します。 +探索を停止するには [m.finalize()](https://manticore.readthedocs.io/en/latest/evm.html?highlight=finalize#manticore.ethereum.ManticoreEVM.finalize) を使用します。 このメソッドが呼び出されると、それ以上のトランザクションは送信されなくなり、Manticoreは探索された各パスのテストケースを生成します。 -### Manticoreを使った実行のまとめ {#summary-running-under-manticore} +### まとめ: Manticoreでの実行 {#summary-running-under-manticore} -上記のステップをまとめると、以下のようになります: +これまでのすべてのステップをまとめると、次のようになります。 ```python from manticore.ethereum import ManticoreEVM @@ -317,14 +314,14 @@ symbolic_var = m.make_symbolic_value() contract_account.f(symbolic_var) print("Results are in {}".format(m.workspace)) -m.finalize() # stop the exploration +m.finalize() # 探索を停止 ``` -紹介したすべてのコードは、[`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py)でアクセスできます。 +上記のすべてのコードは [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) にあります。 -## スローイングパスを取得する {#getting-throwing-paths} +## 例外をスローするパスの取得 {#getting-throwing-paths} -次に、 `f()`において例外を発生させるパスに対する、特定のインプットを生成します。 ここでも、対象となるスマートコントラクトは[`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol)です。 +次に、`f()`で例外を発生させるパスの特定の入力を生成します。 対象は、引き続き次のスマートコントラクト [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol) です。 ```solidity pragma solidity >=0.4.24 <0.6.0; @@ -337,26 +334,26 @@ contract Simple { } ``` -### 状態情報を使用する {#using-state-information} +### 状態情報の使用 {#using-state-information} -実行された各パスには、それぞれのブロックチェーンの状態が含まれています。 状態は、readyまたはkilledのどちらかです。killedは、THROWまたはREVERTに達したことを意味します。 +実行された各パスには、それぞれのブロックチェーンの状態があります。 状態はready(準備完了)かkilled(強制終了)のいずれかです。killedはTHROWまたはREVERT命令に到達したことを意味します。 -- [m.ready_states](https://manticore.readthedocs.io/en/latest/states.html#accessing):readyに該当する状態のリスト(REVERT/INVALIDを実行しなかった状態) -- [m.killed_states](https://manticore.readthedocs.io/en/latest/states.html#accessings):killedに該当する状態のリスト -- [m.all_states](https://manticore.readthedocs.io/en/latest/states.html#accessings):すべての状態 +- [m.ready_states](https://manticore.readthedocs.io/en/latest/states.html#accessing): 準備完了状態のリスト(REVERT/INVALIDを実行していない状態) +- [m.killed_states](https://manticore.readthedocs.io/en/latest/states.html#accessings): 強制終了された状態のリスト +- [m.all_states](https://manticore.readthedocs.io/en/latest/states.html#accessings): すべての状態 ```python for state in m.all_states: - # do something with state + # stateで何かを行う ``` -状態情報は、アクセス可能です。 以下の例をご覧ください: +状態情報にアクセスできます。 以下の例をご覧ください: -- `state.platform.get_balance(account.address)`:アカウント残高 -- `state.platform.transactions`:トランザクションのリスト -- `state.platform.transactions[-1].return_data`:最後のトランザクションで返されたデータ +- `state.platform.get_balance(account.address)`: アカウントの残高 +- `state.platform.transactions`: トランザクションのリスト +- `state.platform.transactions[-1].return_data`: 最後のトランザクションによって返されたデータ -最後のトランザクションによって返されるデータは配列ですが、以下のようにABI.deserializeで値に変換できます: +最後のトランザクションによって返されたデータは配列であり、ABI.deserializeで値に変換できます。例: ```python data = state.platform.transactions[0].return_data @@ -365,7 +362,7 @@ data = ABI.deserialize("uint", data) ### テストケースの生成方法 {#how-to-generate-testcase} -テストケースを生成するには、「[m.generate_testcase(state, name)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=generate_testcase#manticore.ethereum.ManticoreEVM.generate_testcase)」を使用します。 +テストケースを生成するには [m.generate_testcase(state, name)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=generate_testcase#manticore.ethereum.ManticoreEVM.generate_testcase) を使用します。 ```python m.generate_testcase(state, 'BugFound') @@ -373,13 +370,13 @@ m.generate_testcase(state, 'BugFound') ### まとめ {#summary-2} -- 状態は、m.all_statesでイテレートできる -- `state.platform.get_balance(account.address)`は、アカウント残高を返す -- `state.platform.transactions`は、トランザクションのリストを返す -- `transaction.return_data`は、返されたデータを示す -- `m.generate_testcase(state, name)`は、状態に対する入力を生成する +- m.all_statesで状態をイテレートできます +- `state.platform.get_balance(account.address)`はアカウントの残高を返します +- `state.platform.transactions`はトランザクションのリストを返します +- `transaction.return_data`は返されたデータです +- `m.generate_testcase(state, name)`は状態の入力を生成します -### スローイングパス取得のまとめ {#summary-getting-throwing-path} +### まとめ: 例外をスローするパスの取得 {#summary-getting-throwing-path} ```python from manticore.ethereum import ManticoreEVM @@ -395,7 +392,8 @@ contract_account = m.solidity_create_contract(source_code, owner=user_account) symbolic_var = m.make_symbolic_value() contract_account.f(symbolic_var) -## Check if an execution ends with a REVERT or INVALID +## 実行がREVERTまたはINVALIDで終了するかどうかを確認します + for state in m.terminated_states: last_tx = state.platform.transactions[-1] if last_tx.result in ['REVERT', 'INVALID']: @@ -403,13 +401,14 @@ for state in m.terminated_states: m.generate_testcase(state, 'ThrowFound') ``` -紹介したすべてのコードは、[`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py)でアクセスできます。 +上記のすべてのコードは [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) にあります。 -_terminated_stateが返したすべての状態は結果の値がREVERTまたはINVALIDであるため、上記よりも簡略なスクリプトを作成することもできました。上記のスクリプト例は、Manticore APIの操作方法を説明することを目的としたものです。_ +_terminated_stateによって返されるすべての状態は結果としてREVERTまたはINVALIDを持つため、もっと単純なスクリプトを生成することもできましたが、この例はAPIの操作方法を実証するためだけのものです。_ -## 制約を追加する {#adding-constraints} +## 制約の追加 {#adding-constraints} -次に、探索を制限する方法について確認しましょう。 ここでは、`f()`のドキュメンテーションにおいて、この関数は決して`a == 65`で呼び出されることはないと記載されていると想定します。このため、`a == 65`のバグは、実際にはバグではありません。 ここでも、対象のスマートコントラクトは[`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol)です。 +探索を制約する方法を見ていきます。 `f()`のドキュメントには、この関数が`a == 65`で呼び出されることはないと +記載されていると仮定します。したがって、`a == 65`のバグは実際のバグではありません。 対象は、引き続き次のスマートコントラクト [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol) です。 ```solidity pragma solidity >=0.4.24 <0.6.0; @@ -424,22 +423,22 @@ contract Simple { ### 演算子 {#operators} -[演算子](https://github.com/trailofbits/manticore/blob/master/manticore/core/smtlib/operators.py)モジュールは、制約を容易に操作する上で役立ちます。特に、以下の操作を提供します: +[Operators](https://github.com/trailofbits/manticore/blob/master/manticore/core/smtlib/operators.py)モジュールは制約の操作を容易にし、とりわけ以下を提供します。 -- Operators.AND -- Operators.OR -- Operators.UGT(符号なし大なり) -- Operators.UGE(符号なし以上) -- Operators.ULT(符号なし小なり) -- Operators.ULE(符号なし、以下) +- Operators.AND, +- Operators.OR, +- Operators.UGT (符号なしでより大きい), +- Operators.UGE (符号なしで以上), +- Operators.ULT (符号なしでより小さい), +- Operators.ULE (符号なしで以下)。 -このモジュールをインポートするには、以下を使用します: +モジュールをインポートするには、次を使用します。 ```python from manticore.core.smtlib import Operators ``` -`Operators.CONCAT`は、配列に値を連結するために使用します。 例えば、トランザクションのreturn_dataは、他の値に対して検証可能な値に変更する必要があります: +`Operators.CONCAT`は、配列を値に連結するために使用されます。 例えば、トランザクションのreturn_dataは、別の値と比較チェックするために値に変更する必要があります。 ```python last_return = Operators.CONCAT(256, *last_return) @@ -447,11 +446,12 @@ last_return = Operators.CONCAT(256, *last_return) ### 制約 {#state-constraint} -制約の対象は、グローバルあるいは特定の状態のみのどちらでも構いません。 +制約は、グローバルまたは特定の状態に対して使用できます。 #### グローバル制約 {#state-constraint} -グローバル制約を追加するには、`m.constraint(constraint)`を使用します。 例えば以下のように、シンボリックアドレスからコントラクトを呼び出し、このアドレスを特定の値に制限することができます: +グローバル制約を追加するには `m.constrain(constraint)` を使用します。 +例えば、シンボリックアドレスからコントラクトを呼び出し、このアドレスを特定の値に制限することができます。 ```python symbolic_address = m.make_symbolic_value() @@ -462,23 +462,25 @@ m.transaction(caller=user_account, value=0) ``` -#### 状態に対する制約 {#state-constraint} +#### 状態制約 {#state-constraint} -特定の状態に対して制約を追加するには、[state.constrain(constraint)](https://manticore.readthedocs.io/en/latest/states.html?highlight=StateBase#manticore.core.state.StateBase.constrain)を使用します。 特定の状態における一部のプロパティをチェックしてから、状態に制限を追加することができます。 +[state.constrain(constraint)](https://manticore.readthedocs.io/en/latest/states.html?highlight=StateBase#manticore.core.state.StateBase.constrain) を使用して、特定の状態に制約を追加します。 +これは、探索後に状態を制約して、そのプロパティをチェックするために使用できます。 -### 制約を確認する {#checking-constraint} +### 制約のチェック {#checking-constraint} -制約が実行可能かどうかを確認するには、`solver.check(state.constraints)`を使用します。 例えば以下では、symbolic_valueが「65」以外の値でなければならないという制約を追加した上で、状態が実行可能かどうかをチェックします。 +`solver.check(state.constraints)`を使用して、制約がまだ実行可能かどうかを確認します。 +例えば、次はsymbolic_valueを65とは異なる値に制約し、状態がまだ実行可能かどうかをチェックします。 ```python state.constrain(symbolic_var != 65) if solver.check(state.constraints): - # state is feasible + # 状態は実行可能です ``` -### 制約追加のまとめ {#summary-adding-constraints} +### まとめ: 制約の追加 {#summary-adding-constraints} -これまでのコードに制約を追加すると、以下のようになります: +前のコードに制約を追加すると、次のようになります。 ```python from manticore.ethereum import ManticoreEVM @@ -499,11 +501,12 @@ contract_account.f(symbolic_var) no_bug_found = True -## Check if an execution ends with a REVERT or INVALID +## 実行がREVERTまたはINVALIDで終了するかどうかを確認します + for state in m.terminated_states: last_tx = state.platform.transactions[-1] if last_tx.result in ['REVERT', 'INVALID']: - # we do not consider the path were a == 65 + # a == 65のパスは考慮しません condition = symbolic_var != 65 if m.generate_testcase(state, name="BugFound", only_if=condition): print(f'Bug found, results are in {m.workspace}') @@ -513,4 +516,4 @@ if no_bug_found: print(f'No bug found') ``` -ここで紹介したすべてのコードは、[`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py)からアクセスできます。 +上記のすべてのコードは [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) にあります。 diff --git a/public/content/translations/ja/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md b/public/content/translations/ja/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md index 2196e56b178..c881486e9a9 100644 --- a/public/content/translations/ja/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md +++ b/public/content/translations/ja/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md @@ -1,34 +1,29 @@ --- -title: Slitherを使用してスマートコントラクトのバグを見つける方法 -description: Slitherを使用してスマートコントラクトのバグを自動的に見つける方法 +title: "Slitherを使用してスマートコントラクトのバグを見つける方法" +description: "Slitherを使用してスマートコントラクトのバグを自動的に見つける方法" author: Trailofbits lang: ja -tags: - - "Solidity" - - "スマートコントラクト" - - "セキュリティ" - - "テスト" - - "静的解析" +tags: [ "Solidity", "スマート契約", "セキュリティ", "テスト" ] skill: advanced published: 2020-06-09 -source: セキュアなコントラクトの構築 +source: Building secure contracts sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither --- -## Slitherの使い方 {#how-to-use-slither} +## Slitherの使用方法 {#how-to-use-slither} このチュートリアルでは、Slitherを使って、スマートコントラクトのバグを自動で検出する方法を学びます。 - [インストール](#installation) -- [コマンドラインの使い方](#command-line) -- [静的解析入門](#static-analysis):静的解析の簡単な紹介 -- [API](#api-basics):Python APIの説明 +- [コマンドラインの使用方法](#command-line) +- [静的解析入門](#static-analysis): 静的解析の簡単な紹介 +- [API](#api-basics): Python APIの説明 ## インストール {#installation} -Slitherには、Python 3.6以上が必要です。 pipでインストールすることも、Dockerを使用してインストールすることもできます。 +SlitherにはPython 3.6以上が必要です。 pipでインストールすることも、Dockerを使用してインストールすることもできます。 -pipによるSlitherのインストール +pipによるSlitherのインストール: ```bash pip3 install --user slither-analyzer @@ -41,18 +36,18 @@ docker pull trailofbits/eth-security-toolbox docker run -it -v "$PWD":/home/trufflecon trailofbits/eth-security-toolbox ``` -_最後のコマンドは、現在のディレクトリにアクセスできるDockerでeth-security-toolboxを実行します。 ホストからファイルを変更し、Dockerからファイル上のツールを実行することができます。_ +_最後のコマンドは、現在のディレクトリにアクセスできるDockerでeth-security-toolboxを実行します。 ホストからファイルを変更し、Dockerからファイル上のツールを実行できます。_ -Docker内で実行する: +Docker内で、以下を実行します。 ```bash solc-select 0.5.11 cd /home/trufflecon/ ``` -### スクリプトを実行する {#running-a-script} +### スクリプトの実行 {#running-a-script} -Python3でPythonスクリプトを実行するには、以下を実行します: +Python 3でPythonスクリプトを実行するには: ```bash python3 script.py @@ -60,23 +55,23 @@ python3 script.py ### コマンドライン {#command-line} -**コマンドラインとユーザー定義スクリプトの比較**:Slitherには、多くの一般的なバグを見つけるための事前定義された検出器のセットが付属しています。 コマンドラインでSlitherを呼び出すとすべての検出器が実行されますので、静的解析の詳しい知識は必要ありません: +**コマンドラインとユーザー定義スクリプト。** Slitherには、多くの一般的なバグを見つけるための事前定義された検出器のセットが付属しています。 コマンドラインからSlitherを呼び出すとすべての検出器が実行されるため、静的解析に関する詳細な知識は必要ありません。 ```bash slither project_paths ``` -Slitherでは、検出器に加えて、[プリンター](https://github.com/crytic/slither#printers)と[ツール](https://github.com/crytic/slither#tools)によるコードレビュー機能も利用できます。 +検出器に加えて、Slitherにはその[printers](https://github.com/crytic/slither#printers)と[tools](https://github.com/crytic/slither#tools)を介したコードレビュー機能があります。 -プライベート検出器およびGitHubでの統合にアクセスするには、[crytic.io](https://github.com/crytic)を使用します。 +[crytic.io](https://github.com/crytic)を使用すると、プライベートな検出器やGitHubとの統合にアクセスできます。 ## 静的解析 {#static-analysis} -Slither静的解析フレームワークの機能と設計は、ブログ投稿 ([1](https://blog.trailofbits.com/2018/10/19/slither-a-solidity-static-analysis-framework/)、[2](https://blog.trailofbits.com/2019/05/27/slither-the-leading-static-analyzer-for-smart-contracts/)) と[学術論文](https://github.com/trailofbits/publications/blob/master/papers/wetseb19.pdf)で説明されています。 +Slither静的解析フレームワークの機能と設計については、ブログ投稿 ([1](https://blog.trailofbits.com/2018/10/19/slither-a-solidity-static-analysis-framework/), [2](https://blog.trailofbits.com/2019/05/27/slither-the-leading-static-analyzer-for-smart-contracts/)) や [学術論文](https://github.com/trailofbits/publications/blob/master/papers/wetseb19.pdf)で説明されています。 -静的解析には、さまざまな種類があります。 静的解析の技法は、[clang](https://clang-analyzer.llvm.org/)や[gcc](https://lwn.net/Articles/806099/)といったコンパイラで採用されているだけでなく、[Infer](https://fbinfer.com/)、[CodeClimate](https://codeclimate.com/)、および[FindBugs](http://findbugs.sourceforge.net/)、ならびに[Frama-C](https://frama-c.com/)や[Polyspace](https://www.mathworks.com/products/polyspace.html)といったフォーマルなメソッドに基づくツールの基盤でもあります。 +静的解析には、さまざまな種類があります。 おそらくご存じのように、[clang](https://clang-analyzer.llvm.org/)や[gcc](https://lwn.net/Articles/806099/)などのコンパイラはこれらの研究技術に依存していますが、それはまた、([Infer](https://fbinfer.com/)、[CodeClimate](https://codeclimate.com/)、[FindBugs](http://findbugs.sourceforge.net/)、そして[Frama-C](https://frama-c.com/)や[Polyspace](https://www.mathworks.com/products/polyspace.html)のような形式的手法に基づくツール) の基礎ともなっています。 -ここでは、静的解析の手法や研究者について網羅的に取り上げる余裕はありません。 その代わりに、皆さんがバグを特定し、コードを理解する上で効果的に静的解析の手法を利用できるように、Slitherの仕組みを理解する上で必要な事項のみに焦点を当てます。 +ここでは、静的解析の技術と研究者を網羅的に検討するわけではありません。 その代わり、皆さんがバグを発見しコードを理解するためにSlitherをより効果的に使えるよう、Slitherがどのように機能するかを理解するために必要なことに焦点を当てます。 - [コード表現](#code-representation) - [コード解析](#analysis) @@ -84,13 +79,13 @@ Slither静的解析フレームワークの機能と設計は、ブログ投稿 ### コード表現 {#code-representation} -単一の実行パスについて推論する動的解析とは対照的に、静的解析では一度にすべてのパスを対象として推論します。 これには、別のコード表現が必要です。 最も一般的なコード表現は、抽象構文木(AST)および制御フローグラフ(CFG)の2つです。 +単一の実行パスについて推論する動的解析とは対照的に、静的解析では一度にすべてのパスを対象として推論します。 そのために、異なるコード表現に依存しています。 最も一般的なものは、抽象構文木 (AST) と制御フローグラフ (CFG) の2つです。 -### 抽象構文木(AST) {#abstract-syntax-trees-ast} +### 抽象構文木 (AST) {#abstract-syntax-trees-ast} -ASTは、コンパイラがコードを解析するたびに用いられます。 おそらく、静的解析を実行できる最も基本的な構造だと言えるでしょう。 +ASTは、コンパイラがコードを解析するたびに使用されます。 これは、おそらく静的解析を実行できる最も基本的な構造です。 -一言で言えば、ASTは構造化されたツリーであり、通常、各リーフに変数または定数が含まれ、内部ノードはオペランドまたは制御フロー操作です。 次のコードを検討してみましょう: +要するに、ASTは構造化された木であり、通常、各リーフには変数または定数が含まれ、内部ノードはオペランドまたは制御フロー操作です。 次のコードを考えてみましょう。 ```solidity function safeAdd(uint a, uint b) pure internal returns(uint){ @@ -101,15 +96,15 @@ function safeAdd(uint a, uint b) pure internal returns(uint){ } ``` -対応するASTは、次のとおりです: +対応するASTは以下の通りです。 ![AST](./ast.png) -Slitherでは、solcがエクスポートしたASTを使用します。 +SlitherはsolcによってエクスポートされたASTを使用します。 -ASTは簡単に構築できますが、入れ子構造を持ちます。 このため、解析が簡単でない場合があります。 例えば、`a + b <= a`の式で使用される操作を識別するには、まず`<=`を解析し、次に`+`を解析する必要があります。 一般的なアプローチは、ツリーを再帰的に移動するいわゆるVisitorパターンを使用することです。 Slitherには、[`ExpressionVisitor`](https://github.com/crytic/slither/blob/master/slither/visitors/expression/expression.py)という汎用的なVisitorが含まれています。 +ASTは簡単に構築できますが、入れ子構造になっています。 そのため、解析が必ずしも簡単ではない場合があります。 例えば、`a + b <= a`という式で使われる演算を特定するには、まず`<=`を解析し、次に`+`を解析しなければなりません。 一般的なアプローチは、木構造を再帰的に走査する、いわゆるビジターパターンを使用することです。 Slitherには、[`ExpressionVisitor`](https://github.com/crytic/slither/blob/master/slither/visitors/expression/expression.py)に汎用的なビジターが含まれています。 -次のコードは、`ExpressionVisitor`を使用して式に加算が含まれているかどうかを検出します: +次のコードは`ExpressionVisitor`を使用して、式に加算が含まれているかどうかを検出します。 ```python from slither.visitors.expression.expression import ExpressionVisitor @@ -124,58 +119,58 @@ class HasAddition(ExpressionVisitor): if expression.type == BinaryOperationType.ADDITION: self._result = True -visitor = HasAddition(expression) # expression is the expression to be tested +visitor = HasAddition(expression) # expression はテスト対象の式です print(f'The expression {expression} has a addition: {visitor.result()}') ``` -### 制御フローグラフ(CFG) {#control-flow-graph-cfg} +### 制御フローグラフ (CFG) {#control-flow-graph-cfg} -2番目によく用いられるコード表現は、制御フローグラフ(CFG)です。 名前が示すように、すべての実行パスを可視化するグラフベースの表現です。 各ノードには、1つまたは複数の命令が含まれます。 グラフのエッジ部分は、制御フロー操作(if/then/else、ループなど)を表します。 先ほどの例をCFGで表すと、次のようになります: +2番目に一般的なコード表現は、制御フローグラフ (CFG) です。 その名の通り、すべての実行パスを公開するグラフベースの表現です。 各ノードには、1つまたは複数の命令が含まれます。 グラフのエッジは、制御フロー操作 (if/then/else、ループなど) を表します。 前の例のCFGは次のようになります。 ![CFG](./cfg.png) -CFGは、大部分の解析の土台となる表現です。 +CFGは、ほとんどの解析がその上に構築される表現です。 -他にも、様々なコード表現が存在します。 それぞれの表現には、実行したい解析に応じて長所と短所があります。 +他にも多くのコード表現が存在します。 それぞれの表現には、実行したい解析に応じて長所と短所があります。 ### 解析 {#analysis} -Slitherで実行できる最もシンプルな解析タイプは、構文解析です。 +Slitherで実行できる最も簡単な種類の解析は、構文解析です。 ### 構文解析 {#syntax-analysis} -Slitherは、コードおよび表現に含まれるさまざまな構成要素を移動しながら、パターンマッチングに類似したアプローチで矛盾や欠陥を発見します。 +Slitherは、パターンマッチングのようなアプローチを用いて、コードのさまざまな構成要素とその表現を走査し、矛盾や欠陥を見つけることができます。 -例えば、以下の検出器は構文関連の問題を探します: +例えば、以下の検出器は構文関連の問題を探します。 -- [状態変数のシャドーイング](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing):すべての状態変数でイテレートし、継承されたコントラクトから変数がシャドーされているかを確認します([state.py#L51-L62](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/shadowing/state.py#L51-L62))。 +- [状態変数のシャドーイング](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing): すべての状態変数を反復処理し、継承されたコントラクトの変数をシャドーイングしているものがないかチェックします ([state.py#L51-L62](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/shadowing/state.py#L51-L62)) -- [不適切なERC-20インターフェース](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface):不適切なERC-20関数の署名を探します([incorrect_erc20_interface.py#L34-L55](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/erc/incorrect_erc20_interface.py#L34-L55))。 +- [不正確なERC20インターフェース](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface): 不正確なERC20関数のシグネチャを探します ([incorrect_erc20_interface.py#L34-L55](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/erc/incorrect_erc20_interface.py#L34-L55)) ### 意味解析 {#semantic-analysis} -構文解析とは対照的に、意味解析は、より深くコードの「意味」を解析します。 この解析手法は、いくつかの種類に大別できます。 意味解析は、より強力で有益な結果を得られますが、より複雑なコードを書く必要があります。 +構文解析とは対照的に、意味解析はより深く掘り下げ、コードの「意味」を解析します。 この系統には、いくつかの広範な種類の解析が含まれます。 それらはより強力で有用な結果につながりますが、記述もより複雑になります。 -意味解析は、最も高度な脆弱性検出に用いられています。 +意味解析は、最も高度な脆弱性検出に使用されます。 -#### データ依存解析 {#fixed-point-computation} +#### データ依存性解析 {#fixed-point-computation} -`variable_a`の値が`variable_b`の影響を受けるパスが存在する場合、`variable_a`の変数は`variable_b`に対して依存関係を持ちます。 +`variable_a`の値が`variable_b`に影響されるパスが存在する場合、変数`variable_a`は`variable_b`にデータ依存していると言われます。 -次のコードでは、`variable_a`は`variable_b`に依存しています: +次のコードでは、`variable_a`は`variable_b`に依存しています。 ```solidity // ... variable_a = variable_b + 1; ``` -Slitherでは、中間表現(以下を参照)を利用して、[データの依存関係](https://github.com/crytic/slither/wiki/data-dependency)を解析する機能が搭載されています。 +Slitherには、その中間表現 (後のセクションで説明) のおかげで、組み込みの[データ依存性](https://github.com/crytic/slither/wiki/data-dependency)機能が備わっています。 -データ依存関係を解析する具体例としては、[危険をもたらす厳密な等値の検出器](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities)を参照してください。 ここで、Slitherは、ある危険な値に対する厳密な等値比較を発見しようと試みます([incorrect_strict_equality.py#L86-L87](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L86-L87))。その上で、攻撃者がコントラクトをトラップできる状態を防止するために、`==`ではなく、`>=`または`<=`を使用するようにユーザーに警告します。 検出器はまず、`balanceOf(address)`の呼び出しにおける戻り値を危険な値だと見なし([incorrect_strict_equality.py#L63-L64](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L63-L64))、データ依存関係エンジンを使用してその使用状況を追跡します。 +データ依存性の使用例は、[危険な厳密等価性検出器](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities)にあります。 ここでは、Slitherは危険な値との厳密な等価比較を探し ([incorrect_strict_equality.py#L86-L87](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L86-L87))、攻撃者がコントラクトを罠にかけるのを防ぐために、`==`ではなく`>=`または`<=`を使用すべきであるとユーザーに通知します。 とりわけ、検出器は `balanceOf(address)` の呼び出しの戻り値を危険とみなし ([incorrect_strict_equality.py#L63-L64](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L63-L64))、その使用状況を追跡するためにデータ依存性エンジンを使用します。 -#### 不動点の計算 {#fixed-point-computation} +#### 不動点計算 {#fixed-point-computation} -解析がエッジを辿ってCFG全体を移動する場合、すでに訪問済みのノードを発見する可能性が高いでしょう。 例えば、以下のようなループがある場合です: +解析がCFGを走査してエッジをたどる場合、すでに訪れたノードに遭遇する可能性があります。 例えば、以下のようにループが存在する場合です。 ```solidity for(uint i; i < range; ++){ @@ -183,23 +178,23 @@ for(uint i; i < range; ++){ } ``` -この場合、解析をいつ停止するかを指定する必要があります。 それには、(1)ノードごとにイテレートする上限回数を設定するか、(2)いわゆる_不動点_を計算する、という2つの戦略があります。 不動点とは、当該ノードをさらに解析しても有益な情報が得られない点を意味します。 +解析はいつ停止すべきかを知る必要があります。 ここには2つの主要な戦略があります。(1) 各ノードを有限回反復する、(2) いわゆる_不動点_を計算する。 不動点とは、基本的に、そのノードを解析しても、もはや有意義な情報が得られないことを意味します。 -不動点を使用する実例としては、リエントランシー検出器が挙げられます。Slitherでは、当該のノードを探索し、外部からの呼び出しを見つけて、ストレージへの書き込み/読み取りを行います。 この処理を通じて不動点に到達すると([reentrancy.py#L125-L131](https://github.com/crytic/slither/blob/master/slither/detectors/reentrancy/reentrancy.py#L125-L131))、探索を停止し、様々なリエントランシーのパターン([reentrancy_beign.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_benign.py))、([reentrancy_read_before_write.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_read_before_write.py))、([reentrancy_eth.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_eth.py))に基づき、結果を解析してリエントランシーが存在するか否かを判定します。 +不動点の使用例は、リエントランシー検出器に見られます。Slitherはノードを探索し、外部呼び出し、ストレージへの書き込みと読み込みを探します。 不動点に達すると ([reentrancy.py#L125-L131](https://github.com/crytic/slither/blob/master/slither/detectors/reentrancy/reentrancy.py#L125-L131))、探索を停止し、さまざまなリエントランシーパターン ([reentrancy_benign.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_benign.py), [reentrancy_read_before_write.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_read_before_write.py), [reentrancy_eth.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_eth.py)) を通じて結果を解析し、リエントランシーが存在するかどうかを確認します。 -効率的な不動点計算を用いた解析を作成するには、解析において情報がどのように拡散するかをよく理解しておく必要があります。 +効率的な不動点計算を用いた解析を書くには、解析がどのように情報を伝播させるかをよく理解する必要があります。 ### 中間表現 {#intermediate-representation} -中間表現(IR)は、オリジナルのコードよりも静的解析を実行しやすくした言語です。 Slitherでは、SolidityをSlither独自のIRである[SlithIR](https://github.com/crytic/slither/wiki/SlithIR)に変換します。 +中間表現 (IR) とは、元の言語よりも静的解析に適した言語のことです。 Slitherは、Solidityを独自の中間表現である [SlithIR](https://github.com/crytic/slither/wiki/SlithIR) に変換します。 -基本的なチェックを作成したいだけの場合は、SlithIRを理解する必要はありません。 ただし、より高度な意味解析を作成したい場合は、SlithIRの知識が有益になるでしょう。 [SlithIR](https://github.com/crytic/slither/wiki/Printer-documentation#slithir)および[SSA](https://github.com/crytic/slither/wiki/Printer-documentation#slithir-ssa)のプリンターは、コードがどのように変換されるかを理解する上で役立ちます。 +基本的なチェックを書きたいだけなら、SlithIRを理解する必要はありません。 しかし、高度な意味解析を書く予定がある場合は、役立つでしょう。 [SlithIR](https://github.com/crytic/slither/wiki/Printer-documentation#slithir) および [SSA](https://github.com/crytic/slither/wiki/Printer-documentation#slithir-ssa) プリンターは、コードがどのように変換されるかを理解するのに役立ちます。 ## APIの基本 {#api-basics} -Slitherには、コントラクトの基本的な属性や関数について探索できるAPIが含まれています。 +Slitherには、コントラクトとその関数の基本的な属性を探索できるAPIがあります。 -コードベースを読み込むには、以下を実行します: +コードベースを読み込むには、次のようにします。 ```python from slither import Slither @@ -207,32 +202,32 @@ slither = Slither('/path/to/project') ``` -### コントラクトや関数を探索する {#exploring-contracts-and-functions} +### コントラクトと関数の探索 {#exploring-contracts-and-functions} -`Slither`オブジェクトは、以下を持ちます: +`Slither`オブジェクトには以下のものがあります。 -- `contracts (list(Contract)`:コントラクトのリスト -- `contracts_derived (list(Contract)`:他のコントラクトに継承されていないコントラクトのリスト(コントラクトのサブセット) -- `get_contract_from_name (str)`:名前でコントラクトを返します +- `contracts (list(Contract))`: コントラクトのリスト +- `contracts_derived (list(Contract))`: 他のコントラクトによって継承されていないコントラクトのリスト (コントラクトのサブセット) +- `get_contract_from_name (str)`: 名前からコントラクトを返します -`Contract`オブジェクトには、以下のものがあります。 +`Contract`オブジェクトには以下のものがあります。 -- `name (str)`:コントラクトの名前 -- `functions (list(Function))`:関数のリスト -- `modifiers (list(Modifier))`:修飾子のリスト -- `all_functions_called (list(Function/Modifier))`:コントラクトがリーチできるすべての内部関数のリスト -- `inheritance (list(Contract))`:継承されたコントラクトのリスト -- `get_function_from_signature (str)`:署名から関数を返します -- `get_modifier_from_signature (str)`:署名から修飾子を返します -- `get_state_variable_from_name (str)`:名前から状態変数を返します +- `name (str)`: コントラクトの名前 +- `functions (list(Function))`: 関数のリスト +- `modifiers (list(Modifier))`: 修飾子のリスト +- `all_functions_called (list(Function/Modifier))`: コントラクトから到達可能なすべての内部関数のリスト +- `inheritance (list(Contract))`: 継承されたコントラクトのリスト +- `get_function_from_signature (str)`: シグネチャからFunctionを返します +- `get_modifier_from_signature (str)`: シグネチャからModifierを返します +- `get_state_variable_from_name (str)`: 名前からStateVariableを返します -`Function`オブジェクトまたは`Modifier`オブジェクトは、以下を持ちます: +`Function`または`Modifier`オブジェクトには以下のものがあります。 -- `name (str)`:関数の名前 -- `contract (contract)`:この関数を宣言したコントラクト -- `nodes (list(Node))`:この関数/修飾子のCFGを攻勢するノードのリスト -- `entry_point (Node)`: CFG (制御フローグラフ) のエントリポイント -- `variables_read (list(Variable))`: 読み込まれた変数のリスト +- `name (str)`: 関数の名前 +- `contract (contract)`: 関数が宣言されているコントラクト +- `nodes (list(Node))`: 関数/修飾子のCFGを構成するノードのリスト +- `entry_point (Node)`: CFGのエントリポイント +- `variables_read (list(Variable))`: 読み取られた変数のリスト - `variables_written (list(Variable))`: 書き込まれた変数のリスト -- `state_variables_read (list(StateVariable))`: 読み込まれた状態変数のリスト (読み込まれた変数のサブセット) -- `state_variables_written (list(StateVariable))`: 書き込まれた状態変数のリスト (書き込まれた変数のサブセット) +- `state_variables_read (list(StateVariable))`: 読み取られた状態変数のリスト (variables`readのサブセット) +- `state_variables_written (list(StateVariable))`: 書き込まれた状態変数のリスト (variables`writtenのサブセット) diff --git a/public/content/translations/ja/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md b/public/content/translations/ja/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md index 8c4d3d5f70e..eb24ffc5346 100644 --- a/public/content/translations/ja/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md +++ b/public/content/translations/ja/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md @@ -1,12 +1,9 @@ --- -title: Tellorをオラクルとしてセットアップする方法 -description: プロトコルにTellorオラクルを統合する作業を開始するためのガイド +title: "Tellorをオラクルとしてセットアップする方法" +description: "プロトコルにTellorオラクルを統合する作業を開始するためのガイド" author: "Tellor" lang: ja -tags: - - "Solidity" - - "スマートコントラクト" - - "オラクル" +tags: [ "Solidity", "スマート契約", "オラクル" ] skill: beginner published: 2021-06-29 source: Tellor Docs @@ -15,11 +12,11 @@ sourceUrl: https://docs.tellor.io/tellor/ 確認クイズ:あなたのプロトコルはもうすぐ完成しますが、オフチェーンのデータにアクセスするにはオラクルが必要です。何をすればよいでしょうか? -## (ソフトな)前提知識 {#soft-prerequisites} +## (緩やかな) 前提条件 {#soft-prerequisites} この投稿は、なるべくシンプルかつ明快に、オラクルフィードにアクセスする方法を説明するものです。 ただし、オラクルについて理解するには以下のプログラミングスキルが必要です。 -前提条件: +前提知識: - ターミナルを操作できる - npmがインストール済みである @@ -29,17 +26,17 @@ Tellorは、実装可能かつ開発継続中のオープンソースのオラ ## 概要 {#overview} -Tellorは、オフチェーンのデータポイントの値(例:BTC/USD)を請求できるオラクルシステムです。報告者は、イーサリアムの全スマートコントラクトがアクセスできるオンチェーンのデータバンクに対してこの値を追加するために競います。 このデータバンクへの入力は、ステーキング済みの報告者で構成されるネットワークにより保護されています。 Tellorは、暗号資産経済におけるインセンティブの仕組みを活用し、Tellorのトークン、トリビュート(TRB)、および紛争メカニズムに基づき、正直なデータを提出した報告者に報酬を与え、悪意のユーザーを処罰します。 +Tellorは、オフチェーンのデータポイントの値(例:BTC/USD)を請求できるオラクルシステムです。報告者は、イーサリアムの全スマートコントラクトがアクセスできるオンチェーンのデータバンクに対してこの値を追加するために競います。 このデータバンクへの入力は、ステーキング済みの報告者で構成されるネットワークにより保護されています。 Tellorは暗号経済的なインセンティブの仕組みを活用し、TellorのトークンであるTributes(TRB)の発行と紛争メカニズムを通じて、誠実なデータを提出したレポーターに報酬を与え、悪意のあるアクタを罰します。 このチュートリアルでは、以下について説明します: - 導入および稼働に必要な初回ツールキットの設定方法 - 簡単な実例に基づくステップごとの説明 -- 現在Tellorをテストできるネットワークのアドレスリスト +- 現在Tellorをテストできるテストネットのネットワークアドレス一覧 -## Tellorを使うには {#usingtellor} +## UsingTellorの使用 {#usingtellor} -まず、Tellorをオラクルとして使用するために必要な基本ツールをインストールします。 Tellorのユーザーコントラクトをインストールするには、[このパッケージ](https://github.com/tellor-io/usingtellor)を使ってください。 +まず、Tellorをオラクルとして使用するために必要な基本ツールをインストールします。 Tellor User Contractsをインストールするには、[このパッケージ](https://github.com/tellor-io/usingtellor)を使用します: `npm install usingtellor` @@ -49,7 +46,7 @@ Tellorは、オフチェーンのデータポイントの値(例:BTC/USD) ### BTC/USDの例 {#btcusd-example} -この「UsingTellor」コントラクトを継承し、Tellorのアドレスをコントラクタの引数として渡します: +この「UsingTellor」コントラクトを継承し、Tellorのアドレスをコンストラクタの引数として渡します: 具体的なコードは、以下のようになります: @@ -59,7 +56,7 @@ import "usingtellor/contracts/UsingTellor.sol"; contract PriceContract is UsingTellor { uint256 public btcPrice; - //This Contract now has access to all functions in UsingTellor + //このコントラクトは、UsingTellor のすべての関数にアクセスできるようになりました constructor(address payable _tellorAddress) UsingTellor(_tellorAddress) public {} @@ -77,8 +74,8 @@ function setBtcPrice() public { } ``` -コントラクトアドレスの完全なリストについては、[こちら](https://docs.tellor.io/tellor/the-basics/contracts-reference)を参照してください。 +コントラクトアドレスの完全なリストは、[こちら](https://docs.tellor.io/tellor/the-basics/contracts-reference)を参照してください。 -利便性のために、UsingTellorのリポジトリには簡単な統合を目的としたバージョンである[Tellor Playground](https://github.com/tellor-io/TellorPlayground)コントラクトが含まれています。 有益な関数のリストについては、 [こちら](https://github.com/tellor-io/sampleUsingTellor#tellor-playground)を参照してください。 +利便性のために、UsingTellorのリポジトリには簡単な統合を目的としたバージョンである[Tellor Playground](https://github.com/tellor-io/TellorPlayground)コントラクトが含まれています。 便利な関数の一覧は[こちら](https://github.com/tellor-io/sampleUsingTellor#tellor-playground)をご覧ください。 -Tellorオラクルをより堅牢に実装したい場合は、[こちらで](https://github.com/tellor-io/usingtellor/blob/master/README.md)利用可能な関数の全リストを確認してください。 +Tellorオラクルのより堅牢な実装については、利用可能な関数の完全なリストを[こちら](https://github.com/tellor-io/usingtellor/blob/master/README.md)で確認してください。