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.


Hyperledger 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が提供するハードウェアを利用して行われるため完全版ではなく、オープンソース版ではシミュレータが用意されている。


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

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


なお、公式ドキュメント によると、パブリックな 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 - Cloud Foundry Live | 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 v0.8.13 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:Hyperledger 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