chike0905の日記

何者かになりたい

Data-Intensive Applications: Chapter 1

DBとかをちゃんと理解する必要があると痛感し始めたので、ずっと積読していた以下の本を読んでまとめていく。

www.oreilly.com

Chapter 1 Reliable, Scalable, and Maintainable Applications

  • 多くのデータ志向(Data-Intensive)アプリケーションは以下の機能を持っている
    • のちにデータを発見できるようデータを保存する(database)
    • 高負荷な操作の結果を高速化のために記録する(chache)
    • ユーザにデーやをキーワードなどで検索またはフィルタできる(search-index)
    • 非同期処理を行うために、他のプロセスへメッセージを送信する(stream-processing)
    • 周期的に蓄積されたデータを分割する(batch processing)
  • これらが曖昧に聞こるとしたら、それらは 「良い抽象化」 が行われている
    • ユーザは細部を意識しなくとも利用できる
  • しかし現実的には、それぞれのデータベースシステムは、異なる要件を持ち、異なる特徴を持つため、そこまで単純ではない
    • 本書では、それらの原理や実践を解説する

Thinking About Data Systems

  • 本書で着目するシステムの要素は以下
    • Reliable: システムはいかなる逆境に直面しても正常に動き続ける
      • 正常: 許容されるレベルの正しい機能が動作する
      • 逆境: ハードウェア/ソフトウェアの故障、ヒューマンエラー
    • Scalable: システムが成長するとともに、合理的な方法で処理する手段が存在する
    • Maintainable: 多くの人々がそのシステムを操作しても、生産的に動作することが可能であるべき
      • 多くの人々の操作: エンジニアリング、オペレーション、新しいユースケースに対して対応する、現在の動作をメンテナンスする

Reliability

  • 典型的なソフトウェアのReliabilityとは以下のような特性
    • アプリケーションはユーザが期待した機能を提供する
    • ミスや、ソフトウェアの意図しない利用においても許容する
    • 必要とされるユースケースに対して、意図されたデータロードやボリュームの元でパフォーマンスを保つ
    • 認証されていないアクセスや、不正利用を妨げる
    • たとえ悪いことが起きても、正常に動作し続ける
  • 「悪いこと」はfaultと呼ばれ、faultを予期し、それらと強調するシステムを「fault-tolerant」や「resilient」と呼ぶ
    • どんな「fault」に対して予期を行うのかを議論しなければならない
    • 「fault」と「failure」は異なる
      • fault: システムの性能から議論されるコンポネントに対する障害
      • failure: システムが完全に停止するなどで、ユーザにサービスが提供できなくなる障害
  • faultをゼロにすることは不可能
    • 「fault-tolerant」はfailureを起こさないようにfaultを避けること
  • 直感に反して、fault-tolerantシステムにおいて、faultを慎重に扱うほど、faultの発生率は増加する

Hardware Faults

  • 多くの機器を動かす時、常にfailureは起こりうる
    • ハードディスクの故障、RAMの故障、停電etc...
    • MTFFの議論
  • 機器故障に対応するために、冗長性の確保はよくある対処
    • 近年まで、ハードウェアによる冗長性を確保することはアプリケーションにとって有効な手段だった
      • 1台のコンピュータが完全に故障することは稀であるため
    • しかし、データリュームと、アプリケーションの必要とする計算力が増えるにつれ、多くの機器を組み合わせる必要が出てきた
    • したがって、システム全体がfailureに陥らないために、ハードウェアのfault-tolerantに加えて、ソフトウェアでfault-tolerantを確保するテクニックが求められるようになった

Software Errors

  • ソフトウェアのfaultは、複数機器にまたがるため、予期することが難しく、予期せぬハードウェア故障よりもfailureを産みやすい
    • 特定の入力によってソフトウェアのバグが発現し、全てのアプリケーションサーバがクラッシュする
    • 共有リソースを使うプロセスが暴走する
    • サービスが依存するシステムが低速化、応答しなくなる、あるいは異常な応答をする
    • 特定のfaultが他のコンポネントのfaultのトリガーとなり、連鎖的にfaultが発生する
  • これらを引き起こすバグは、通常とは異なる環境・入力によって引き起こされるまで発見されずに放置されることが多い
  • ソフトウェアのバグの問題を即座に解決する方法はない
    • 細かい操作がそれらを助ける
      • システムとのインタラクションや、推測を注意深く考える
      • プロセスを独立させる
      • プロセスのクラッシュや再起動をハンドルする
      • システムの挙動を計測、監視、分析を行う

Human Errors

  • ソフトウェアは人間によってデザイン、構築され、そのオペレーションも人間が行う
    • 人間はunreliableである
  • Unreliableな人間に対して、システムをReliableにするアプローチがある
    • エラーとなる機会を最小限になるようにシステムを設計する
    • failureを引き起こす部分と、人間がミスを起こしやすい場所を切り分ける
    • 全てレベルに対してのテスト(Unitテスト、インテグレーションテスト、そしてマニュアルテスト)を実施する
    • failureの影響を最小化するために、ヒューマンエラーからの迅速かつ簡易なリカバリを許容する
    • パフォーマンやエラーレートなどの詳細かつ明確な監視を実施する
    • 良いマネジメントプラクティイスやトレーニングを作成する

Scalability

  • システムがreliableであったとしても、それは必ずしも続くということを意味しない
    • 以前よりより多くのデータ量を扱うことで破られる可能性がある
  • Scalabilityはシステムがロードの増加をうまく扱うことができるかどうかを示す指標
    • もしシステムをある方法で増強すると、どのような点が向上するのか?
    • ロードの増加に対して、どのように計算資源を追加できるか?

Describing Load

  • ロードは少数のパラメータ(load parameter)で表現される
  • Twitterの例: Home TimeLineの更新
    • あるユーザがフォローしているユーザのツイートを収集しなければならない
    • 手法1: ユーザのフォロイーを取得し、そのフォロイーのツイートを検索する
    • 手法2: ユーザのHome TimeLineをキャッシュし、その更新のみ検索する
    • 当初は手法1だったが、多くのフォロイーを持つユーザのロードに対応できないため、手法2を導入した
      • 現在では組み合わせて利用されている

        Describing Performance

  • ロードが増加した場合、どのように対応できるか
    • システムのリソースを追加した時、どのようにパフォーマンスに影響するか?
    • ローフォが増加した時、どのくらいのリソースを追加すればパフォーマンスを変えずに動作できるか?
  • パフォーマンスの考え方
    • パフォーマンスを考える時、計測値は単体で考えるのではなく、その分散(distribution)を考えるべき
    • あるリクエストに対する応答時間を考える時、計測値は測定毎に様々な影響を受けた値になる
    • 1つの共通の視点は平均(ここではmean)だが、これは多くのユーザのあくまで平均であり、実際のユーザの体感する時間を示すわけではない
    • もう一つの指標は、中央値(median)
      • 半分の人はその時間内にレスポンスを受けることがわかる
    • さらなる外れ値などを考慮したい際は、より多くの割合による値を用いることができる
      • 90%、99%、99.9%の人がレスポンスを受ける時間
      • ただし、高い割合による値を早くするのは、システム管理者の管理下を超えた要素が多く存在するため、容易ではない
    • それらの目標値を定義するためService Level Objectives(SLO)、Service Level Agreements(SLA)を定義する

Approaches for Coping with Load

  • ロードパラメータが増加した時、どのようにパフォーマンスを維持したら良いだろうか
    • scaling up: 垂直スケール、マシンパワーの増強など
    • scaling out: 水平スケール、複数台マシンによる分散処理
  • 近年のシステムでは柔軟性(elastic)を備えている
    • ロード増加を検知すると計算資源を自動で追加する
    • しかし、複数台のマシンを協調させるとシステムの複雑性が増す
    • 分散システムのツールと抽象化が高度化したことによりアプリケーションから透過的に用いることが可能となった
  • 大規模なシステムのアーキテクチャは大抵、特定のアプリケーションへ最適化されている
    • そのために、良くscaleするアーキテクチャでは、増加する共通のロードパラメータを推測することが必要

Maintaniability

  • ソフトウェアのコストの多くは、開発自体ではなく、その運用メンテナンスである
    • メンテナンスが行われていない、すでにサポートされていないコンポネントなどを用いるシステムをレガシーなシステムと呼ぶ
    • レガシーなシステムに対して、一般的な提案を与えるのは困難
    • しかし、レガシーシステムの影響を最小限に留める努力はできる
  • Maintaniabilityを考えるための3つのデザイン
    • Operability: スムーズにシステムを稼働させることがオペレータにとって容易であること
    • Simplicity: 新しいエンジニアがシステムを理解することが容易であること
    • Evolvability: エンジニアが新しい機能を追加したり、要件が変わるような想定されていないユースケースへの対応などが容易であること

Operability: Making Life Easy for Operations

  • 「良いオペレーションは悪いソフトウェアに制限され、良いソフトウェアは悪いオペレーションに制限される」
  • 良いオペレーションの例
    • 故障状態などに陥ったとしても、それを監視し、迅速に復旧できる
    • 問題の原因をトレースできる
    • ソフトウェアやプラットフォームを最新に保つ
    • 問題のある更新を避けるために、異なるシステムがお互いに与える影響を把握する
    • 将来起こる問題を予測し、発生する前に解決する
    • デプロイ、設定、マネジメントなどのグッドプラクティスやツールを確立する
    • 複雑なメンテナンスタスクを実施する(プラットフォームの移行など)
    • 設定変更によるシステムのセキュリティを管理する
    • プロセスを定義しオペレーションを予測可能にして、運用環境を安定的に維持する
    • 個人が離脱したとしても運用可能なように、システムに関する組織の知識を蓄積する
  • 上記を実現するための取り組み例
    • 質の良い監視により、システムの内部や挙動を可視化する
    • 標準的なツールを用いて自動化や統合化を行う
    • 個人の機器への依存を避ける
    • 理解しやすいドキュメントを提供する
    • 予測可能な部分は自動化し、必要に応じてマニュアルコントロールにする
    • 予測可能な挙動を提示する

Simplicity: Mannaging Complexity

  • ここでは、ソフトウェアの機能を削る必要性を解くのではなく、「意図しない」複雑性を持たせないようにすることを意味する
  • そのための良い手法は、「抽象化」である

Evolvability: Making Change Easy

感想

データを扱うシステムに関する書籍として、ブロックチェーンを研究するにあたり一度は通読しなければならないと思っていた。ここで示されている概念だけでも、ブロックチェーンが課題としている部分が多々あり、そのあたりの整理をしていくにあたり面白い議論が展開されることが期待でき、1章から今後が楽しみである。

輪読スライドを作ろう

概要

研究室で、論文などを輪読し、スライド発表する際に基礎となる点を雑記的にまとめておく。論文輪読でなくとも、スライドを作る一般的な時の注意点も含まれるが、頭の片隅においておくとよい。

なぜ論文輪読、スライド作成をするか

多くの人は、研究をするためのサーベイとして、論文を読むだろう。論文は何かしらの「問題」に対する「解法」を提示し、「解法が正しいことの証明(評価)」を述べた文章である。論文を書くためには、論文がどのように書かれた文章であるかを知るのがもっとも手早い。そのため論文を読む。

また、論文をスライドにまとめることで、論文に多く記載されている情報の中から、「核となるアイディア」を抜き出す、いわゆる抽象化を行うスキルを養うことができる。自分で論文を書く際も、「核となるアイディア」が効率的に読者に伝わるように書かなければならないし、良い論文はそれがわかりやすい文章体裁になっている。それらをうまく抽出できれば、自分で論文を書く際にも抽出されるであろう核の部分と、それを補助するような書き方で、論文を書き進めることができるようになることが期待できる。

スライド体裁編

ページ番号を振ろう

スライドにはどんなスライドであっても絶対にページ番号を振ることを忘れてはならない。ページ番号がなければ発表する際にはいいかもしれないが、発表後に議論をする際、「〜ページの図だけど〜」みたいなツッコミが非常にしづらくなってしまう。

文字ばかりのスライドにならないようにする

人の発表を聴いてる時に、文字ばかりのスライドだと聴いていてもなかなか頭に入ってこないだろう。論文をまとめる際にも、なるべく端的に、その論文で述べられていることの核となるトピックセンテンスの部分を抜き出して記述するべきである。図が入らず、文字だけのページになってしまった場合も、核となる部分を四角く囲む、論理展開を四角を矢印で結んだりすることで、視覚的に理解できるようにするべきである。

箇条書きの段は深くしすぎない

論文をスライドにまとめるので、書ける文章量は格段に少なくなる。そのため、箇条書きにしようと思うと、ついつい書いてあることをコピペして、文章量が多くなり、文字ばかりのスライドになってしまう。筆者はスライドを作る際は、箇条書きの段はせめて2段、3段目は本当に必要な補足情報があるなど、最低限にするよう心がけている。

良いスライドテンプレートを使う

スライドテンプレートはシンプルである方が良い、と筆者は思っている。背景にデザインが仕込まれたものなどでも良いのだが、文字、図がおけるスペースが小さくなってしまうことも多々ある。自分好みのテンプレートを使うことも重要だが、塩梅を考えよう。筆者は昔、背景が真っ黒に白抜き文字のテンプレートを使っていたが、視認性の悪さ、論文とスライドで図の白黒を反転しなければならない煩雑さから、白背景にするようにした。

デザイン・配置を気にする

文字が多くなりがちな論文発表において、なるべく四角で囲むなど視覚的効果を狙うべき、と述べた。そこで、その四角のデザイン(例えばサイズ、角の丸さなど)をなるべく揃えた方が良い。デザインはよくわからない、という人でも、とりあえず整列させる、中心を合わせる、そうしたことを意識するだけで、視覚的にスッキリさせることができる。

スライド中身編

タイトルページに書くべきこと

論文の場合、「どこで」「いつ」「誰が」発表した論文なのか、ということが非常に重要である。そのため、

  • 何のカンファレンス/ジャーナルで
  • いつ発表され
  • 誰が著者か、その所属はどこか

の情報を必ず入れるべきである。これらの情報がないと、その論文がどのくらいのレベルのもので、どういう想定読者なのか、ということがはっきりせず、前提を理解することが難しくなってしまうことがある。

最初のページに書くべきこと

最初のページでは、「この論文は何の分野で、何をしようとして、どのようなことをした論文なのか」を概観する。大概は論文のIntroductionにまとめてある内容で大丈夫だとは思うが、輪読発表を聴いている人に、前提知識をつらつら述べる前に、この論文の核の部分を伝えるようにしよう。前提知識が必要な部分は、適度に抽象化し、わかるように書く、あるいは発表時に口頭で補い、のちに詳しく説明する必要があれば、スライドを追加しても良いだろう。まずは全体像がつかめることが重要である。

以降のページに書くべきこと

論文は、提案されている仕組みをしっかり説明するために、多くのことが書かれている。当然、関係ない話は書かれていないはずなのだが、それらを全て解説するわけにはいかない。そこで、最初のページで概観を述べた後は、以下の部分に留意してまとめていくと良いだろう。

  • (必要ならば)前提とする知識
  • この論文の主眼とする問題とその定式化
  • この論文で提案される手法
  • 提案手法が問題を解けていることの証明(評価)

それぞれ各1Pを目安にまとめることを心がけよう。1Pの制約は厳しいので、適度に複数枚になっても良いが、そうすると長々とダレる発表になってしまうことも多い。そのため、制約のある中でどれだけ効率的に伝えることができるか、というのも一つのトレーニングであるため、意識すると良い。

図の引用と再作成

論文では、適度に図が入って解説されていることが多い。しかし、論文上の図は、紙面サイズの都合などで必ずしもそのままわかりやすい図であるとは限らない。そのため、図を引用する際は、その図がちゃんと自分で説明できるか、図で説明したいことが伝わるか、を考えなければならない。図がわかりにくければ、自分で理解した上で再作成するのも良い勉強になるだろう。

(補足)心構え編

なるべく早めに準備しよう

論文の輪読を始めるのは、学部生か、修士に入ったばかりの学生などが想定される。ありがちなパターンは、ギリギリに準備をしようとして、読み始めるとまず書いてあることが理解できない、結果スライド作成が間に合わない、論文をコピペしただけの自分で理解できていないスライドになってしまう、といったことだろう。「論文を読んで理解し、人に伝えられるように整理する」というのは、想像以上に時間と労力がかかる。だからこそ、トレーニングになるのであって、そこに「このぐらいでできるだろう」という目論見を持って臨むと痛い目を見ることがしばしばある。

先輩・教員は使い倒せ

一方でレーニンなのだから、自分で頑張ったけどわからない、となったら先輩・同期・教員に聞くべきである。教員・先輩はそのためにいるはずだし、教員・先輩自身もそうして教えられ、トレーニングを積んだ結果、そのスキル・知識を獲得しているのである。学生であれば、学費を払っている分は回収しなければ勿体無い。当然なにもしない、自分で理解する努力をしないでいきなり質問するのは門前払いになるだろうが、自分でここまで理解できました、ここがわかりません、としっかり「無知の知」をした上で尋ねれば、きっと答えてくれるはずである。

まとめ

論文の輪読、というのは基本的に論文を書く・発表のためのトレーニンである。自分で研究を進めて、発表をする際にも、どのような流れで発表すれば聴衆に伝わるか、を考えて発表を設計しなければならない。そのためにも、すでに発表されている(すでにストーリーが完成している)論文を読み、それをなぞるスライドを作ることで、論文とはどういうストーリーで書く・発表するべきものなのか、を学ぶことができる。最後に心構えでも書いたが、あくまでトレーニングであるためそのためのリソース(教員・先輩・資材)は十二分に活用することをオススメする。

btcdで独自ネットワークを構築する

概要

BitcoinのGo言語実装としてbtcdがある。Bitcoinの様々な研究開発を行う際に、実際のmainnetとは異なり価値の持たない通貨を扱う別のネットワークであるtestnetがよく用いられる。しかし、testnet上では、さまざまな試験が世界中で行われており、TXを投入しても承認に時間がかかるなど、様々な試験をするには適さない面がある。本項では、btcdを拡張し、testnet相当の別のネットワークを構築する手順を記す。

環境

  • btcd v0.20.1-beta (commit id: f3ec13030e4e828869954472cbc51ac36bee5c1d)
    • 以下のコマンドで$GOPATHへダウンロードし、$GOPATH/src/github.com/btcsuite/btcd以下のソースを改修していく go get -d github.com/btcsuite/btcd
  • btcwallet (commit id:704cd189ac2386b54ab64e17f67f1c7c5ef5c7ac)
    • btcd上で送金などの操作を行うために利用
    • btcd同様に以下のコマンドでダウンロードし、改修を行う go get -d github.com/btcsuite/btcwallet

Btcdの改修

Btcdで書き換えるべきファイル

  • chaincfg/genesis.go: Genesis Blockの設定パラメータ
  • chaincfg/params.go : ネットワーク全体に関わるパラメータ
  • config.go: 上記chaincfg/params.goを読み込むプログラム
  • params.go: config.goからchaincfg/params.goを読み込むためのインターフェース
  • wire/protocol.go: ノード通信時のメッセージプレフィックスなどの設定

パラメータ読み込み部の改修

btcdでは、btcd.go内でloadConfig()を呼ぶことで、ノードの設定を読み込んでいる。loadConfig()の実態はconfig.goの中にある。まずは、type config structの中のコマンドラインパラメータの中に、mynetのオプションを追加する。

type config struct {
...
    TestNet3             bool          `long:"testnet" description:"Use the test network"`
+    MyNet               bool          `long:"mynet" description:"Use the my network"`
    RegressionTest       bool          `long:"regtest" description:"Use the regression test network"`
    SimNet               bool          `long:"simnet" description:"Use the simulation test network"`
...

次にloadConfig()内のネットワークパラメータ読み込み部に追記する。

func loadConfig() (*config, []string, error) {
...
        if cfg.TestNet3 {
                numNets++
                activeNetParams = &testNet3Params
        }
        if cfg.RegressionTest {
                numNets++
                activeNetParams = &regressionNetParams
        }
+        if cfg.MyNet {
+                numNets++
+                activeNetParams = &myNetParams
+        }
        if cfg.SimNet {
                numNets++
                // Also disable dns seeding on the simulation test network.
                activeNetParams = &simNetParams
                cfg.DisableDNSSeed = true
        }
...

ここで呼ばれる*NetParamsは、params.go内で定義されるため、追記する。ここでRPCのためのポートを任意に設定する。

...
// MyNetParams
var MyNetParams = params{
        Params:  &chaincfg.MyNetParams,
        rpcPort: "11454",
}
...

通信時のprefix設定

ノードは通信時に、ネットワークを識別するためのprefixをつける。それらは、wire/protocol.goに定義されている。このprefixはどのように定義されるべきかは筆者は把握していない。ここでは任意に設定した。

// BitcoinNet represents which bitcoin network a message belongs to.
type BitcoinNet uint32

// Constants used to indicate the message bitcoin network.  They can also be
// used to seek to the next message when a stream's state is unknown, but
// this package does not provide that functionality since it's generally a
// better idea to simply disconnect clients that are misbehaving over TCP.
const (
        // MainNet represents the main bitcoin network.
        MainNet BitcoinNet = 0xd9b4bef9

        // TestNet represents the regression test network.
        TestNet BitcoinNet = 0xdab5bffa

        // TestNet3 represents the test network (version 3).
        TestNet3 BitcoinNet = 0x0709110b

        // MyNet represents the my network.
        MyNet BitcoinNet = 0x79616a75              

        // SimNet represents the simulation test network.
        SimNet BitcoinNet = 0x12141c16
)

設定パラメータの作成

上記で読み込むよう設定したchaincfg.MyNetParamschaincfg/params.goの中で定義される。今回はtestnetとregtestの設定をベースに以下の設定を追記した。ここではここで記される内容の詳細には立ち入らないが、ここのパラメータを色々いじることで様々なネットワークを作ることができる。

...
// MyNet
var MyNetParams = Params{
    Name:        "mynet",
    Net:         wire.MyNet,
    DefaultPort: "11451",
    DNSSeeds: nil,

    // Chain parameters
    GenesisBlock:             &MyNetGenesisBlock,
    GenesisHash:              &MyNetGenesisHash,
    PowLimit:                 regressionPowLimit,
    PowLimitBits:             0x207fffff, // same as regtest
    BIP0034Height:            0, // always
    BIP0065Height:            0, // always
    BIP0066Height:            0, // always
    CoinbaseMaturity:         100,
    SubsidyReductionInterval: 210000,
    TargetTimespan:           time.Hour * 24 * 14, // 14 days
    TargetTimePerBlock:       time.Minute * 10,    // 10 minutes
    RetargetAdjustmentFactor: 4,                   // 25% less, 400% more
    ReduceMinDifficulty:      true,
    MinDiffReductionTime:     time.Minute * 20, // TargetTimePerBlock * 2
    GenerateSupported:        true,

    // Checkpoints ordered from oldest to newest.
    Checkpoints: nil,

    // Consensus rule change deployments.
    //
    // The miner confirmation window is defined as:
    //   target proof of work timespan / target proof of work spacing
    RuleChangeActivationThreshold: 1512, // 75% of MinerConfirmationWindow
    MinerConfirmationWindow:       2016,
    Deployments: [DefinedDeployments]ConsensusDeployment{
        DeploymentTestDummy: {
            BitNumber:  28,
            StartTime:  0, // always
            ExpireTime: 0, // always
        },
        DeploymentCSV: {
            BitNumber:  0,
            StartTime:  0, // always
            ExpireTime: 0, // always
        },
        DeploymentSegwit: {
            BitNumber:  1,
            StartTime:  0, // always
            ExpireTime: 0, // always
        },
    },

    // Mempool parameters
    RelayNonStdTxs: true,

    // Human-readable part for Bech32 encoded segwit addresses, as defined in
    // BIP 173.
    Bech32HRPSegwit: "tb", // always tb for test net

    // Address encoding magics
    PubKeyHashAddrID:        0x00, // starts with 1
    ScriptHashAddrID:        0xc4, // starts with 2
    WitnessPubKeyHashAddrID: 0x03, // starts with QW
    WitnessScriptHashAddrID: 0x28, // starts with T7n
    PrivateKeyID:            0xef, // starts with 9 (uncompressed) or c (compressed)

    // BIP32 hierarchical deterministic extended key magics
    HDPrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94}, // starts with tprv
    HDPublicKeyID:  [4]byte{0x04, 0x35, 0x87, 0xcf}, // starts with tpub

    // BIP44 coin type used in the hierarchical deterministic path for
    // address generation.
    HDCoinType: 1,
}
...

Genesis Blockのパラメータ設定

chaincfg.MyNetParamsの中からGenesis Blockのデータと、そのハッシュをchaincfg/genesis.goから読み込んでいる。そこでGenesisハッシュ値などを設定する必要がある。そのため、Genesis Blockの生成は既存のスクリプトを用いて行った。しかしこのスクリプトはPython2.7でしか動作しない。 https://github.com/lhartikk/GenesisH0

以下のスクリプトで生成する。今回は、簡易的に実装するためにregtestと同様の値(ごく簡単なdifficultyでの生成)を行っている。

$ python genesis.py -n 00000000 -b 545259519 
04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73
algorithm: SHA256
merkle hash: 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
pszTimestamp: The Times 03/Jan/2009 Chancellor on brink of second bailout for banks
pubkey: 04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f
time: 1581663396
bits: 0x207fffff
Searching for genesis hash..
genesis hash found!
nonce: 1
genesis hash: 718790f769dc86320b52d4c176a94e775947e922dd019e99b91cc2f9ddbc48b8

上記スクリプトで生成した値をそれぞれのフィールドへ適用させる。ここでmyNetGenesisHashの値はリトルエンディアンになっていることに注意する。

...
var myNetGenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
    0xb8, 0x48, 0xbc, 0xdd, 0xf9, 0xc2, 0x1c, 0xb9,
    0x99, 0x9e, 0x01, 0xdd, 0x22, 0xe9, 0x47, 0x59,
    0x77, 0x4e, 0xa9, 0x76, 0xc1, 0xd4, 0x52, 0x0b,
    0x32, 0x86, 0xdc, 0x69, 0xf7, 0x90, 0x87, 0x71,
})

// myNetMerkleRoot is the hash of the first transaction in the genesis
// block for the my network.  It is the same as the merkle root
// for the main network.
var myNetGenesisMerkleRoot = genesisMerkleRoot

// myNetGenesisBlock defines the genesis block of the block chain which
// serves as the public transaction ledger for the my network.
var myNetGenesisBlock = wire.MsgBlock{
    Header: wire.BlockHeader{
        Version:    1,
        PrevBlock:  chainhash.Hash{},          // 0000000000000000000000000000000000000000000000000000000000000000
        MerkleRoot: myNetGenesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
        Timestamp:  time.Unix(1581663396, 0),
        Bits:       0x207fffff,               // 545259519 [7fffff0000000000000000000000000000000000000000000000000000000000]
        Nonce:      1,
    },
    Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
}
...

ビルドと起動確認

以下のコマンドでビルドする。$GOPATH/binにバイナリが吐き出される。

GO111MODULE=on go install -v . ./cmd/...

以下のコマンドでbtcdが起動する。設定したGenesisで正常に2回起動されれば成功。というのも、初回の起動の時にgenesisで設定したものがブロックとしてストレージに書き込まれ、2回目の起動の時にのみGenesisHashとの照合が行われるため。もし2回目が起動しなければ、Genesis設定周りのパラメータを再確認する。

$GOPATH/bin/btcd --mynet

btcctlを用いたインタラクション

btcdは同梱されているbtctlを用いてインタラクションを行う。btcctl自体にmynetオプションを追加しても良いが、ここではconfigファイルを用いた設定方法を記す。

まずは、btcdとbtcctlの設定ファイルを以下のように記述し、それぞれbtcd.confbtcctl.confとして保存する。

[Application Options]
rpcuser=myuser
rpcpass=hogehoge

btcdを以下のコマンドで起動する。

$GOPATH/bin/btcd --mynet -C $PATH_TO_btcd.conf

別のコンソールで、以下のコマンドでインタラクションできることを確認する。ここではparams.goで設定したRPC Portを指定するのを忘れてはならない。

$GOPATH/bin/btcctl -C $PATH_TO_btcctl.conf -s localhost:11454 getblockchaininfo

マイニングの実施と確認

今回設定しているchaincfg/params.goの中で、btcdで作ったネットワーク上でregtestと同じようにgenreate(ブロックの生成)が簡単にできるようになっている。そこでブロックの生成を実施し、送金を試してみる。

btcwalletと改修後のビルド注意点

btcdからは送金などにまつわるいわゆるwalletの機能は除外されている。walletの機能はbtcwalletという実装に含まれている。このbtcwalletは、RPCでbtcdと通信し、walletの機能を提供する。したがって、go実装のものを使ってbitcoinの操作を行うためには、btcctlをインターフェースとして、nodeへの操作時はbtcdへ、walletの操作時はbtcwalletへRPCでコマンドを送信し操作する事になる。

ここでは、btcwalletを以下のコマンドでダウンロードし、mynetオプションを利用できるように改修していく。

go get -d github.com/btcsuite/btcwallet

改修したbtcwalletをビルドする際は以下のコマンド。この時、公式のドキュメントではGO111MODULE=on環境変数を設定するが、btcdの中のchaincfgをモジュールとして読み込んでいる。そのため、改修したローカルのbtcdを参照するようにGO111MODULE=offとしなければならないことに注意する。

GO111MODULE=off go install -v . ./cmd/...

btcwalletの改修

基本的には、コマンドラインmynetオプションを追加し、それが指定された時は、指定したchaincfgのパラメータを読みにいく、という基本的な流れは変わらない。以下にdiffを記す。

+++ b/config.go
@@ -53,6 +53,7 @@ type config struct {
        CreateTemp    bool                    `long:"createtemp" description:"Create a temporary simulation wallet (pass=password) in the data directory indicated; must call with --datadir"`
        AppDataDir    *cfgutil.ExplicitString `short:"A" long:"appdata" description:"Application data directory for wallet config, databases and logs"`
        TestNet3      bool                    `long:"testnet" description:"Use the test Bitcoin network (version 3) (default mainnet)"`
+       MyNet         bool                    `long:"mynet" description:"Use the my Bitcoin network (default mainnet)"`
        SimNet        bool                    `long:"simnet" description:"Use the simulation test network (default mainnet)"`
        NoInitialLoad bool                    `long:"noinitialload" description:"Defer wallet creation/opening on startup and enable loading wallets over RPC"`
        DebugLevel    string                  `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}"`
@@ -365,6 +366,10 @@ func loadConfig() (*config, []string, error) {
                activeNet = &netparams.SimNetParams
                numNets++
        }
+       if cfg.MyNet {
+               activeNet = &netparams.MyNetParams
+               numNets++
+       }
        if numNets > 1 {
                str := "%s: The testnet and simnet params can't be used " +
                        "together -- choose one"
diff --git a/netparams/params.go b/netparams/params.go
index c15047e..aeb5a57 100644
--- a/netparams/params.go
+++ b/netparams/params.go
@@ -37,3 +37,10 @@ var SimNetParams = Params{
        RPCClientPort: "18556",
        RPCServerPort: "18554",
 }
+
+// Mynet
+var MyNetParams = Params{
+       Params:        &chaincfg.MyNetParams,
+       RPCClientPort: "11454",
+       RPCServerPort: "11455",
+}

BtcdとBtcwalletのセットアップ

まずはbtcwalletでwalletを作成する。

$GOPATH/bin/btcwallet --mynet --create

次に、btcdを起動するが、デフォルトではwallet<->btcdとwallet<->btcctlの通信はTLSを用いる。今回は、簡易的に試験するために、それらを外すオプションをつける。

$GOPATH/bin/btcd --mynet -C $PATH_TO_btcd.conf --notls

次に、btcwalletを起動し、btcdへ接続する。このとき,-uオプション(ユーザ名)と-Pオプション(パスワード)は、btcd.confで設定したものを入力する。btcd同様にTLSを無効化するオプションを入れている。

$GOPATH/bin/btcwallet -u myuser -P hogehoge --mynet --noclienttls --noservertls

btcctlから、btcd、btcwalletにそれぞれgetblockchaininfoコマンドを送信し、それぞれ同じ出力が得られれば改修は成功。

# to btcd
$GOPATH/bin/btcctl -C $PATH_TO_btcctl.conf -s localhost:11454 --notls getblockchaininfo
# to btcwallet
$GOPATH/bin/btcctl -C $PATH_TO_btcctl.conf -s localhost:11455 --notls getblockchaininfo

coinbaseアカウントの設定

btcwalletではcreate時にdefaultアカウントが作成されている。以下のコマンドでアカウントのアドレスが確認できる。

$GOPATH/bin/btcctl -C $PATH_TO_btcctl.conf -s localhost:11455 getaccountaddress default

出力されたアドレスを、btcd.confへcoinbaseアカウントとして設定し、再起動する。

[Application Options]
rpcuser=myuser
rpcpass=hogehoge
+ miningaddr=$DUMPED_DEFAULT_ACCOUNT

generateコマンドの実行と残高確認

bitcoinでは、マイニング報酬は100ブロック以降でないと送金できない。そのため、101ブロックマイニングをgenerateコマンドで実行する。

$GOPATH/bin/btcctl -C $PATH_TO_btcctl.conf -s localhost:11454 generate 101

btcwallet側のログに、同期したメッセージが表示されれば以下のコマンドで残高が増えているのが確認できる。同期が起こらなければ、btcwalletを再起動すると起動時にbtcdとブロックの同期が行われる。

$GOPATH/bin/btcctl -C $PATH_TO_btcctl.conf -s localhost:11455 getbalance default

dockerで複数ノードを起動

適当に書いた以下のDockerfileで本実装を複数ノードで動かしてみる。

FROM golang:1.13.4-buster
LABEL maintainer="Rysouke Abe <chike@sfc.wide.ad.jp>"

RUN apt update 
RUN apt install -y vim

RUN go get -d github.com/btcsuite/btcd
RUN rm -r /go/src/github.com/btcsuite/btcd
RUN git clone https://github.com/chike0905/btcd.git /go/src/github.com/btcsuite/btcd
WORKDIR /go/src/github.com/btcsuite/btcd
RUN git checkout mynet
RUN go install -v . ./cmd/...

WORKDIR /go
RUN go get -d github.com/btcsuite/btcwallet
RUN rm -r /go/src/github.com/btcsuite/btcwallet
RUN git clone https://github.com/chike0905/btcwallet.git /go/src/github.com/btcsuite/btcwallet
WORKDIR /go/src/github.com/btcsuite/btcwallet
RUN git checkout mynet
RUN GO111MODULE=off go install -v . ./cmd/...

WORKDIR /root/.btcd
ADD elements/btcd.conf /root/.btcd/btcd.conf
WORKDIR /root/.btcwallet
ADD elements/btcctl.conf /root/.btcwallet/btcctl.conf

WORKDIR /root
CMD ["bash"]

btcdmynet:latestとしてビルドする。

docker build -t btcdmynet:latest .

HVのコンテナ同士でのみ通信できるブリッジネットワークを作成し、dockerコンテナをそれぞれそれらに接続する。

docker network create --driver=bridge --internal internalnet

dockerコンテナをそれぞれ起動。

docker run -it --rm --net internalnet --name btcd1 btcdmynet:latest 
docker run -it --rm --net internalnet --name btcd2 btcdmynet:latest 

上記手順でそれぞれのコンテナ上でbtcdを起動させてから、btc1からbtcctlのaddnodeコマンドでpeerを追加する

btcctl -C .btcwallet/btcctl.conf -s localhost:11454 --notls addnode $ADDR_BTC2 add

btc1上で上記手順を用いてgenerateコマンドを走らせると、btc2上に同期されていることがわかる。

まとめ

本稿では、testnet相当の別のネットワークを柔軟に設定するために、btcd/btcwalletを改修して独自のネットワークを構築する手順を記した。本項では、chaincfg/params.goの中のパラメータの詳細には立ち入らなかったが、それらをいじることで様々なパラメータを持つネットワークを構築することが可能である。PoW周りのパラメータなど、様々な値を操作することが可能であり、様々な実験が実施可能であることが期待できる。

筆者の感想にはなるが、btcdはbitcoindに比べて後発なのもあり、そのアーキテクチャなどが洗練されつつあるイメージがある。本稿で述べたような手法を用いながら、様々な検証を行い、さらなる洗練が期待される。

行こうぜ!政策・メディア!

本記事は、SFC Advent Calendar 201920日目です。

何を書くのか

先日以下のようなTweetを見かけた。

この「行こうぜ政策・メディア*1!」というフレーズは、もともと自分が2016年度に卒論を書いていた時に発狂して○NE ○K R○CKのような暑苦しいバンドマンのように院進を勧め流ように叫んでいたものである。 全文は以下

行こうぜ!行こうぜ政策・メディア!まずはお前らの卒論を見せてくれ!

さて、現在それから3年の時が経ち、自分は博士課程に進んだ。その中で、政策・メディア(修士課程)に進む、ということはどんなことだったのか、を書いておきたい。技術記事じゃなくてごめんなさい

 前提

自分は現在技術系研究室に所属し、ブロックチェーンの研究をしている。学部1年の頃からずっとこの研究室にいるので、ここに書くのは技術系研究室での非常に偏った視点であることは注意していただきたい。

なぜ自分が院進したか

端的に言って、就職したくなかったのである。学部3年当時バンドマンだった*2自分は、どうしても自分がその1年後に就職しているイメージがつかなかった。自分は大学に入るまで1浪しているのだが、その1年間で心に刻んだことは「浪人した意味のある大学生活にしたい」だった。4年になるにあたって、それまで漫然と過ごしてきた研究生活の中で、デジタル通貨というテーマを発見し、それがブロックチェーンという基盤技術から構成され、無限の広がりを見せている期待感もあった。そんなこんなで、デジタル通貨(ブロックチェーン)をテーマに大学院に進学することを決めた。

卒論、そして院進

卒論はボスより「3Dプリンタ」と「ブロックチェーン」で面白いことをやってくれ、とネタをもらった。それまで一回も3Dプリントなどしたことなかったが、たまたまタイミングよく受給が決まった奨学金から3Dプリンタを購入し、研究を進めた。その中で、色々論文を読み漁るようになった段階で気づいたことがあった。明らかに数学的知識や情報工学的基礎知識が足りない。 SFCでは、必修は情報基礎ぐらいしかなく、体系立って情報工学的知識*3を学ぶことは難しい。ふわふわプログラミングの授業で最終課題を適当に作っていた自分には論文に書いてあるのはかなり難しいことのように思えた。そのため、無理やり論文を読めるところだけ読み、なんとか卒論を完成させた。

卒業後は、政策・メディアに進むにあたり、ひとまず卒論の内容を国内の研究会で発表することにした。研究会なので、卒論を10ページ程度に圧縮して発表すればそれなりにウケはよかった。その後焦って無理やりジャーナルに投稿してrejectされたりもした。そんな中で、自分で簡単なブロックチェーンを実装したり、色々論文を読んだり、某横須賀の研究所にインターンに行ったりした。その中で、自分はだんだんこの「研究」をしたいのだ、という気持ちが強くなり博士課程への進学を考えることになった。あとM2になる時に結婚したりしたのだが、それはまた別の話。

政策・メディアでの気づき

M1の終わりあたりに、M2の人たちの修論発表と、後輩の卒論発表を見て気づくことがあった。それは、あるシステムを作った時に誰にとって嬉しいのかということに修士の方が非常に意識的なのである。それは研究一般に関してそうだよ、と言われればそうなのかもしれないが、自分にとっては大きな気づきであった。誰にとって嬉しいのか、ということは技術それだけを見ていても考えられるものではない。技術とは、人が作るものであり、人が使うものなのである。したがって、その技術を使う人々が形作る社会を構想しなければならないのだ。そうした意味で、社会面と技術面の融合が必要だし、文理融合が必要なのである。

また、学部から通じてSFCはアクティブ・ラーニング的なカリキュラムが組まれている。そのため、先に述べたような「情報工学的知識」は教えられるものではなく、自分から必要になったら学んでいくしかないのである。また、よく言われることとして、「卒論は所属研究室の中でその分野に関して一番詳しい、修論は日本で一番、博論は世界で一番」ということがある。そのため、自分が修士後半から徐々に感じ始めたのは、「ブロックチェーン」の技術的詳細に関しては、研究室の中で議論できる人が少なくなってきた、ということである*4。うれしい悲鳴ではあるのだが、研究を始めたばっかりのひよっ子には、何をどうしたらいいのか、わからないで苦しむ日々が続いた。

結局、政策・メディアの修士課程では、研究を必死こいて行い、論文発表をする中で、社会に対して何が必要で、この技術は何ができるのか、というのを問う力を養うことができた。専門に関する知識は、ほとんどが自学であったと思う*5

何が言いたいのか

よく「より専門を深めたいので院進します」と言う人がいる。院に行けば「研究」をすることになるので、専門は当然深まるが、それは結果であり、専門的知識が授業などで補うことができるかは疑問である。それよりかは、先にも述べたように、誰にとって、何が嬉しいのか、と言うことを設計する力の方が、よく教えられた気がする。どこの大学院でもそう言う面はあるのかもしれないが、政策・メディアは、SFCという文理融合学部の地続きになっているだけあり、その視点の趣が強いように感じた。より高度な技術や専門を深めたいのであれば、その専門の研究科を探し、そこに進学した方が良いのではないか、と思う。

自分が専門とするブロックチェーンは一特性として、技術と社会が表裏一体になっているシステムである。だからこそ面白いと思っているし、政策・メディアのような文理融合の研究科でやる意味があるのだ。本記事の読者が、SFCでどんな研究をしているかは知る余地はないが、SFCの理念に洗脳されている感銘を受けている一人として、SFCの中では、単なる技術/社会の研究だけでなく、技術が形作る社会/社会が要求する技術の研究が、盛んになることを願ってやまない。

そして修士まで進んだ君、行こうぜ!政策・メディア後期博士課程!

*1:SFCの大学院「政策・メディア研究科」のこと

*2:PVとかも作って活動していた

*3:ここでいう情報工学的知識が何を指すか、すら曖昧なくらいに

*4:そのために研究室内にブロックチェーン専門のグループを作ったりした

*5:当然教員の方々に多大なサポートを受けながら、である

【論文メモ】貨幣の起源と貨幣の未来

Bitcoin/ブロックチェーンの研究を進めていくにあたり、そもそも「お金」とは一体全体何であるか、ということが気になってきた。そこで、畑違いではあるが、経済学、貨幣論に関する論文を読んでみた。

論文情報

内容

はじめに

  • 貨幣の起源に関しての代表的見解について検討する
  • 最古の議論はアリストテレスによる「社会契約説」
  • その後、ジョン・ローによる「物々交換仮説」が提起される
  • 物々交換の成立には「欲求の二重の一致」が不可欠
    • しかし、その「一致」の困難性から時代が下るとともに否定される
  • 物々交換説に代わる仮説の登場
    • 貨幣国定説(クナップ)
    • 「信用は貨幣に先立つ」(イネス)
    • 宗教起源説(福田徳三)
    • 「贈与から経済的な交換取引が生じる」(モース)
  • 本稿ではそれらを概観し、説得的かつ妥当である説を議論
    • その上で「キャッシュレス化の進展」および「仮想通貨」について議論

貨幣起源説の多様性

  • 交換の人類史
    • 自給自足経済
    • 物々交換経済
    • 間接交換経済(貨幣経済
  • 間接交換には、「誰もがその受け渡しを拒まず、全ての財の中でもっとも受容されやすい特定の財」が必要

物々交換仮説(貨幣自生説)

  • ある商品と別の商品の交換を仲介するために貨幣が生まれた
  • Jevons, W.S.による指摘
    • 以下の貨幣なき物々交換に伴う困難が想定される
      1. 欲求の二重の一致の欠如
        • 物々交換には「誰が何を保有し、何が必要とされているか」の情報が必要
        • この情報を得るためには時間などの費用を負担しなければならない
        • また自分が保有する物の価値が劣化しないように維持する費用もかかる
        • 貨幣はこれらの負担を節約する
          • 貨幣という一般受容性の高い財に交換できる
          • 物々交換における偶然性・局所性を克服
      2. 価値尺度の欠如
        • 価値尺度が存在しなければ、あらゆる財をその量によって表現しなければならない
        • その場合、財同士の交換に無数の交換比率が発生する
        • 貨幣が存在すれば一つの財に対する交換比率はの数が劇的に減少する
      3. 分割方法の欠如
        • コートの一部を切り取り、肉と交換することはできない
        • 必要に応じて価値を分割し、分配する手段が必要
        • 貨幣は数量で表現されるため、分割可能
  • Samuelson, P.Aによる指摘
    • 物々交換の仲介として、様々なものが「商品貨幣」として扱われた
    • しかし、分割方法の欠如などから、やがて金属に限定された

物々交換仮説の否定

  • 今日においては、物々交換仮説は完全に否定されている
  • Dalton, Georgeによる指摘
    • 物々交換自体は決して量的に支配的な取引モデルではなかった
    • 現代においても物々交換は生じるが、其の場凌ぎなどの取引にしか用いられない
  • Davis, Glynによる指摘
    • 物々交換仮説による説明はあまりにも物々交換の不利益を強調する
    • 物々交換の明白な欠点よりもずっと重要な要因を排除しがち
  • Karimzadi, Shahzavarによる指摘
    • 物々交換の時代と定義しうる、その境界は存在しない
    • 「存在しない憶測的なモデル」に他ならない
  • Wray, L.A.による指摘
    • 種族社会における交換は本質的に儀式的なもの
      • 共益主義の習慣により、取引者の利益を最大化するのではなく、種族の成員を親しくするよう意図されたもの
      • 相対的な諸価格は市場の競争ではなく習慣で決定されていた
    • 物々交換仮説は金属が取引費用を減少させる仮定に基づく
      • しかし、金属の純度や精巧さが鋳貨で重要視された
      • これらの鋳貨費用を鑑みると、牛より貨幣が評価されたと考えることは難しい

貨幣法制説(貨幣国定説)

  • 現代においても、国家の保障なき貨幣は流通困難であることから、重要な一説と言える
  • アリストテレスによる指摘
    • 共同体を構成する人々の「申し合わせ」によって、貨幣は人為的に創造された
  • クナップによる指摘
    • 貨幣は金属、紙で創造されていようがその素材は無関係
    • その使用を規制する法制にその精髄はある

宗教起源説

  • 福田徳三による指摘
    • 他人から財の移転を受けると、その人に対し、それだけの債務が発生する
      • 一種の瀆れと見たのではないか
      • 禊や祓によって瀆う必要がある
    • これ位に対して、対価を払うことでその債務を清算する
    • 貨幣の引き渡しは、決定的、最終的、無条件的
      • ものの引き渡しではそうはならない

貨幣起源説の精査

  • 主な仮説を並べると表のようになる
    • 物々交換仮説は否定されており、社会契約説/貨幣国定節は、特定の共同体以前にも貨幣が存在したことを鑑みると、「貨幣の起源」としては否定される
時代 提唱者 概要
社会契約説 古代ギリシャ(前350ごろ) アリストテレス 共同体を構成する人々による「申し合わせ」ないし「社会的合意」
物々交換仮説 18世紀初頭 ジョン・ロー 物々交換の困難を克服するために自然発生
貨幣国定説 20世紀初頭  クナップ   法制によって定義された貨幣で交換を行う 
宗教起源説 20世紀 福田など 宗教的な負債の清算手段として発生
  • Quiggin, A.H.による指摘
    • 貨幣は「交換の手段」「価値の標準」「富のシンボル」という機能を満たす形態に限定される
    • さらに、一般に受容される貨幣は「持ち運びの便宜」「分割の容易性」「耐久性」「認識の容易性」を満たす不可欠の性質を持つ
    • 結果として、威厳をもつ、あるいは本質的、宗教的ないし神秘的な長所を持つものが貨幣として扱われる
  • アインチヒによる指摘
    • 原始貨幣と現代貨幣の違いを説明
    • 原始貨幣の条件ないし特徴
      1. 原始貨幣は近代の貨幣の一部機能しか満たさない
      2. 原始貨幣は一般に地域・時代・民族によって異なり、形態も様々ん
      3. 原始貨幣は日常的に使われるものではなく、損害に対する代償支払い、争い・規則違反に対する贖罪など社会秩序安寧・維持に用いられる
  • ラニーによる指摘
    • 現代貨幣は「多目的貨幣」
    • 原始貨幣は「特定目的貨幣」
  • 福田の指摘の精査
    • 「時と所の上に置いて制限を受けず価値移転の用具たりうること」
    • 上記が一定の経済圏において満たされるものが「貨幣」である
    • この指摘をアインチヒ、ボラニーの指摘と併せると、原始貨幣は「貨幣」ではない

貨幣の起源の結論

  • 特定目的貨幣であり、民族、地域、時代によって異なるもの
    • 宗教的、神秘的な長所を持ち、債務清算(価値移転)の用具として一定の経済圏の中で受容されたもの原始貨幣である

キャッシュレス化の進展

  • 貨幣の起源から貨幣の未来を展望する
  • キャッシュレス化を経済産業省は目論むが、現状日本におけるキャッシュレス決済の普及は韓国、中国、アメリカなどに大きく下回る
    • 背景には、クレジットカード端末設置店舗が少ない
      1. 導入費用および決済手数料が高い
      2. 決済端末の操作が煩雑
      3. ATMが普及しており、消費者の現金志向が高い
      4. 治安の良さや現金に対する消費者の信頼をベースとする生活習慣
    • QRコード決済においてその導入費用や煩雑さは緩和されつつある
  • キャッシュレス化の弊害
    1. 高齢化におけるスマホ決済に困難をきたす人々の増大
    2. 個人情報の保護
      • 決済会社(カード会社/スマホ決済会社)は消費行動を観測し、信用度などを測ることが可能
      • その一方で監視されるというリスクを孕む
    3. 情報格差による経済格差
      • デジタル端末にアクセス可能な人々とそうでない人々で情報格差・経済格差が生まれがちになる
  • 以上をまとめると、キャッシュレス化に伴うマイナス面を考慮した冷静な議論が必要である

仮想通貨の普及と課題

  • 仮想通貨がキャッシュレス社会の一翼を担う存在になりうるか
    • あまりにもテクニカルな専門用語が飛び交い、枝葉末節まで踏み込んだ議論が展開されている場合が少なくない
    • 仮想通貨それ自体が理解することが容易でないものである
  • 仮想通貨とは政府や中央銀行による価値の保証のない通貨
    • 交換取引所において法定通貨と交換することで入手可能
    • 電子データとして存在し、不正防止のために暗号技術を導入
      • 複数のコンピュータで記録を共有・相互監視するシステム
  • 仮想通貨のメリット・デメリット
    • 中央銀行・民間金融期間を経由せずに取引されるため、海外送金が安価かつ送金・決済時間を大幅に短縮
    • しかし、利用者の信頼によってのみ価値が保証されている
    • 匿名性も高く、違法取引、脱税、マネーロンダリングなどに利用されやすい
    • 最大の問題は投機の対象となることで、通貨価値が大きく変動する
      • バブルのような状況も散見される
      • その中でハッカーによる不正流出などが発生している
  • このような状況を受けて、金融庁は2016年に改正資金決済法を成立させた
    • 仮想通貨取引所・交換所を登録制に
      • 利用者は本人確認が義務付けられる
    • 仮想通貨の定義も実施(1号仮想通貨、2号仮想通貨)
  • 仮想通貨の未来への疑問
    1. 一般受容性の欠如
      • 現時点では限られた人々にしか受容されていない
    2. 信用を担保する発行者が存在しない
      • 発行はあくまで、インターネット上のシステム
      • 「ゲーム」を設計・開発した人々がコインの価値に責任を持たない
      • 「信用貨幣」の信用は「発行主体への信用」である
      • その不在によって、限りなく幻想に近く、その信用は簡単に失われる
    3. 仮想通貨価値の不安定性
      • 多くの人々は投機目的で購入
      • 投機対象の通貨としては価値が不安定
      • 一方金融庁によれば「有価証券」ではないので、原則として仮想通貨は投資家保護の対象にならない
    4. 安価な送金コストの背後にあるリスク
      • 既存金融機関はシステムの安全性確保・維持のために手数料を取る
      • 仮想通貨ネットワークには脆弱性が潜んでいるため、利用者はリスクの存在を知らなければならない
  • まとめ
    • 価値の乱高下や、価値自体の消失が起こる仮想通貨は「通貨」としての期待をかけることはできない
    • 貨幣が貨幣たる所以は価値が安定していること
    • 貨幣は社会を構成する人々から躊躇なく受け入れられる一般的受容性を持っているからこそ貨幣である
      • 投機の対象とならざるを得ない仮想通貨は「通貨」たり得ない

感想

前半の貨幣論の推移に関しては、かなり興味深く読み進めることができた。物々交換仮説が否定されつつある、ということは聞いていたが、それが実際どのような議論なのか、というのを理解できただけでも収穫があった。また、筆者が現状主張する「宗教起源説」も非常に興味深いと感じた。

その一方で後半の技術的観点に対しては、非常に議論がナイーブと言わざるを得ない。キャッシュレス化の弊害としてデジタルディバイトを挙げているが、それがもたらす経済学的効用などの議論に持ち込むべきなのではないだろうか。そもそもデジタルディバイドに関しては、技術者たちは日々UI/UXの研鑽を続けているだろうし、キャッシュレス化の未来を議論するにはいかさかナイーブに感じる。

その上で、仮想通貨は中央による価値の担保がないため、価値が乱高下するため、通貨たり得ない、と結んでいるが、その議論もナイーブに感じる。そもそも仮想通貨の価値はどのように成立するか、それを技術的、経済的インセンティブから分析するような仕事が、今後求められているのではないだろうか。

また、中央銀行によるコントロールが存在しないことも挙げているが、仮想通貨はそもそもそうしたコントロールがなく、ソフトウェアがコントロールを行う思想の元設計されている、と認識している。その上で、我々が考えるべきなのは、ソフトウェアのガバナンスであり、その運用スキームであると思う。

何れにせよ、本論文著者にとっては技術は専門外であろうので、そうした部分の議論がナイーブさがあれど、一つの貨幣論の推移を辿るには非常に面白く感じた。こうした仮想通貨を経済学的観点から分析・精査する論文も少しずつ読んでいきたい。

Easy-RSA2を用いたオレオレCAの構築と各種鍵/証明書の発行

概要

OpenSSLを用いて認証局(CA)を構築するツール群であるEasy-RSA2を用いてオレオレCA(の鍵と証明書)を作成し、OpenVPNを構築に必要な各サーバ、クライアントそれぞれの鍵と証明書を発行する。

そもそもCA/サーバ証明書とは何か?

通常インターネット上で行われるTLS通信においては、接続先サーバが確かにそのサーバであることを認証するために、接続先サーバの公開鍵を含むサーバ証明書をサーバーから提供される。このサーバ証明書には、主に公開鍵と、接続先サーバが提供した公開鍵が確かに接続先サーバ公開鍵であることを証明するCA(認証局 による署名が含まれる。その上で、そのCAが確かに当該証明書に含まれる署名をした主体であることを証明するために、上位のCAが当該CAの中間証明書を発行する。ここで上位のCAに証明されるCAのことを中間CAと呼ぶ。中間CAがその上位の中間CAに証明を受けることで、上位のCAの署名が正しく検証できれば(上位のCAが信頼できれば)、下位のCAの署名の正当性が保証される。この連鎖構造の仕組みをトラストチェーンなどと呼ばれる。

f:id:chike0905:20191127123227p:plain

その連鎖の上で、最上位には信頼される第三者機関としてルートCAが存在し、その証明書の正当性はルート証明書として自身が証明する形で証明される(自己証明書などと呼ばれる)。そのルート証明書の正当性はルートCAが信頼されていることを前提に、TLSクライアントとなるWebブラウザなどにルートCAの公開鍵が事前インストールされており、ルート証明書をクライアントは検証をすることができるため、ルートCAの下位に位置する中間CAの証明を実施しすることが可能となる。

f:id:chike0905:20191127123231p:plain

ルートCAは誰でも構築することができ、そのためのサーバ証明書発行ソフトウェアも配布されているが、実際に運用することは難しい。なぜなら、ルートCAは信頼される必要があるため、どこの馬の骨かわからない人が構築したルートCAの発行する証明書は本当に証明を正しく行えるかは大いに疑問が残るためだ。自己を信頼に足りうる形で証明するには非常に大きなコストがかかるが、自己証明書の発行自体は誰でもできるため、自己証明をしただけで信頼性のない証明書はオレオレ証明書1などと呼ばれる。

しかし、そもそも現行のルートCAも正しく運用されているかは、個人のユーザ単位では知り得ない。サーバ証明書の実態は当該サーバの公開鍵に対する署名であり2、ルートCAの自己証明書秘密鍵さえあれば誰でも作成が可能である。また、ルートCAが証明する中間のCA、および中間CAが証明する各サーバの証明の信頼性は全てルートCAが担保する事になる。したがって、ルートCAはその信頼性を担保するためには、自己証明を行うための秘密鍵を厳重に管理する必要がある。3

環境構築

  • Debian 9上で実行
    • easy-rsaのインストール sudo apt install easy-rsa
    • CA用のディレクトリを作成 make-cadir dirname
    • dirnameが作成され、中に鍵生成スクリプトなどが入っているのがわかる
  • 以降dirname内で作業を行うものとする
    • 鍵生成をスクリプトで実施するとdirname/keysに鍵や証明書が生成される

      CAサーバ上での鍵生成

      環境変数設定

  • OpenSSLの設定ファイルがバージョンごとにdirname内に作成されている
    • OpenSSLのバージョンを確認し、バージョンにあったものをopenssl.cfgとしてコピーする
    • 次に書き換えるvarsの中から読み込んでいる
  • 環境変数varsに設定
  • 書き換え例(ここではvars.newにコピーして作成)
diff vars vars.new
< export KEY_COUNTRY="US"
< export KEY_PROVINCE="CA"
< export KEY_CITY="SanFrancisco"
< export KEY_ORG="Fort-Funston"
< export KEY_EMAIL="me@myhost.mydomain"
< export KEY_OU="MyOrganizationalUnit"
---
> export KEY_COUNTRY="JP"
> export KEY_PROVINCE="Kanagawa"
> export KEY_CITY="Fujisawa"
> export KEY_ORG="Keio Univ."
> export KEY_EMAIL="oreoreca@myhost.mydomain"
> export KEY_OU="OreOre"
  • 書き換え後環境変数を読み込むためsource varsを実行

CA作成

  • 以下のコマンドでCAの鍵と証明書作成
    • CAの情報の入力を求められるが、varsで設定した値が初期値として読み込まれる
./build-ca

サーバ証明書作成

  • serverの部分に設定したものがホスト名として設定される
./build-key-server server

クライアント側の証明書・鍵作成

  • clientの部分に設定したものがホスト名/ユーザ名として設定される
./build-key client

Diffie Hellmanパラメータファイルの作成

  • 以下のコマンドでkeys以下にdh2048.pemなどのファイルが作成される
    • ファイル名数字部分は、vars内のKEY_SIZEで設定したビット数になる(デフォルトで2048)
    • 実行には少し時間がかかる
./build-dh

証明書廃止リストの作成

  • 証明書廃止リストは、実際にクライアント証明書の作成と廃止を行わないと作成できない
  • ダミーのクライアント証明書をいったん作成&廃止することにより証明書廃止リストを作成する
    • ダミー証明書の作成 ./build-key dummy
    • ダミー証明書の廃止 ./revoke-full dummy
    • ./keys内に証明書廃止リストであるcrl.pemが作成されているのを確認する

作成したファイルの一覧

  • dirname/keys以下にそれぞれ以下のようなファイルが上記手順で生成されている
    • これらのファイルの中でも、秘密にするべきファイルの配送には厳重な管理が必要であり、VPNなどを構築する際には、運用を精査しなければならない。
ファイル名 設置場所 目的 秘密かどうか
ca.crt サーバ、 全てのクラインアント ルートCAの証明書 いいえ
ca.key 署名するPCのみ ルートCAの鍵 はい
dh{n}.pem サーバのみ Diffie Hellman パラメータ いいえ
server.crt サーバ サーバの証明書 いいえ
server.key サーバ サーバの鍵 はい
client.crt クライアント クライアントの証明書 いいえ
client.key クライアント クライアントの鍵 はい
crl.pem クライアント クライアント証明書廃止リスト いいえ

参考


  1. オレオレ詐欺から連想し、自分が自分であることを証明する(が、実際には攻撃者が「騙り」をしているかもしれない)信頼のおけない証明書としての俗語

  2. 実際にはその他の情報も含まれるが、抽象化のために割愛する。

  3. ここではその運用リスクなどに関しては構成などに依存する部分があるため議論しないが、以降に記載されるCAの構築を行なったとしても正しい運用を怠れば、全く意味のないものとなる。

Bitcoind Chainparams調査

Bitcoind Chainparams調査 Bitcoin coreのchainparams.h/.cppを読んでパラメータの意味を調査する。なお本調査では、すべでのコードを精読したわけではなく、呼び出し部分などを参照しながら変数名から推測して執筆しているために、大いに間違っている可能性がある。間違いに気づいたら指摘していただけるとすごく嬉しい。

読んでいるコードは以下

大枠の理解

  • main/test各ネットワークによってそれぞれchainparams.cppでデフォルト値を設定
  • chainparams.hconsensus/params.hを呼んで内包している

consensus/params.hの中身

  • この中のstruct Paramschainparamsから呼んでいる
  • 主に各BIPをアクティベートするためのパラメータと、PoWの設定に関するパラメータ
  • BIP関連大枠としてはソフトフォークの方法として以下の3つがあり、それぞれのアクティベートのためのパラメータが設定されている
    • 各ソフトフォークの方法に関してはこの記事がわかりやすい
    • flag day activation: BIP16
    • IsSuperMajority:BIP34、BIP65、BIP66
    • version bits(BIP9):BIP112、BIP141
      • BIP9に関するパラメータはconsensus/params.h中のstruct BIP9Deploymentで定義されている

パラメータ

params 説明
nSubsidyHalvingInterval int ブロック毎の報酬を半減させるブロック数
BIP16Exception uint256 BIP16が強制的にアクティブになるフラグのブロックハッシュ
BIP34Height int BIP34がアクティブになるブロック高
BIP34Hash uint256 BIP34がアクティブになるブロックハッシュ
BIP65Height int BIP65がアクティブになったブロック高
BIP66Height int BIP66がアクティブになったブロック高
nRuleChangeActivationThreshold unit32_t ソフトフォーク時のアクティベーションするブロックの割合閾値(nMinerConfirmationWindowブロック中の何ブロックの形で指定)
nMinerConfirmationWindow unit32_t アクティベーション時の閾値を定義するためのブロック数(原則としてnPowTargetTimespan / nPowTargetSpacing?)
vDeployments[MAX_VERSION_BITS_DEPLOYMENTS] BIP9Deployment BIP9を利用したソフトフォークパラメータ(struct BIP9Deploymentで定義)
powLimit unit256 PoWのDifficulty最大値
fPowAllowMinDifficultyBlocks bool PoWのDifficultyを最大値と同値を許すかどうか
fPowNoRetargeting bool PoWの難易度調整を行うかどうか
nPowTargetSpacing int64_t PoWの平均成功間隔(秒単位) 
nPowTargetTimespan int64_t PoWの難易度調整を行う間隔(秒単位)
DifficultyAdjustmentInterval int64_t PoWの難易度調整を行う間隔(ブロック単位、nPowTargetTimespan / nPowTargetSpacingから計算して自動設定)
nMinimumChainWork uint256 新規forkチェーン発見時に、最低限満たしているべきPoW難易度
defaultAssumeValid unit256 指定ブロック以前のブロックは正しいとみなすフラグとなるブロックのハッシュ

chainparams.hの中身

  • 中身をみていくと、「チェーンのパラメータ」というより、ノード同士のコミュニケーションであったり、ノード動作/ブートストラップのためのパラメータが主に設定されていた
params 説明
consensus Consensus::Params 上記参照
pchMessageStart CMessageHeader::MessageStartChars 通信時のメッセージヘッダーへ挿入するメッセージPrefix
nDefaultPort int ノードのデフォルト通信ポート
nPruneAfterHeight uint64_t 保存するブロックファイルを圧縮する時の、圧縮せず元データを維持する先頭ブロックからのブロック数
m_assumed_blockchain_size uint64_t 現在の大まかなブロックチェーンデータサイズ(GB単位)
m_assumed_chain_state_size uint64_t 現在のChainStateDBの大まかなサイズ(GB単位)
vSeeds std::vector\<std::string> Seedノードのドメインリスト
base58Prefixes[MAX_BASE58_TYPES] std::vector Base58Checkのencodingを行う際のプレフィックス(それぞれタイプはchainparams.hclass CChainParamsenum Base58Typeで定義)
bech32_hrp std::string bech32におけるHuman Readable Part
strNetworkID std::string ネットワークを識別するID(mainnet/testnetなど)
genesis CBlock Gensisブロック(chainparams.cpp内のCreateGenesisBlock()を用いて生成)
vFixedSeeds std::vector SeedノードのIPv6アドレス(chainparamsseeds.h内で定義)
fDefaultConsistencyChecks bool ノード起動時にBlockIndexやmempoolなどに一貫性があるかのチェックを実施するか(要調査)
fRequireStandard bool スタンダードなTXフォーマットでないTXを扱うかどうか(testnetではtrue)
checkpointData CCheckpointData 検証時にチェックポイントとするブロック高とブロックハッシュのリスト(構造はcheckpoint.hで定義)
chainTxData ChainTxData チェーンの中にあるTXの統計データなど、ノードブートストラップ時のチェーン検証進捗計算のために使われている?(詳細はchainparams.h内のstruct ChainTxDataで定義)
m_fallback_fee_enabled bool WalletでTX作成時にfeeが指定されなかった時に、指定値のfeeを払うようにするかどうか(feeの指定値は起動時に-fallbackfeeパラメータで指定)

感想

  • consensus/params.hの中でソフトフォーク関連のパラメータを定義しているのはなんとなく面白い
    • しかし、ハードコードしている部分が増えるのはノードをブートストラップする時の効率がよくなるのは理解できるが、ソフトウェアとガバナンスの観点からは疑問
  • consensus/params.hの中に半減期の期間の定義があるのはそこで本当にいいのだろうか?と直感的には思った
  • nMinerConfirmationWindowDifficultyAdjustmentIntervalとは別に設定されているのは面白いかもしれない
  • 様々なパラメータが歴史的経緯とともに混沌としていった形跡が見受けられ、一度再設計してみるのも面白いかもしれない
    • BIP関連
  • ノードの運用用のパラメータ(fDefaultConsistencyChecksなど)とノードがコミュニケーションを取るためのパラメータはうまく整理して分離できたりしないだろうか

    • Bitcoinノードの実装と運用、そしてガバナンスに関してコードの観点から疑問を再確認できたので、いい機会だった
  • 最初に読み始めたコードがv0.17ベースのものだったので、すでに削除されたパラメータ(fMineBlocksOnDemand)についても調査していた

    • この辺の歴史的経緯を整理しながらパラメータを整理してドキュメンテーションするのもこのコミュニティには必要なんじゃないだろうかと思ったり