web3.pyでEthereumのガス価格を調べてみた

# Ethreumのガス価格高い...

「Ethreumのガス価格高い...きっとリリース当初は安かっただろうに、いつからこんなに高くなったんだよ！」
と思ったので、Ethreumのガス価格がいつ上がったのか、勉強を兼ねてガス価格を調べて~~推移グラフを作って~~みようと思います。

「推移グラフどうすれば作れるんだろう？解析系とかグラフはPythonだよね！」
「Pythonといば、jupyter notebookだよね！」
と、どちらも使ったことがないので勉強を兼ねてpythonとjupyter notebookでやってみようと思います。

# jupyter notebookのdockerを探す

自分で構築するのは手間なので、jupyterのサイトからDockerのイメージを拾ってきます。
https://jupyter.org/index.html
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359665/503ab7bf-30b3-d80c-97da-66fd8e398076.png)


コンテナのイメージ見つからない...
私の見逃しかもしれませんが、見つからないのでDockerHubより拾ってきます。
https://hub.docker.com/search?q=jupyter%20notebook&type=image

公式っぽいやつがありました。
https://hub.docker.com/u/jupyter
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359665/e2fff3c3-0f42-6088-86d8-592b1c68aa3b.png)

色々なイメージがあるので、Documentを参照
https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html
どれがよいのわからないので、とりあえずbaseでやってみたいと思います！

# jupyter notebookを動かしてみる

[Running Container](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/running.html)を参考に動かしていきます。

```console
docker run -p 8888:8888 -v 'pwd':/home/jovyan/work jupyter/base-notebook
```

立ち上がりました！
![Screenshot_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359665/486cb746-91cc-d3dd-2186-57511833bd8c.png)

動かし方がわからないので、以下を参照。
[The Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/stable/notebook.html)
[The Python Tutorial](https://docs.python.org/3/tutorial/)

Jupyter Notebookは、テキスト書きながらPythonも動かせるの便利ですね！
では、本命のweb3を動かしていきます！

# web3をインストールする
[Web3.py](https://web3py.readthedocs.io/en/stable/)を参考にインストールしていきます！

```console
$ pip install web3
gcc -pthread -B /opt/conda/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/opt/conda/include/python3.7m -c cytoolz/dicttoolz.c -o build/temp.linux-x86_64-3.7/cytoolz/dicttoolz.o
  unable to execute 'gcc': No such file or directory
  error: command 'gcc' failed with exit status 1
```

いきなり詰まった...gccがないと...
インストールしてきます。

```console
$ apt install gcc
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
```

権限なし、ルートですか？と聞かれました...
sudoコマンドで実施！と、と行きたいところです、rootのパスワードがわからないのでsudo使えません。

[メモ: Jupyter使ってみた](https://qiita.com/tukiyo3/items/7eb46892d11833077952)を参照してみると、オプションでsudo使えるようになるらしいです。
[Docker Options](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/common.html?highlight=GRANT_SUDO#docker-options)のページにも記載ありますね。

では、気を取り直して再実施！

```console
docker run -p 8888:8888 -e GRANT_SUDO=yes --user root -v 'pwd':/home/jovyan/work jupyter/base-notebook start-notebook.sh
```

```console
jovyan@23d55cfc4fd4:~$ sudo apt install gcc
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package gcc
```

sudoできたみたいですが、gccのパッケージが見つからないらしいです...

[Ubuntu 18.04のapt installで”Unable to locate package”のエラーはsources.listを編集して解決できる](https://tech.sairilab.com/2018/09/post-171/)
を見てみると設定内容がおかしい可能性がある？確認してみます。

```
$ cat /etc/apt/sources.list
# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
# newer versions of the distribution.
deb http://archive.ubuntu.com/ubuntu/ bionic main restricted
# deb-src http://archive.ubuntu.com/ubuntu/ bionic main restricted

## Major bug fix updates produced after the final release of the
## distribution.
deb http://archive.ubuntu.com/ubuntu/ bionic-updates main restricted
# deb-src http://archive.ubuntu.com/ubuntu/ bionic-updates main restricted

## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
## team. Also, please note that software in universe WILL NOT receive any
## review or updates from the Ubuntu security team.
deb http://archive.ubuntu.com/ubuntu/ bionic universe
# deb-src http://archive.ubuntu.com/ubuntu/ bionic universe
deb http://archive.ubuntu.com/ubuntu/ bionic-updates universe
# deb-src http://archive.ubuntu.com/ubuntu/ bionic-updates universe

## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
## team, and may not be under a free licence. Please satisfy yourself as to
## your rights to use the software. Also, please note that software in
## multiverse WILL NOT receive any review or updates from the Ubuntu
## security team.
deb http://archive.ubuntu.com/ubuntu/ bionic multiverse
# deb-src http://archive.ubuntu.com/ubuntu/ bionic multiverse
deb http://archive.ubuntu.com/ubuntu/ bionic-updates multiverse
# deb-src http://archive.ubuntu.com/ubuntu/ bionic-updates multiverse

## N.B. software from this repository may not have been tested as
## extensively as that contained in the main release, although it includes
## newer versions of some applications which may provide useful features.
## Also, please note that software in backports WILL NOT receive any review
## or updates from the Ubuntu security team.
deb http://archive.ubuntu.com/ubuntu/ bionic-backports main restricted universe multiverse
# deb-src http://archive.ubuntu.com/ubuntu/ bionic-backports main restricted universe multiverse

## Uncomment the following two lines to add software from Canonical's
## 'partner' repository.
## This software is not part of Ubuntu, but is offered by Canonical and the
## respective vendors as a service to Ubuntu users.
# deb http://archive.canonical.com/ubuntu bionic partner
# deb-src http://archive.canonical.com/ubuntu bionic partner

deb http://security.ubuntu.com/ubuntu/ bionic-security main restricted
# deb-src http://security.ubuntu.com/ubuntu/ bionic-security main restricted
deb http://security.ubuntu.com/ubuntu/ bionic-security universe
# deb-src http://security.ubuntu.com/ubuntu/ bionic-security universe
deb http://security.ubuntu.com/ubuntu/ bionic-security multiverse
# deb-src http://security.ubuntu.com/ubuntu/ bionic-security multiverse
```

別途ubuntuのコンテナ立ち上げて比較してみましたが、設定的には特に問題なさそう？
接続先の調子が悪いのかもしれないので変えてみます。

```console
$ sudo sed -i.bak -e "s%http://[^ ]\+%http://ftp.riken.go.jp/Linux/ubuntu/%g" /etc/apt/sources.list
$ sudo apt update
$ sudo apt install -y gcc
```

上手くインストールできたみたいです。改めてweb3をインストールしていきます。

```console
$ pip install web3
```

エラーらしきものは出ていないので、多分大丈夫！

# web3を動かしてみる
web3のインストールが終わったので、[web3 クイックスタート](https://web3py.readthedocs.io/en/stable/quickstart.html)を参考に動かしていきます！
クイックスタートによれば、以下で実行すれば[自動検出](https://web3py.readthedocs.io/en/stable/providers.html#automatic-provider-detection)したノードで接続してくれるらしいです。

```python
from web3.auto import w3

## 最新ブロック番号確認
w3.eth.blockNumber

## 結果
CannotHandleRequest: Could not discover provider while making request: method:eth_blockNumber
```

エラーです...
そもそも自動検出できず接続できていないと思うので、接続を確認してみます。

```python
from web3.auto import w3
# 接続
connected = w3.isConnected()
# 接続確認
w3.isConnected()
​
## 結果
False
```

思っていた通り、接続できていませんね。
自動検出できないので、[プロバイダー経由(Infura利用)](https://web3py.readthedocs.io/en/stable/providers.html#automatic-provider-detection)で実施していきます。

```python
import os
os.environ['WEB3_INFURA_PROJECT_ID'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

from web3.auto.infura import w3

## 接続確認
w3.isConnected()

## 結果
True

## 最新ブロック番号確認
w3.eth.blockNumber

## 結果
8649321
```

上手くいきました！
これで、本題のgas価格調査に専念できます！

# gas価格どうやって調べる？
[web3のDocument](https://web3py.readthedocs.io/en/stable/web3.eth.html)を読んだ感じ、支払われたgas価格を一括で取得することや、1ブロック内のgas価格の平均を取得ことはできなさそうです。

なので、確定済みの各トランザクションを参照して設定されていたgas価格を取得。
また、ブロック情報よりいつ支払われたのかを集めていきたいと思います。

## 大まかな流れ
1. 指定したブロック番号のブロック情報を取得
2. ブロック情報よりタイムスタンプを取得
3. ブロック情報より、そのブロックに書き込まれたトランザクションのハッシュキーを取得
4. 取得したハッシュキーに紐づくトランザクション情報を取得
5. トランザクション情報よりgas価格を取得
6. 上記1～5を最新ブロック~はじめのブロックまで実行

それではやっていきたいと思います！

# 取得してみる

とりあえず、ブロック情報からデータを取得しています。

```python
## 最新のブロック番号取得
blockNum = w3.eth.blockNumber

## ブロック内のトランザクション数取得
trnCnt = w3.eth.getBlockTransactionCount(blockNum)

## ブロック情報取得
blockInfo = w3.eth.getBlock(blockNum)
blockTime = blockInfo.timestamp
print(blockTime)

# ハッシュ取得
for i in blockInfo.transactions:
    print(i.hex())

## 結果
### ブロックのタイムスタンプ
1569839877

### Hashキー
0x30e53b0d2fcd311c3e86882411fbe5a664aad4c2e916b2dc66f14b7dfb5c9607
0x9eb0014f39a8b125c9fb3af16acb656ba5521fbb131e7d048a06418f4e58d5c9
0x3d3ea22a206d9ab677d491d29d6b3a7872bf656ea9fde957eaaa5fbc20010bfa
0xfe4fbf0134bca20b648439e324c4d7050cf3aa1f298aa9dbe358952abc717ef3
0x5e5dc0a326af387d4a5fad2fccc7c413a916e2b7a69d5d92870d144cac35268b
0xbf98e08abbb19a3d19361283d6cdfc32356e6028ebde0f8f8ed587ef5db9873c
0x80d70995a09d2d5fa63d7f6634d3d9ed7aada5a983087c6263b7df8a597ef867
0xcdaaf2c0e9db6b09d428cfab445967f06d8d908b8608b92f728ff40dc8a03325
0x0578d7d5d9203625d639b6f2c47505eb0dfc898212e6223fb9a7f329848199e6
0x8f85c2c7215822c3d41112e13b59137463d01aabd714fd4e8045b98ad57e637e
0x0458e77ede635cbc8854ad3c453af8b855ec19b2392742d58a91965967b42b75
## ※多いので省略

```

ブロックのタイムスタンプとブロック内のトランザクションのハッシュが取得できました。
次に、ハッシュをキーにトランザクション情報を参照、ガス価格を取得してみます。

```python

# ハッシュ取得
for i in blockInfo.transactions:
    trn = w3.eth.getTransaction(i.hex())
    print(trn)

# 結果
AttributeDict({'blockHash': HexBytes('0x37c104512632908379055be95a37ea4816fce5d146ca566b2ca48c8e510608a0'), 'blockNumber': 8707563, 'from': '0x4cab0C59508A05B093a8b935efC7d83536985948', 'gas': 180000, 'gasPrice': 50000000000, 'hash': HexBytes('0x8644667289396baaea59d8a04f5c3c49697138f17a29a99224fc58bda8671a18'), 'input': '0xa9059cbb000000000000000000000000bf569c643205f4cb07cfe3a1e28051f8813c8d1500000000000000000000000000000000000000000000000000000000105df629', 'nonce': 6, 'r': HexBytes('0x679e675a2b4e8960346a07b70ca9bea64fd92de29ceebaad5346d80832d4e0e4'), 's': HexBytes('0x046d311a054e432cc8554849d3b5f9f76258a43e23369175c429e663e9de0b3d'), 'to': '0xdAC17F958D2ee523a2206206994597C13D831ec7', 'transactionIndex': 0, 'v': 27, 'value': 0})
50000000000
AttributeDict({'blockHash': HexBytes('0x37c104512632908379055be95a37ea4816fce5d146ca566b2ca48c8e510608a0'), 'blockNumber': 8707563, 'from': '0xCAc725beF4f114F728cbCfd744a731C2a463c3Fc', 'gas': 90000, 'gasPrice': 40000000000, 'hash': HexBytes('0x438b7eb12c940ac383132a2cd34c35caf2548a929696a3bec043eab200c8455d'), 'input': '0x', 'nonce': 59587, 'r': HexBytes('0x021cf194001c5216f3df0afcf1ba6b94a294ba42db52c1c40f34d24d7afdce49'), 's': HexBytes('0x6d36c62d25d69e871488943495321cd74e54bb40fae27a643fd5537ca425e8df'), 'to': '0x0F2103e5D3A43232C772d96e693129AC275C4580', 'transactionIndex': 1, 'v': 37, 'value': 27939000000000000000})
40000000000
AttributeDict({'blockHash': HexBytes('0x37c104512632908379055be95a37ea4816fce5d146ca566b2ca48c8e510608a0'), 'blockNumber': 8707563, 'from': '0xd93F91139A5cDA0cd7173Efb2272bfe88f016B77', 'gas': 90000, 'gasPrice': 30000000000, 'hash': HexBytes('0xdf063fa82276f4db0f2aba3bd4aabced2f0cf0924cd1d6cd1151866a946b3604'), 'input': '0xa9059cbb000000000000000000000000fa886a0441ce8e12d8c8b73b5110c95a785a22b9000000000000000000000000000000000000000000000032bd9c00dc05a00000', 'nonce': 7735, 'r': HexBytes('0x8bd5ff2eb2b3638377456a9a6054627b1caf79ef3069bcfbe0ff2d589cd29048'), 's': HexBytes('0x4d71f94f64fa964bbe3885693be0b4ad2d28e86354be54de455e848a56ad4c44'), 'to': '0x62B9f8741Bf53a6986A5411C0557c30F6F11f3af', 'transactionIndex': 2, 'v': 37, 'value': 0})
30000000000
AttributeDict({'blockHash': HexBytes('0x37c104512632908379055be95a37ea4816fce5d146ca566b2ca48c8e510608a0'), 'blockNumber': 8707563, 'from': '0xa71C8bAe673f99ac6c0f32C56eFc89A8DDb9A501', 'gas': 80000, 'gasPrice': 30000000000, 'hash': HexBytes('0x5020fd3158aab9d170f4965b2e74e8a941904f5b9ba90352980e1bef2aae606b'), 'input': '0xa9059cbb0000000000000000000000005ceff524f39017d8a40888990a9b0507ad21d92e00000000000000000000000000000000000000000000000000000001b99a3b60', 'nonce': 104025, 'r': HexBytes('0x1bb58f072d989f33605027221763c3f3e8de4fa44f931c15330f399e84ee0295'), 's': HexBytes('0x7acd5f7448b546bb191644bfcce29e2c1d97fbdd562aa229448909c5653acf14'), 'to': '0x2E65E12b5f0fD1D58738c6F38dA7D57F5F183d1c', 'transactionIndex': 3, 'v': 37, 'value': 0})
30000000000
AttributeDict({'blockHash': HexBytes('0x37c104512632908379055be95a37ea4816fce5d146ca566b2ca48c8e510608a0'), 'blockNumber': 8707563, 'from': '0x2aa7Ccf96915E8bbcdAf1119E5D12fB925671126', 'gas': 90000, 'gasPrice': 30000000000, 'hash': HexBytes('0x2f258d817e4eb008e364e08ceb8775aa1f77c8172527ac1ddc636928089f6d2b'), 'input': '0xa9059cbb00000000000000000000000069d6c1db6aa3f3d6a4862b0ed95c54e588d4b1e90000000000000000000000000000000000000000000000055de6a779bbac0000', 'nonce': 7724, 'r': HexBytes('0xe159b028de1112b228118940ad93358d827adfb0f2b62fa316f01a6a83836e83'), 's': HexBytes('0x152e4b5b67bfddf2a101c3c6bb41ee259415e0ce235276bda609fcfcca11cdbf'), 'to': '0x62B9f8741Bf53a6986A5411C0557c30F6F11f3af', 'transactionIndex': 4, 'v': 38, 'value': 0})
30000000000

## ※多いので省略
```

トランザクションからガス価格を取得することができました。
次に上記で取得したガス価格をブロック毎で平均をとってCSVに書き出していきます。
一旦最新ブロックから100前までを取得してCSVに書き出すようにしてみました。

```python
import os
import csv

os.environ['WEB3_INFURA_PROJECT_ID'] = 'xxxxxxxxxxxxxxxxxxxxxxxxx'

from web3.auto.infura import w3

## 最新ブロックから遡るブロック数定義
loopCnt= 100

## 最新のブロック番号取得
blockNum = w3.eth.blockNumber
startNum = blockNum - loopCnt

for n in range(loopCnt):
    ## 確認対象のブロック番号生成
    checkBlockNum = startNum + n
        
    ## ブロック内のトランザクション数取得
    trnCnt = w3.eth.getBlockTransactionCount(checkBlockNum)
    
    ## ブロック情報取得
    blockInfo = w3.eth.getBlock(checkBlockNum)
    blockTime = blockInfo.timestamp

    trnPriceList = []

    # ブロックから指定カウント数遡ってループ
    for i in blockInfo.transactions:
        # トランザクション情報取得
        trn = w3.eth.getTransaction(i.hex())
        trnPriceList.append(trn.gasPrice)

    # トランザクションが存在する場合のみ稼働
    ave = 0
    if (len(trnPriceList) > 0):
        ave = sum(trnPriceList) / len(trnPriceList)
    
    with open('test.csv', 'a') as f:
        writer = csv.writer(f)
        writer.writerow([str(checkBlockNum), str(blockTime), str(ave)]) 

```

以下、取得結果です。
左から、ブロック番号、エポックタイム、ガス価格の平均です。

```text:test.csv
8668152,1570090620,25737295016.04712
8668153,1570090663,15188108970.96063
8668154,1570090680,15081271002.94958
8668155,1570090693,10705396821.333334
8668156,1570090700,23601282993.980263
8668157,1570090729,17564398681.084034
8668158,1570090749,14801600786.350994
8668159,1570090767,6162881355.932203
8668160,1570090768,2621904761.904762
8668161,1570090784,25980830650.065117
8668162,1570090833,20152735185.724136
8668163,1570090868,29673365116.25523
8668164,1570090905,15378159519.74603
8668165,1570090909,24083973707.967136
8668166,1570090951,22836224048.909092
8668167,1570090961,6250193358.596491
8668168,1570090973,17731984959.88158
8668169,1570090992,14799237565.67401
8668170,1570091012,17137592202.861925
8668171,1570091031,12502113690.261084

※長いので省略
```

CSVに書き出すことができました！
次にこのCSVを入力にグラフを作っていこうと思いますが、~~熱が冷めたので~~記事が長くなってきたので次回にしようと思います！

# 参考

[attributedict 0.3.0](https://pypi.org/project/attributedict/)
[hexbytes 0.2.0](https://pypi.org/project/hexbytes/)
[HexBytesオブジェクトから16進文字列を取得する方法は？](https://stackoverflow.com/questions/50508444/how-to-get-hex-string-from-a-hexbytes-object)