たにしきんぐダム

プログラミングやったりゲームしてます

Bitcoin SPV Wallet を Golang で実装してみた

Golang と Blockchain 技術の勉強に、こちら の記事を参考にして Bitcoin の SPV Wallet を Golang で実装してみました。

github.com

結構雑ですが、とりあえず以下のことができます。

  • Bitcoin Address の生成
  • UTXOをかき集めて残高を計算
  • UTXOをかき集めて指定したBitcoin Addressに送金

実用には耐えませんが簡単なSPV Walletの一通りの機能は実装できたかなと思います

(そんなことなくてエラーハンドリングが雑だったりP2Pノードとしての役目を放棄してたり、取得するmerkleblockをハードコードした高さより高いblockのみ取得するようになってたりする)

参考

実装の際には主に @lotz氏のこちらの記事を参考に実装しました(ほとんど氏の実装をGolangに移植しただけw + 計算したUTXOを使って送り先と金額を指定するだけで送金できる機能)

こちらの記事では同様のWalletがHaskellで実装されており、解説がすごく丁寧で分かりやすいので、細かい解説が気になる方は是非読んでみてください!!

qiita.com

その他参考 🙏🙏

感想

Blockchain技術に興味を持ってMastering Bitcoinを読んだり簡易的なBlockchainもどきを実装してみて、全体像はなんとな~く分かってきたものの

  • Merkle Tree でトランザクションを検証するために Merkle Path を取得するってあるけど、具体的にどういう形で送られてくるんだ!?
  • スクリプトってどう表現されるんだ...
  • 「署名する」ってあるけど具体的にはどうするんじゃ

などなど細かい疑問がたくさんありました。

しかし今回SPV Walletを実装してみて少なくともSPV Walletの実装についてはしっかり理解できました。SPV Walletの細かい挙動に興味のある人は是非氏の記事を読んで自分の好きな言語で実装してみると良さそう!

Golangで簡易的ワーカー/ジョブキュー

  • goroutineの数を制限しつつ並列にジョブを実行したい
  • ジョブは定期的に投入できたい
  • ジョブが空っぽになっても終了しないで欲しい
  • SIGTERMとか送ったら実行中のジョブが完了するまで待ってから終了してほしい

というようなものが欲しくなることが最近よくある!のでメモ

参考

実装

gistbe9fac4aa02419d68c6770a85e53c936

この実装ではmainの中のforループでのみジョブをenqueueしているが、

  • 定期的に外部リソース(APIたたいたり、DBを眺めにいったり)からデータを取得してジョブをenqueueしたり
  • HTTPサーバーをたててHTTP経由でジョブをenqueueできるようにして、別のプロセスから定期的にジョブをenqueueしたり

とかいろいろできて便利〜

JavaScript とクロスブラウザでの IME event handling (2017年)

この記事は CAMPHOR Advent Calendar 2017 22日目の記事です。
昨日は @ryota-ka による Type-level TypeScript - ryota-ka's blog でした。

CompositionEvent が多くの主要ブラウザでサポートされた2017年冬なら、JavaScriptで日本語入力に対応したちょっとした入力補完機能の実装はシュッとできると思ったのですが、ブラウザによって細かい実装が異なっていてちまちまとしたworkaroundが必要になったのでメモ

検証環境

KeyboardEvent.keyCode

IME によるテキスト編集中は keyodown イベントによって発火する KeyboardEventkeyCode229 になります。 これを使えば IMEによるテキスト編集中の keydown イベントにトリガする処理を制御することができる。

editorElem.addEventListener('keydown', function(event) {
  if (event.keyCode !== 229) {
    // do something ...
  }
});

https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode

If an Input Method Editor is processing key input and the event is keydown, return 229.

Gecko

GeckoではIMEによるテキスト編集中はkeydown/keyupイベントが発火しないみたいですね

https://bugzilla.mozilla.org/show_bug.cgi?id=354358

deprecated

KeyboardEvent.keyCode は deprecated なようです。W3Cでも keyCode 属性が記載されていたのは Legacy Key Attributes の欄ですね、keyCode はプラットフォームや実装依存の値なので、排除していきたい流れなのかも。

KeyboardEvent.keyCode - Web APIs | MDN

KeyboardEvent.isComposing

KeyboardEvent.isComposing - Web APIs | MDN

  • IMEのような text composition system によるテキスト編集中は true となるreadonlyな属性
    • 後述する compositionstartcompositionend の間で発火したイベントなら true
  • Browser compatibility
    • MDN の Browser compatibility では(デスクトップの) IE, Safari で未対応と書かれていますが、検証環境の Safari 11.0.2 では KeyboardEvent.isComposing の値を取得することができました。
    • 検証環境の Microsoft Edge 41、IE11 では、KeyboardEvent.isComposingundefined になってしまうようでした。

将来的にはこちらを使うと良いのだろうけど、IE, Edge で未対応なのでまだ使えそうにはないですね...

InputEvent.isComposing

InputEvent.isComposing - Web APIs | MDN

  • text composition system によるテキスト編集中は true となる readonly な属性
    • 後述する compositionstartcompositionend の間で発火したイベントなら true
  • Browser compatibility
    • MDN の Browser compatibility では(デスクトップの) IE, Safari, Chrome, Opera で未対応と書かれていますが、検証環境のOpera49、Google Chrome では InputEvent.isComposing を取得できた。
    • Microsoft Edge 41、IE11 では、KeyboardEvent.isComposingundefined になってしまうようでした。

こちらも将来的にIMEによるテキスト編集中の InputEvent かどうかの判断にはこの値を使うと良いのだろうけど、IE, Edge で未対応だとまだ使えそうにないですね。

CompositionEvent

CompositionEvent - Web APIs | MDN

  • compositionstart: IMEによるテキスト編集開始時に発火
  • compositionupdate: IME で編集中のテキストが変更された時に発火
  • compostionend: IMEによるテキスト編集終了時に発火

  • MDN の Browser compatibility によれば Opera は未サポート、Safari? と書かれていますが、検証環境のSafari, Operaではイベントは発火するようでした。
    • ただし data 属性は未実装だったりする様子
  • InputEventIMEによるテキスト編集中のものかどうかを調べるには InputEvent.isComposing を使う代わりに、CompostionEvent を監視して状態を管理すると良さそう
var isComposing = false;

editorElem.addEventListener('input', function(event) {
  if (!isComposing) {
    // do something ...
  }
});
editorElem.addEventListener('compostionstart', function(event) {
  isComposing = true;
});
editorElem.addEventListener('compostionstart', function(event) {
  isComposing = false;
});

楽ちん

CompositionEvent/InputEvent の発火順序

詳しくは

IMEによるテキスト編集開始/編集中

event note
compositionstart 編集開始時
beforeinput
compositionupdate
ここでDOMが更新される
input

テキスト編集終了時

event note
compositionend
beforeinput
ここでDOMが更新される
input

このようなイベント発火順序となるのでIMEによるテキスト編集中の InputEvent は必ず compositionstartcompositionend の間に発火する、はず。

確認してみた

実験用スクリプトを書いて検証環境のブラウザで確認してみた

  • IE11, Edge41: テキスト編集終了時の input イベントが発火してないように見える?
  • Google Chrome, Safari, Opera: テキスト編集終了時の input イベントが compositionend の前に発火してる?

うーむ僕のスクリプトの書き方が悪いだけなのかな?詳しい人教えてください

CompositionEvent/KeyboardEvent の発火順序

詳しくは https://www.w3.org/TR/uievents/#events-composition-key-events

IMEによるテキスト編集開始/編集中

event note
keydown
compositionstart 編集開始時
compositionupdate
keyup

テキスト編集終了時

event note
keydown
compositionend
input
keyup

確認してみた

またもや雑に実験用スクリプトを使って検証環境で調べてみる

  • Firefox: 最初の方でも書いたけどIME編集中はkeydown/keyupイベントが発火しない https://bugzilla.mozilla.org/show_bug.cgi?id=354358
  • Safari: テキスト編集終了時keydowncompositionend の後に発火してる(このときの keyCode は 229)
  • Edge: テキスト編集終了時に keydownkeyup も発火しない

現状だとブラウザ実装によってイベント発火順序が結構違うみたいですね...あまり使うことはない気がするけど、ブラウザ間のイベント発火順序の違いを吸収するためのworkaroundは Handling IME events in JavaScript – Not Rocket Science に詳しく書かれていて良かった。

参考

まとめ

現状だとブラウザ互換性を考えると結構複雑ですね、2017年になっても日本語入力はやっぱり難しい。

Python機械学習プログラミング を読んだ

機械学習に入門しようと思い、重い腰をあげて実践的な入門書として評判が良さそうだったPython機械学習プログラミング」を読みました。

この本を読んでやっと機械学習分野の全体像がうっすら見えてきて、やっと入り口に立てたなと思います。読んで本当に良かった。 機械学習初心者の人が買うか迷っていたらオススメしたい。

この記事を書いた人のレベル

  • 機械学習に関する知識は情報系の学部レベルの機械学習の授業を受けた程度
  • Pythonの文法とか使い方はだいたい知ってる
  • 情報系の学部初等レベルの数学はある程度分かる(けど結構忘れてる)

読書の進め方

読書会にした

一人で読み進めると絶対途中で飽きるだろうなと思ったので、興味のありそうな友人を2人巻き込んで、読書会形式で進めることにしました。

  • 1~2週間ごとに1章ずつみんな読んでくる
  • みんなで集まって(今回は全員住んでる場所がバラバラだったのでSkypeで)読んだ章で分かりにくかったことなどを話し合う
  • 特に発表資料とかは作ってない、ゆるゆる読書会
  • 勉強会としての体はあまり成してなかったけど、ペースメーカーとして大いに役に立った

読書プラン

翻訳の福島さんがこの本の読み進め方ガイドを書いてくれているのですが、基本的には特訓コース(全部読むコース)でやりつつ、読書会参加メンバーの興味や知識を鑑みて8章と9章(機械学習の応用)と13章(Theano/Kerasの使い方)は興味があったら読んでくるという感じで進めました。 thinkit.co.jp

Theanoは開発中止になっちゃったけど、Python Machine Learning, Second Edition ではTheanoの代わりにTensorFlowを使ったチュートリアルになったり、RNNやCNNについて取り上げた章が追加されているようですね。

どのくらい読み込んだか

  • Pythonでのプログラミング
    • Python自体の文法には慣れていたのですが、NumPyやPandasやscikit-learnを使った機械学習プログラミングの経験はあまり無かったので、最初のうちは写経したり、同様の手法を別のデータに適用したりして遊んだ。
    • 後半からは慣れてきたので写経せずにコード読みつつ、気になる部分だけJupyter Notebookで実験してみるぐらいになってた
  • 機械学習理論

Pros/Cons

良かったところ

  • 手を動かしながら学べるのが良かった
  • 機械学習の実践を見据えた構成になっていて、実際に機械学習プログラムを書くときに、どういうことをすれば良いかが分かった
    • この本をよむ前は
      • 理論的な入門書を読んでも、理論はわかったけど結局どういうことをすれば良いのか分からない
      • Pythonだとどうすれば良いのか分からず、愚直に実装して疲れ果てたり
      • 機械学習入門記事を読んで適当にscikit-learnで機械学習してみたはいいけどモデルの評価とかどうすれば良いのかよくわからない
    • ということが多かったのだけれど、この本を読んですっきりと整理できたのが本当に良かった

あまり良くなかったところ

  • 数式が平易に解説されていて(逆に)少しわかりにくかった。
    • 大学初等レベルの数学が分かっていなくても雰囲気が分かるように平易に書いてくれている(どうしても難しくなるところは潔く省略されてた)のだけれど、個人的には煙に巻かれたような気持ちになった
    • とはいえ他の本を参照すれば全然OK

まとめ

機械学習初心者にとっては本当に良い本でした。機械学習やってみたいけど何すればいいのかよくわからない/理論よりの機械学習入門書読んだけど何すれば良いのか分からんという人には絶対オススメしたい。

今後はこの本の範囲外の機械学習(理論)について勉強しつつ、kaggleや適当なデータセットを使って手を動かしていきたいと思う。

ISUCON7予選敗退した

ISUCON7 開催&日程決定! #isucon : ISUCON公式Blog

(開催から2週間も経ってしまった...!)(ポエミーな感じです) ISUCON7予選に id:astjid:aerealid:tanishiking24 との3人で「秒速5000兆クエリ」というチームを組んで参加しました。最終スコアは 92037 で本戦進出ならずでした...。

反省はいろいろあるけど思いついたことはだいたい試したのでまた来年頑張ろうという気持ちです、100万円欲しかった!2人ともめちゃくちゃ頼もしくて助かりまくりでした

めちゃくちゃ楽しかったし、あの参加者の数で予選から3台構成すごすぎる、運営のみなさまありがとうございました!

予選まで

pixiv社さんの社内ISUCONを利用させていただいて練習した。それまではPerlで出る?Goで出る?と言語も決まっていなかったのだけれど、このISUCONにはPerl実装が用意されていなさそうだったのでGoで練習し、その流れでそのまま予選もGoでという雰囲気が出てきたのでありがたかった。

コンテストをがっつり解くというよりは以下のようなことをして本番でスムーズに導入できるようにという感じ(良かった)

  • 3人で作業するときにどういう感じで作業しようかを認識合わせしたり
  • ISUCONのためのツールのインストール/使ってみる素振りをしたり

予選

開始が遅れるとのことだったので、ちょっと良い肉を食べてくることにした。肉はめちゃくちゃ美味しかったのだが、つい最近まで学生で安い肉しか消化してこなかった胃がびっくりしたのか、ISUCONへの緊張のためか、予選開始の5分くらいまで腹痛に苦しめられることになってしまった、なんでや。

hafuu.com

おすすめです。


3人の中でISUCON経験が一番多い id:astj が司令塔となってくれた。僕以外の2人が開発環境/デプロイまわりの整備やMackerelプラグインのインストールなどをテキパキとこなしてくれて、その間に僕はログ解析ツールのインストールやアプリのコードを読んでおいて後で共有するなどした

なんか画像配信部分がボトルネックになってそうだけど、結構ヘビーな変更になりそうだし、軽くやれそうな高速化からやっていきましょうということでインデックスを貼ったり、未読数の全件カウントをredisに保存したり、ちょっとしたN+1を1クエリでできるようにした。

この時点で3~4万点くらいには上がっていたけれど、トップ勢はぶっちぎりで高いスコアを出していたのでやっぱり画像配信なんとかせないかんねとなって、画像配信まわりに手をつけ始める。


変更のない画像へのリクエストに対して304を返す作業は他の2人が進めてもらっていたので、僕はその間に複数台サーバーで画像をうまいこと静的に返せるようにいくつか実験をして失敗したりしていた

  • 画像へのリクエストのファイル名に、どちらのサーバーでアップロードされた画像かわかるようにprefixをつける。prefixをみてnginxでサーバーをふりわけする作戦
    • default.png とか最初からサーバーに上がっている画像への取り扱いがうまくいかずテンパってしまい結局revertする...
    • 今になって考えればそんなに難しいことじゃないはずだし、上位のチームも似たようなことをやっていたようなのでここちゃんと丁寧にやれば良かった...

その間に他の2人が変更のない画像に対して Last-modified をつけて画像の読み込み頻度を減らす作戦を実装していたのだけれど、手元のChromeIf-Modified-since をつけてリクエストしてくれるのにベンチマーカーは If-Modified-Since つけてくれなくてなんでだ〜ってなっていた。 結局2人が Cache-Control: max-age=xxx をつけたら304返せるようになって、max-ageがきれたときにrevalidateするような実装だったんだな〜ってなったり、最初からサーバーに上がってる画像だけでも静的に配信するようにするなどして9万点がでた。

そのあとはmy.cnfのパラメータを変えたりしてエンキューしまくっていたのだけれど点数が大きく上がったり下がったりしていたので、パラメータの変更が良かったのか悪かったのかよく分からなかった...そんなこんなしてたら制限時間が迫ってきたのでこれまでの最高スコアに近い点数が出た時点で終了!!!ということに。

反省

  • 落ち着きたい
    • あせっていたのもあって冷静になればできるはずの実装に失敗し続けていた
  • とにかく手を動かし続ければ良かった
    • アイコンの配信がボトルネックになっていたのでN+1や未読数のキャッシュをしても大してスコアが伸びず、まずはアイコンなんとかしないとダメだよな〜となってオタオタしていたが、そのボトルネックを抜けたあとはアプリチューニングの勝負だったので、オタオタしてないで(その時点でスコアが伸びるか分からなくても)ひたすら小さなボトルネックをこつこつ潰していたらもっと良いスコアが出せたかもしれないなと反省しています...
  • 手元で動作するからといって安心しない
    • 手元のChromeに対しては304が返せていたのにベンチマーカには304が返せてないし、Last-modified-Since指定するだけで良いはずだよね??と思考停止していた。色々実験すれば良かったのに、とにかくドキュメントなどを読み返すことしかできなかった、無念(キャッシュ周りもっと勉強しよう)

いろいろ反省はあるけど、あの緊張感と限られた時間内ではやれることはやった気がする。来年はもっと強くなって本戦も出るし100万もゲットやで

余談

「秒速5000兆クエリ」、5000兆円ネタにもあやかっているし、「秒速5センチメートル」とも、「びょうそくごせんち」までprefixが一致していて良い名前だなと思いました。