ブロックチェーンで勉強でつまづかないためにハッシュ関数について調べる

# はじめに
4月からブロックチェーンについて調査を始めました。
最初はEthereumやSolidityやTruffleなどブロックチェーン関係でよく聞くキーワードで検索してデモを真似して作ってみるという作業をしていましたが、いまいち理解出来ない日が続きました。

そこで改めて基礎から理解しようと調べていたところ、ブロックチェーンの理解には**ハッシュ化**というプロセスが重要そうということが分かってきました。
個人的にはハッシュ化という概念を知ったことでブロックチェーンについて理解が深まった気がしています。
ということで今回はハッシュ化について書いていった後、イーサリアムでの秘密鍵、公開鍵、アドレスの生成方法について検証していきたいと思います。

# 本記事の対象者
* これからブロックチェーンについて学んでいきたい方
* Solidityなどのチュートリアルをやってみたが仕組みはいまいちよく分からない方

# 本記事の対象じゃない方
* 既にブロックチェーンの仕組みやスマートコントラクトについて理解している方
* 既にスマートコントラクトを自力で開発できる方

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [ハッシュ化とは](#Chapter1)
2. [Pythonでハッシュ化処理の確認](#Chapter2)
3. [Ethereumでの秘密鍵→公開鍵→アドレスの流れ](#Chapter3)
4. [参考文献](#Reference)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# 1.ハッシュ化とは
ブロックチェーンのサービスを利用する際にウォレットなどを使うと思いますが、ウォレットのアドレスを生成する時にハッシュ化という処理が行われています。アドレスは銀行口座の口座番号のようなものです。

※例えば、よく使われるウォレットのMetaMaskの画面で記載されている。「0xEE6...」という数字の羅列がアドレスです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2514460/36bdf654-62a4-cdbc-9611-43859fa912f4.png)

ハッシュ化とは、ある文字列や数字の羅列を一定のルール(ハッシュ関数)に基づいた計算手順によって別の値(ハッシュ値)に置換すること。

ハッシュ関数の特徴として主に3つあげられます。
1. 関数を通したあとのデータからは、もとのデータが何か分からない。
1. どんなデータをハッシュ化しても、同じ長さのハッシュ値になる。
1. 同じデータを何度ハッシュ化しても、常に同じハッシュ値になる。

上記3つの特徴があるハッシュ値をブロックに記帳していくことで、改ざんが難しいデータ保存が実現されます。
詳しい説明は[EnterChain](https://enterchain.online/home)というサイトの説明がとても分かりやすかったので参考にしてみてください。

<a id="#Chapter2"></a>
# 2.Pythonでハッシュ化処理の確認
ここからはPythonでハッシュ化の処理を行ってみます。

## 実行環境
Google Colaboratoryを活用しました。

## ツール
今回はPythonのhashlibというライブラリでハッシュ値の生成を行ってみました。

## コード
まずはハッシュという入力データをハッシュ値に置換したいと思います。
※ブロックチェーンではSHA256という種類のハッシュ値がよく使われるそうなので、今回はSHA256を使ってみます。

```python hash1.py
# -*- coding: utf-8 -*-
import hashlib 

message = "佐藤"

#ハッシュ値を求めた結果
a = hashlib.sha256(message.encode("utf-8")).hexdigest()
print(a)
len(a)
```


**出力結果**
```
7cd4c4592a343046c20e95436c242fe385bd0d84a0ca4290af0f7b0eee2a257f
64
```

佐藤のハッシュ値として**7cd4c4592a343046c20e95436c242fe385bd0d84a0ca4290af0f7b0eee2a257f**が出力され、文字数は**64文字**でした。
ハッシュ値→もとのデータの推測はできないことが分かります。

次にという入力データをハッシュ値に置換してみたいと思います。
```python hash2.py
import hashlib 
message2 = "鈴木"

#ハッシュ値を求めた結果
b = hashlib.sha256(message2.encode("utf-8")).hexdigest()
print(b)
len(b)
```

**出力結果**
```
5358567fbd3ac1b8150589b5e95646f694f3dc13b377b8f1bb3c46243e106308
64
```

鈴木のハッシュ値として**5358567fbd3ac1b8150589b5e95646f694f3dc13b377b8f1bb3c46243e106308**が出力され、文字数は**64文字**でした。

別の値をハッシュ化しても同じ64文字になりました。

最後に佐藤を再度ハッシュ化したいと思います。
```python hash3.py
message3 ="佐藤"

#ハッシュ値を求めた結果
c = hashlib.sha256(message3.encode("utf-8")).hexdigest()
print(c)
len(c)
a == c 
```

**出力結果**
```
7cd4c4592a343046c20e95436c242fe385bd0d84a0ca4290af0f7b0eee2a257f
64
True
```

同じデータをハッシュ化しても、同じハッシュ値になっています。

<a id="#Chapter3"></a>
# 3.Ethereumでの秘密鍵→公開鍵→アドレスの流れ
ちなみにEthreumではハッシュ関数等を活用し、下記の流れでアドレスを生成しています。

1. 秘密鍵からECDSA(楕円曲線DSA)で公開鍵（64バイト）を生成
2. 公開鍵をハッシュ関数Keccak-256に通し文字列（32バイト）を得る
3. 最初の12バイトを消し20バイトのアドレスになる
4. そのアドレスにprefixの0xを加えることで最終的なアドレスを得る(0x5eD8Cee6b63b1c6AFce3AD7c92f4fD7E1B8fAd9F)

秘密鍵→公開鍵→アドレスの順でデータを生成していることが分かります。
上記の流れもPythonで再現してみたいと思います。

### 秘密鍵(Private Key)の作成
```python
!pip install ecdsa
```

```python
from ecdsa import SECP256k1
from ecdsa import SigningKey

sk = SigningKey.generate(curve=SECP256k1) #this is your sign (private key)
private_key = sk.to_string().hex() #private key to hex (=16進数)
print("Private key (hex): ",private_key)
```

**出力結果**
```
Private key (hex):  1ee322d2fef6de95151127615c2fb62fdd5c3f67bf08278d154261d6ab4cc1f8
```

### 秘密鍵から公開鍵(Public Key)の作成
楕円曲線暗号を活用し、秘密鍵から公開鍵を作成します。
```python
vk = sk.get_verifying_key() #this is your verification key (public key)
public_key_hex = vk.to_string().hex()
print("Public key (hex)： {}".format(public_key_hex))
```

**出力結果**
```
Public key (hex)： 22924320e31b923ae778a77339a1f6522e23a431319ed454e04119f23053b67994a1a43204c3b107d66759c89bb53ccaa9949a07e0ae2f6aa8cca7aa9354deaa
```

public keyのx,y座標を出力する。
``` python
public_key_x = public_key_hex[:64]
public_key_y = public_key_hex[64:]
print("x of Public key: {}".format(public_key_x))
print("y of Public key: {}".format(public_key_y))
```

**出力結果**
```
x of Public key: 22924320e31b923ae778a77339a1f6522e23a431319ed454e04119f23053b679
y of Public key: 94a1a43204c3b107d66759c89bb53ccaa9949a07e0ae2f6aa8cca7aa9354deaa
```

非圧縮点を意味する"04"prefixを先頭につけて、シリアライズする。
```python
public_key = "04" + public_key_x + public_key_y
print("Public key (serialized): {}".format(public_key))
```

**出力結果**
```
Public key (serialized): 0422924320e31b923ae778a77339a1f6522e23a431319ed454e04119f23053b67994a1a43204c3b107d66759c89bb53ccaa9949a07e0ae2f6aa8cca7aa9354deaa
```

### アドレス(Address)の作成
公開鍵をハッシュ関数に通してハッシュ値を得ます。
Keccak256というハッシュ関数を用いて公開鍵からアドレスを作成します。

```python 
!pip install pysha3
```

```python
import sha3

k = sha3.keccak_256()
k.update(public_key_hex.encode("utf-8"))
k_output = k.hexdigest()
k_output
```

**出力結果**
```
eca1ad35847ae91e24c8e3a6ee18d19bd0798fc8d911a6f16da7f9103d4c4429
```

20バイトのアドレスにする
```python
address = k_output[-40:]
address
```

**出力結果**
```
ee18d19bd0798fc8d911a6f16da7f9103d4c4429
```

prefixの0xを先頭に加える
```python
address_prefix = "0x" + address
address_prefix
```

**出力結果**
```
0xee18d19bd0798fc8d911a6f16da7f9103d4c4429
```

イーサリアムのウォレットで使われていそうなアドレスが生成できました。
このあと、EIP-55というルールで大文字小文字混在にし、アドレスの入力誤りを検知できるようにするためのチェックサムを埋め込むこともあります。興味があれば調べてみてください。

# まとめ
ハッシュ値の性質について確認した後に、秘密鍵、公開鍵、アドレスの生成について検証してみました。イーサリアムの裏側の仕組みについて確認したことでブロックチェーンへの理解度が少し高まった気がします。何かご参考になれば幸いです。


<a id="#Reference"></a>
# 4.参考文献
- [EnterChain](https://enterchain.online/home)
- [「ハッシュ化とは？」今さら聞けない！基本の『キ』](https://www.videor.co.jp/digestplus/market/2019/04/13407.html)
- [Pythonのhashlibでsha256ハッシュ値を求める](https://www.mathpython.com/ja/sha256/?msclkid=a297b7eacf4011ec98fe233cd00ee347)
- [秘密鍵から公開鍵そしてアドレスが生成されるまでの流れ【仮想通貨】](https://zoom-blc.com/from-private-key-to-address)
- [Pythonでイーサリアムで使われている暗号技術を学ぶ ① 秘密鍵・楕円曲線・公開鍵・アドレス - ](https://jp.magicode.io/ganesya/articles/8f8fc444240d49d0a48708f269ada1f1)