Web3.0ブロックチェーン開発の学びメモ - 7日目
概要
こちらの記事の 7 日目です!
今日はウォレットの作成を実装していこうと思います。引き続きですが、こちらの Udemy 講座を参考にさせてもらっています。
現役シリコンバレーエンジニアが教える Go で始めるスクラッチからのブロックチェーン開発入門
とても分かりやすく、丁寧に説明されているので、興味があればぜひ購入してみてください。
ウォレットの作成
まずは復習になりますが、ウォレットには対になる秘密鍵、公開鍵とアドレスを持っています。
公開鍵は秘密鍵から作成され、アドレスは公開鍵から作成されるといったイメージになります。
トランザクションデータをノードに送信する際のイメージは以下です。
- トランザクションを秘密鍵によって署名する。→ 図の Signature を得る
- 公開鍵、Signature(トランザクションを秘密鍵によって署名したもの)、トランザクションのデータをノードに送信する
- ノードは公開鍵によって、Signature を復号し、トランザクションデータを得る。
- 一緒に送信したトランザクションデータと復号した結果が正しいかを検証する。
この流れで検証することによって、改ざんされたかどうかをチェックすることができる。といった感じです!
鍵とアドレスを作成するコード
まず、publicKey, privateKey, address は以下のような手順で作成していきます。
かなり複雑なのですが、これは Bitcoin の各キーとアドレスの作成手順に則ったものとなっておりますので、理解すると言うよりはルールとして割り切って進めた方が良いかもです!
- PrivateKey と PublicKey を作成
- PublicKey に SHA-256 をかけてハッシュ化する →32byte
- 2 でハッシュ化したものをさらに RIPEMD-160 でハッシュ化 →20byte
- ブロックチェーンのメインネットワークの場合は先頭 1btey に「0x00」を付け加える →21byte
- 4 の結果に SHA-256 をかける
- 5 の結果にさらに SHA-256 をかける
- 6 の結果の最初の 4byte を checksum として使用
- 4 の結果の末尾 4byte に 7 で得た checksum を付け加える →25byte
- 8 の結果に base58 でエンコードしたものがブロックチェーンのアドレスとなる。
このような手順を踏んでいきます。実際にコードを見ていきましょう!
type Wallet struct {
privateKey *ecdsa.PrivateKey
publicKey *ecdsa.PublicKey
blockchainAddress string
}
func NewWallet() *Wallet {
// 1. PrivateKeyとPublicKeyを作成
privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
w := new(Wallet)
w.privateKey = privateKey
w.publicKey = &w.privateKey.PublicKey
// 2. PublicKeyにSHA-256をかけてハッシュを作成→32byte
h2 := sha256.New()
// publicKeyのX,Yでハッシュ化(digest2)
h2.Write(w.publicKey.X.Bytes())
h2.Write(w.publicKey.Y.Bytes())
digest2 := h2.Sum(nil)
// 3. 2でハッシュ化したものをさらにRIPEMD-160でハッシュ化→20byte(digest3)
h3 := ripemd160.New()
h3.Write(digest2)
digest3 := h3.Sum(nil)
// 4. ブロックチェーンのメインネットワークの場合は先頭1bteyに「0x00」を付け加える
vd4 := make([]byte, 21)
vd4[0] = 0x00
copy(vd4[1:], digest3[:])
// 5. 4の結果にSHA-256をかける
h5 := sha256.New()
h5.Write(vd4)
digest5 := h5.Sum(nil)
// 6. 5の結果にさらにSHA-256をかける
h6 := sha256.New()
h6.Write(digest5)
digest6 := h6.Sum(nil)
// 7. 6の結果の最初の4byteをchecksumとして使用
chsum := digest6[:4]
// 8. 4の結果の末尾4byteに7で得たchecksumを付け加える→25byte
dc8 := make([]byte, 25)
copy(dc8[:21], vd4[:]) //最初の21byteが4の結果
copy(dc8[21:], chsum) //末尾4桁が7で得たchecksum
// 9. 8の結果にbase58でエンコードしたものがブロックチェーンのアドレスとなる。
address := base58.Encode(dc8)
w.blockchainAddress = address
return w
}
ここら辺はかなり複雑になっていると思いますので、ルールとして知っておくくらいで良いかと思います。
Signature(トランザクションの署名)の生成と検証
秘密鍵とトランザクションから Signature を生成
上の図で見てきた Signature を生成する流れをコードで見ていきます!
type WalletTransaction struct {
senderPrivateKey *ecdsa.PrivateKey
senderPublicKey *ecdsa.PublicKey
senderBlockchainAddress string
recipientBlockchainAddress string
value float32
}
type Signature struct {
R *big.Int
S *big.Int
}
func (wt *WalletTransaction) GenerateSignature() *Signature {
m, _ := json.Marshal(wt) //MarshalJSONをオーバーライドして、senderPrivateKey, senderPublicKey以外のプロパティをマーシャルする。
h := sha256.Sum256([]byte(m))
r, s, _ := ecdsa.Sign(rand.Reader, wt.senderPrivateKey, h[:]) //秘密鍵とトランザクションデータからSignature(RとS)を生成
return &Signature{r, s}
}
GenerateSignature メソッド内で ecdsa.Sign を使用して Signature を作成しています。
検証
送信されてきたトランザクションをブロックチェーン(ノード側)で検証するコードを見ていきます。
func (bc *Blockchain) VerifyTransactionSignature(senderPublicKey *ecdsa.PublicKey, s *Signature, t *Transaction) bool {
m, _ := json.Marshal(t)
h := sha256.Sum256([]byte(m))
return ecdsa.Verify(senderPublicKey, h[:], s.R, s.S)
}
ecdsa.Verify を使用して、公開鍵、トランザクション情報、signature を元に改ざんされていなければ true が返るようなメソッドになります!
まとめ
ざっと見ていきましたが、ここはセキュリティの知識も必要で難しかったですね...
一旦、今はこうゆうものなのだという感じの理解で良いかと思います!
今後、もしブロックチェーンに関連する案件に関わることができたら深掘っていこうと思います
総まとめ
7 日間かけて一旦、ブロックチェーンの概要と Go でコードを用いて主要な部分を学んできました!
今回、参考にさせていただいたこちらの講座ではさらに Web サーバを構築して、動作するアプリケーションを開発するところまで解説があるので、気になった方はぜひチェックしてみてください。
私は年末年始休みがほとんど終わってしまい、嫁さんが家を出ても困るので今回はこの辺にしておこうと思います 😅 笑
では、2024 年、今年も頑張っていきましょう!!
Discussion