はじめに
4月からブロックチェーンについて調査を始めました。
最初はEthereumやSolidityやTruffleなどブロックチェーン関係でよく聞くキーワードで検索してデモを真似して作ってみるという作業をしていましたが、いまいち理解出来ない日が続きました。
そこで改めて基礎から理解しようと調べていたところ、ブロックチェーンの理解にはハッシュ化というプロセスが重要そうということが分かってきました。
個人的にはハッシュ化という概念を知ったことでブロックチェーンについて理解が深まった気がしています。
ということで今回はハッシュ化について書いていった後、イーサリアムでの秘密鍵、公開鍵、アドレスの生成方法について検証していきたいと思います。
本記事の対象者
- これからブロックチェーンについて学んでいきたい方
- Solidityなどのチュートリアルをやってみたが仕組みはいまいちよく分からない方
本記事の対象じゃない方
- 既にブロックチェーンの仕組みやスマートコントラクトについて理解している方
- 既にスマートコントラクトを自力で開発できる方
目次
1.ハッシュ化とは
ブロックチェーンのサービスを利用する際にウォレットなどを使うと思いますが、ウォレットのアドレスを生成する時にハッシュ化という処理が行われています。アドレスは銀行口座の口座番号のようなものです。
※例えば、よく使われるウォレットのMetaMaskの画面で記載されている。「0xEE6...」という数字の羅列がアドレスです。
ハッシュ化とは、ある文字列や数字の羅列を一定のルール(ハッシュ関数)に基づいた計算手順によって別の値(ハッシュ値)に置換すること。
ハッシュ関数の特徴として主に3つあげられます。
- 関数を通したあとのデータからは、もとのデータが何か分からない。
- どんなデータをハッシュ化しても、同じ長さのハッシュ値になる。
- 同じデータを何度ハッシュ化しても、常に同じハッシュ値になる。
上記3つの特徴があるハッシュ値をブロックに記帳していくことで、改ざんが難しいデータ保存が実現されます。
詳しい説明はEnterChainというサイトの説明がとても分かりやすかったので参考にしてみてください。
2.Pythonでハッシュ化処理の確認
ここからはPythonでハッシュ化の処理を行ってみます。
実行環境
Google Colaboratoryを活用しました。
ツール
今回はPythonのhashlibというライブラリでハッシュ値の生成を行ってみました。
コード
まずはハッシュという入力データをハッシュ値に置換したいと思います。
※ブロックチェーンではSHA256という種類のハッシュ値がよく使われるそうなので、今回はSHA256を使ってみます。
# -*- coding: utf-8 -*-
import hashlib
message = "佐藤"
#ハッシュ値を求めた結果
a = hashlib.sha256(message.encode("utf-8")).hexdigest()
print(a)
len(a)
出力結果
7cd4c4592a343046c20e95436c242fe385bd0d84a0ca4290af0f7b0eee2a257f
64
佐藤のハッシュ値として7cd4c4592a343046c20e95436c242fe385bd0d84a0ca4290af0f7b0eee2a257fが出力され、文字数は64文字でした。
ハッシュ値→もとのデータの推測はできないことが分かります。
次にという入力データをハッシュ値に置換してみたいと思います。
import hashlib
message2 = "鈴木"
#ハッシュ値を求めた結果
b = hashlib.sha256(message2.encode("utf-8")).hexdigest()
print(b)
len(b)
出力結果
5358567fbd3ac1b8150589b5e95646f694f3dc13b377b8f1bb3c46243e106308
64
鈴木のハッシュ値として5358567fbd3ac1b8150589b5e95646f694f3dc13b377b8f1bb3c46243e106308が出力され、文字数は64文字でした。
別の値をハッシュ化しても同じ64文字になりました。
最後に佐藤を再度ハッシュ化したいと思います。
message3 ="佐藤"
#ハッシュ値を求めた結果
c = hashlib.sha256(message3.encode("utf-8")).hexdigest()
print(c)
len(c)
a == c
出力結果
7cd4c4592a343046c20e95436c242fe385bd0d84a0ca4290af0f7b0eee2a257f
64
True
同じデータをハッシュ化しても、同じハッシュ値になっています。
3.Ethereumでの秘密鍵→公開鍵→アドレスの流れ
ちなみにEthreumではハッシュ関数等を活用し、下記の流れでアドレスを生成しています。
- 秘密鍵からECDSA(楕円曲線DSA)で公開鍵(64バイト)を生成
- 公開鍵をハッシュ関数Keccak-256に通し文字列(32バイト)を得る
- 最初の12バイトを消し20バイトのアドレスになる
- そのアドレスにprefixの0xを加えることで最終的なアドレスを得る(0x5eD8Cee6b63b1c6AFce3AD7c92f4fD7E1B8fAd9F)
秘密鍵→公開鍵→アドレスの順でデータを生成していることが分かります。
上記の流れもPythonで再現してみたいと思います。
秘密鍵(Private Key)の作成
!pip install ecdsa
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)の作成
楕円曲線暗号を活用し、秘密鍵から公開鍵を作成します。
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座標を出力する。
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を先頭につけて、シリアライズする。
public_key = "04" + public_key_x + public_key_y
print("Public key (serialized): {}".format(public_key))
出力結果
Public key (serialized): 0422924320e31b923ae778a77339a1f6522e23a431319ed454e04119f23053b67994a1a43204c3b107d66759c89bb53ccaa9949a07e0ae2f6aa8cca7aa9354deaa
アドレス(Address)の作成
公開鍵をハッシュ関数に通してハッシュ値を得ます。
Keccak256というハッシュ関数を用いて公開鍵からアドレスを作成します。
!pip install pysha3
import sha3
k = sha3.keccak_256()
k.update(public_key_hex.encode("utf-8"))
k_output = k.hexdigest()
k_output
出力結果
eca1ad35847ae91e24c8e3a6ee18d19bd0798fc8d911a6f16da7f9103d4c4429
20バイトのアドレスにする
address = k_output[-40:]
address
出力結果
ee18d19bd0798fc8d911a6f16da7f9103d4c4429
prefixの0xを先頭に加える
address_prefix = "0x" + address
address_prefix
出力結果
0xee18d19bd0798fc8d911a6f16da7f9103d4c4429
イーサリアムのウォレットで使われていそうなアドレスが生成できました。
このあと、EIP-55というルールで大文字小文字混在にし、アドレスの入力誤りを検知できるようにするためのチェックサムを埋め込むこともあります。興味があれば調べてみてください。
まとめ
ハッシュ値の性質について確認した後に、秘密鍵、公開鍵、アドレスの生成について検証してみました。イーサリアムの裏側の仕組みについて確認したことでブロックチェーンへの理解度が少し高まった気がします。何かご参考になれば幸いです。