ClojureでBitcoinアドレスを生成する


初めに

最近話題のBitcoinですが、いざ仕組みを調べてみようと思うと、非技術者向けの例え話や暗号理論に偏りすぎているものが多くて、なかなか実像を掴みにくいです。
そこで、この記事では1つ1つコードで実装しながら段階を踏んで、実際に使用できるBitcoinアドレスを生成していきます。

生成アルゴリズム

準備

鍵の生成

まずは、ECDSA sampleで鍵のペアを生成します。このとき、curveはsecpk1-256を選んでください。
また、クライアントサイドで生成しているとのことなので、秘密鍵の流出については大丈夫かと思われますが、心配な方はopensslでローカルで生成してください。

関数の準備

byte列を扱う上で、必要となる関数を先に定義しておきます。

;; "0cab3f...."というbyte列の文字列をbyteのシーケンスに変換
(defn read-byte [x]
  (->> (partition-all 2 x)
    (map (partial apply str))
    (map hex2dec)
    (map byte)))

;; (read-byte "0cab3f")
;; => (12 -85 63)
;; "0cab3f...."というbyte列の文字列をbyte-arrayに変換
(defn read-bytes [x]
  (->> (partition-all 2 x)
    (map (partial apply str))
    (map hex2dec)
    (map byte)
    byte-array))

;; (read-bytes "0cab3f")
;; => #object["[B" 0x36519d "[B@36519d"]
(defn hex2dec [x]
  (let [n (Long/parseLong x 16)]
    (if (>= n 128) (- n 256) n)))

鍵の定義

(def pubkey "0452fc6e03ce4d2125167a16...")
(def prvkey "02f29aae71872ed03b32b460...")

生成

呼称

これ以降は、SHA256->RIPEMD160をHash160、SHA256->SHA256をHash256とします。
また、Hash #1-2 を主ハッシュ、Hash#2-2をチェックサムと呼びます。

Hash160

(require '[pandect.core :as p])

(defn hash160 [value]
  (-> value (p/sha256-bytes) (p/ripemd160)))

Hash256

(defn hash256 [value]
  (-> value (p/sha256-bytes) (p/sha256)))

base58

(def base-string "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")

(defn int-to-base58 [num leading-zeros]
  (loop [acc [] n num]
    (if (pos? n)
      (let [s (nth base-string (rem n 58))]
        (recur (cons s acc) (quot n 58)))
      (apply str (concat (repeat leading-zeros (first base-string)) acc)))))

(defn encode [bytes]
  (let [leading-zeros (->> bytes (take-while zero?) count)
        n (java.math.BigInteger. 1 (byte-array bytes))]
    (int-to-base58 n leading-zeros)))

参考: dbasch/base58

主ハッシュ

(defn gen-main-hash [k]
  (str nw-id (hash160 (read-bytes k))))

チェックサム

(defn gen-checksum [k]
  (->> (hash256 (read-bytes (gen-main-hash k)))
    (take 8)
    (apply str)))

アドレス

(defn gen-address [k]
  (->> (str (gen-main-hash k) (gen-checksum k))
    read-byte
    encode))

実際に生成

(gen-address pubkey)
=> "1MGmkfg9kse2Aribbg2fWGGRGqhVxCFetF"

検証

生成したアドレスの検証はBlockExplorerで行います。
https://blockexplorer.com/address/生成したアドレス に飛ぶと、正当なアドレスならトランザクションやQRコードが表示されます。
例: https://blockexplorer.com/address/1MGmkfg9kse2Aribbg2fWGGRGqhVxCFetF

参考文献

bitcoinwiki
Bitcoinを技術的に理解する
ビットコインの仕組み
Mastering Bitcoinを読む Chapter4 公開鍵、ビットコインアドレス
dbasch/base58