メインコンテンツへスキップ

Pythonデベロッパーのためのイーサリアム入門、パート1

pythonweb3.py
初級
Marc Garreau
Snake charmers(opens in a new tab)
2020年9月8日
22 分の読書 minute read

これまで耳にしてきたイーサリアムの世界へ飛び込む準備はできましたか? この投稿では、ブロックチェーンの基礎について簡単に説明し、次に、シミュレートされたイーサリアムノードと対話(ブロックデータの読み取り、アカウント残高の確認、トランザクションの送信)する方法について説明します。 その過程で、アプリを構築する従来の方法とこの新しい分散型パラダイムの違いに焦点を当てます。

前提条件(ソフト)

この投稿は、幅広いデベロッパーに参照していただきたいと考えています。 Pythonツールを使用しますが、これらは概念を伝える手段にすぎませんので、Pythonデベロッパーでなくても問題ありません。 しかし、ここでは皆さんがPythonについての知識があることを前提に話を進めますので、すぐにイーサリアムに特化した部分の説明に移ります。

前提知識:

  • ターミナルを操作できる。
  • Pythonで数行のコードを書いたことがある。
  • Pythonバージョン3.6以降がマシンにインストールされている(仮想環境(opens in a new tab)の使用を強くお勧めします)。
  • Pythonのパッケージインストーラーであるpipを使用したことがある。 これらのうちあてはまらないものがある方、あるいはこの記事にあるコードを実行することがない方でも十分に理解できる内容です。

ブロックチェーンについて

イーサリアムを説明する方法はたくさんありますが、その中心となるのはブロックチェーンです。 ブロックチェーンは一連のブロックで構成されています。まずはその説明から始めましょう。 簡単に言うと、イーサリアムブロックチェーンの各ブロックは、数値などのメタデータとトランザクションのリストにすぎません。 JSON形式では、次のようになります。

1{
2 "number": 1234567,
3 "hash": "0xabc123...",
4 "parentHash": "0xdef456...",
5 ...,
6 "transactions": [...]
7}
コピー

ブロックは、その前のブロックを参照します。parentHashは、単に前のブロックのハッシュ値です。

注: イーサリアムは [ハッシュ関数](https://wikipedia.org/wiki/Hash_function)を定期的に使用して、固定サイズの値(ハッシュ)を生成します。 イーサリアムではハッシュ値が重要な役割を果たしますが、今のところは固有のIDと考えておくとよいでしょう。

ブロックチェーンと各ブロック内のデータを表す図

ブロックチェーンは基本的にはリンクリストであり、各ブロックは前のブロックを参照します。

このデータ構造は目新しいものではありませんが、ネットワークを管理するルール(つまり、ピアツーピアプロトコル)は目新しいものです。 中央集権型ではないため、ピアのネットワークは連携してネットワークを維持する必要がありますが、次のブロックに含めるトランザクションを決定する際には競い合うことになります。 例えば、友人に送金する場合、トランザクションをネットワークにブロードキャストして、そのトランザクションが次のブロックに含まれるのを待つ必要があります。

ブロックチェーンにおいて、あるユーザーから別のユーザーに本当に送金されたのかを検証するための唯一の手段は、そのブロックチェーンに固有の(そのブロックチェーンで作成され管理される)通貨を使用することです。 イーサリアムでは、この通貨は「イーサ(ETH)」と呼ばれます。イーサリアムブロックチェーンには、アカウント残高に関する唯一の公式な記録が含まれています。

新しいパラダイム

この新しい分散型技術スタックは、新しいデベロッパーツールを生み出しました。 このようなツールは多くのプログラミング言語に存在しますが、今回はPythonを例にとって説明します。 繰り返しになりますが、Python以外の言語をご使用の場合でも、内容を理解するのはそれほど難しいことではありません。

イーサリアムと対話する必要があるPythonデベロッパーは、Web3.py(opens in a new tab)にアクセスしてください。 Web3.pyは、イーサリアムノードへの接続と、そのノードとのデータの送受信を簡単に行えるようにするライブラリです。

注: 「イーサリアムノード」と「イーサリアムクライアント」は同じ意味で使用されます。 どちらもイーサリアムネットワークの参加者が実行するソフトウェアを指します。 このソフトウェアは、ブロックデータの読み取り、新しいブロックがチェーンに追加されたときの更新データの受信、新しいトランザクションのブロードキャストなどを行います。 技術的には、クライアントはソフトウェアを意味し、ノードはソフトウェアを実行しているコンピュータを意味します。

イーサリアムクライアント は、プロセス間通信(IPC)(opens in a new tab)、HTTP、またはWebSocketで接続するように設定できるため、Web3.pyでも同じ設定にする必要があります。 Web3.pyは、これらの接続オプションをプロバイダーとして参照します。 Web3.pyインスタンスをノードに接続するために、3つのプロバイダーのいずれかを選択する必要があります。

Web3.pyがIPCを使用してアプリケーションをイーサリアムノードに接続する方法を表す図

同じプロトコル(この図ではプロセス間通信(IPC))を介して通信するようにイーサリアムノードとWeb3.pyを設定します。

Web3.pyが正しく設定できたら、ブロックチェーンとの対話を開始できます。 参考までに、Web3.pyの使用例をいくつか示します。

1# read block data:
2w3.eth.get_block('latest')
3
4# send a transaction:
5w3.eth.send_transaction({'from': ..., 'to': ..., 'value': ...})
コピー

インストール

このチュートリアルでは、Pythonインタプリタ内で作業します。 ディレクトリ、ファイル、クラス、関数は作成しません。

注: 以下の例では、「$」で始まるコマンドはターミナルで実行することを意味しています。 (「$」を入力しないでください。「$」は単に行の始まりを意味します。)

まず、使いやすい環境となるようにIPython(opens in a new tab)をインストールしてください。 IPythonには、数ある機能の中でも特に便利なタブ補完機能があり、Web3.pyでの補完の候補を簡単に確認できます。

pip install ipython

Web3.pyはweb3という名前で公開されています。 次のようにしてインストールします。

pip install web3

あと1つ操作が必要です。後でブロックチェーンのシミュレーションを行いますが、それにはさらに数個の依存関係が必要となります。 それらは以下のコマンドでインストールできます。

pip install 'web3[tester]'

これで準備完了です。

注: web3[tester]パッケージは、Python 3.10.xxまで対応しています。

サンドボックスの起動

ターミナルでipythonを実行し、新しいPython環境を開きます。 これは、pythonを実行するのと同じですが、より多くの機能を使用できます。

ipython

実行しているPythonとIPythonのバージョンに関する情報が出力され、次のような入力待ちの状態になります。

1In [1]:
コピー

これは対話型のPythonシェルであり、 実質的には、さまざまなものを実行できるサンドボックスです。 ここまで完了したら、いよいよWeb3.pyをインポートします。

1In [1]: from web3 import Web3
コピー

Web3モジュールの紹介

Web3(opens in a new tab)モジュールは、イーサリアムへの入り口であるだけでなく、便利な関数も提供しています。 その一部をご紹介します。

イーサリアムのアプリケーションでは、通常、通貨の単位を変換する必要があります。 Web3のモジュールには、この変換のためだけのfromWei(opens in a new tab)toWei(opens in a new tab)のようなヘルパーメソッドがあります。

注: コンピュータは小数の計算処理が苦手です。 小数点以下を含む計算を避けるため、デベロッパーはしばしばドルをセントに換算して格納します。 例えば、価格が $5.99の商品は、599としてデータベースに保存されます。イーサ(ETH)でトランザクションを処理する場合も、同様のパターンが使用されます。 ただし、ETHの小数点は2桁ではなく、18桁です! ETHの最小単位はweiと呼ばれます。これが、トランザクションを送信するときに指定される値です。

1 ETH = 1000000000000000000 wei

1 wei = 0.000000000000000001 ETH

好きな数字でweiとETH(ether)を変換してみてください。 なお、ETHとweiの間には、さまざまな単位の名称があります(opens in a new tab)。 その中でよく使われているのは、gweiです。これは基本的に手数料を意味します。

1In [2]: Web3.to_wei(1, 'ether')
2Out[2]: 1000000000000000000
3
4In [3]: Web3.from_wei(500000000, 'gwei')
5Out[3]: Decimal('0.5')
コピー

Web3モジュールのその他のユーティリティメソッドとしては、データ形式のコンバータ(例: toHex(opens in a new tab)) 、アドレスヘルパー(例: isAddress(opens in a new tab)) 、ハッシュ関数(例: keccak(opens in a new tab))が挙げられます。 これらについては、この連載の後半で取り上げます。 利用可能なすべてのメソッドとプロパティを表示するには、Web3と入力して、 ピリオドの後にタブキーを2回押してください。これにより、IPythonの自動補完機能が起動します。

チェーンとの通信

便利な手法も素晴らしいですが、ブロックチェーンの話に移りましょう。 次のステップでは、Web3.pyがイーサリアムノードと通信できるように設定します。 ここでは、IPC、HTTP、またはWebSocketプロバイダーを使用することができます。

HTTPプロバイダーを使用した場合の完全なワークフローの例としては、次のようなものがあります(ここでは、この手順は実行しません)。

  • イーサリアムノード(Geth(opens in a new tab)など)をダウンロードします。
  • 1つのターミナルで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アプリケーションをつなげるEthereumTesterProviderを表す図

EthereumTesterProviderはシミュレートされたノードに接続します。これは、簡単な開発環境で使用する場合に便利です。

このシミュレートされたノードはeth-tester(opens in a new tab)と呼ばれ、前述のpip install web3[tester]コマンドでインストールされています。 以下のコマンドで、テストプロバイダーを使用するようWeb3.pyを簡単に設定できます。

1In [4]: w3 = Web3(Web3.EthereumTesterProvider())
コピー

これで「チェーンサーフィン」をする準備ができました。 そのような呼び方をする人はいないと思いますが、 そう呼ぶことにしました。 さあ、クイックツアーを始めましょう。

クイックツアー

最初に、正常に接続できているかサニティチェックをします。

1In [5]: w3.is_connected()
2Out[5]: True
コピー

テスタープロバイダーを使用しているため、このテストはあまり有用ではありません。失敗した場合は、w3変数をインスタンス化するときに入力ミスをした可能性があります。 内側の括弧、つまりWeb3.EthereumTesterProvider()が含まれていることを再確認してください。

ツアー #1: アカウント

テスタープロバイダーでは便宜上、複数のアカウントが作成されており、テスト用のETHが入金されています。

まず、これらのアカウントの一覧を表示しましょう。

1In [6]: w3.eth.accounts
2Out[6]: ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
3 '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF',
4 '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69', ...]
コピー

このコマンドを実行すると、0xで始まる10個の文字列のリストが表示されます。 それぞれがパブリックアドレスであり、これらはある意味、当座預金の口座番号のようなものです。 あなた宛てにETHを送金する人に、これらのアドレスを教えることになります。

前述のとおり、テスタープロバイダーでは、これらの各アカウントにテスト用のETHがあらかじめ入金されています。 では、最初のアカウントの残高を確認しましょう。

1In [7]: w3.eth.get_balance(w3.eth.accounts[0])
2Out[7]: 1000000000000000000000000
コピー

この0の数を見てください! 満面の笑みで偽の銀行に駆け込む前に、前述した通貨の単位の説明を思い出してください。 ETHの値は、最小単位であるweiで表されます。 ETH(ether)に変換すると次のようになります。

1In [8]: w3.from_wei(1000000000000000000000000, 'ether')
2Out[8]: Decimal('1000000')
コピー

テスト用とはいえ、100万ETHは依然としてすごい額です。

ツアー #2: ブロックデータ

このシミュレートされたブロックチェーンの状態を見てみましょう。

1In [9]: w3.eth.get_block('latest')
2Out[9]: AttributeDict({
3 'number': 0,
4 'hash': HexBytes('0x9469878...'),
5 'parentHash': HexBytes('0x0000000...'),
6 ...
7 'transactions': []
8})
コピー

ブロックに関する多くの情報が返されますが、いくつか注意すべき点があります。

  • テスタープロバイダーを設定した時期がいつであっても、ブロック番号はゼロになります。 実際のイーサリアムネットワーク(新しいブロックが12秒ごとに追加される)とは異なり、このシミュレーションは、作業を行うまで待機します。
  • まだ何も作業を行っていないため、同じ理由で、transactionsは空のリストになります。 最初のブロックは空のブロックであり、チェーンを開始するためだけに使用されています。
  • parentHashは、単なる一連の空バイトである点に注意してください。 これは、始まりのブロックとも呼ばれる、チェーンの最初のブロックであることを意味します。

ツアー #3: トランザクション

保留中のトランザクションが存在するまで、ゼロのブロックで止まっているので、トランザクションを作成してみましょう。 あるアカウントから別のアカウントに、テスト用のETH(ether)を少し送金します。

1In [10]: tx_hash = w3.eth.send_transaction({
2 'from': w3.eth.accounts[0],
3 'to': w3.eth.accounts[1],
4 'value': w3.to_wei(3, 'ether'),
5 'gas': 21000
6})
コピー

通常はここで、自分のトランザクションが新しいブロックに含まれるまで数秒間待つことになります。 全体のプロセスは次のようになります。

  1. トランザクションを送信し、トランザクションハッシュを保持します。 トランザクションを含むブロックが作成され、ブロードキャストされるまで、トランザクションは「保留中(pending)」になります。 tx_hash = w3.eth.send_transaction({ … })
  2. トランザクションがブロックに含まれるのを待ちます。 w3.eth.wait_for_transaction_receipt(tx_hash)
  3. アプリケーションロジックが続行されます。 成功したトランザクションを表示します。 w3.eth.get_transaction(tx_hash)

このシミュレーション環境では、トランザクションが新しいブロックに瞬時に追加されるので、そのトランザクションをすぐに確認できます。

1In [11]: w3.eth.get_transaction(tx_hash)
2Out[11]: AttributeDict({
3 'hash': HexBytes('0x15e9fb95dc39...'),
4 'blockNumber': 1,
5 'transactionIndex': 0,
6 'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf',
7 'to': '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF',
8 'value': 3000000000000000000,
9 ...
10})
すべて表示
コピー

fromtovalueの各フィールドは、send_transaction呼び出し時の入力と一致しなければなりません。 このトランザクションが、ブロック番号1の最初のトランザクション('transactionIndex': 0)として含まれていることを確認できることも心強い点です。

さらに、関係する2つのアカウントの残高を確認することで、このトランザクションが成功していることを簡単に検証できます。 1つ目のアカウントから2つ目のアカウントへ、3 ETHが移動しているはずです。

1In [12]: w3.eth.get_balance(w3.eth.accounts[0])
2Out[12]: 999996999979000000000000
3
4In [13]: w3.eth.get_balance(w3.eth.accounts[1])
5Out[13]: 1000003000000000000000000
コピー

2つ目のアカウントの残高は、 1,000,000 ETHから1,000,003 ETHに増えているので、正しいようです。 1つ目のアカウントはどうなったのでしょうか? 3 ETHより少し多い金額が失われたようです。 残念ながら、人生にはタダというものはなく、イーサリアムのパブリックネットワークを利用するには、そのサポート役であるピアに対価を支払う必要があります。 トランザクションを送信したアカウントから、少額のトランザクションフィーが差し引かれました。このフィーは、消費されたガスの量(ETH送金では21000単位のガス)にベースフィーを掛けたものです。ベースフィーは、ネットワークのアクティビティに加えて、ブロック内にトランザクションを含めるバリデータに送信されるチップによって異なります。

ガスの詳細

注: パブリックネットワークにおいてトランザクションフィーは、ネットワークの需要やどれだけ迅速にトランザクションを処理する必要があるのかによって変動します。 フィー(手数料)の計算方法の内訳に興味がある場合は、[ブロックに含まれるトランザクションの仕組み](https://medium.com/ethereum-grid/ethereum-101-how-are-transactions-included-in-a-block-9ae5f491853f)に関する以前の投稿をご覧ください。

ちょっと一息

しばらく続けたので、ここで一息つきたいところです。 イーサリアムの世界はまだまだ広く、この連載の第2回ではさらに深く掘り下げていきます。 今後のコンセプトは、実際のノードへの接続、スマートコントラクト、トークンです。 何かご質問があれば、 ぜひお聞かせください。 皆様からのご意見は、今後の活動に反映させていきます。 Twitter(opens in a new tab)でリクエストを受け付けています。

最終編集者: @hesoponyo(opens in a new tab), 2024年4月1日

このチュートリアルは役に立ちましたか?