Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Tendermint をさわってみる

Yuka Tsuboi
September 03, 2018

Tendermint をさわってみる

20180903 ビットコインとか勉強会 #21 でお話ししたスライドです

Yuka Tsuboi

September 03, 2018
Tweet

More Decks by Yuka Tsuboi

Other Decks in Technology

Transcript

  1. スケーラビリティ問題を解決するアプローチ - On-chain / 1st layer - コンセンサスアルゴリズムの改善 - Casper

    (Proof of Stake): 通貨の保有量に応じてマイナーを選出することで、計算を必要とする Proof of Work よりも高速にコンセンサスを得られる - 処理の並列化 - Sharding: トランザクションを分割し、ノードグループ毎に割り当てることで並列処理を可能にする - Off-chain / 2nd layer - ステートチャネルで処理 - Raiden Network: ペイメントチャネルで取引を行い、チェーンには最新の状態だけを記録することで、 取引を早く安く行える - サイドチェーンで処理 - Plasma: 子チェーンでトランザクションを処理し、親チェーンにはブロックのハッシュだけを記録することで、 トランザクションの処理を早く安く行える
  2. - On-chain / 1st layer - コンセンサスアルゴリズムの改善 - Casper (Proof

    of Stake): 通貨の保有量に応じてマイナーを選出することで、計算を必要とする Proof of Work よりも高速にコンセンサスを得られる - 処理の並列化 - Sharding: トランザクションを分割し、ノードグループ毎に割り当てることで並列処理を可能にする - Off-chain / 2nd layer - ステートチャネルで処理 - Raiden Network: ペイメントチャネルで取引を行い、チェーンには最新の状態だけを記録することで、 取引を早く安く行える - サイドチェーンで処理 - Plasma: 子チェーンでトランザクションを処理し、親チェーンにはブロックのハッシュだけを記録することで、 トランザクションの処理を早く安く行える スケーラビリティ問題を解決するアプローチ Tendermint
  3. - On-chain / 1st layer - コンセンサスアルゴリズムの改善 - Casper (Proof

    of Stake): 通貨の保有量に応じてマイナーを選出することで、計算を必要とする Proof of Work よりも高速にコンセンサスを得られる - 処理の並列化 - Sharding: トランザクションを分割し、ノードグループ毎に割り当てることで並列処理を可能にする - Off-chain / 2nd layer - ステートチャネルで処理 - Raiden Network: ペイメントチャネルで取引を行い、チェーンには最新の状態だけを記録することで、 取引を早く安く行える - サイドチェーンで処理 - Plasma: 子チェーンでトランザクションを処理し、親チェーンにはブロックのハッシュだけを記録することで、 トランザクションの処理を早く安く行える スケーラビリティ問題を解決するアプローチ Tendermintをベースとしたブロックチェーン Cosmos, Ethermint Tendermint
  4. セキュアかつスケーラブルなブロックチェーンを誰でも容易に構築するための コンセンサスエンジンとアプリケーションインターフェースをパッケージングしたソフトウェア 特徴 - 1秒あたり数千トランザクションを処理できる - ブロック生成後すぐにファイナリティを得られる - 3分の1のノードが停止、または悪意を持った振る舞いをしても安全 -

    任意のプログラミング言語でブロックチェーンプロトコルの実装ができる ブロックチェーンプロトコルの開発者は、Tendermintを用いることで コンセンサスアルゴリズムやP2Pネットワークのセキュリティやスケーラビリティを意識せず また特定のプログラミング言語にも縛られずに 独自のブロックチェーンプロトコルを開発することができる
  5. Application BlockChain Interface (ABCI) 1台のマシン上でのステートマシン(= アプリケーションロジック)と 複数のマシンへステートマシンを複製する仕組み(= コンセンサスエンジン)との間に シンプルなインターフェースを提供することで、それぞれの関心事を分離する コンセンサスエンジン

    ステートに対するトランザクションが すべてのマシンに同じ順序で複製されることを保証する アプリケーションロジック トランザクションを検証し、OKなら トランザクションを実行してステートを更新する ABCI
  6. メッセージプロトコル メッセージはGoogleが開発しているデータフォーマット”Protocol Buffer”で定義する 主要なメッセージタイプとしてCheckTx, DeliverTx, Commit等がある - CheckTx - リクエストされたトランザクションの検証を行う

    - DeliverTx - リクエストされたトランザクションの検証と実行を行い、アプリケーションステートを更新する - Commit - 現在のアプリケーションステートのルートハッシュを計算する - レスポンスとして返却されたルートハッシュを次のブロックのヘッダにセットする
  7. ブロックチェーンプロトコル ABCIはコネクション指向で、メッセージのやりとりにはコネクションを確立する タイミングと用途に応じて、3つのコネクションを明示的に使い分ける - Mempool Connection - ユーザからトランザクションを受け取ったときに確立するコネクション - Consensus

    Connection - コンセンサスの得られた新しいブロックがコミットされたときに確立するコネクション - Query Connection - アプリケーションステートの状態を問い合わせるときに確立するコネクション
  8. Mempool Connection ユーザからトランザクションを受け取ったときに確立するコネクション CheckTXメッセージを送ってトランザクションを検証し、 受け取ったトランザクションを他のノードにブロードキャストするかどうかを判断する ABCI ABCI サーバ ABCI クライアント

    アプリケーションロジック コンセンサスエンジン 2. CheckTXメッセージを送信 4. OK or NG を返却 3. トランザクションを検証 5. OKならメモリプールに溜 めておき、受け取ったの と同じ順序で他のノード にブロードキャストする。 NGなら破棄する 1. トランザクションを 受け取る
  9. Consensus Connection コンセンサスの得られた新しいブロックがコミットされたときに確立するコネクション BeginBlock, [DeliverTX, ...], EndBlock, Commit の一連のメッセージを送って ブロックのすべてのトランザクションを実行し、次のブロック生成の準備をする

    ABCI ABCI サーバ ABCI クライアント アプリケーションロジック コンセンサスエンジン 4. ステートのルートハッシュを返却 3. DeliverTXで送られた トランザクションを検証・ 実行してステートを更新 する 5. ステートのルートハッシュを 次のブロックのヘッダにセッ トする 1. コンセンサスを得ら れた新しいブロック がチェーンにコミット される 2. BeginBlock, [DeliverTX,…], EndBlock, Commitメッセージ を送信
  10. コンセンサスアルゴリズム 部分的に非同期で、決定論的な BFT-based PoS - 部分的に非同期 - トランザクションを収集/検証/拡散するのに特定の時間を設定する(Bitcoinは10分、 Ethereumは15秒) =

    同期的 - タイムアウトするまで非同期で続く = 部分的非同期, 弱い同期 - 決定論的 - ランダム性がなく、完全に決定論的にプロポーザーを選出する - 通貨の保有量に応じた加重ラウンドロビン方式でローテーションする Chain-based PoS (Casper) と BFT-based PoS との違いについては Consensus Compare: Casper vs. Tendermint を参照してください
  11. コンセンサスを得るまでのステップ 1. Transaction Submittion - ユーザからトランザクションが送られるとローカルのMempoolキャッシュに保持される - ABCIのMempool Connectionでトランザクションがアプリケーションで検証され、ローカ ルのMempoolに保持された後、他ノードのMempoolにブロードキャストされる

    2. Propose - P2Pネットワークに参加するノード(バリデータ)の中から、通貨保有量に応じた加重ラウン ドロビン方式で”プロポーザー”が選ばれる - メモリプールに溜まっているトランザクションを近隣のバリデータにブロードキャストする
  12. コンセンサスを得るまでのステップ 3. Pre-Vote - 提案されたブロックに1回目の投票(署名)を行う - 2/3以上のバリデータが投票し終わるか、タイムアウトに達するまで待つ - 投票が集まればすぐに次のステップに進むことができる =

    部分的非同期 4. Pre-Commit - Pre-Voteで2/3以上のバリデータが投票したブロックに、2回目の投票(署名)を行う - 2/3以上のバリデータが投票し終わるか、タイムアウトに達するまで待つ - 1ラウンドでPre-Commitできるのは1度だけ。そのため、同時に別のブロックがPre- Commitされることはなく、フォークが発生しない = すぐにファイナリティが得られる
  13. ルートディレクトリの初期化: $HOME/.tendermint 下に秘密鍵やチェーンの初期定義ファイルが生成される $ tendermint init I[09-04|14:50:24.402] Generated private validator

    module=main path=/root/.tendermint/config/ priv_validator.json I[09-04|14:50:24.403] Generated node key module=main path=/root/.tendermint/config/ node_key.json I[09-04|14:50:24.403] Generated genesis file module=main path=/root/.tendermint/config/ genesis.json $ tendermint node --proxy_app=kvstore I[09-04|14:50:33.146] Starting multiAppConn module=proxy impl=multiAppConn I[09-04|14:50:33.146] Starting localClient module=abci-client connection=query impl=localClient I[09-04|14:50:33.146] Starting localClient module=abci-client connection=mempool impl=localClient I[09-04|14:50:33.146] Starting localClient module=abci-client connection=consensus impl=localClient I[09-04|14:50:33.146] ABCI Handshake module=consensus appHeight=0 appHash= I[09-04|14:50:33.146] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0 I[09-04|14:50:33.148] Completed ABCI Handshake - Tendermint and App are synced module=consensus appHeight=0 appHash= ノードの起動(1): kvstoreというサンプルアプリケーションをインプロセスで起動し、ABCIハンドシェイクをして接続する
  14. ノードの起動(2): ノードがバリデータとして起動し、周辺のピアと接続しにいこうとする (が、見つからないため1ノードで続行する) ノードの起動(3): コンセンサスが開始され、プロポーザーとして選出される。プロポーザルブロックを生成する I[09-04|14:50:33.148] This node is a

    validator module=consensus addr=11C18BAB5694F38F8A2AC7A8FEDC90C62D320700 pubKey=PubKeyEd25519{9E12F91773DCCE385FC02778AB7BB94D9C0D2249C70F44AEA7179758FBF03F86} I[09-04|14:50:33.361] Starting Node module=main impl=Nodeactor … 中略 … I[09-04|14:50:33.368] Started node module=main nodeInfo="NodeInfo{id: d662eb55503a3934ec72a6636db84a539f9ce5fe, moniker: instance-2, network: test-chain-HVlSTN [listen 10.142.0.2:26656], version: 0.23.0-013b9cef ([amino_version=0.10.1 p2p_version=0.5.0 consensus_version=v1/0.2.2 rpc_version=0.7.0/3 tx_index=on rpc_addr=tcp://0.0.0.0:26657])}" I[09-04|14:50:33.368] Ensure peers module=p2p numOutPeers=0 numInPeers=0 numDialing=0 numToDial=10 I[09-04|14:50:33.368] No addresses to dial nor connected peers. Falling back to seeds module=p2p E[09-04|14:50:33.368] Couldn't connect to any seeds module=p2p I[09-04|14:50:34.357] enterNewRound(1/0). Current: 1/0/RoundStepNewHeight module=consensus height=1 round=0 I[09-04|14:50:34.357] enterPropose(1/0). Current: 1/0/RoundStepNewRound module=consensus height=1 round=0 I[09-04|14:50:34.357] enterPropose: Our turn to propose module=consensus height=1 round=0 proposer=11C18BAB5694F38F8A2AC7A8FEDC90C62D320700 privValidator="PrivValidator{11C18BAB5694F38F8A2AC7A8FEDC90C62D320700 LH:0, LR:0, LS:0}" I[09-04|14:50:34.360] Signed proposal module=consensus height=1 round=0 proposal="Proposal{1/0 1:08E44C262B06 (-1,:0:000000000000) 05A0C21B1A54 @ 2018-09-04T14:50:34.358047426Z}" I[09-04|14:50:34.363] Received proposal module=consensus proposal="Proposal{1/0 1:08E44C262B06 (-1,:0:000000000000) 05A0C21B1A54 @ 2018-09-04T14:50:34.358047426Z}" I[09-04|14:50:34.369] Received complete proposal block module=consensus height=1 hash=048C7EC62CF3BB8C145F96517AAB93262F762B7962B79
  15. ノードの起動(4): Pre-Vote ノードの起動(5): Pre-Commit I[09-04|14:50:34.369] enterPrevote(1/0). Current: 1/0/RoundStepPropose module=consensus I[09-04|14:50:34.369]

    enterPrevote: ProposalBlock is valid module=consensus height=1 round=0 I[09-04|14:50:34.372] Signed and pushed vote module=consensus height=1 round=0 vote="Vote{0:11C18BAB5694 1/00/1(Prevote) 048C7EC62CF3 FB3D599C0A0D @ 2018-09-04T14:50:34.369900185Z}" err=null I[09-04|14:50:34.374] Added to prevote module=consensus vote="Vote{0:11C18BAB5694 1/00/1(Prevote) 048C7EC62CF3 FB3D599C0A0D @ 2018-09-04T14:50:34.369900185Z}" prevotes="VoteSet{H:1 R:0 T:1 +2/3:048C7EC62CF3BB8C145F96517AAB93262F762B79:1:08E44C262B06(1) BA{1:x} map[]}” I[09-04|14:50:34.375] enterPrecommit(1/0). Current: 1/0/RoundStepPrevote module=consensus height=1 round=0 I[09-04|14:50:34.375] enterPrecommit: +2/3 prevoted proposal block. Locking module=consensus height=1 round=0 hash=048C7EC62CF3BB8C145F96517AAB93262F762B79 I[09-04|14:50:34.377] Signed and pushed vote module=consensus height=1 round=0 vote="Vote{0:11C18BAB5694 1/00/2(Precommit) 048C7EC62CF3 E6A1CD8E697A @ 2018-09-04T14:50:34.375424414Z}" err=null I[09-04|14:50:34.380] Added to precommit module=consensus vote="Vote{0:11C18BAB5694 1/00/2(Precommit) 048C7EC62CF3 E6A1CD8E697A @ 2018-09-04T14:50:34.375424414Z}" precommits="VoteSet{H:1 R:0 T:2 +2/3:048C7EC62CF3BB8C145F96517AAB93262F762B79:1:08E44C262B06(1) BA{1:x} map[]}” ノードの起動(6): Commit I[09-04|14:50:34.380] enterCommit(1/0). Current: 1/0/RoundStepPrecommit module=consensus height=1 commitRound=0 I[09-04|14:50:34.380] Commit is for locked block. Set ProposalBlock=LockedBlock module=consensus height=1 commitRound=0 blockHash=048C7EC62CF3BB8C145F96517AAB93262F762B79 I[09-04|14:50:34.381] Finalizing commit of block with 0 txs module=consensus height=1 hash=048C7EC62CF3BB8C145F96517AAB93262F762B79 root=
  16. I[09-04|14:50:34.381] Block{ Header{ ChainID: test-chain-HVlSTN Height: 1 Time: 2018-09-04 14:50:34.357894057

    +0000 UTC NumTxs: 0 TotalTxs: 0 LastBlockID: :0:000000000000 LastCommit: Data: Validators: A84F985E84234524C58721C7BF452D4E73A48B88 App: Consensus: D6B74BB35BDFFD8392340F2A379173548AE188FE Results: Evidence: }#048C7EC62CF3BB8C145F96517AAB93262F762B79 Data{ }# EvidenceData{ }# Commit{ BlockID: :0:000000000000 Precommits: }# }#048C7EC62CF3BB8C145F96517AAB93262F762B79 module=consensus I[09-04|14:50:34.388] Executed block module=state height=1 validTxs=0 invalidTxs=0 I[09-04|14:50:34.391] Committed state module=state height=1 txs=0 appHash=0000000000000000 module=txindex height=1 ノードの起動(7): 新しいブロックが生成され、アプリケーションステートもコミットされる
  17. let createABCIServer = require(‘abci') let state = { count: 0

    } let handlers = { // …中略… deliverTx (request) { let tx = padTx(request.tx) // padTxはトランザクションデータが4bytesかチェックする関数 let number = tx.readUInt32BE(0) if (number !== state.count) { return { code: 1, log: 'tx does not match count' } } state.count += 1 // 検証がOKならステートを更新 return { code: 0, log: 'tx succeeded' } } } let port = 26658 createABCIServer(handlers).listen(port, () => { console.log(`listening on port ${port}`) }) js-abciを用いたアプリケーションロジックのサンプル: トランザクション毎にカウントアップしてステートに保持する