akiyoko blog

akiyoko の IT技術系ブログです

たった5分でデモが動かせる! Hyperledger Sawtooth(ハイパーレッジャー・ソウトゥース)でブロックチェーンを手軽に体験してみよう

この投稿は 「ブロックチェーン Advent Calendar 2017 - Qiita」 の 17日目の記事です。

こんにちは akiyoko です。

はじめに

仮想通貨やブロックチェーンの昨今の盛り上がりにより、ブロックチェーンの情報が本やインターネットで簡単に手に入るようになってきました。


そろそろ、
「ブロックチェーンの理論は大体分かった。次のステップとして、

面倒な設定なしに、5分ほどで簡単にデモが動かせる、そんなお手軽なブロックチェーンがあればいいのになぁ。。

と考えている技術者の方が増えてきているのではないでしょうか。

f:id:akiyoko:20171217035811p:plain:w300


ハイ、あるんです!

今回は、たった5分で簡単にデモが動かせる分散型台帳フレームワーク、その名も「Hyperledger Sawtooth」を紹介したいと思います。


ちなみに「Sawtooth」は「ノコギリ」ですが、何故このネーミングになったのかについては調べてもよく分かりませんでした。 *1

f:id:akiyoko:20171217202359p:plain:w200


 

Hyperledger Sawtooth とは

Hyperledger(ハイパーレッジャー)とは

Hyperledger は Linux Foundation が 2015年12月に発足したプロジェクトで、様々なビジネス領域で実際に使えるエンタープライズ向けのブロックチェーン技術の標準化を目指しており、現在 180を超える企業(*2)が協力して分散型台帳フレームワークのオープンソース開発や実証実験、コミュニティの構築や教育によるエコシステムの拡大に取り組んでいます。

Hyperledger is an open source collaborative effort created to advance cross-industry blockchain technologies. It is a global collaboration, hosted by The Linux Foundation, including leaders in finance, banking, Internet of Things, supply chains, manufacturing and Technology.


About – Hyperledger

Hyperledger incubates and promotes a range of business blockchain technologies, including distributed ledger frameworks, smart contract engines, client libraries, graphical interfaces, utility libraries and sample applications.


Blockchain Technology Projects – Hyperledger


Hyperledger のフレームワークと言えば何よりもまず、IBM が初期の開発をリードし現在では大手IT企業が多数参加している「Hyperledger Fabric」がその代名詞として挙げられることが多いですが(*3)、他にも

といったプロダクトがオープンソースとしてプロジェクトにホストされており(*5)、特徴や採用している技術、ユースケースがそれぞれ異なります。


Hyperledger のフレームワーク全体に共通したゴールとしては、スループットやファイナリティなどの問題を解決し、エンタープライズ用途に耐えうるレベルのプロダクトを提供しようとしていることが挙げられます。またビジネスで利用するためには、クローズドで開発されたプロダクトよりもオープンソースで開発されているプロダクトの方が評価しやすく、情報の透明性もあり、コミュニティが充実しているプロダクトだと採用されやすいというケースもあります。この Hyperledger プロジェクトにより、分散型台帳やブロックチェーン技術が実ビジネス領域に広く浸透していくことが期待されています。



 

Hyperledger Sawtooth(ソウトゥース)とは

Hyperledger Sawtooth は、オープンソースの分散型台帳(DLT)であり、スマートコントラクトエンジン(Smart Contract Engine)です。米 Intel 社が開発していたものが、2016年5月に Hyperledger プロジェクトにオープンソースとして提供されました。


Hyperledger Fabric と同様に、モジュラー型アーキテクチャ(Modular Architecture)を念頭に設計されており、コンポーネントがそれぞれ部品化されていてプラガブル(置き換え可能)になっているのが特徴です。

面白いのがコンセンサスアルゴリズムさえもプラガブルになっている点で、今のところ、Sawtooth 独自の「PoET(Proof of Elapsed Time)」と開発・検証用の「Dev Mode」が用意されています。

PoET は、Enclave(trusted function *6)によって決められた待ち時間が一番短いノードがリーダーになる、という抽選アルゴリズムです。PoW のようにハッシュキャッシュに対する計算コストを掛ける必要がなく、Intel社が言うには数千台のノードが参加してもスケールするとのことです。ただし PoET は、Intel製 SGXチップ搭載ノードによる 「Trusted Execution Environment(TEE)」をベースにすることで安全にランダムなリーダーを選出する仕組みを取っているため、

独自のコンセンサスアルゴリズムPoETを持つが、これは本来intelが提供するハードウェアを利用して行われるため完全版ではなく、オープンソース版ではシミュレータが用意されている。


さまざまなブロックチェーン技術

といった制限もあるようです。


なお、公式ドキュメント によると、パブリックな Validator(ノード)ネットワークを構成することもできるが、チェーン内通貨などのインセンティブの設計が必要でそれは Sawtooth のスコープ範囲外とのこと。なので実質、コンソーシアム型かプライベート型のどちらかで利用されることが多いと思われます。



GitHub を見ると、現時点でソースコード全体の7割ほどが Python で書かれています(*7)。モデルに相当する「Transaction Family」やスマートコントラクトに相当する「Transaction Processor」を開発者が任意に実装することができ、SDK も用意されています。SDK は Python の他、C++, Go, Java, Javascript など複数の言語で開発ができるようにパッケージが個別に提供されています。




 

全体像

だらだらと解説をしてきましたが、文章だけでは全体像がよく分からないと思いますので、私の理解した範囲で Hyperledger Sawtooth の利用イメージの全体図を描いてみました。今回動かす「intkey」と呼ばれる “out of the box” なサンプルデモを Docker 上で動かす場合の構成図を示しています。公式ドキュメント の図をもう少し詳しくした感じのイメージです。


f:id:akiyoko:20171216193555p:plain



Hyperledger's Sawtooth Lake Bets on Modular Blockchains and Elapsed-Time Consensus | Altoros」の図はかなり細かい部分まで書かれていてすごく良いのですが、説明が少なくてここから正確に理解するはなかなか難しいと思います。


登場人物

用語の定義は ここ に書かれています。

 

Transaction Family

モデルに相当する構造体です。State を変更するためのフィールドや Transaction Processor を呼び出す際のアクションなどを任意に定義できます。上の図には描いていませんが、Client で Transaction を作成する際に利用しています。

A transaction family is a set containing: a transaction payload format, a model for storing information in global state, and a procedure for validating a transaction and updating state based on the transaction payload.


参考:IntkeyMessageFactory クラス

 

Transaction

State を更新するための情報、ヘッダ、署名を含んだデータで、「intkey」デモでは、「キーに対応する数値を登録する」「登録済みのキーに対応する数値を増やす」「登録済みのキーに対応する数値を減らす」といった一つ一つの処理に対応するトランザクションを表します。

A transaction is a protobuf object containing a payload, header, and signature. The signature is generated by the transaction signer by signing the transaction header with the transaction signer’s private key.

 

Batch

Transaction を複数含んでおり、ある程度まとまった処理を表現できます。Transaction に依存関係がある場合などにうまく活用できそうです。Batch はシステムが扱う最小単位で、最終的に全ての変更が反映されるか全く反映されないかのいずれかになります。

a batch is the atomic unit of change in the system. If a batch has been applied, all transactions will have been applied in the order contained within the batch. If a batch has not been applied (maybe because one of the transactions is invalid), then none of the transactions will be applied.


https://sawtooth.hyperledger.org/docs/core/releases/latest/architecture/transactions_and_batches.html#why-batches

 
ということで、ブロックチェーン上のブロックとバッチ(Batch)とトランザクション(Transaction)の関係は以下の図のようになります。つまり、ブロックの中に複数のバッチが含まれ、バッチの中に複数のトランザクションが含まれることになります。

f:id:akiyoko:20171217234733p:plain:w400

 

State

ブロックチェーンとは別に管理されるグローバルな状態を表します。Ethereum で言うところの「world state」に相当します。

Radix Merkle Tree 形式で管理されており、Transaction 実行後のそれぞれの状態がネームスペース付きの70桁のアドレスを割り当てられて保管されています。なお、Radix アドレスの先頭6桁は Transaction Family 固有のネームスペースを表す領域になっています。 *8

 

Validator

Hyperledger Sawtooth の中心的な役割を担います。トランザクションのバッチを処理して、ブロックを作成するまでのサイクルを実行します。ビットコインのブロックチェーンで言うところの「フルノード」が近いかもしれません。

非常に紛らわしいのですが、下にも書かれている通り「Validator」がデータのチェックをおこなっているわけではなく、実際にはトランザクションやバッチのデータ検証は「Transaction Processor」などに移譲していることが多いようです(「intkey」でもそのような作りになっています)。

A validator is the component ultimately responsible for validating batches of transactions, combining them into blocks, maintaining consensus with the network, and coordinating communication between clients, other validators, and transaction processors. Much of the actual validation is delegated to other components, such as transaction processors and the active consensus module.

 

Transaction Executor

しかるべきタイミングで Transaction Processor と呼ばれるプログラム(Ethereum で言うところのスマートコントラクトか)にトランザクションのデータを渡す役割を担っています。

The Executor is responsible for the execution of transactions by sending them to transaction processors. The overall flow for each transaction is:

  • The Executor obtains the next transaction and initial context from the scheduler
  • The Executor obtains a new context for the transaction from the Context Manager by providing the initial context (contexts are chained together)
  • The Executor sends the transaction and a context reference to the transaction processor
  • The transaction processor updates the context’s state via context manager calls
  • The transaction processor notifies the Executor that the transaction is complete
  • The Executor updates the scheduler with the transaction’s result with the updated context


https://sawtooth.hyperledger.org/docs/core/releases/latest/architecture/scheduling.html

 

Transaction Processor

トランザクションを検証し、処理をおこなってグローバルな State を書き換えることができます。開発者が事前に任意のプログラムを書くことができます。

A transaction processor validates transactions and updates state based on the rules defined by a given transaction family.

いわゆるスマートコントラクトです。

An intkey transaction processor implemented as a smart contract

TransactionHandler は Transaction Processor の一つで、よりビジネスロジック(アプリケーション)寄りとのこと。

参考:IntkeyTransactionHandler


 

デモを動かしてみよう

さて、いよいよデモを動かすときが来ました。(ここまで長かった。。)

今回動かすのは、Sawtooth のサンプルで一番簡単な「intkey」というデモになります。クライアントから REST API を通して数字を登録したり増減させたりすることができるシステムで、ある程度まとまった処理がブロックとしてチェーンに次々に繋がっていくのが体験できます。Docker イメージの中にすでに組み込まれているので、Docker コンテナを起動するだけでデモを実行できる環境が整います。


 
以降、macOS の Docker 上で Hyperledger Sawtooth の「intkey」デモを動かしていきます。


ちなみに私のローカル環境は以下の通りです。

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.12.6
BuildVersion:	16G1114

$ docker --version
Docker version 17.09.1-ce, build 19e2cf6


 

0.Docker for Mac をインストール

こちらは事前に済ませておいてください。

ここは「5分」に加えちゃだめですよ。


 

1.Hyperledger の最新版 sawtooth-core をダウンロード

まず、Hyperledger の sawtooth-core の GitHub から、以下の手順で「sawtooth-core-1.0.0rc5.zip」をダウンロードします。なお現時点での最新版は「v1.0.0rc5」となっていました。

f:id:akiyoko:20171216234020p:plain
f:id:akiyoko:20171216234035p:plain


まだ 1分しか経っていませんよね?



 

2.コンテナを起動

zipファイルを解凍し、以下のコマンドを実行してコンテナを起動します。

$ docker-compose -f ~/Downloads/sawtooth-core-1.0.0rc5/docker/compose/sawtooth-default.yaml up

しばらくすると、各種コンテナが起動します。遅くても 2〜3分くらいで全て起動し終わるでしょうか。


「docker ps」コマンドを叩いて、コンテナのステータスを確認しましょう。

$ docker ps
CONTAINER ID        IMAGE                                       COMMAND                  CREATED              STATUS              PORTS                              NAMES
fbb42c0ae927        hyperledger/sawtooth-all:1.0                "bash -c 'sawtooth..."   About a minute ago   Up About a minute   4004/tcp, 8008/tcp                 sawtooth-shell-default
94c6768b9c5f        hyperledger/sawtooth-xo-tp-python:1.0       "xo-tp-python -vv ..."   About a minute ago   Up About a minute   4004/tcp                           sawtooth-xo-tp-python-default
e008347a256d        hyperledger/sawtooth-intkey-tp-python:1.0   "intkey-tp-python ..."   About a minute ago   Up About a minute   4004/tcp                           sawtooth-intkey-tp-python-default
7e194e70fda1        hyperledger/sawtooth-settings-tp:1.0        "settings-tp -vv -..."   About a minute ago   Up About a minute   4004/tcp                           sawtooth-settings-tp-default
f51e20d2ce48        hyperledger/sawtooth-rest-api:1.0           "sawtooth-rest-api..."   About a minute ago   Up About a minute   4004/tcp, 0.0.0.0:8008->8008/tcp   sawtooth-rest-api-default
b8e2627fe1cd        hyperledger/sawtooth-validator:1.0          "bash -c 'sawadm k..."   About a minute ago   Up About a minute   0.0.0.0:4004->4004/tcp             sawtooth-validator-default


起動に3分掛かったとして、今のところ計4分ですね。


 

3.クライアント上でコマンドを実行

次に、ターミナル上で

$ docker exec -it sawtooth-shell-default bash

と実行し、起動したクライアント(図の「Client」)のコンテナにログインします。



ハイ、準備は終了です。
4分30秒くらいでセッティングが完了したと思います。



以降は、sawtooth コマンドを利用して intkey の処理を実行していきます。なおその際、「Using Sawtooth with Docker — Sawtooth v1.0.5 documentation」を見ながら進めてもいいのですが、現時点でドキュメントが少し古くて(ドキュメントの対象バージョンが「v0.8.13」になっているため)、ポート番号が間違っている箇所があります。REST API の「8080」ポートは最新版では「8008」が正しいので、適宜「8008」に読み替えて進めてください。



 

genesis ブロックを確認
# sawtooth block list --url http://rest-api:8008
NUM  BLOCK_ID                                                                                                                          BATS  TXNS  SIGNER
0    d16b15f03ff35031f094f5dadf4211ee7bb41c4bb707f0e7d0e015b70b85b8164a326dec294ff02e2aefcf1aa6cdbe3be8eb70d5d63cf322a269feda12b78534  1     1     02850b3d81f14c7c1b9f2aee1274e2...

コンテナ起動直後にブロックを確認すると、genesis ブロックが 1つ作成されているのが確認できます。

ちなみに、以下のように実行すると、REST API が返す JSON を見ることができます。

# curl http://rest-api:8008/blocks


 

State を確認
# sawtooth state list --url http://rest-api:8008
ADDRESS                                                                 SIZE  DATA
000000a87cb5eafdcca6a8cde0fb0dec1400c5ab274474a6aa82c12840f169a04216b7  110   b'\nl\n&sawtooth.settings.vote.authorized_keys\x12B039f7586349ccf1490127db60acd6ac6c00cdebf5ea67ccc...
HEAD BLOCK: "d16b15f03ff35031f094f5dadf4211ee7bb41c4bb707f0e7d0e015b70b85b8164a326dec294ff02e2aefcf1aa6cdbe3be8eb70d5d63cf322a269feda12b78534"

初期状態のステータスを確認することができます。Radix Merkle Tree 形式で格納されています。

なお、「DATA」には dict オブジェクトを cbor でシリアライズした文字列が格納されていて、誰でも簡単に復元可能です。また「HEAD BLOCK」には、ブロックチェーンに書き込まれた最後のブロックの「BLOCK_ID」が表示されています。つまり、State にはブロックチェーンの最後のブロックが何であるかが保持されているということが分かります。


ちなみに、ターミナルの横幅の関係で「...」と切れてしまっているのを表示する場合は、「sawtooth state show」コマンドで詳細を見ることができます。

# sawtooth state show 000000a87cb5eafdcca6a8cde0fb0dec1400c5ab274474a6aa82c12840f169a04216b7 --url http://rest-api:8008
DATA: "b'\nl\n&sawtooth.settings.vote.authorized_keys\x12B039f7586349ccf1490127db60acd6ac6c00cdebf5ea67cccc6453a9f5b295c1777'"
HEAD: "d16b15f03ff35031f094f5dadf4211ee7bb41c4bb707f0e7d0e015b70b85b8164a326dec294ff02e2aefcf1aa6cdbe3be8eb70d5d63cf322a269feda12b78534"


 

バッチを作成
# intkey create_batch --key-count 3 --count 2
Writing to batches.intkey...

このコマンドは、複数のトランザクションを含むバッチをいくつか作成し、ローカルに「batches.intkey」というファイルとして書き出してくれます。この時点では、トランザクションやバッチはブロックチェーンには書き込まれません。ローカルファイルにデータを書き込むだけです。


このコマンドが具体的に何をやっているかと言うと、6桁のランダムな文字列を key_count 個分作成してそれぞれに対応するランダムなデフォルト値(9000〜100000)を設定し、作成したキーの値を 1〜10 のうちのランダムな値で足したり引いたりするトランザクションを作成しています。

前半部分では、ランダムな文字列とそのデフォルト値を新規作成する処理を 1トランザクションとし、key_count 個分のトランザクションをまとめて 1バッチとしています。後半部分では、値を増減させるトランザクションをランダム回数分繰り返したものを含んだバッチを、count 個数分作成しています。


例えば、key_count = 3, count = 2 の場合だと、

"transactions": [
    {"Verb": "set", "Name": "vJKChA", "Value": 9223},
    {"Verb": "set", "Name": "NcEktd", "Value": 9459},
    {"Verb": "set", "Name": "wVgqYd", "Value": 9011},
]

といったトランザクションを含むバッチを1個だけ新規作成し、次に、

"transactions": [
    {"Verb": "inc", "Name": "wVgqYd", "Value": 3},
    {"Verb": "dec", "Name": "vJKChA", "Value": 9},
    {"Verb": "inc", "Name": "NcEktd", "Value": 4},
]
"transactions": [
    {"Verb": "inc", "Name": "NcEktd", "Value": 2},
    {"Verb": "dec", "Name": "wVgqYd", "Value": 1},
    {"Verb": "inc", "Name": "NcEktd", "Value": 3},
    {"Verb": "dec", "Name": "vJKChA", "Value": 1},
    {"Verb": "inc", "Name": "wVgqYd", "Value": 6},
    {"Verb": "inc", "Name": "NcEktd", "Value": 4},
    {"Verb": "dec", "Name": "vJKChA", "Value": 1},
    {"Verb": "dec", "Name": "NcEktd", "Value": 9},
]

という値を更新するためのトランザクションをそれぞれランダム個含むバッチを2個作成することになります(分かりやすく模式化して書いたので実際のものとは多少異なります)。


 

バッチを Validator ネットワークに送信
# sawtooth batch submit -f batches.intkey --url http://rest-api:8008
batches: 3,  batch/sec: 44.84783422259765

先ほど作成したファイルを読み込んで、バッチを一斉にネットワークに送信します。

「batches: 3」とログが出ているので、バッチを3つ(新規作成1つ+更新2つ)送信したことが分かります。Validator ネットワーク側では 1〜3台のノードが個別にトランザクションを受け取って処理を実行することが予想されます。もちろん順序が逆にならないようにスケジューリングされて実行されるはずなので、ご安心を。


 

ブロックを確認
# sawtooth block list --url http://rest-api:8008
NUM  BLOCK_ID                                                                                                                          BATS  TXNS  SIGNER
2    2ba9acebbe53f32909c1b341ace92d7d6d467dfd8a7de4deeed7d719f4794eff01a4c258ce3798fa8742f4deba214e11c6a73255acdf4f48205c15c17d3bcbf6  2     8     02850b3d81f14c7c1b9f2aee1274e2...
1    a275a1f80ec4e89f38e5f06f598891e24997bd1d29c2e471682f4d82b6a36ecc070beb7b3b483598a23552fbe0dae7588a7efb031bcbde6b62acbf73946655d4  1     3     02850b3d81f14c7c1b9f2aee1274e2...
0    d16b15f03ff35031f094f5dadf4211ee7bb41c4bb707f0e7d0e015b70b85b8164a326dec294ff02e2aefcf1aa6cdbe3be8eb70d5d63cf322a269feda12b78534  1     1     02850b3d81f14c7c1b9f2aee1274e2...

「NUM」が「1」「2」のブロックが合計2つ増えています。クライアントからバッチを3個一斉に送信したのに、最終的には別々のブロックとしてチェーンに固定化されているのがミソと言えばミソでしょう。Sawtooth ではチェーン内部にタイマーがあり、一定時間ごとにブロックが承認されるようになっているようなのでこのようなことが起こり得るのです。

また、NUMが「1」のブロックの「BATS」が「1」、「TXNS」が「3」となっているのは、ブロックの中にバッチが1個、トランザクションが3個入っていることを示しています。同じように、NUMが「2」のブロックの「BATS」が「2」、「TXNS」が「8」となっているので、ブロックの中にバッチが2個、トランザクションが8個入っていることが推測されます。

実際に最後のブロックの中身を以下のコマンドで確認すると、「batches」直下に「(バッチの)header」が2つあるのでバッチが2個、1つ目のバッチの「transactions」直下に「(トランザクションの)header」が1つ、2つ目のバッチの「transactions」直下に「header」が7つあるので合計8個のトランザクションが含まれていることが確認できます。

# sawtooth block show 2ba9acebbe53f32909c1b341ace92d7d6d467dfd8a7de4deeed7d719f4794eff01a4c258ce3798fa8742f4deba214e11c6a73255acdf4f48205c15c17d3bcbf6 --url http://rest-api:8008
batches:
- header:
    signer_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
    transaction_ids:
    - d9069e5d4d254faf9d42dcf66ca468edfef1a0bc7370465e90c48a9b46061bf63bdf92a76fbaa846e6ca802e0f5003ed02e4561e3e516f783177b2be1c82a450
  header_signature: 69e787f67dbdfac18799e799d344c4fe3f70f99de58993241a249f63789ad2c549560fc874b3bb1c78d52d33f7882b24290240bc1f9142b5e9db1b49c52002f7
  trace: false
  transactions:
  - header:
      batcher_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
      dependencies:
      - 18b1b2e26d6bcf605c601d118fe27a84108c8abf1110c15a8fb9e84b283681176758235e622d963f930f26876ffaff72637492fb296da23f4ff559d226c372ab
      family_name: intkey
      family_version: '1.0'
      inputs:
      - 1cf1265008cef7e8135dc4a2f0a8d94f75fa526a1f7d383cc82fd5e3c384744de2c1c9
      nonce: 0x1.68d516ec2ccfdp+30
      outputs:
      - 1cf1265008cef7e8135dc4a2f0a8d94f75fa526a1f7d383cc82fd5e3c384744de2c1c9
      payload_sha512: f1c7d99b8c5fea748e2ed97e3c61dcfb1edd8a32415945a0a1c80e39b71c9a7e1128f852b315c0478d62a6b9026e1c5247f9d60d63c062b9d1a102df4381d4bc
      signer_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
    header_signature: d9069e5d4d254faf9d42dcf66ca468edfef1a0bc7370465e90c48a9b46061bf63bdf92a76fbaa846e6ca802e0f5003ed02e4561e3e516f783177b2be1c82a450
    payload: o2ROYW1lZnp3bVNCSWVWYWx1ZQJkVmVyYmNpbmM=
- header:
    signer_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
    transaction_ids:
    - ae4c9d201f108f2e1f7f89e00032c6176d518415dcfc0fb4584f32a3ddaae8e8644c3028357ae34d08eebf95a9a0d65e31402a2c0e6cf4f67d35d3b3ec5c28d4
    - 87f681c683075bbd9e85c0c754ea797f8d407bfb6d9e3b0d669bec068ea3e1d7752b2b5136718c87dfb95ec3cfe4e810ba591d6b93dd2e69f202f0fd0a58c210
    - 582073cb877d06086500303ad8e5736d7aad7d7575e41f42b1ba4324e52bfd7e457effbc151842d04208a56a90a4b86a0d48167d365a1e46e3845823488460ed
    - b119f313f8cd778d06b311dcc613d6e4e12db8822f7e76bdbe8adfb234072cdc3d449e04d0711cf22bd34179d5677b9d83ee7604b62c5d43646118d2153be4b3
    - 47442f8dd34c17cc1ac1dc38f043fcd45e87d1a7a584a15c614b290abdaaea2771f968c156776107582dc39bc9a2f0c5f242348bf790a00c14e1c207a3bd4992
    - f54356ec854c54fb2abe0f6fd767d1207b461ba82f563c296f611c3b4fc1962b615e124d1649460e5ddca64d28f2f7477302a145189c44a664b8fd61d0e605c8
    - 07b1eb851b952778c928d380d1ea6121949f64cbedb16f41e1d9334000815b69207e4d51373c2a77b0dcd609c295577c10b17a00c765bfad565fda2fc9bebc68
  header_signature: 9a6e057f190e10d7a5d52a0892fc724f58106cc78d9db32cf7da1aedd93f0c9e170c5db853899bbb6b4f1cd1838ab5145d5e568ccde6b777119b5c29305b3083
  trace: false
  transactions:
  - header:
      batcher_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
      dependencies:
      - 67a02d58c36134362b63a2043fb95e0f4f1830293227fc1a859d4fd979d9679e3c2d450a3279b582a02248d66514334af70d9198cc05049740d1bdac9daac32d
      family_name: intkey
      family_version: '1.0'
      inputs:
      - 1cf126c0efc8717958cef02cc3131b80479462b9c90dc6aa0fe7e09229504bf4a62d27
      nonce: 0x1.68d516ec2d0c1p+30
      outputs:
      - 1cf126c0efc8717958cef02cc3131b80479462b9c90dc6aa0fe7e09229504bf4a62d27
      payload_sha512: b339df5cfa78060d82790be2b9f7d0db8064fb89a3c42ab8f3c1246bc3247bd9d86c4860324584fd8ef58bd948997bb417f0a093fb6355ccc776b346d51e9bc3
      signer_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
    header_signature: ae4c9d201f108f2e1f7f89e00032c6176d518415dcfc0fb4584f32a3ddaae8e8644c3028357ae34d08eebf95a9a0d65e31402a2c0e6cf4f67d35d3b3ec5c28d4
    payload: o2ROYW1lZm54QmtodWVWYWx1ZQZkVmVyYmNkZWM=
  - header:
      batcher_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
      dependencies:
      - 67a02d58c36134362b63a2043fb95e0f4f1830293227fc1a859d4fd979d9679e3c2d450a3279b582a02248d66514334af70d9198cc05049740d1bdac9daac32d
      family_name: intkey
      family_version: '1.0'
      inputs:
      - 1cf126c0efc8717958cef02cc3131b80479462b9c90dc6aa0fe7e09229504bf4a62d27
      nonce: 0x1.68d516ec2d2d3p+30
      outputs:
      - 1cf126c0efc8717958cef02cc3131b80479462b9c90dc6aa0fe7e09229504bf4a62d27
      payload_sha512: 16bb60a1f4f07335a1aa2417e758d41945ea64cc6743dce71871a291ecbd5b25964272207c05ca03be8eb6c4a9b914d15bebf0e8e41769dfeb3564926f37f142
      signer_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
    header_signature: 87f681c683075bbd9e85c0c754ea797f8d407bfb6d9e3b0d669bec068ea3e1d7752b2b5136718c87dfb95ec3cfe4e810ba591d6b93dd2e69f202f0fd0a58c210
    payload: o2ROYW1lZm54QmtodWVWYWx1ZQlkVmVyYmNpbmM=
  - header:
      batcher_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
      dependencies:
      - 67a02d58c36134362b63a2043fb95e0f4f1830293227fc1a859d4fd979d9679e3c2d450a3279b582a02248d66514334af70d9198cc05049740d1bdac9daac32d
      family_name: intkey
      family_version: '1.0'
      inputs:
      - 1cf126c0efc8717958cef02cc3131b80479462b9c90dc6aa0fe7e09229504bf4a62d27
      nonce: 0x1.68d516ec2d4ddp+30
      outputs:
      - 1cf126c0efc8717958cef02cc3131b80479462b9c90dc6aa0fe7e09229504bf4a62d27
      payload_sha512: c8056631e8a3096891b7b6c2ab1e4f7275f697ce806762dcad037bcf25502a617b2d6fcd6b3d5dca71fb9427f2ab46d99261b0eee939d88c07483fdcb99673bc
      signer_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
    header_signature: 582073cb877d06086500303ad8e5736d7aad7d7575e41f42b1ba4324e52bfd7e457effbc151842d04208a56a90a4b86a0d48167d365a1e46e3845823488460ed
    payload: o2ROYW1lZm54QmtodWVWYWx1ZQJkVmVyYmNkZWM=
  - header:
      batcher_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
      dependencies:
      - 67a02d58c36134362b63a2043fb95e0f4f1830293227fc1a859d4fd979d9679e3c2d450a3279b582a02248d66514334af70d9198cc05049740d1bdac9daac32d
      family_name: intkey
      family_version: '1.0'
      inputs:
      - 1cf126c0efc8717958cef02cc3131b80479462b9c90dc6aa0fe7e09229504bf4a62d27
      nonce: 0x1.68d516ec2d6f2p+30
      outputs:
      - 1cf126c0efc8717958cef02cc3131b80479462b9c90dc6aa0fe7e09229504bf4a62d27
      payload_sha512: 6c07331ddb97af0db05aa9fb64afe7ac52a7e0229992ca487a812e296ff9adedebaba2fcd4e0c7cec71d5505df942e84b6031baeb025e2841545092c1a154985
      signer_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
    header_signature: b119f313f8cd778d06b311dcc613d6e4e12db8822f7e76bdbe8adfb234072cdc3d449e04d0711cf22bd34179d5677b9d83ee7604b62c5d43646118d2153be4b3
    payload: o2ROYW1lZm54QmtodWVWYWx1ZQNkVmVyYmNkZWM=
  - header:
      batcher_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
      dependencies:
      - 9c6e0588503d407a662a416d2d551d1e65998ddfefd5d377f3175a2a24a0cee47ee0692a033a0f52d9155ba036ff57b73a26faf4ad3f6a60e4a27068ddfe4323
      family_name: intkey
      family_version: '1.0'
      inputs:
      - 1cf12656d2b508c206f56463d26e9045aad7d42527de7899f4acfc93d1e9f7b9c8c2c8
      nonce: 0x1.68d516ec2d8f3p+30
      outputs:
      - 1cf12656d2b508c206f56463d26e9045aad7d42527de7899f4acfc93d1e9f7b9c8c2c8
      payload_sha512: d27adf0758adac89fde1bd1ec866a86ea63073ff811323b3ddec639a625c87af4edf45ed94d6fbef2e4a65bb98055b0a2e2eb1894e1f169ff10059b7dc1bd9de
      signer_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
    header_signature: 47442f8dd34c17cc1ac1dc38f043fcd45e87d1a7a584a15c614b290abdaaea2771f968c156776107582dc39bc9a2f0c5f242348bf790a00c14e1c207a3bd4992
    payload: o2ROYW1lZndWZ3FZZGVWYWx1ZQlkVmVyYmNkZWM=
  - header:
      batcher_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
      dependencies:
      - 67a02d58c36134362b63a2043fb95e0f4f1830293227fc1a859d4fd979d9679e3c2d450a3279b582a02248d66514334af70d9198cc05049740d1bdac9daac32d
      family_name: intkey
      family_version: '1.0'
      inputs:
      - 1cf126c0efc8717958cef02cc3131b80479462b9c90dc6aa0fe7e09229504bf4a62d27
      nonce: 0x1.68d516ec2db21p+30
      outputs:
      - 1cf126c0efc8717958cef02cc3131b80479462b9c90dc6aa0fe7e09229504bf4a62d27
      payload_sha512: bd4e037e91ebd277a2e03821aa01fc821be97d27aeb69edd008d6453fde1f846a6fe564dccafec9061bd07f903f50ddc95bdfbebbb74e13251fb9539c13f673f
      signer_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
    header_signature: f54356ec854c54fb2abe0f6fd767d1207b461ba82f563c296f611c3b4fc1962b615e124d1649460e5ddca64d28f2f7477302a145189c44a664b8fd61d0e605c8
    payload: o2ROYW1lZm54QmtodWVWYWx1ZQVkVmVyYmNpbmM=
  - header:
      batcher_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
      dependencies:
      - 9c6e0588503d407a662a416d2d551d1e65998ddfefd5d377f3175a2a24a0cee47ee0692a033a0f52d9155ba036ff57b73a26faf4ad3f6a60e4a27068ddfe4323
      family_name: intkey
      family_version: '1.0'
      inputs:
      - 1cf12656d2b508c206f56463d26e9045aad7d42527de7899f4acfc93d1e9f7b9c8c2c8
      nonce: 0x1.68d516ec2de3ap+30
      outputs:
      - 1cf12656d2b508c206f56463d26e9045aad7d42527de7899f4acfc93d1e9f7b9c8c2c8
      payload_sha512: 08e0a3f2ff71ed2a5ba635cd528e7a7606135cd116d16bc471f86889a1ad51bc378ac9381c77a965c15935e505680a05007badaf561ab8773fc533df791673ec
      signer_public_key: 02991e34411f9282a0a057d4adf0b00f44605d0da25bf8141e1e7b2875486083ac
    header_signature: 07b1eb851b952778c928d380d1ea6121949f64cbedb16f41e1d9334000815b69207e4d51373c2a77b0dcd609c295577c10b17a00c765bfad565fda2fc9bebc68
    payload: o2ROYW1lZndWZ3FZZGVWYWx1ZQRkVmVyYmNpbmM=
header:
  batch_ids:
  - 69e787f67dbdfac18799e799d344c4fe3f70f99de58993241a249f63789ad2c549560fc874b3bb1c78d52d33f7882b24290240bc1f9142b5e9db1b49c52002f7
  - 9a6e057f190e10d7a5d52a0892fc724f58106cc78d9db32cf7da1aedd93f0c9e170c5db853899bbb6b4f1cd1838ab5145d5e568ccde6b777119b5c29305b3083
  block_num: '2'
  consensus: RGV2bW9kZQ==
  previous_block_id: a275a1f80ec4e89f38e5f06f598891e24997bd1d29c2e471682f4d82b6a36ecc070beb7b3b483598a23552fbe0dae7588a7efb031bcbde6b62acbf73946655d4
  signer_public_key: 02850b3d81f14c7c1b9f2aee1274e2862d0e707dbe57136602ec5437dab774b431
  state_root_hash: 3370c09db54b81f7ddf5fcd4b2d38f18c8f0cd8dbebf90a8eb30b23eb9c92325
header_signature: 2ba9acebbe53f32909c1b341ace92d7d6d467dfd8a7de4deeed7d719f4794eff01a4c258ce3798fa8742f4deba214e11c6a73255acdf4f48205c15c17d3bcbf6

なお、「inputs」や「outputs」は State のアドレスを表しており、それぞれのトランザクションの処理結果の格納先を指し示しています。



 

最終的な State を確認

最後に State の値がどうなったかを確認してみます。

# sawtooth state list --url http://rest-api:8008
ADDRESS                                                                 SIZE  DATA
000000a87cb5eafdcca6a8cde0fb0dec1400c5ab274474a6aa82c12840f169a04216b7  110   b'\nl\n&sawtooth.settings.vote.authorized_keys\x12B039f7586349ccf1490127db60acd6ac6c00cdebf5ea67ccc...
1cf1265008cef7e8135dc4a2f0a8d94f75fa526a1f7d383cc82fd5e3c384744de2c1c9  11    b'\xa1fzwmSBI\x19\xe9\xe6'                                                                         ...
1cf12656d2b508c206f56463d26e9045aad7d42527de7899f4acfc93d1e9f7b9c8c2c8  11    b"\xa1fwVgqYd\x191'"                                                                               ...
1cf126c0efc8717958cef02cc3131b80479462b9c90dc6aa0fe7e09229504bf4a62d27  13    b'\xa1fnxBkhu\x1a\x00\x01I\xd9'                                                                    ...
HEAD BLOCK: "2ba9acebbe53f32909c1b341ace92d7d6d467dfd8a7de4deeed7d719f4794eff01a4c258ce3798fa8742f4deba214e11c6a73255acdf4f48205c15c17d3bcbf6"

アドレスが「1cf1265008ce...」「1cf12656d2b5...」「1cf126c0efc8...」のレコードが増えています(先頭6桁が同じになっている理由はお分かりですよね)。最後に処理されたトランザクションの「outputs」の値が「1cf12656d2b508c206f56463d26e9045aad7d42527de7899f4acfc93d1e9f7b9c8c2c8」で、それに合致するアドレスの「DATA」が、最新の Status の値になっているはずです。


cbor でシリアライズされた値をデコードしてみます。

>>> import cbor
>>> cbor.loads(b"\xa1fwVgqYd\x191'")
{'wVgqYd': 12583}

確認することができました。
結構増えてますね。



 

まとめ

5分ほどでデモを動かせるところまで簡単にセットアップができてしまう、お手軽なブロックチェーン「Hyperledger Sawtooth」を紹介しました。


11月末に参加した勉強会「「bitFlyer Drink Meetup! #9」に参加してきました - akiyoko blog」でこのプロダクトを知り、プロダクト自体が Python で書かれていて、Python でスマートコントラクトを自由に書けるというところにグッと来たのが、この記事を書こうと思ったきっかけです。

軽い気持ちで「ブロックチェーン」の Advent Calendar に登録してしまった今日までの二週間ほどで急ごしらえで勉強した内容を書き殴ったので、間違っているところがいろいろあるかと思います。もし間違いがありましたら、温かい目で見ると同時に優しく教えていただけるとありがたいです。



明日は、m0t0k1ch1 さんの「ブロックチェーン Advent Calendar 2017 - Qiita」 18日目の記事です。
よろしくお願いします。


 

おまけ

最近読んだ本ですが、金融分野において、今回紹介した Hyperledger Sawtooth の兄貴分である「Hyperledger Fabric」や R3コンソーシアムが開発した「Corda」、Ripple 社が推進する「ILP」などのコンソーシアム型あるいはプライベート型の分散型台帳フレームワークを利用した大規模な実証実験が進められており、そう遠くない将来にブロックチェーン技術が金融業界に革命を起こす、という衝撃の内容です。

ビットコインじゃないブロックチェーンについて知りたい方は是非。金融業界の方で「これから何が起こるのか知りたい!」という方はマストだと思います。

*1:Intel のチップだから「ノコギリ波」なのかな。。

*2:https://hyperledger.org/members

*3:有名すぎるため、単に「Hyperledger」と書かれているものが実際には「Hyperledger Fabric」を差していることが多いので注意が必要です。Hyperledger あるある

*4:https://www.boj.or.jp/announcements/release_2017/data/rel170227a7.pdf

*5:Blockchain Technology Projects – Hyperledger

*6:このあたりが何を意味しているかがよく分かってません。。

*7:ちなみに、Fabric は Go、Iroha は C++ で書かれているそうです。 https://www.boj.or.jp/announcements/release_2017/data/rel170227a7.pdf

*8:https://sawtooth.hyperledger.org/docs/core/releases/latest/architecture/global_state.html?highlight=merkle#radix-addresses

「ビットコインとか勉強会#13」に参加してきました

会場

株式会社 LIFULL
〒102-0083 東京都千代田区麹町 1-4-4 8F

Twitter

twitter.com




ビットコイン取引高日本一の仮想通貨取引所 coincheck bitcoin
ビットコイン取引高日本一の仮想通貨取引所 coincheck bitcoin


 

全体の感想など

「ビットコインとか勉強会」の参加はこれで 5回目です。 *1, *2, *3, *4


最近はブロックチェーンの本を読んだりインターネットや Twitter で情報と集めたりしていろいろ勉強をしていて、仮想通貨やブロックチェーンの勉強会に行っても昔と比べて理解度が上がっていると思っていたのですが、「insight」や「electrumx」などのサーバサイドプロダクトの存在は初めて知りました。まだまだアンテナの張り方が足りないようですね。

もう少し話を聞きたかったので急遽、懇親会にも参加してみました。久々でしたが、すごくいい話が聞けました。

今回はどちらかと言うと開発者寄りの話でしたが、実際に手を動かしてコマンドを叩いてみるようなもくもく会的なイベントにも今後参加していきたいと思います。





 

ビットコインを支えるインフラについて

Yuki Akiyama 氏 (@you21979)(ビットバンク株式会社 ビットコインエンジニア)


暗号通貨 Advent Calendar 2017 - Qiita」を立てた方だとか。最近では、

などの記事も。

  • なぜインフラが必要なのか?
    • ウォレットを作るときに困るから
    • (自分以外の)ビットコインアドレスを指定して未使用のトランザクション(UTXO)の一覧を取得する、などの一見簡単なことができない
  • 解決するプロダクト
    • insight, electrumx
    • 端末側でウォレットを動かすためのバックエンドサーバ
  • insight
    • Node.js で作られている
    • REST-API で簡単に情報を取得できる(これがスゴイところ)
    • 5つの部品から構成されている
      • 魔改造版 bitcoind
      • bitcore-lib
      • bitcore-node(UTXOのデータベース)
      • insight-ui(ブロックエクスプローラの画面)
      • insight-api(REST-API)
    • マルチプラットフォームのウォレットをホスティングできる
    • まだ SegWit に対応していない(v5 から対応予定?)
      • Confirmされるまで見れない?
    • 最新版に追従しにくい。自分で魔改造版 bitcoind にパッチを当てる必要がある。かなり大変
    • データベースが巨大。来年には500BG超えそう。。
    • 開発が滞ってた?最近活発に?
    • オルトコインの対応は? ⇒ しにくい
      • 開発チームが insight の対応をしているところもあるが(Litecoin, ZCash, Dash, Zcoin)、結構不安定?
  • electrumx
    • ウォレットソフト electrum のサーバサイド実装
    • Python3で作られている。作者は kyuupichan
    • electrum を使ったクライアント? 例えば、Coinomi など
    • 最初からオルトコインに対応できるように設計されているのがスゴイ
      • 現在30種類くらい対応
      • coins.py に記述されているコインであれば、設定ファイルに記載するだけで対応可能。
      • 積極的にプルリクを取り込んでくれる
    • REST-API じゃないが、プッシュ通知とかがあって便利
    • APIの設計が細切れ?になってて、必要なデータを一度で取得できないため、いくつか組み合わせる必要あり
    • トランザクションをデコードしてくれない。自分で解析が必要
    • システム的に、定期的にメンテが必要。ダウンタイムが必要。オルトコインだと1ヶ月ちょっと??(ビットコインだと1年くらい)
    • ウォレット作るなら十分。他のことをやろうとすると大変??


 

しっかり学ぶ ICOのベストプラクティス

千賀 優作 氏 (@syrohei)(一般社団法人 分散技術総合研究所 代表理事)



千賀さんは最強のイーサリアマーだそうです。
RICO をオープンソースで開発中とのことで、それについての説明です。

  • スマートコントラクトが必要
  • (EIP20Token以外の)ICOの規格が統一されていない
    • 今年のICO の 90〜95%くらいは EIP20トークン?
    • Golem, Gnosis は受け取ったEtherを認識できないとか?で ICO が失敗したとか??
  • 学習コスト、開発コスト、ハードルが高い
  • セキュリティ的な要件、法的リスクもある
  • ICOのベストプラクティスとは?
    • スムーズなICOを実現するための準備
      • なりすましやフィッシング詐欺に適切な対処が可能
    • スマートコントラクトのメリットを強力に活用
      • Trusted なアプリケーションを低コストで稼働
    • コードの信頼性を維持
      • 絶対にバグをうまないコードを
  • RICOTruffle フレームワークによるベストでナイスな ICO開発
  • Ethereum のウィークポイント
    • Ethereum の tx は一つの tx で一つの method しか実行できない
      • 設計を考え直すこともしばしば
    • スマートコントラクトの無限ループが発生しやすい
      • 再帰的呼び出しに弱い(The DAO の攻撃)
    • 設計が複雑になりやすい。挙動を正確に把握しないとガスが無駄になりがち
    • 構造体が使いにくい(代わりにコントラクトを新規に生成したほうがいい?)
    • function() は多用しない(無限ループの脆弱性)
    • できる限りグローバル変数(外部変数)は使わない。設計を見直す
    • 条件分岐(if else)を多用し過ぎるとテストしづらくなるので、適度に列挙型 enum で状態遷移を明確に
    • 常にガスの利用を意識した処理を心がける(ストレージをなるべく使わない)
  • Truffle は Solidity ベースのスマートコントラクトを作成するのに最適
    • 最低限のコントラクト管理機能があり、テストも簡単に書ける
    • もはやデファクトスタンダードに
  • RICO における Proof of Donation による寄付証明とイベントハンドリング
  • RICO は、ICOに最適化されたオープンソースフレームワーク
    • Truffle のテンプレートエンジン
    • 誰かが誰かに寄付をした、ということを証明する
    • (機能拡張すれば)ホワイトリストにも対応できる

イーサリアムのスマートコントラクトについて知りたい、あるいは開発をしてみたいという方にはこちらの本が有益だと思います。私も最近読み終えました。

*1:<過去記事> akiyoko.hatenablog.jp

*2:<過去記事> akiyoko.hatenablog.jp

*3:<過去記事> akiyoko.hatenablog.jp

*4:<過去記事> akiyoko.hatenablog.jp

まだ CSV の文字化けで消耗してるの?(Excel で直接開いても文字化けしない CSVファイルを Python3 で作成するスマートな方法)

この投稿は 「python Advent Calendar 2017 - Qiita」 の 9日目の記事です。

こんにちは、akiyoko です。
「Python Advent Calendar」は 4年連続 4度目の参加になります。 *1, *2, *3



はじめに

皆さん、CSV は好きですよね? Excel も大好きですね?
じゃあ当然、CSVファイルは Excel で開きますよね。

文字化けは? ・・もちろん嫌いですよね。
でも CSVファイルを Excel で開こうとしたときに、こんな文字化け地獄を経験したことはありませんでしたか? *4


f:id:akiyoko:20171205230423p:plain:w400


ということで今回は、Excel で直接開いたときに文字化けしない CSV ファイルを Python3 で作成する方法 を紹介したいと思います。(おまけで Python2 でのやり方も書いておきますが、今時 Python2 で消耗している人なんていないですよね? *5



 

結論

結論を先に書くと、

  • Unicode の文字符号化方式は 「UTF-16(正確には、BOMありの UTF-16 LE)」
  • タブ区切り

で CSVファイルを作成すれば、Excel で直接開いても文字化けせず、それぞれの値がセルごとに分かれて表示されます。


(参考)Which encoding opens CSV files correctly with Excel on both Mac and Windows? - Stack Overflow



Windowsでは、リトルエンディアンのUTF-16符号化スキームが使われている。内部表現では16ビット符号なし整数を符号単位とするUTF-16符号化形式(CEFなのでBOMはなし)として扱い、ファイルなどではBOMありのUTF-16符号化スキーム(リトルエンディアン)が主である。


UTF-16 - Wikipedia

Note Microsoft uses UTF-16, little endian byte order.


Using Byte Order Marks | Microsoft Docs

とあるように、Microsoft Excel が 「BOMありの UTF-16 LE」を扱っているため、この方法がベストと言えそうです。


なお、「CSV(Comma-Separated Values)」と言いながらも区切り文字がタブなので、厳密には「TSV(Tab-Separated Values)」と呼ぶべきでしょうか。議論の余地はあるものの(*6)、拡張子を「.csv」としておくことでダブルクリック時に自動的に Excel が起動してくれるので(アプリケーションが関連付けられているので)、拡張子は「.csv」とした方がよいでしょう。



 

検証(Python 3)

ファイルオープン時に「encoding='utf-16'」と指定することで、符号化方式が「UTF-16 LE with BOM」となります。
「encoding='utf-8-sig'」(UTF-8 with BOM)だと、環境によっては(Mac + Excel 2011 とか?)文字化けすることがあるので推奨しません。

import csv


def main():
    rows = [['髙﨑 將'], ['あああ', 'いいい', 'ううう'], ['Ⅰ・Ⅱ・Ⅲ', '①②③']]

    # OK
    with open('utf_16_excel_tab.csv', 'w', newline='', encoding='utf-16') as f:
        w = csv.writer(f, dialect='excel-tab', quoting=csv.QUOTE_ALL)
        w.writerows(rows)

    # これでもOK
    with open('utf_16_excel_tab_2.csv', 'w', newline='', encoding='utf-16') as f:
        w = csv.writer(f, dialect='excel', delimiter='\t', quoting=csv.QUOTE_ALL)
        w.writerows(rows)

    # 文字化けしないが、セルごとに分かれないのでNG
    with open('utf_16.csv', 'w', newline='', encoding='utf-16') as f:
        w = csv.writer(f, quoting=csv.QUOTE_ALL)
        w.writerows(rows)

    # 文字化け (しない場合もある)
    with open('utf_8_sig.csv', 'w', newline='', encoding='utf-8-sig') as f:
        w = csv.writer(f, quoting=csv.QUOTE_ALL)
        w.writerows(rows)

    # 文字化け (しない場合もあるが、セルごとに分かれないのでNG)
    with open('utf_8_sig_excel_tab.csv', 'w', newline='', encoding='utf-8-sig') as f:
        w = csv.writer(f, dialect='excel-tab', quoting=csv.QUOTE_ALL)
        w.writerows(rows)

    # 文字化け
    with open('utf_8.csv', 'w', newline='', encoding='utf-8') as f:
        w = csv.writer(f, quoting=csv.QUOTE_ALL)
        w.writerows(rows)

    # 文字化け
    with open('utf_8_excel_tab.csv', 'w', newline='', encoding='utf-8') as f:
        w = csv.writer(f, dialect='excel-tab', quoting=csv.QUOTE_ALL)
        w.writerows(rows)


if __name__ == '__main__':
    main()


OK

f:id:akiyoko:20171205231119p:plain:w500

セルごとに分かれない

f:id:akiyoko:20171205231145p:plain:w500

文字化け

f:id:akiyoko:20171205231136p:plain:w500


なお、確認した環境は、

  • macOS 10.12.16 + Microsoft Office 365 & Excel for Mac 2011
  • Windows 10 + Microsoft Office 2010

です。



ちなみに、open 時に「newline=''」を指定している理由は、Windows 対策のためです。


(参考)

 

おまけ(Python 2)

# -*- coding: utf-8 -*-
import cStringIO

import codecs
import unicodecsv as csv


class UnicodeWriter:
    """
    A CSV writer which will write rows to CSV file "f",
    which is encoded in the given encoding.
    """

    def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
        # Redirect output to a queue
        self.queue = cStringIO.StringIO()
        self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
        self.stream = f
        self.encoder = codecs.getincrementalencoder(encoding)()

    def writerow(self, row):
        self.writer.writerow([s.encode("utf-8") for s in row])
        # Fetch UTF-8 output from the queue ...
        data = self.queue.getvalue()
        data = data.decode("utf-8")
        # ... and reencode it into the target encoding
        data = self.encoder.encode(data)
        # write to the target stream
        self.stream.write(data)
        # empty queue
        self.queue.truncate(0)

    def writerows(self, rows):
        for row in rows:
            self.writerow(row)


def main():
    rows = [[u'髙﨑 將'], [u'あああ', u'いいい', u'ううう'], [u'Ⅰ・Ⅱ・Ⅲ', u'①②③']]

    with open('test_unicode_writer.csv', 'w') as f:
        w = UnicodeWriter(f, dialect=csv.excel_tab, encoding='utf-16')
        w.writerows(rows)


if __name__ == '__main__':
    main()

https://docs.python.org/2/library/csv.html#examples の UnicodeWriter をそのまま使えばいいよという話ですが、それにしても面倒臭いですよね。いっそ滅んでしまえばいいのに、Python2。



 

まとめ

Excel で直接開いても文字化けしない CSVファイルを Python3 で作成するには、

  • ファイルオープン時に「encoding='utf-16'」と指定
  • csv.writer の引数に「dialect='excel-tab'」と指定

とするのがスマートで確実です。

今回はちょっとレガシーな話題でした。



明日は、driller さんの「python Advent Calendar 2017 - Qiita」 10日目の記事です。
よろしくお願いします。




 

おまけ

文字コードに詳しくなりたい人は、こちらをどうぞ。

*1:《過去記事》akiyoko.hatenablog.jp

*2:《過去記事》akiyoko.hatenablog.jp

*3:《過去記事》akiyoko.hatenablog.jp

*4:ネタが古いですね。図は、「悪循環画像ジェネレータ」を利用させていただきました。

*5:・・はい、私です。

*6:「タブ区切りは CSV じゃなくて TSV だろ」問題がついに解決した - 頭ん中

「あなたの趣味は?」のアンケート結果を R で因子分析してみた

この投稿は 「R Advent Calendar 2017 - Qiita」 の 4日目の記事です。

こんにちは、akiyoko です。
「R Advent Calendar 2017」は 3年ぶり 2回目の参加になります。 *1

何をしたのか?

「あなたの趣味は?」というアンケート結果に対して「因子分析」を実施 することで回答の奥に潜む共通要因を探り、どんな趣味趣向を持った人たちがアンケートに回答してくれていたのか? を分析してみました。


具体的にはこんなシチュエーションです。

現在、アンケートの回収に Google Forms を使っていて、回答項目の一つに「あなたの趣味は何ですか?」(*2)という項目が含まれています。例えばこんな感じです(実際には他にも項目があります)。


f:id:akiyoko:20171202235107p:plain:w300

趣味アンケート

Q1.あなたの趣味は何ですか? 当てはまるものを次の中から全てお答えください


【選択肢】特に無い/将棋/囲碁/チェス/ポーカー/麻雀/その他のボードゲーム・テーブルゲーム/TVゲーム・PCゲーム(携帯用を含む)/映画鑑賞/音楽鑑賞/楽器演奏/演芸・演劇鑑賞/美術鑑賞/スポーツ観覧/ダンス/料理・お菓子作り/ガーデニング/読書/ネットサーフィン/パチンコ・パチスロ/競馬・競輪・競艇/カラオケ/その他(自由回答)


回答結果をグラフにすると、以下のようになりました。

f:id:akiyoko:20171203003721p:plain:w500

しかしながら、このままでは面白くない。何か新しい視点で分析できないものか? *3

ということで、このアンケート回答者の趣味の傾向から、どんなクラスタの人たちがアンケートに回答してくれているのか?ということを趣味の背後に潜んでいる共通因子から推測してみよう、と思い立ったわけです。


 

そもそも因子分析とは?

こちらの説明が非常に分かりやすいです。

  • 因子分析は,複数の変数間の関係性を探る際によく用いられる手法である(ただし正確には潜在的な変数を仮定するのだが,以下に説明する)。
  • 因子分析をする目的は,「因子」を見つけることである。
  • 因子とは,実際に測定されるものではなく,測定された変数間の相関関係をもとに導き出される「潜在的な変数」(観測されない,仮定された変数)である。
  • 言い換えると,因子分析とは「ある観測された変数(たとえば質問項目)が,どのような潜在的な因子から影響を受けているか」を探る手法といえる。


心理データ解析第8回(1)」より


よく「主成分分析」と混同しやすい「因子分析」ですが、

  • 因子分析をする目的は「共通因子を見つけること」である
  • その一方で,主成分分析の目的は「情報を縮約すること」である。

心理データ解析補足01」より

といった違いがあります。上記サイトの図が特に分かりやすいです。

私の感覚では、主成分分析は、主成分を1つに縮約して観測された変数に重み付けをして各データ行の「合計得点」を求めたいときや、主成分を2つに次元圧縮して特徴量を一つの散布図にまとめて表示するときに使うもの、と考えています。



 

なぜ R?

普段は Excel や Python でデータ分析をしていて R は使わないのですが、私の知る限りでは、因子分析に関しては Excel 単体ではできず、Python でも簡単にできるようなものがありません(scikit-learn には sklearn.decomposition.FactorAnalysis というクラスがあるのですがそれを使ったサンプルが何故かあまりありません。主成分分析をするサンプルならいつくもあるのですが・・)。
その点、R なら超簡単に出来てしまうことが分かってからは、因子分析には R を利用するようにしています。

実践

ここからが本番です。

Google Forms のアンケート結果をクレンジングしてファイルを CSV形式で保存し、R に読み込ませて分析します。

データクレンジング

Google Forms からアンケート結果を xlsx形式でエクスポートすると、こんな感じになっています。

f:id:akiyoko:20171202163927p:plain


Excel関数を使ってチェックボックス形式のデータを整形します。
具体的には、別の Excelファイルを新規作成し、(選択肢として不要なので)「特に無い」「その他」の列を削除した後、以下のようにして「0」「1」のデータに変換します。

f:id:akiyoko:20171203003640p:plain


[ファイル]>[名前を付けて保存]から、ファイルを CSV 形式で保存します。
ファイル名は、例えば「factor_analysis.csv」など、日本語を使わない方が無難です(RStudio から読み込めない可能性があります)。

将棋,囲碁,チェス,ポーカー,麻雀,その他のボードゲーム・テーブルゲーム,TVゲーム・PCゲーム(携帯用を含む),映画鑑賞,音楽鑑賞,楽器演奏,演芸・演劇鑑賞,美術鑑賞,スポーツ観覧,ダンス,料理・お菓子作り,ガーデニング,読書,ネットサーフィン,パチンコ・パチスロ,競馬・競輪・競艇,カラオケ
0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0
0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0
1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0
(以下略)

 

RStudio にデータ投入

インストールしていない場合は、公式ダウンロードページ から RStudio をダウンロードしてインストールします。
私が使ったバージョンは、RStudio for Mac(Version 1.0.153)です。


作成した CSVファイルを読み込みます。

> data <- read.csv(file="~/Downloads/factor_analysis.csv", header=T, fileEncoding="Shift-JIS")
> head(data)
  将棋 囲碁 チェス ポーカー 麻雀 その他のボードゲーム.テーブルゲーム TVゲーム.PCゲーム.携帯用を含む. 映画鑑賞 音楽鑑賞 楽器演奏 演芸.演劇鑑賞 美術鑑賞 スポーツ観覧 ダンス 料理.お菓子作り
1    0    0      0        0    0                                   1                               0        0        0        0             0        0            0      0               0
2    0    1      0        0    0                                   0                               0        0        0        0             0        0            0      0               0
3    1    0      0        0    0                                   0                               0        0        0        0             0        0            1      0               0
4    1    1      0        0    1                                   1                               0        0        0        0             0        0            0      0               0
5    0    0      0        1    0                                   0                               0        0        0        1             0        0            0      0               0
6    0    0      0        0    0                                   0                               0        1        1        0             0        1            0      0               0
  ガーデニング 読書 ネットサーフィン パチンコ.パチスロ 競馬.競輪.競艇 カラオケ
1            0    0                1                 0              0        0
2            0    0                0                 0              1        0
3            0    1                1                 0              0        0
4            0    0                1                 0              0        0
5            0    0                0                 0              0        0
6            0    1                0                 0              0        0

上記は CSV ファイルのエンコード形式が「Shift-JIS」の場合の記述ですが、もし「UTF-8」にした場合は「fileEncoding="UTF-8"」とすれば OK です。


 

RStudio で因子分析

以降は、ほぼ「Rで因子分析やってみた」のままです。

まずは、因子数を決定します。

> c <- cor(data)
> e <- eigen(c)$values
> e
 [1] 2.7796088 2.6282309 1.7220938 1.6669475 1.4724370 1.3176913 1.2386144 1.0709480 0.9855064 0.8927945 0.7569210 0.6899642 0.6341069 0.6018854 0.5724795 0.4168670 0.3761065 0.3551432 0.3143882
[20] 0.2790165 0.2282491

固有値のグラフをスクリープロットとして出力します。

> plot(e, type="b", main="Scree Plot", xlab="Number", ylab="Eigenvalue")

f:id:akiyoko:20171203004038p:plain:w500


固有値の減少がなだらかになる直前までの固有値の数を因子数とする(スクリー基準)、および、因子数の基準となる固有値の最小値を「1」とする(カイザーガットマン基準)を考慮して、因子数を「6」としました。 *4, *5


最後に、因子数を 6 として因子分析をおこないます。

> factanal(x=data, factors=6, rotation="promax")

「rotation」の引数は、"none", "varimax", "promax" などがありますが、今回は、因子間の相関を仮定しないする(2017.12.26 訂正)プロマックス回転「 promax」を選択しました。 *6


f:id:akiyoko:20171203004437p:plain
f:id:akiyoko:20171203004450p:plain


この結果を因子負荷量の大小で色付けすると、以下の表のようになります。

f:id:akiyoko:20171203004907p:plain:w500


上記の表から、以下のように共通因子(趣味趣向クラスタ)を分類してみました。あくまでも仮説ですが。

  • 因子1:「麻雀」「囲碁」「将棋」の因子負荷量が大きい ・・・ 囲碁将棋系
  • 因子2:「美術鑑賞」「演芸・演劇鑑賞」の因子負荷量が大きい ・・・ アート系?
  • 因子3:「楽器演奏」「料理・お菓子作り」「ダンス」の因子負荷量が大きい ・・・ 音楽系?
  • 因子4:「競馬・競輪・競艇」「パチンコ・パチスロ」の因子負荷量が大きい ・・・ ギャンブル系
  • 因子5:「その他のボードゲーム・テーブルゲーム」「チェス」の因子負荷量が大きい ・・・ その他のボードゲーム系
  • 因子6:「映画鑑賞」「音楽鑑賞」の因子負荷量が大きい ・・・ オーソドックスな趣味系?


一部強引なところもあるかもしれませんが、アンケートに回答した人が「ああ、私はこの趣味趣向クラスタだな」と分かりやすいような分類になっているのではないかと思います。




 

まとめ

  • 因子分析は R でやれば超簡単
  • 数行で書けるよ

これを言うためだけに 8000字オーバーの記事を書いてしまいました。。



明日は、yamano357 さんの「R Advent Calendar 2017 - Qiita」 5日目の記事です。
よろしくお願いします。


 

おまけ

因子分析の結果表示でそれぞれの出力行がガタついて項目がズレてしまう場合は、
[Preferences]>[Appearance]から、Editor font に「Osaka-Mono」を選択すれば解決します(Mac の場合)。


f:id:akiyoko:20171203182418p:plain:w350



今回の場合はグラフ描画時に日本語を出力していませんが、RStudio でグラフを描画する際に文字化けしてしまう場合は、

par(family="HiraKakuProN-W3")

と事前に実行しておけばよいです。 *7

*1:《過去記事》akiyoko.hatenablog.jp

*2:今回はインドア系の趣味についてのみアンケートしました。

*3:選択肢に挙げた15種類の趣味は、総務省統計局が5年ごとに実施している「社会生活基本調査」(出典:平成28年 社会生活基本調査結果(総務省統計局))の「趣味・娯楽」の34種類の区分のうちの15種類と一致させているので、やろうと思えば、その種類別行動者数(≒趣味にしている人の割合)と照らし合わせることで世間一般の平均的な趣味趣向と比較することもできます。

*4:http://cogpsy.educ.kyoto-u.ac.jp/personal/Kusumi/datasem06/minemoto.pdf

*5:実際には「5」から「7」を順次選んで計算したところ、「6」が結論を導きやすかったという理由もあります。

*6:因子分析における因子軸の回転法について | Sunny side up!

*7:Rとウェブ解析:MACでグラフの日本語文字化けを防ぐ簡単な方法

「bitFlyer Drink Meetup! #9」に参加してきました

主催

bitFlyer

会場

株式会社bitFlyer 本社
東京都港区赤坂 9‐7‐1 ミッドタウン・タワー 8F



 

全体の感想など

11/28 から 11/30 までの直近3日間だけで

  • POSレジでの決済サービス開始 *1
  • 米国進出(bitFlyer USA Inc.) *2
  • FX 初のサーキットブレイク発動 *3

と(良くも悪くも)話題に事欠かない日本最大の仮想通貨取引所を運営する bitFlyer の勉強会に参加してきました。


bitFlyer ビットコインを始めるなら安心・安全な取引所で


国内シェアが8割、テレビCM を大量投入するなど、ビットコイン・仮想通貨の取引所としてはノリにノッている bitFlyer ですが、全銀協が推薦する実証実験プラットフォームベンダーに選出されたとの発表もあったことから、取引所以外にブロックチェーン自体にも注力していこうという方向性が窺えます。 *4


現在は「miyabi」という自社製ブロックチェーンをゼロから作っているとのことです。開発言語は C# で、チームがまだ小さいということもあってかお披露目はまだ当分先のようです。 *5



あと、会場はこんな感じで(私の写真ではありません)、六本木ミッドタウンの新オフィスに引っ越して 1ヶ月とのことでフロアも雰囲気もピカピカなオフィスでした。社員も90名に急増したとのことで、さすがノリノリですね(よく見ると床に刻み海苔らしきものが散らばってましたが、前日のイベントでピザでも食べたのでしょうか。まさにノリノリ)。




bitFlyer ビットコインを始めるなら安心・安全な取引所で



さて、LT の内容ですが、Hyperledger Sawtooth は今回初めて聞きました。PoET(Proof of Elapsed Time)も初耳です。

公式ドキュメント によると、Intel SGX のチップを使った Intel サーバ群を TEE(Trusted Execution Environments:信頼済み実行環境?)としたブロックチェーン上で、待ち時間をランダムに割り当てられたノードペアがブロックの承認をおこなうという仕様になっているようです(間違っていたらすいません)。

ただし PoET が動作するのは、Intel の分散台帳プラットフォーム「Sawtooth Lake」など限定的なブロックチェーン上のみのようで、実質 PoET はパブリックチェーンとしては利用できないということでしょうかね。
しかも、

実験用途にしか利用を推奨していない。
独自のコンセンサスアルゴリズムPoETを持つが、これは本来intelが提供するハードウェアを利用して行われるため完全版ではなく、オープンソース版ではシミュレータが用意されている。


開発は続けられているが、正式なプロダクトへの採用を勧めていない為、選択肢にはならない。


さまざまなブロックチェーン技術」より

とあるように、メインは実験用途とか。登壇者も「実務では使っていない」と言っていましたので、miyabi 開発のために他製品を調査してみたということなのかもしれません(実際、他の方も「社内勉強会の資料を外に出せるように手を入れた」と言っていました)。なお今回のデモは Docker のシミュレータ上で動作させるため、Intel SGX の TEE は利用しておらず、Mac でも問題なく動かせるというカラクリのようです。

また、(サンプルの ○✕ゲームの実装がそうなっているだけかもしれませんが、)Ether の Gas のようなトランザクション手数料の仕組みがないため、ノードのマイニングの報酬がなく、ノードを立てる経済インセンティブをどう設計するかについてはシステム上の懸念が残ります。というかそもそも、 Hyperledger 系プロダクトは分散台帳と暗号技術で守られた P2Pネットワーク、コンセンサスアルゴリズムを使いやすいように提供しているだけで、ブロックチェーン経済圏は範疇外ということなのかもしれません。

ブロックチェーンプラットフォームとしてメジャーなのはBitcoinとEthereumですが、それらとHyperledgerとの大きな違いは、bitcoinにとっての「bitcoin」、ethereumにとっての「ether」のような仮想通貨の単位がHyperledger には存在しないことです。Hyperledgerでは、純粋にブロックチェーンの持つ分散型台帳という特性を仮想通貨に限らず社会の様々なソリューションの技術基盤として汎用的に活用できるように開発されています。


Hyperledger入門 : Hyperledger(ハイパーレッジャー)って何? | BlockChain Online ブロックチェーンオンライン」より


Hyperledger プロジェクトの立ち位置については、http://doublehash.me/tag/hyper-ledger/ の図が分かりやすかったです。しかしながら Hyperledger Sawtooth はどちらかと言えば、Transaction and data は「Public」、Mining/Consensus は「Trusted(consensus)」(つまり図の左下のエリア)なんじゃないかな?と思いました。




まあそれはさておいて、(私が長年 Python を使ってきたということもあって)Python で任意のスクリプトを書けるのは面白いな、と思いました。
Python 製のブロックチェーンとしては他にも、

  • HydraChain
    • Ethereum の拡張版で、コンソーシアム型またはプライベート型のブロックチェーンを構築可能。Python でコントラクトを書けるのが特徴。
  • BigchainDB
  • DragonChain

などがありますが、Python で自由にコントラクトを記述できる有名どころとしては HydraChain が挙げられるでしょうか。ただし、HydraChain はノードが最低限 6台必要ということで検証用途としては Hyperledger Sawtooth に軍配が上がるということなのかもしれません(実際試してみましたが、Docker 上でデモを動かすのは非常に簡単でした)。



 

WindowsのDocker環境でブロックチェーンを動かしてみる

越智 佳景 氏(ブロックチェーンエンジニア 株式会社 bitFlyer)

  • Hyperledger
    • ビジネスのためのブロックチェーン技術 *6
    • Linux Foundationで開発
    • オープンソース
    • Sawtooth (ソウトゥース)は Hyperledger プロジェクトの一つで Intel が開発を主導 *7
  • PoW は参加者が限定されていない場合に有効
  • Sawtooth トランザクション
    • アプリ開発者が任意に設計できる
    • Validator が Transaction Processor(ビットコインのスクリプトに相当)に問い合わせ
    • ステートを変更する
  • Sawtooth ブロック
    • 代表ノードがブロックを作る
      • Intel SGX のチップ、Intel のサーバを信用する??
    • PoET(Proof of Elapsed Time:時間に応じてブロックを作れる可能性が高まる)
  • Docker
    • 3月あたりにEE(Enterprise)とCE(Community)に分かれた
    • 実行方法は二つ
      • Dockerfile(docker build . で Imageを作って docker run)
      • 複数コンテナをまとめて起動する場合は docker-compose.yaml(docker compose up でコンテナを起動)

なおブロックについては、チェーン内部にタイマーがあり、一定時間ごとに自動的にブロックが作られるようになっているとのこと。また State は、トランザクション外(メモリ上?)で管理されるそうです。




 

macOS の Docker環境で Hyperledger Sawtooth を動かす

ここから、私の macOS の Docker 上で Hyperledger Sawtooth のデモを動かしてみます。

環境は以下の通りです。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.12.6
BuildVersion:   16G1036

$ docker --version
Docker version 17.09.1-ce, build 19e2cf6


Sawtooth Enterprise Blockchain に事例が紹介されていますが、その他に ○✕ゲームがデモ用に実装されているので、それを Docker 上で動作させてみます。と言っても、発表の内容をなぞるだけですが。


まず、Hyperledger の sawtooth-core の GitHub から、以下の手順で sawtooth-core-1.0.0rc3.zip をダウンロードします。なお現時点での最新版は「v1.0.0rc3」。

f:id:akiyoko:20171201024521p:plain

f:id:akiyoko:20171201024759p:plain


docker/compose/sawtooth-default.yaml を開いて、以下のように shell をコピペして shell1, shell2 に書き換えます。

  ...

  shell1:
    image: hyperledger/sawtooth-all:1.0
    container_name: sawtooth-shell-default1
    expose:
      - 8008
      - 4004
    depends_on:
      - rest-api
    entrypoint: "bash -c \"\
        sawtooth keygen && \
        tail -f /dev/null \
        \""

  shell2:
    image: hyperledger/sawtooth-all:1.0
    container_name: sawtooth-shell-default2
    expose:
      - 8008
      - 4004
    depends_on:
      - rest-api
    entrypoint: "bash -c \"\
        sawtooth keygen && \
        tail -f /dev/null \
        \""

 
以下のコマンドを実行して、コンテナを起動します。

$ docker-compose -f ~/Downloads/sawtooth-core-1.0.0rc3/docker/compose/sawtooth-default.yaml up

しばらくすると、各種コンテナが起動します。

$ docker ps
CONTAINER ID        IMAGE                                       COMMAND                  CREATED             STATUS              PORTS                              NAMES
2efe61c116df        hyperledger/sawtooth-all:1.0                "bash -c 'sawtooth..."   16 minutes ago      Up 15 minutes       4004/tcp, 8008/tcp                 sawtooth-shell-default1
9d14523518bb        hyperledger/sawtooth-all:1.0                "bash -c 'sawtooth..."   16 minutes ago      Up 15 minutes       4004/tcp, 8008/tcp                 sawtooth-shell-default2
59f216a70497        hyperledger/sawtooth-settings-tp:1.0        "settings-tp -vv -..."   16 minutes ago      Up 15 minutes       4004/tcp                           sawtooth-settings-tp-default
0f0d651f61b3        hyperledger/sawtooth-rest-api:1.0           "sawtooth-rest-api..."   16 minutes ago      Up 16 minutes       4004/tcp, 0.0.0.0:8008->8008/tcp   sawtooth-rest-api-default
f17d965f771b        hyperledger/sawtooth-xo-tp-python:1.0       "xo-tp-python -vv ..."   16 minutes ago      Up 16 minutes       4004/tcp                           sawtooth-xo-tp-python-default
2ae2e16405f2        hyperledger/sawtooth-intkey-tp-python:1.0   "intkey-tp-python ..."   16 minutes ago      Up 15 minutes       4004/tcp                           sawtooth-intkey-tp-python-default
25803f8bec59        hyperledger/sawtooth-validator:1.0          "bash -c 'sawadm k..."   16 minutes ago      Up 16 minutes       0.0.0.0:4004->4004/tcp             sawtooth-validator-default


次に、別々のターミナル上で

《ターミナル 1》

$ docker exec -it sawtooth-shell-default1 bash

《ターミナル 2》

$ docker exec -it sawtooth-shell-default2 bash

を実行し、起動した 2つの shell クライアントのコンテナにそれぞれログインします。


xo の遊び方、コマンドなどの仕様については、以下を参照。


《ターミナル 1》

### user1 用の鍵を作成
# sawtooth keygen user1
writing file: /root/.sawtooth/keys/user1.priv
writing file: /root/.sawtooth/keys/user1.pub

### ゲームを新規作成
# xo create --url http://rest-api:8008 --username user1 game1
Response: {
  "link": "http://rest-api:8008/batch_statuses?id=5ebd578b0c6800460bcf2e53cd8275f2e8266fee9e9fd421f94f7b92d0b785fe28debe5c0a986fb44c784df7a1e35cf678d8f3fe3e3255fcc1fe0b710420cbd8"
}

### ゲームが作成されたか確認
# xo list --url http://rest-api:8008
GAME            PLAYER 1        PLAYER 2        BOARD     STATE
game1                                           --------- P1-NEXT

### user1 が一手目を打つ
# xo take --url http://rest-api:8008 --username user1 game1 1
Response: {
  "link": "http://rest-api:8008/batch_statuses?id=19189296519d8ced6d94902203004607532bb59b5dd8b75fcc5ff99022dc02fb54e26c6618dec172d27e09cb7808d3f2b14aac7cdece01960a321b532673afe7"
}

### ゲームの状況を確認
# xo show --url http://rest-api:8008 game1
GAME:     : game1
PLAYER 1  : 03a8de
PLAYER 2  :
STATE     : P2-NEXT

  X |   |
 ---|---|---
    |   |
 ---|---|---
    |   |


《ターミナル 2》

### user2 用の鍵を作成
# sawtooth keygen user2
writing file: /root/.sawtooth/keys/user2.priv
writing file: /root/.sawtooth/keys/user2.pub

### ゲームの一覧を確認
# xo list --url http://rest-api:8008
GAME            PLAYER 1        PLAYER 2        BOARD     STATE
game1           03a8de                          X-------- P2-NEXT

### user2 が 2手目を打つ
# xo take --url http://rest-api:8008 --username user2 game1 3
Response: {
  "link": "http://rest-api:8008/batch_statuses?id=1d33ab7613cb9cffc5813502b1a47b1f9ea78d291fe2ce69187bd6128034593f0889eba850cf7e92e32db0f702cbc55e61508d1e743dd3fad19c2a47cd574792"
}

### 試合のステータスを確認
# xo show --url http://rest-api:8008 game1
GAME:     : game1
PLAYER 1  : 03a8de
PLAYER 2  : 039346
STATE     : P1-NEXT

  X |   | O
 ---|---|---
    |   |
 ---|---|---
    |   |

という感じで、xo-tp-python がトランザクションを処理しています。
もちろん鍵が合わないユーザーは、間違った順番で take できないように検証がおこなわれます。

なおブロックは、チェーン内部にタイマーがあり、一定時間ごとに自動的にブロックが作られるようになっているとのこと。ちなみに、State はトランザクション外(メモリ上?)で管理されるということです。

「BPStudy#123 〜技術書籍執筆の実際、ノウハウ」に参加してきました

主催

BPStudy

会場

国際英語学校代々木教会ビル会場 大会議室6F
東京都渋谷区代々木1-29-5 (教会ビル)

Twitter

twitter.com



 

全体の感想など

最近、何か技術本を書きたいなぁ、と思い始めていました。
というのも、
techbookfest.org

というのを少し前に知ったからです。
こういうアウトプットのし方もあるのね、と。

あと、ちょうど最近、

というのもホットエントリーになっていたり。


ちなみに結城浩さんは、過去に自身のブログでこういったことも書いています。

本を出版するのは、一冊目が一番たいへんだ。 ほんとうに、ほんとうに一冊目はたいへん。 二冊目以降は(一冊目に比べれば)夢のように楽だ。 何が楽になるかというと、自分の中に生じる不安を吹き飛ばすのが楽になる。 「こんなに大変で、自分はやりとげられるだろうか」という不安に対しては、 「うん、大丈夫。こういう大変さはいつもと同じだ」 と答えられるようになる。 「こんなに一冊の本に時間をかけても大丈夫なのだろうか」という不安に対しては 「気持ちが前向きになっているから大丈夫。この時間は品質向上に必要な時間なのだ」 と自分に対して答えられるようになる。 いつも、祈りは必要だ。いつも、呼吸が必要なように。 それはそれとして、 二冊目以降、 自分の不安な気持ちをなだめるためのエネルギーを、 具体的な仕事に向けることができるのはとても楽なことだ。


一冊の本の7分目ほどで、何か大切なものがキラキラッと見えることがある。 本を書き始める前には知らなかった何か、 ささやかではあるけれど本質的な何かを見つかる瞬間だ。 それは著者にとって大きな報酬の1つだ。 物語をつかむ瞬間。 大きな喜びの瞬間。


でも、一番大きな喜びは、出版後にやってくる。 それは、読者から送られてくる「なるほど、わかりました!」というフィードバックだ。


本を書くということ


私自身はこれまで本を書いたことはありませんが、この一年ほど某会報誌に毎月10ページほど寄稿していたこともあり、書くことの辛さ、難しさは多少は理解しているつもりです。でもどうせ書くなら、多くの人に届けられるものを書きたい。そんなことを悶々と思っていたときに偶然見つけたこの勉強会。これは行くしかないでしょうと。


そして今回感じたこと。本を書く人は、人に何かを伝えるのが上手い。
というか、話の「構成」がうまいのかなあ、と感じました。きちんと伝えられるような構成をまず考えているというか、伝えたいことを中心に話を作っているというか。技術書とは言えど、本を買った人に何かを残す、という使命をきちんと意識しているように思いました。



 

まず、共著からやってみよう 〜 Pythonエンジニアファーストブックで学んだノウハウの共有

鈴木 たかのり 氏(株式会社ビープラウド)

Slide:Pythonエンジニアファーストブックの紹介


  • 6章を5人で書いた。
  • ターゲットは、これからPythonを仕事で使うエンジニア
  • Pythonエンジニア養成読本を改訂
    • Pythonのバージョンを3系に
    • Bottle → Djangoに変更
    • ライブラリ、ツールを最新に
  • スケジュール
    • 2016年12月に改訂を打診
    • 3月にキックオフの飲み会
    • 6月に Web以外脱稿
    • 7月に Web脱稿
    • 9月9日発刊
  • 打ち合わせでストーリー作成
  • LEGOのデータを使おう!
    • 実際に Scrapy で LEGO のデータをクローリングしている人がいる *1

 
Pythonエンジニアファーストブックで学んだノウハウの共有
清原 弘貴 氏(株式会社ビープラウド)

Slide:共著からやってみよう 〜Pythonエンジニアファーストブックで学んだノウハウの共有

  • 初めてなら共著をオススメ
    • 今まで他の本をやってきた人の執筆ノウハウが使える
    • 執筆の流れが理解できる
  • 執筆ノウハウ
    • Sphinx で書く
    • term-validator でビルドのたびに漢字や表記の揺れを自動でチェックしてくれる
    • TODOや指摘をプルリクでやり取り(GitLab?)
    • Dropbox で PDFファイルを直接レビュー(すごい!!オススメ!)
      • PDFにコメントを付けられる
      • Dropbox と Adobe が提携してるから実現してる??
  • Slack でやりとり
  • コードは GitHub で公開している
  • 執筆の流れ
    • 意外と時間が掛かるし、流れや見積もり感覚が開発と少し違う
  • 良かったところ
    • 知識を補完し合える(得意・不得意)
    • よく知ってる人としてのレビュー、知らない観点からのレビュー
    • 著者みんなで告知できる
      • Twitter、POP行脚、イベント発表、打ち上げ
  • 積極的にレビューするのが大事
  • 共著に参加するには?
    • 声が掛かりそうな人と仲良くなる
    • 私できますよ的な雰囲気をアピール
    • 信頼貯金を貯めよう
  • 「意見は率直に」
    • by「ピクサー流 創造するちから」



 

技術書を書くということ。商業誌を書くということ 〜 Jupyter 実践入門執筆プロジェクトを終えて

池内 孝啓 氏(株式会社 slideship)


  • 技術書を書くということ
  • 動機
    • 利己的
      • 自己や自社のブランディング
      • 経験
      • 印税
    • 利己的
      • 世に広める
      • 後世に記録を残す
  • ブランディング
    • キャリアの方向性と書籍の領域がマッチしていればブランディングになる
    • 実績として分かりやすい
  • 技術の輪を広げる
    • コミュニティに還元(Pay it forward)
  • 印税?
    • 300頁/1時間あたり1.5頁=200時間
    • 一冊 120万円とすると、割に合わない?
  • 納本制度
    • Jupyter本も納本済み
    • 知識や技術が陳腐化しても、当時の考えや歴史は覆らない
  • 商業誌を書くということ
  • 利益を出す必要がある
  • 良い本かどうかはマーケットが判断
  • Jupyter本は、2016年2月頃に技術評論社へ企画を持ち込む
    • 紆余曲折を経て2017年に企画スタート
  • テーマ選択の理由は「行ける!」と思ったから
  • ベネフィットを届ける
  • 読者は高度な専門知識に対してお金を払っている
    • 正確な情報
    • 妥当な方法
  • 読者がどれだけ利益を得たかが全て
    • 知らなかったことが知れる。分からなかったことが分かる
    • 学習のきっかけ
  • 売上の向上のために
    • ニーズに答える
    • ベネフィットを提供
    • マーケットの大きなところを狙う
  • 利益率を上げるために
    • 執筆コストを下げる
    • 執筆ノウハウを共有
  • 執筆プロジェクトは、ステークホルダーの利益をどう最大化させるかという視点をもっと持つ
  • O'Reillyのアトラス?どうなったか不明
  • テキストからコンバート?
  • GitBook?(Sphinx の代替として)
    • Markdown を束ねるツール


 

はじめての執筆でわかった技術書ができるまでの流れ

岩崎 圭 氏(株式会社 SQUEEZE)

docs.google.com


  • スケジュール
    • 2016年10月キックオフ
    • 2017年6月が本来の〆切 → 8月に完成
  • 形式は Markdown
  • GitLab の git で管理
  • やり取りは Slack
  • pyhack の常連 + ブログも書いていたので? 声をかけてもらったのかな?
  • まずは企画を通す
  • 最初のキックオフMTGでアウトラインを出す
  • ツールは、Sphinx, Re:VIEW
  • 執筆が上手くいかない問題
    • 書く時間を上手く確保できない
      • 平日仕事が終わったら必ず「コワーキングスペースに行く」を習慣化
    • 筆が進まない問題
      • 各章で最初にアウトラインを書くようにした
    • 執筆のアクティビティを Slack に流す
      • 執筆が死んでる感を出さないように
  • 1章あたりどんなに短くとも2週間は掛かった
  • 原稿消滅事件
  • レビューはDropboxコメント機能を利用
  • 修正は時間と体力との戦い
  • もう少し上手くやりたかったこと
    • 日本語力不足の解消
    • 最低限の校正の自動化(textlint x CI など)
    • 原稿の自動ビルド(テンションが違う)
    • 原稿の時点である程度レビューしたかった
    • 素のMarkdownがつらい
      • コラム、Note、Point など書籍ならではのブロックの表現
  • とんでもない量の時間と体力を費やして本当に疲れた
  • 技術的な内容を日本語に変換するのが難しい

gitlab.com の原稿消失事件は、2月のコレらしい。
結局大丈夫だったとのこと。




 

LT

 

技術書は Jupyter Notebook で書けばいいんじゃない?

driller / どりらん 氏

  • Jupyter Notebook とドキュメントビルダーだけで OK!
  • nbconvert で変換
  • Shpinx, Pelican
  • nbsphinx
  • PDF や ePub を作成するなら ⇒ Sphinx
  • HTML形式だけでよいなら ⇒ Miyadaiku
  • ただし、テキスト校正ツールが使えない、コーディング規約のチェックが使えないなどの問題も

 

技術書査読・校正の現場から

Hayao Suzuki 氏


 

一流のエンジニア/経営者になるため基礎力を磨け!

高崎健太郎

  • 一流になるためには学びが重要
  • 自習、経験、環境
  • ぶれない軸
  • 一緒に学び合える仲間
  • 学びを与えてくれるもの
    • 歴史、スポーツ
      • 同じテーマで議論できる
      • 葛藤、挫折、新たな挑戦。組織論、戦術論


 

技術書の原稿はWordで書いちゃダメゼッタイという話

生形 可奈子 氏(アシアル株式会社)

  • スラスラわかるJavaScript は単著
  • 増刷で修正する場合は、軽微な修正であっても原稿データと同期を取る
  • Amazonレビューの「違反を報告」はヤバイ

「fin-pyもくもく会 #10」に参加してきました

主催

fin-py

会場

株式会社インタートレード
東京都中央区新川一丁目17番21号 茅場町ファーストビル3階

Twitter

twitter.com



 

全体の感想など

fin-py は今回で 3回目の参加となりました。

Python は業務で7年ほど&プライベートでもちょくちょく触っているのでそこそこ使いこなせますが、これまで金融業界の経験もないし、個人的にほとんど投資をしたことがないので、Finance の部分は素人同然。発表のときに「約定」を「やくてい」と読んでしまうほどのレベルです。。


まあ、金融業界にいなくても独学で十分という話もありますが。。


あと今回は参加者が多くてビックリしました。特に初参加の方が多かったようです。Quantopian 関連の人が多かったのでしょうか、詳しくはよく分かりません。



《過去記事》
akiyoko.hatenablog.jp

akiyoko.hatenablog.jp



 

やりたかったこと

bitFlyer の BTC-FX API を使ってトリガー検知

bitFlyer Lightning FX(ビットコイン FX) では、預入証拠金に対して最大15倍の取引ができるのが特徴です。最低取引金額が 0.001 BTC になっているため、1BTC=100万円として 1,000 円(レバレッジ15倍なら預入証拠金は約67円)から取引ができることになります。


bitFlyer ビットコインを始めるなら安心・安全な取引所で
bitFlyer ビットコインを始めるなら安心・安全な取引所で


背景としては、この一年の仮想通貨のトレンドとして、

Ripple(3月上旬)
アルトコイン(〜6月中旬)
草コイン(〜7月?)
ICO(〜9月中旬)
ハードフォーク(〜11月?)
チャイナショック(9月初旬) ⇒ アルトコインが焼かれる
CME先物発表(10月下旬) ⇒ アルトコインが焦土に
取引所増加
BTC-FX ⇐ 今ココ!?


みたいな流れがあったりするので、ちょっと触ってみようかと(まだ触ったことはありません)。


 

やったこと

  • FX 戦士たちの戦略調査
  • bitFlyer Lightning(FX取引所)を眺めてみる
    • いろんな音が鳴ってた ♪
  • pybitflyer 触ってみた
  • ccxt で書いてみた
  • テクニカル指標を扱うライブラリ「TA-Lib」を使ってグラフ書いてみた

 

環境
$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.12.6
BuildVersion:   16G1036

$ python --version
Python 3.5.2 :: Anaconda 4.2.0 (x86_64)


 

TA-Lib のインストール

テクニカル指標を扱うライブラリ。
GitHub - mrjbq7/ta-lib: Python wrapper for TA-Lib (http://ta-lib.org/).

$ brew install ta-lib
$ pip install TA-lib

TA-Lib (0.4.10)

「error: command '/usr/bin/clang' failed with exit status 1」とのエラーが出るので、まず先に「brew install ta-lib」が必要。

(参考)python - /usr/bin/clang command failing trying to pip install TA-lib on MacOS - Stack Overflow


 

pybitflyer のインストール

id:yag_ays さん作の bitFlyer REST API のラッパーライブラリ。
bitFlyer LightningのAPIをPythonから使えるパッケージ「pybitflyer」を作りました - おおかみ山

$ pip install pybitflyer

pybitflyer (0.1.7)


使ってみる。

import pybitflyer

exec(open('env.py', 'rb').read())

api = pybitflyer.API(
    api_key=BITFLYER_KEY,
    api_secret=BITFLYER_SECRET,
)
api.board(product_code='FX_BTC_JPY')

こんな感じで、板情報が取れます(それぞれ100件くらい)。

{'asks': [{'price': 942400.0, 'size': 0.00310928},
  {'price': 942497.0, 'size': 0.60458056},
  {'price': 942498.0, 'size': 0.04},
  ...],
 'bids': [{'price': 942212.0, 'size': 0.0521},
  {'price': 942152.0, 'size': 0.3},
  {'price': 942151.0, 'size': 0.22},
  ...],
 'mid_price': 942306.0}


 

ccxt で書いてみた

pybitflyer とほぼ同じことが書けます。

import ccxt
from pprint import pprint

exec(open('env.py', 'rb').read())

ex = ccxt.bitflyer({
    'apiKey': BITFLYER_KEY,
    'secret': BITFLYER_SECRET,
})

pprint(ex.fetch_ticker('FX_BTC_JPY'))
print('-' * 50)
pprint(ex.fetch_order_book('FX_BTC_JPY'))
{'ask': 1019470.0,
 'average': None,
 'baseVolume': 234473.73139995,
 'bid': 1019144.0,
 'change': None,
 'close': None,
 'datetime': '2017-11-26T12:56:20.000Z',
 'first': None,
 'high': None,
 'info': {'best_ask': 1019470.0,
          'best_ask_size': 0.0152,
          'best_bid': 1019144.0,
          'best_bid_size': 1.4489,
          'ltp': 1019144.0,
          'product_code': 'FX_BTC_JPY',
          'tick_id': 10554736,
          'timestamp': '2017-11-26T12:56:20.9',
          'total_ask_depth': 8018.9239892,
          'total_bid_depth': 11740.4605367,
          'volume': 249894.26662009,
          'volume_by_product': 234473.73139995},
 'last': 1019144.0,
 'low': None,
 'open': None,
 'percentage': None,
 'quoteVolume': 249894.26662009,
 'symbol': 'FX_BTC_JPY',
 'timestamp': 1511700980000,
 'vwap': None}
--------------------------------------------------
{'asks': [[1019470.0, 0.0152],
          [1019475.0, 0.0152],
          [1019494.0, 0.2008],
          ...,
          [1735257.0, 0.79804842]],
 'bids': [[1019144.0, 1.4489],
          [1019143.0, 2.331],
          [1019140.0, 0.01],
          ...,
          [95000.0, 2.0]],
 'datetime': '2017-11-26T12:56:22.000Z',
 'timestamp': 1511700981575}


しかしながら、pybitflyer と同じく 100件ほどしか板情報が取れないため、例えばボリンジャーバンドを計算するための ticker の情報はどうやって収集すればいいの?というのが懸念点。



 

TA-Lib を使ってグラフ書いてみた

とりあえず、さくらのクラウドサーバ上の MongoDB に格納している ticker データからグラフ作ってみました。

前回の反省を活かし、Jupyter Notebook で。

% matplotlib inline

from datetime import datetime, timedelta

import matplotlib.pyplot as plt
import pandas as pd
import talib
from pymongo import MongoClient, ASCENDING
from sshtunnel import SSHTunnelForwarder

plt.style.use('ggplot')
pd.options.display.max_rows = 20

now = datetime.now()
df = None

with SSHTunnelForwarder(
    '153.xxx.xxx.xxx',
    ssh_username='ubuntu',
    ssh_pkey='/Users/akiyoko/.ssh/sakura.pem',
    remote_bind_address=('127.0.0.1', 27017)
) as server:
    # server.start()
    print(server.local_bind_port)

    client = MongoClient('127.0.0.1', server.local_bind_port)
    db = client['bittrex']
    collection = db['tickers']

    records = list(collection.find(
        {
            'symbol': 'BTC',
            'timestamp': {'$gte': now - timedelta(minutes=60 * 24)},
        },
        {'_id': False},
    ).sort('timestamp', ASCENDING))

    df = pd.DataFrame(records)
    # Need?
    # https://stackoverflow.com/a/17328858/8426544
    #df = df.set_index(pd.DatetimeIndex(df['timestamp']))

print(df.dtypes)

df['sma'] = talib.SMA(df['last'].values, timeperiod=12)
print(df)

ax = df.plot(x='timestamp', y='last')
df.plot(ax=ax, x='timestamp', y='sma', secondary_y=True)


f:id:akiyoko:20171126220525p:plain





 

LT

pandas はデータをリサンプリングできる。例えば、1分足から5分足へ。

例えば、こんな感じ。

% matplotlib inline

import matplotlib.pyplot as plt
import pandas as pd
import talib

plt.style.use('ggplot')
pd.options.display.max_rows = 20

df = pd.read_csv('DAT_ASCII_EURUSD_M1_2015.csv', sep=';',
                 names=('Time', 'Open', 'High', 'Low', 'Close', ''),
                 index_col='Time', parse_dates=True)
df.index += pd.offsets.Hour(7)

# cf. https://stackoverflow.com/a/36223274/8426544
# cf. http://nekopuni.holy.jp/2015/01/pythonnysol%E7%82%BA%E6%9B%BF%E9%AB%98%E9%A0%BB%E5%BA%A6%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E4%BB%BB%E6%84%8F%E3%81%AE%E3%83%AD%E3%82%A6%E3%82%BD%E3%82%AF%E8%B6%B3%E3%83%81%E3%83%A3/
df = df.resample('1H').agg({
    'Open': 'first',
    'High': 'max',
    'Low': 'min',
    'Close': 'last',
}).ffill()
df['FastMA'] = talib.SMA(df['Close'].values, timeperiod=10)
df['SlowMA'] = talib.SMA(df['Close'].values, timeperiod=30)

df.plot(y=['Close', 'FastMA', 'SlowMA'])

from pandas_highcharts.display import display_charts
df = pd.DataFrame({'Close': df['Close'], 'FastMA': df['FastMA'], 'SlowMA': df['SlowMA']})
display_charts(df, chart_type="stock", title="MA cross", figsize=(640, 480), grid=True)

(参考)PythonでFXシストレのバックテスト(1)


resample() メソッドを使用することで時系列データの頻度を変換できます。日次のデータを週次や月次などのデータに変換できます。


どりらんさん共著の Jupyter 本に載ってる!(P.128 あたり)


Jupyter 上で動かせるチャートを書くなら、pandas-highcharts が良さげ。
python-highcharts もあるよ。