FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

RISC-VのCompressed命令のISS実装 (1)

RISC-VのCompressed命令について理解したので、自作ISSへの実装を進めた。

Compressed 命令の仕様については以下のまとめた。

msyksphinz.hatenablog.com

というわけで、実装自体はテンプレートに従って追加していったのだが、いくつか注意しなければならないのが、上記のエントリにも書いたように命令のエンコーディングがかぶっているところだ。 RV32 と RV64/RV128 で、同じエンコーディングでも命令が異なっている部分がある。 ここについては少しケアしないとすべてのテストパタンを動作させることはできない。 とりあえずRV64のみ対応させ、RV32 / RV64の切り替えについては別の機構を追加することにした。

テストパタンについては、2種類用意されており、これをどちらもパスさせなければならないのが最終目標だ。 とりあえず、RV64のテストパタンだけ通すことにした。

  • rv64uc-v-rvc : RV64 Compressed命令テスト、仮想アドレスモード
  • rv64uc-p-rvc : RV64 Compressed命令テスト、物理アドレスモード
  • rv32uc-v-rvc : RV32 Compressed命令テスト、仮想アドレスモード
  • rv32uc-p-rvc : RV32 Compressed命令テスト、物理アドレスモード

とりあえず RV64UC-V-RVC については最後までテストが通るようになった。

f:id:msyksphinz:20180606233716p:plain
図. RV64 RVCのテストパタン実行結果。RVC命令が正しくデコードされ実行されている。

次は、RV32モードでのテストパタンをパスさせよう。

TensorFlow+Kerasに入門(2. Keras2cppを試行)

FPGAの部屋のmarseeさんの記事を見て、TensorFlow+Kerasに入門してみた。 というかmarseeさんの記事で掲載されているソースコードをほとんどCopy & Pasteして実行してみているだけだが...

前回でKerasでMNISTの学習ができるようになったので、次はモデルをJSONで出力、重みパラメータをh5で保存して、C++のプログラムに変換してみたい。 というか、Pythonで実行するのが面倒なので基本的にC++で実行できるようになりたいという話だけなのだが...

メンテナンスはされていないのかもしれない?が、keras2cppというツールを試行してみた。

一点注意なのは、keras2cppは推論の部分のみをC++に変換するらしい。学習済みのモデルとパラメータを使ってC++に変換するようだ。

Keras2cppを使うためには、Kerasで生成したモデルとパラメータを出力する。Jupyter Notebook上で以下を実行した。

from keras.models import load_model

model.save('mnist_cnn10_model.h5') # creates a HDF5 file 'my_model.h5'
with open('mnist_cnn10_model.json', 'w') as fout:
    fout.write(model.to_json())

これで、JSONとh5ファイルが保存されるので、Keras2cppでC++のファイルに変換する。 ただし、keras2cppはPython2.x系で動作するようで、とりあえずKerasを動かすためにPython 2.xのTensorFlowとKerasをインストールした。

$ pip2 install tensorflow keras

これで変換を行う。以下のコマンドを実行する。

$ python ./dump_to_simple_cpp.py -a ../machine-learning/keras/keras-introduction/mnist_cnn10_model.json -w ../machine-learning/keras/keras-introduction/mnist_cnn10_model.h5 -o dumped.nnet

dumped.nnetが生成されたので確認する。単なるテキストファイルのようだ。

layers 8
layer 0 Conv2D
layer 1 Activation
relu
layer 2 MaxPooling2D
2 2
layer 3 Flatten
layer 4 Dense
1440 100
[-0.00869748 -0.03056295  0.02002885  0.05073868 -0.02439169 -0.01678224
 -0.00400479 -0.05224665  0.00741209  0.06284878  0.02699389  0.05822603
 -0.02994106 -0.030139    0.03076001 -0.05264932  0.00020877 -0.0064121
  0.03952856 -0.03737402  0.04069051  0.05051897  0.05892574  0.06181276
 -0.00088991 -0.01981324 -0.0137962  -0.03498058  0.03866504  0.02167791
...

これを使ってC++のコードをコンパイルする。以下のコードを実行する。随分とシンプルだが、内部のソースコードを見ると、テスト用の入力として./example/sample_mnist.datをロードしていた。

g++ -std=c++11 keras_model.cc example_main.cc
  • example_main.cc
  DataChunk *sample = new DataChunk2D();
  sample->read_from_file("./example/sample_mnist.dat");
  std::cout << sample->get_3d().size() << std::endl;
  KerasModel m("./example/dumped.nnet", true);
  m.compute_output(sample);

sample_mnist.dat の入力画像で、Excelでプロットしてみると'5'だった。なるほど。

f:id:msyksphinz:20180701135331p:plain

さて、コンパイルしたa.outを実行してみよう。最後に最終レイヤの値一覧が表示されている。

$ ./a.out
This is simple example with Keras neural network model loading into C++.
Keras model will be used in C++ for prediction only.
1
Reading model from ./example/dumped.nnet
Layers 12
Layer 0 Convolution2D
Layer 1 Activation
Layer 2 Convolution2D
Layer 3 Activation
Layer 4 MaxPooling2D
Layer 5 Dropout
Layer 6 Flatten
Layer 7 Dense
Layer 8 Activation
Layer 9 Dropout
Layer 10 Dense
Layer 11 Activation
DataChunk2D 1x28x28
DataChunkFlat values:
0.037296 0.00783805 0.0658804 0.217285 0.0109373 0.34731 0.0135039 0.0217452 0.266247 0.0119571

というわけで、5に相当する値が0.34731 で最も大きい。正しく推論ができているようだ。

f:id:msyksphinz:20180701135833p:plain

「ブロックチェーン・プログラミング」を読む (ビットコイン送金を体験)

ブロックチェーン・プログラミング」第2章はビットコインの基本を復習しておく。 Mastering Bitcoinを読んでいるので、ある程度理解できるのでサクサク読み進めていく。

今回は第4章の途中まで。第3章は暗号技術でちょっと難しいのでとりあえず読み飛ばす。

ブロックチェーン・プログラミング 仮想通貨入門 (KS情報科学専門書)

ブロックチェーン・プログラミング 仮想通貨入門 (KS情報科学専門書)

4. ビットコイン・ワレット

4.1 ワレットの初期化

下記のコマンドにより、自分の秘密鍵と公開鍵が生成される。2つのビットコインアドレスを生成したのは、送受信をチェックするためだ。

$ bitcoin-cli getnewaddress
2N4b5AbK45czUSr9FeL2sXfivh49v9nanAy
$ bitcoin-cli getnewaddress
2N6bBDdqbAcXbD3fqD2jMcAMzcKfP1aUELj

次に、アカウントを設定する。送信元のビットコインアドレスについてはアカウントを設定しないと、送信ができないらしい。そんなもん?

$ bitcoin-cli setaccount 2N4b5AbK45czUSr9FeL2sXfivh49v9nanAy msyksphinz
  • ワレット秘密鍵の暗号化する。パスワードにより暗号化する。
$ bitcoin-cli encryptwallet <パスワード>
wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.

次に、testnetでビットコインを入手する。https://testnet.manu.backend.hamburg/faucet を参照する。

![1528119813642](C:\msys64\home\masayuki\work\manuscript\bitcoin\testnet_wallet.png)

ビットコインを入手する。これにより、0.275BTC が自分のワレットに転送された。

$ bitcoin-cli getbalance
0.27500000
$ bitcoin-cli listaccounts
{
  "": 0.00000000,
  "msyksphinz": 0.27500000
}

4.3 ビットコインの送金

まずはビットコインの送金のために、まずは取引手数料を設定する。

$ bitcoin-cli settxfee 0.00001
true

つぎに、受信先アドレスに対して送金を行った。送信額は0.1BTCだ。




このトランザクションを確認してみよう。

$ bitcoin-cli gettransaction b7b240c18ecabc9cdfcabbc6a2cfe407f52a1f15d4907504b04cf613441b44d3
{
  "amount": 0.00000000,
  "fee": -0.00000166,
  "confirmations": 5,
  "blockhash": "0000000000000015c537afbf8b6a4084fc690041506af2b2dec26015a01ed69e",
  "blockindex": 11,
  "blocktime": 1528120342,
  "txid": "b7b240c18ecabc9cdfcabbc6a2cfe407f52a1f15d4907504b04cf613441b44d3",
  "walletconflicts": [
  ],
  "time": 1528120295,
  "timereceived": 1528120295,
  "bip125-replaceable": "no",
  "details": [
    {
      "account": "msyksphinz",
      "address": "2N6bBDdqbAcXbD3fqD2jMcAMzcKfP1aUELj",
      "category": "send",
      "amount": -0.10000000,
      "label": "",
      "vout": 1,
      "fee": -0.00000166,
      "abandoned": false
    },
    {
      "account": "",
      "address": "2N6bBDdqbAcXbD3fqD2jMcAMzcKfP1aUELj",
      "category": "receive",
      "amount": 0.10000000,
      "label": "",
      "vout": 1
    }
  ],
  "hex": "0200000000010102e517bb9c31447838b5d84359da3df79f19665763fa3ff1c57f7ee3c5ba395e00000000171600146fc7265dad5654f1721c5945daf11e8fde5e7578feffffff02ba060b010000000017a9147778f63cb2d4c6f0326938e7a405fd304b60def787809698000000000017a914925fa46ef41f3d1b37683a73091764948d2261c98702483045022100aa6e36bbab10e8b3b4a95bd83711d02aaf7b2f1b4169012ebcdc1862e984851c02207b3880824a1fec98f3522e37b7b5378d42324b0e689139341f15700d0c3076f30121023cda393b2cd148239c138d1f7e01d74127c08c1eabd11f146ae40db92ed2c69a272c1400"
}

4.4 ビットコイン・ワレットによる暗号鍵管理

ビットコイン・ワレットは日本語にすると財布だが、通常の財布よりもセキュリティを強化する必要がある。店舗のワレットのビットコインアドレスがただ1つだとすると、その1つの秘密鍵が漏洩すると、その店の資産がすべて盗まれてしまう。

ビットコインの送金履歴は誰でも読み取ることができるので、第三者が購入者の購入履歴を追跡することが可能になるため、店舗が受領の度にビットコインアドレスを使い捨てにする方式で、プライバシーを問題を解決される。

セキュリティを確保するために有名な手法として、2レベルセキュリティという手法がある。以下の2つのストレージに分けて管理することである。

  • ホットストレージ : オンライン状態のマシンで管理するストレージ
  • コールドストレージ : オフライン状態のマシンで管理するストレージ

ここでリスクを避けるためには、ホットストレージにおける公開鍵の管理とコールドストレージの秘密鍵の管理が完全に独立に運用できることが理想である。

ECDSAにおいて、秘密鍵と独立鍵を独立に生成する方法を考える。楕円曲線暗号の公開鍵の足し算は、火砲の定義から以下のようになる。

 (a+b)G=aG+bG

ここで  priv を最初の秘密鍵 privG を最初の公開鍵とする。このとき、 (priv+i) を新しい秘密鍵とすると、これに対応する公開鍵は (priv+i)G=privG+iG となり、 privG は既に明らかになっているため、パラメータGにより公開鍵を生成できることを意味する。

このような親の鍵から子の鍵を生成している方法を決定性ワレットと呼ぶ。

しかしこのままでは i を一つずつずらしながら試行することで公開鍵を推定することができるようになってしまう。したがって、インデックスに加えて256ビットのサイズを持つ巨大な乱数を1つ追加する。これにハッシュ関数を適用する。このインデックスと乱数を含んだハッシュ値チェーンコード(chain code)と呼ばれている。最初の秘密鍵 mマスター秘密鍵と呼ばれ、マスター秘密鍵から生成された M=mGマスター公開鍵 と呼ばれる。

 \text{chain_code}=h(i, c)

したがって、秘密鍵と公開鍵の関係は以下のようになる。

 (priv+\text{chain_code})G = privG + \text{chain_code}G

ビットコインの決定性ワレットでは、ルートシードというデータを秘密情報の起点にして、マスター秘密鍵と最初のチェーンコードを生成している。

これでも、ルートシードが漏洩してしまった場合は、すべての秘密鍵が芋づる式に漏洩してしまうので、一般ユーザのワレットのセキュリティとしてストレージのセキュリティレベルをより多くのレベルに分割する。この方法を階層的決定性ワレット(HDワレット) と呼ぶ。

4.5 HDワレットの実装例

HDワレットの実装例をまずは写経だ。

ルートシードとマスター鍵を作成する。

irb(main):001:0> require 'bitcoin'
=> true
irb(main):002:0> require 'securerandom'
=> false
irb(main):004:0> root_seed = SecureRandom.hex (32)
=> "a16f2eb626481c1fe2e7d80f7b9fa7c980107d0173a5bc170618a6c741fa1f5b"
irb(main):005:0> master_key = Bitcoin::ExtKey.generate_master (root_seed.htb)
=> #<Bitcoin::ExtKey:0x00005570693cea68 @number=0, @depth=0, @parent_fingerprint="00000000", @priv_key=#<Bitcoin::Key:0x0000557069403b28 @key=#<OpenSSL::PKey::EC:0x0000557069403a88>, @pubkey_compressed=true>, @chain_code="\xD1\xDD\x1C\xB9&IAs\xB5\xD2i8:\xD3\xD3Tb\x83\xE7\xF2\xF8i\xF7\xC0H\xDF\xD6\xE2\x9B\xAB\xE1Z">
irb(main):006:0> m = master_key.priv
=> "b00b62ff659fee0d8f517cc8d80f21b59ccb354eaaefac85bccb777ee90c137c"
irb(main):007:0> M = master_key.pub
=> "03e96f71630c1a138fc5f7fe06c4bb42024c0b6506aa6d62168251479b1e678d92"
irb(main):008:0> master_key.chain_code
=> "\xD1\xDD\x1C\xB9&IAs\xB5\xD2i8:\xD3\xD3Tb\x83\xE7\xF2\xF8i\xF7\xC0H\xDF\xD6\xE2\x9B\xAB\xE1Z"
irb(main):009:0> master_key.depth
=> 0
irb(main):010:0> key44 = master_key.derive (2*31+44)
=> #<Bitcoin::ExtKey:0x00005570694817f8 @number=106, @depth=1, @parent_fingerprint="f86ba5b0", @priv_key=#<Bitcoin::Key:0x0000557069480d30 @key=#<OpenSSL::PKey::EC:0x0000557069480cb8>, @pubkey_compressed=true>, @chain_code="\x1EL\xBE\xBE\xEA\x9El\x18\b\x01\x16\x03\xA7\xA7;i\xDC\xFF\xC6\x15}\x8A\x87\x8C1\x01>\xC2\xEE\x8B^V">
irb(main):011:0> key44.depth
=> 1
irb(main):012:0> key440=key44.derive (2*31+0)
=> #<Bitcoin::ExtKey:0x000055706946d078 @number=62, @depth=2, @parent_fingerprint="a6af8e02", @priv_key=#<Bitcoin::Key:0x000055706946bd18 @key=#<OpenSSL::PKey::EC:0x000055706946bca0>, @pubkey_compressed=true>, @chain_code="\x8B\x9E\rG08\xF3\x7F\xA1\xFE\xED\e\xB9JRS\xDB\x05d2\x92\xC0\x94@\xEB.\xB9\xCF\xF8\x03\x1E\x1F">
irb(main):013:0> key440.depth
=> 2
irb(main):014:0> key4400 = key440.derive (2*31+0)
=> #<Bitcoin::ExtKey:0x000055706944f5f0 @number=62, @depth=3, @parent_fingerprint="80f1d049", @priv_key=#<Bitcoin::Key:0x000055706944e4c0 @key=#<OpenSSL::PKey::EC:0x000055706944e448>, @pubkey_compressed=true>, @chain_code="0\x88\xFD\xA4\xAF\xA9N\xD2;\x82d\vr\f\x8E\x9F\x8D\xEFc_\xF7|\xEE\x142\x9Eo=3\t\x9Bb">
irb(main):015:0> key4400.depth
=> 3
irb(main):016:0> key4401=key440.derive (2*31+1)
=> #<Bitcoin::ExtKey:0x000055706940cc00 @number=63, @depth=3, @parent_fingerprint="80f1d049", @priv_key=#<Bitcoin::Key:0x000055706940ba08 @key=#<OpenSSL::PKey::EC:0x000055706940b990>, @pubkey_compressed=true>, @chain_code="\xBEF\xA1\x05\xD1|p-f\x7F\x1A\xEF\xC8\n\\\x1FB\xD1C\x0F\xBC\x0F\x99\x10\xC0c\xC4i\x97\xF3\xC9A">
irb(main):017:0> key4401.depth
=> 3
irb(main):018:0> key4400.to_base58
=> "xprv9ycPRjdqs6AJMwofjNTG43myRsFXwZwQNWffouUBXDww32up9iRwZAhYd1UhM5nCFkD76BnCGXEzoATrEAoEeqoSga1U95S9MF14G1C4Uoo"
irb(main):019:0> key4401.ext_pubkey.to_base58
=> "xpub6CbjqFAjhTibePZJexeY27NunEQtQiMYWStBgB4Z6eSuzpRZTzqXAaaJWCbB41QejM9wBUZUBxb1GpiDwQbuspGWxaSgx35GBRMLXNtWRkS"
irb(main):020:0> ext_privkey = Bitcoin::ExtKey.from_base58 ("xprv9ycPRjdqs6AJMwofjNTG43myRsFXwZwQNWffouUBXDww32up9iRwZAhYd1UhM5nCFkD76BnCGXEzoATrEAoEeqoSga1U95S9MF14G1C4Uoo")
=> #<Bitcoin::ExtKey:0x0000557069382758 @number=62, @depth=3, @parent_fingerprint="80f1d049", @priv_key=#<Bitcoin::Key:0x00005570693811f0 @key=#<OpenSSL::PKey::EC:0x00005570693806d8>, @pubkey_compressed=true>, @chain_code="0\x88\xFD\xA4\xAF\xA9N\xD2;\x82d\vr\f\x8E\x9F\x8D\xEFc_\xF7|\xEE\x142\x9Eo=3\t\x9Bb">
irb(main):021:0> xt_pubkey = Bitcoin::ExtPubkey.from_base58 ("xpub6CbjqFAjhTibePZJexeY27NunEQtQiMYWStBgB4Z6eSuzpRZTzqXAaaJWCbB41QejM9wBUZUBxb1GpiDwQbuspGWxaSgx35GBRMLXNtWRkS")
=> #<Bitcoin::ExtPubkey:0x0000557068f91838 @depth=3, @number=63, @parent_fingerprint="80f1d049", @chain_code="\xBEF\xA1\x05\xD1|p-f\x7F\x1A\xEF\xC8\n\\\x1FB\xD1C\x0F\xBC\x0F\x99\x10\xC0c\xC4i\x97\xF3\xC9A", @pub_key=#<OpenSSL::PKey::EC::Point:0x0000557068f8aba0 @group=#<OpenSSL::PKey::EC::Group:0x0000557068f8baa0>>>

マスター秘密鍵$m$とマスター公開鍵 $M$ を生成する。

irb(main):005:0> master_key = Bitcoin::ExtKey.generate_master (root_seed.htb)
=> #<Bitcoin::ExtKey:0x00005570693cea68 @number=0, @depth=0, @parent_fingerprint="00000000", @priv_key=#<Bitcoin::Key:0x0000557069403b28 @key=#<OpenSSL::PKey::EC:0x0000557069403a88>, @pubkey_compressed=true>, @chain_code="\xD1\xDD\x1C\xB9&IAs\xB5\xD2i8:\xD3\xD3Tb\x83\xE7\xF2\xF8i\xF7\xC0H\xDF\xD6\xE2\x9B\xAB\xE1Z">
irb(main):006:0> m = master_key.priv
=> "b00b62ff659fee0d8f517cc8d80f21b59ccb354eaaefac85bccb777ee90c137c"
irb(main):007:0> M = master_key.pub
=> "03e96f71630c1a138fc5f7fe06c4bb42024c0b6506aa6d62168251479b1e678d92"
irb(main):008:0> master_key.chain_code
=> "\xD1\xDD\x1C\xB9&IAs\xB5\xD2i8:\xD3\xD3Tb\x83\xE7\xF2\xF8i\xF7\xC0H\xDF\xD6\xE2\x9B\xAB\xE1Z"
irb(main):009:0> master_key.depth
=> 0

第2階層の m/44'を導出する。

irb(main):010:0> key44 = master_key.derive (2*31+44)
=> #<Bitcoin::ExtKey:0x00005570694817f8 @number=106, @depth=1, @parent_fingerprint="f86ba5b0", @priv_key=#<Bitcoin::Key:0x0000557069480d30 @key=#<OpenSSL::PKey::EC:0x0000557069480cb8>, @pubkey_compressed=true>, @chain_code="\x1EL\xBE\xBE\xEA\x9El\x18\b\x01\x16\x03\xA7\xA7;i\xDC\xFF\xC6\x15}\x8A\x87\x8C1\x01>\xC2\xEE\x8B^V">
irb(main):011:0> key44.depth
=> 1

第3階層m/44'/0' を導出する。

irb(main):012:0> key440=key44.derive (2*31+0)
=> #<Bitcoin::ExtKey:0x000055706946d078 @number=62, @depth=2, @parent_fingerprint="a6af8e02", @priv_key=#<Bitcoin::Key:0x000055706946bd18 @key=#<OpenSSL::PKey::EC:0x000055706946bca0>, @pubkey_compressed=true>, @chain_code="\x8B\x9E\rG08\xF3\x7F\xA1\xFE\xED\e\xB9JRS\xDB\x05d2\x92\xC0\x94@\xEB.\xB9\xCF\xF8\x03\x1E\x1F">
irb(main):013:0> key440.depth
=> 2

第4階層のアカウントの導出。

irb(main):014:0> key4400 = key440.derive (2*31+0)
=> #<Bitcoin::ExtKey:0x000055706944f5f0 @number=62, @depth=3, @parent_fingerprint="80f1d049", @priv_key=#<Bitcoin::Key:0x000055706944e4c0 @key=#<OpenSSL::PKey::EC:0x000055706944e448>, @pubkey_compressed=true>, @chain_code="0\x88\xFD\xA4\xAF\xA9N\xD2;\x82d\vr\f\x8E\x9F\x8D\xEFc_\xF7|\xEE\x142\x9Eo=3\t\x9Bb">
irb(main):015:0> key4400.depth
=> 3
irb(main):016:0> key4401=key440.derive (2*31+1)
=> #<Bitcoin::ExtKey:0x000055706940cc00 @number=63, @depth=3, @parent_fingerprint="80f1d049", @priv_key=#<Bitcoin::Key:0x000055706940ba08 @key=#<OpenSSL::PKey::EC:0x000055706940b990>, @pubkey_compressed=true>, @chain_code="\xBEF\xA1\x05\xD1|p-f\x7F\x1A\xEF\xC8\n\\\x1FB\xD1C\x0F\xBC\x0F\x99\x10\xC0c\xC4i\x97\xF3\xC9A">
irb(main):017:0> key4401.depth
=> 3
irb(main):018:0> key4400.to_base58
=> "xprv9ycPRjdqs6AJMwofjNTG43myRsFXwZwQNWffouUBXDww32up9iRwZAhYd1UhM5nCFkD76BnCGXEzoATrEAoEeqoSga1U95S9MF14G1C4Uoo"
irb(main):019:0> key4401.ext_pubkey.to_base58
=> "xpub6CbjqFAjhTibePZJexeY27NunEQtQiMYWStBgB4Z6eSuzpRZTzqXAaaJWCbB41QejM9wBUZUBxb1GpiDwQbuspGWxaSgx35GBRMLXNtWRkS"
irb(main):020:0> ext_privkey = Bitcoin::ExtKey.from_base58 ("xprv9ycPRjdqs6AJMwofjNTG43myRsFXwZwQNWffouUBXDww32up9iRwZAhYd1UhM5nCFkD76BnCGXEzoATrEAoEeqoSga1U95S9MF14G1C4Uoo")
=> #<Bitcoin::ExtKey:0x0000557069382758 @number=62, @depth=3, @parent_fingerprint="80f1d049", @priv_key=#<Bitcoin::Key:0x00005570693811f0 @key=#<OpenSSL::PKey::EC:0x00005570693806d8>, @pubkey_compressed=true>, @chain_code="0\x88\xFD\xA4\xAF\xA9N\xD2;\x82d\vr\f\x8E\x9F\x8D\xEFc_\xF7|\xEE\x142\x9Eo=3\t\x9Bb">
irb(main):021:0> xt_pubkey = Bitcoin::ExtPubkey.from_base58 ("xpub6CbjqFAjhTibePZJexeY27NunEQtQiMYWStBgB4Z6eSuzpRZTzqXAaaJWCbB41QejM9wBUZUBxb1GpiDwQbuspGWxaSgx35GBRMLXNtWRkS")
=> #<Bitcoin::ExtPubkey:0x0000557068f91838 @depth=3, @number=63, @parent_fingerprint="80f1d049", @chain_code="\xBEF\xA1\x05\xD1|p-f\x7F\x1A\xEF\xC8\n\\\x1FB\xD1C\x0F\xBC\x0F\x99\x10\xC0c\xC4i\x97\xF3\xC9A", @pub_key=#<OpenSSL::PKey::EC::Point:0x0000557068f8aba0 @group=#<OpenSSL::PKey::EC::Group:0x0000557068f8baa0>>>

TensorFlow+Kerasに入門(1. 環境構築+サンプルコード実行)

FPGAの部屋のmarseeさんの記事を見て、TensorFlow+Kerasに入門してみた。 というかmarseeさんの記事で掲載されているソースコードをほとんどCopy & Pasteして実行してみているだけだが...

f:id:msyksphinz:20180701130913p:plain

TensorFlow+Kerasの実行環境にはVirtualBox上で動作させるUbuntu 18.04 LTSを使用した。Jupyter Notebookを立ち上げて、ポートを指定してWindows上のブラウザに転送して操作する。

$ sudo apt install -y python-pip
$ sudo pip3 install --upgrade tensorflow keras jupyter matplotlib pandas
$ jupyter-notebook --ip=* --no-browser &

これで、VirtualBox上の設定で8888ポートを別のポートにフォワーディングし、Windows上からアクセスしてJupyter Notebookが操作できるようになった。

少しは待った点としては、PydotがPython 3.x系では開発が中止されているらしく、サンプルプログラムが正しく実行されなかった。 それ以外は、サンプルプログラムと、MNISTのニューラルネットワークのモデルは正しく動作しているようだった。

以下の keras.utils.vis_utils import plot_modelから呼び出したplot_modelが正しく動作しないようだ。これがあるとネットワークの構成などをちゃんと表示してくれるらしいのだが...

# 学習済みモデルの読み込み

from keras.models import load_model

model = load_model('mnist_cnn10_model.h5')
model_list = model.get_weights()
print(model_list)

from keras.utils.vis_utils import plot_model
model.summary()
plot_model(model, show_shapes=True, to_file='./model.png')
  • 実行結果
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-23-e3a6ae28afe6> in <module>()
      9 from keras.utils.vis_utils import plot_model
     10 model.summary()
---> 11 plot_model(model, show_shapes=True, to_file='./model.png')

/usr/local/lib/python3.6/dist-packages/keras/utils/vis_utils.py in plot_model(model, to_file, show_shapes, show_layer_names, rankdir)
    131             'LR' creates a horizontal plot.
    132     """
--> 133     dot = model_to_dot(model, show_shapes, show_layer_names, rankdir)
    134     _, extension = os.path.splitext(to_file)
    135     if not extension:

/usr/local/lib/python3.6/dist-packages/keras/utils/vis_utils.py in model_to_dot(model, show_shapes, show_layer_names, rankdir)
     53     from ..models import Sequential
     54 
---> 55     _check_pydot()
     56     dot = pydot.Dot()
     57     dot.set('rankdir', rankdir)

/usr/local/lib/python3.6/dist-packages/keras/utils/vis_utils.py in _check_pydot()
     18     if pydot is None:
     19         raise ImportError(
---> 20             'Failed to import `pydot`. '
     21             'Please install `pydot`. '
     22             'For example with `pip install pydot`.')

ImportError: Failed to import `pydot`. Please install `pydot`. For example with `pip install pydot`.

「ブロックチェーン・プログラミング」を読む (ビットコインとブロックチェーンの基本)

ブロックチェーン・プログラミング」第2章はビットコインの基本を復習しておく。 Mastering Bitcoinを読んでいるので、ある程度理解できるのでサクサク読み進めていく。

ブロックチェーン・プログラミング 仮想通貨入門 (KS情報科学専門書)

ブロックチェーン・プログラミング 仮想通貨入門 (KS情報科学専門書)

2. ビットコインブロックチェーンの基本

2.1 ビットコイン・ネットワーク

2.2 ブロックチェーンの同期

2.3 電子マネーの理想像を実現したビットコイン

  • 電子マネーの最大の問題: 二重使用
  • ビットコインは、この問題を参加者全員による台帳記録の検査という方法で解決した。

2.4 分散台帳としてのブロックチェーン

2.5 トランザクションのブロードキャストによる送金

2.6 非可逆的記録としてのブロックチェーン

  • 台帳の改ざんを防ぐために用意されている2つの技術: ハッシュチェーンマイニング

  • 暗号学的ハッシュチェーン: データのハッシュ値を次のデータに埋め込み、一連のデータのチェーンを作る。

  • ブロック高: ブロックチェーンの順序を表す。ブロック高が0のブロックを、世界の始まりを意味するジェネシス・ブロックと呼ばれる。

  • プルーフ・オブ・ワークマイニングの計算によって得られる計算結果

    • もしブロックチェーンの内容を書き換えようとすると、ブロックに含まれるプルーフ・オブ・ワークも再計算する必要があり、そのためには計算競争に参加しているすべてのマイナーを上回る計算能力が必要となる。
    • マイニングの能力に応じて、2016ブロックごとに平均時間が産出され、新しくブロックが作成されるのに要する時間の平均が10分になるように調整される。
    • マイニングプール: 複数のマイナーたちが協力してマイニングを行い、提供した計算能力の比率に応じて報酬を分け合う。
  • マイナーが作成した新しいブロックは、ビットコイン・ネットワーク全体にブロードキャストされる。

    • このブロックは他のノードによって正当性を検証され、正しければノードのブロックチェーンに接続される。
    • 正しくなければブロックは破棄されるので、マイナーは慎重にブロックを作成する必要がある。

2.7 公開鍵の電子署名を利用した電子通貨の使用

2.8 ブロックチェーンの記録の正当性とコンセンサス

  • ビットコインの送金情報は世界中のいたるところで発生し、世界中に向けてブロードキャストされるが、ネットワークのレイテンシにより時間がかかる。
    • マイナーがマイニングしたブロックを受け取るノードは、地球上の場所により異なる。ノードにより受け取る順番が異なる可能性がある。
    • 複数の分岐が発生した場合、ビットコインのコンセンサスでは、最も長い枝が正当なブロックチェーンであるというルールになっている。
  • マイナーが意図的に作成したチェーンを最長となるまで伸ばして、正統なチェーンとできるならば、そのマイナーによって歴史が支配できてしまう状態になる。
    1. 送金者による商店への支払いを意味するトランザクションをブロードキャストし、そのトランザクションがブロックに取り込まれたことを確認した商店が商品を発送したとする。
    2. マイナーでもある送金者は、商品を受け取った後に、その支払いに使ったUTXOを自分自身に送信するという、競合する別のトランザクションを作り、商店への支払いトランザクションが含まれるブロックの直前のブロックに後続するブロックに組み込んでブロードキャストする。
  • ただし、上記の状態は、競争相手のマイナーたち全員を上回る計算能力を得た時に限る。
  • コンセンサス・アルゴリズム(合意形成アルゴリズム): ブロックチェーンの中で、分散システム全体で正当な情報を唯一に定める方法。
    • ビザンティン合意問題 / FLP不可能性: 故障や裏切り者が存在するような分散システムにおいて、合意形成アルゴリズムを求める問題。難問。
    • シビル問題多数の架空の人格の集団を作って攻撃を行う。
  • プルーフ・オブ・ワーク法 は、マイナーたちの欲望に基づく計算競争を利用したコンセンサス。
  • トランザクションの確認数: トランザクションがブロックに取り込まれてから現在の最新のブロックが接続されるまで何個のブロックが存在しているか、という数のこと。

RISC-V SpikeシミュレータでC/C++のprintfを実現する仕組み (1. RISC-V バイナリの解析)

RISC-V Spikeシミュレータでは printf()std::cout を使ってもコンソールで出力することができる。

  • test_output.c
#include <stdio.h>
int main ()
{
  printf ("Hello World, C\n");
  return 0;
}
  • test_output.cpp
#include <iostream>
int main ()
{
  std::cout << "Hello World, C++\n";
  return 0;
}
all: test_output_cpp test_output_c
run: run_cpp run_c

test_output_cpp: test_output.cpp Makefile
        riscv64-unknown-elf-g++ -O3 $< -o $@

test_output_c: test_output.c Makefile
        riscv64-unknown-elf-gcc -O3 $< -o $@

run_cpp: test_output_cpp Makefile
        spike -l pk $< > run_output_cpp.log

run_c: test_output_c Makefile
        spike -l pk $< > run_output_c.log
$ make run
Hello World, C
Hello World, C++

このprintf()が正しく実行されるためには、riscv-pk (Proxy Kernel) という仕組みを利用していて、Host とのインタフェースを取り持つモジュール(HTIF)が接続されている。

$ spike pk run_output_cpp
$ spike pk run_output_c

命令のトレースログを取っていき、どのような仕組みで printf() が実現されているのかを探していく。

だいたい以下のようになっていき、途中からecall命令で特権モードに移動するようだ。

main() (00000000000100b0)
  - puts () (00000000000103ac)
    - _puts_r () (0000000000010332)
    - strlen ()
    - __sfvwrite_r () (00000000000106c2)
  • run_output_cpp.log
...
core   0: 0x0000000000011e22 (0x00004781) li      a5, 0
0 0x0000000000011e22 (0x4781) x15 0x0000000000000000
core   0: 0x0000000000011e24 (0x05000893) li      a7, 80
0 0x0000000000011e24 (0x05000893) x17 0x0000000000000050
core   0: 0x0000000000011e28 (0x00000073) ecall   <-- printf() が呼ばれる際のEcall
core   0: exception trap_user_ecall, epc 0x0000000000011e28
core   0: 0x0000000080001854 (0x14011173) csrrw   sp, sscratch, sp
1 0x0000000080001854 (0x14011173) x 2 0x0000000080025000
core   0: 0x0000000080001858 (0x00011463) bnez    sp, pc + 8
1 0x0000000080001858 (0x00011463)
core   0: 0x0000000080001860 (0x00007129) addi    sp, sp, -320
1 0x0000000080001860 (0x7129) x 2 0x0000000080024ec0
core   0: 0x0000000080001862 (0x0000e406) sd      ra, 8(sp)
1 0x0000000080001862 (0xe406)
core   0: 0x0000000080001864 (0x0000ec0e) sd      gp, 24(sp)
1 0x0000000080001864 (0xec0e)
core   0: 0x0000000080001866 (0x0000f012) sd      tp, 32(sp)
1 0x0000000080001866 (0xf012)
core   0: 0x0000000080001868 (0x0000f416) sd      t0, 40(sp)
1 0x0000000080001868 (0xf416)
core   0: 0x000000008000186a (0x0000f81a) sd      t1, 48(sp)
1 0x000000008000186a (0xf81a)
core   0: 0x000000008000186c (0x0000fc1e) sd      t2, 56(sp)
1 0x000000008000186c (0xfc1e)
core   0: 0x000000008000186e (0x0000e0a2) sd      s0, 64(sp)
1 0x000000008000186e (0xe0a2)
...

2018/06/11追記。libpkではなく ${RISCV}/riscv64-unknown-elf/bin/pk だった。このダンプ結果は無意味。

ここから先はriscv-pkに渡っていく。${RISCV}/riscv64-unknown-elf/lib/riscv-pk/libpk.a を見てみる。riscv64-unknown-elf-objdump でダンプしていく。

  • libpk.a のダンプ結果
セクション .text の逆アセンブル:

0000000000000000 <trap_entry-0x2>:
   0:   0001                    nop

0000000000000002 <trap_entry>:
   2:   14011173                csrrw   sp,sscratch,sp  <-- ここにジャンプされる?
   6:   00011463                bnez    sp,e <.L1^B1>
   a:   14002173                csrr    sp,sscratch

000000000000000e <.L1^B1>:
   e:   7129                    addi    sp,sp,-320
  10:   e406                    sd      ra,8(sp)
  12:   ec0e                    sd      gp,24(sp)
  14:   f012                    sd      tp,32(sp)
  16:   f416                    sd      t0,40(sp)
  18:   f81a                    sd      t1,48(sp)
  1a:   fc1e                    sd      t2,56(sp)
  1c:   e0a2                    sd      s0,64(sp)
  1e:   e4a6                    sd      s1,72(sp)

このとき、 riscv-pk/pk/entry.S を追いかけていく。handle_trap() に渡っていく。

trap_entry :entry.S
  - handle_trap : riscv-pk/pk/handlers.c

handlers.c を参照した。handle_syscall() が呼ばれているのかな。

  • riscv-pk/pk/handlers.c
void handle_trap(trapframe_t* tf)
{
  if ((intptr_t)tf->cause < 0)
    return handle_interrupt(tf);

  typedef void (*trap_handler)(trapframe_t*);

  const static trap_handler trap_handlers[] = {
    [CAUSE_MISALIGNED_FETCH] = handle_misaligned_fetch,
    [CAUSE_FETCH_PAGE_FAULT] = handle_fault_fetch,
    [CAUSE_ILLEGAL_INSTRUCTION] = handle_illegal_instruction,
    [CAUSE_USER_ECALL] = handle_syscall,
    [CAUSE_BREAKPOINT] = handle_breakpoint,
    [CAUSE_MISALIGNED_STORE] = handle_misaligned_store,
    [CAUSE_LOAD_PAGE_FAULT] = handle_fault_load,
    [CAUSE_STORE_PAGE_FAULT] = handle_fault_store,
  };

  kassert(tf->cause < ARRAY_SIZE(trap_handlers) && trap_handlers[tf->cause]);

  trap_handlers[tf->cause](tf);
}
  • handlers.c
static void handle_syscall(trapframe_t* tf)
{
  tf->gpr[10] = do_syscall(tf->gpr[10], tf->gpr[11], tf->gpr[12], tf->gpr[13],
                           tf->gpr[14], tf->gpr[15], tf->gpr[17]);
  tf->epc += 4;
}

次に、do_syscall() が呼ばれる。ここからどこへ進むんだ?

  • riscv-pk/pk/syscalls.c
long do_syscall(long a0, long a1, long a2, long a3, long a4, long a5, unsigned long n)
{
  const static void* syscall_table[] = {
    [SYS_exit] = sys_exit,
    [SYS_exit_group] = sys_exit,
    [SYS_read] = sys_read,
    [SYS_pread] = sys_pread,
    [SYS_write] = sys_write,
    [SYS_openat] = sys_openat,
    [SYS_close] = sys_close,
    [SYS_fstat] = sys_fstat,
    [SYS_lseek] = sys_lseek,
    [SYS_fstatat] = sys_fstatat,
    [SYS_linkat] = sys_linkat,
    [SYS_unlinkat] = sys_unlinkat,
    [SYS_mkdirat] = sys_mkdirat,
    [SYS_renameat] = sys_renameat,
    [SYS_getcwd] = sys_getcwd,
    [SYS_brk] = sys_brk,
    [SYS_uname] = sys_uname,
    [SYS_getpid] = sys_getpid,
    [SYS_getuid] = sys_getuid,
    [SYS_geteuid] = sys_getuid,
    [SYS_getgid] = sys_getuid,
    [SYS_getegid] = sys_getuid,
    [SYS_mmap] = sys_mmap,
    [SYS_munmap] = sys_munmap,
    [SYS_mremap] = sys_mremap,
    [SYS_mprotect] = sys_mprotect,
    [SYS_prlimit64] = sys_stub_nosys,
    [SYS_rt_sigaction] = sys_rt_sigaction,
    [SYS_gettimeofday] = sys_gettimeofday,
    [SYS_times] = sys_times,
    [SYS_writev] = sys_writev,
    [SYS_faccessat] = sys_faccessat,
    [SYS_fcntl] = sys_fcntl,
    [SYS_ftruncate] = sys_ftruncate,
    [SYS_getdents] = sys_getdents,
    [SYS_dup] = sys_dup,
    [SYS_readlinkat] = sys_stub_nosys,
    [SYS_rt_sigprocmask] = sys_stub_success,
    [SYS_ioctl] = sys_stub_nosys,
    [SYS_clock_gettime] = sys_stub_nosys,
    [SYS_getrusage] = sys_stub_nosys,
    [SYS_getrlimit] = sys_stub_nosys,
    [SYS_setrlimit] = sys_stub_nosys,
    [SYS_chdir] = sys_chdir,
    [SYS_set_tid_address] = sys_stub_nosys,
    [SYS_set_robust_list] = sys_stub_nosys,
  };

...

set_tohost によりこれらの処理が実現されているようだ。tohostにアクセスすることで処理が行われている?

  • riscv-pk/pk/htif.c
long frontend_syscall(long n, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6)
{
  static volatile uint64_t magic_mem[8];

  static spinlock_t lock = SPINLOCK_INIT;
  spinlock_lock(&lock);

  magic_mem[0] = n;
  magic_mem[1] = a0;
  magic_mem[2] = a1;
  magic_mem[3] = a2;
  magic_mem[4] = a3;
  magic_mem[5] = a4;
  magic_mem[6] = a5;
  magic_mem[7] = a6;

  htif_syscall((uintptr_t)magic_mem);

  long ret = magic_mem[0];

  spinlock_unlock(&lock);
  return ret;
}
  • riscv-pk/machine/htif.c
extern uint64_t __htif_base;
volatile uint64_t tohost __attribute__((section(".htif")));
volatile uint64_t fromhost __attribute__((section(".htif")));
volatile int htif_console_buf;
static spinlock_t htif_lock = SPINLOCK_INIT;
uintptr_t htif;

static void __set_tohost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
{
  while (tohost)
    __check_fromhost();
  tohost = TOHOST_CMD(dev, cmd, data);
}

static void do_tohost_fromhost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
{
  spinlock_lock(&htif_lock);
    __set_tohost(dev, cmd, data);

    while (1) {
      uint64_t fh = fromhost;
      if (fh) {
        if (FROMHOST_DEV(fh) == dev && FROMHOST_CMD(fh) == cmd) {
          fromhost = 0;
          break;
        }
        __check_fromhost();
      }
    }
  spinlock_unlock(&htif_lock);
}

void htif_syscall(uintptr_t arg)
{
  do_tohost_fromhost(0, 0, arg);
}

「ブロックチェーン・プログラミング」を読む (bitcoinツールの導入)

"Mastering Bitcoin"によりビットコインの基礎中の基礎を学んだので、次にブロックチェーンをより実践的に触ってみたいと思い、以下の書籍を購入した。

ブロックチェーン・プログラミング 仮想通貨入門 (KS情報科学専門書)

ブロックチェーン・プログラミング 仮想通貨入門 (KS情報科学専門書)

ツールの導入

bitcoin-cli というツールを使うため、ビルドを行った。Ubuntu 16.04 LTSを使用している。

まずは必要なパッケージの導入だ。これをさぼると、例えばGUI版のインストールが行われない。

$ sudo add-apt-repository ppa:bitcoin/bitcoin
$ sudo apt update
$ sudo apt-get install -y libdb4.8-dev libdb4.8++-dev

$ sudo apt-get install build-essential libtool autotools-dev autoconf pkg-config libssl-dev
$ sudo apt-get install libboost-all-dev
$ sudo apt-get install libqt5gui5 libqt5core5a libqt5dbus5 qttools5-dev qttools5-dev-tools libprotobuf-dev protobuf-compiler
$ sudo apt-get install libqrencode-dev autoconf openssl libssl-dev libevent-dev
$ sudo apt-get install libminiupnpc-dev

次に、GitHubから bitcoin ツールのダウンロードとビルドを行う。

$ clone https://github.com/bitcoin/bitcoin.git
$ cd bitcoin/
$ git checkout v0.16.0
$ ./autogen.sh
$ ./configure
$ make -j4
$ sudo make install

設定ファイル bitcoin.conf の記述

Bitcoinには2つのネットワークがあるらしい。

  • testnet : 開発や実験のためのネットワーク。testnetで取引されるビットコインには価値がない。
  • mainnet : 実際に送金を行うことができるネットワーク。mainnet で取引できるビットコインには価値がある。

怖いので、もちろん testnet を使用する。以下のような設定ファイル bitcoin.conf を作成し、 ${HOME}/.bitcoin/ に配置した。

testnet=3                                 # use testnet
txindex=1                                 # make index, reference transaction ID
server=1                                  # accept command
rest=1                                    # enable REST interface
rpcuser=ユーザ名            # JSON RPC username
rpcpassword='パスワード'   # JSON RPC password
rpcport=18332                 # JSON PRC port number (testnet)

次に、bitcoin-cliを使って情報を取得してみる。書籍では、bitcoin-cli getinfoを使っているが、今回使用した v0.16.0 ではコマンド名が変わっている。

$ bitcoin-cli getinfo
error code: -32601
error message:
getinfo

This call was removed in version 0.16.0. Use the appropriate fields from:
- getblockchaininfo: blocks, difficulty, chain
- getnetworkinfo: version, protocolversion, timeoffset, connections, proxy, relayfee, warnings
- getwalletinfo: balance, keypoololdest, keypoolsize, paytxfee, unlocked_until, walletversion

bitcoin-cli has the option -getinfo to collect and format these in the old format.

例えば、bitcoin-cli getblockchaininfo を実行してみる。

$ bitcoin-cli getblockchaininfo
{
  "chain": "test",
  "blocks": 0,
  "headers": 418000,
  "bestblockhash": "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
  "difficulty": 1,
  "mediantime": 1296688602,
  "verificationprogress": 5.536704525417526e-08,
  "initialblockdownload": true,
  "chainwork": "0000000000000000000000000000000000000000000000000000000100010001",
  "size_on_disk": 293,
  "pruned": false,
  "softforks": [
    {
      "id": "bip34",
      "version": 2,
      "reject": {
        "status": false
      }
    },
    {
      "id": "bip66",
      "version": 3,
...

ネットワークノードの接続状態を確認してみる。8ノードと接続され、安定している。

$ bitcoin-cli getconnectioncount
8