まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

タグ:blockchain

IBM Cloud から提供されている 30 日間無料 Kubernetes サービスIBM Kubernetes Service 、以下 "IKS")環境を使って利用することのできるコンテナイメージを1日に1個ずつ 30 日間連続で紹介していきます。

環境のセットアップや制約事項については Day0 のこちらの記事を参照してください。

Day 6 からはデータベース系コンテナとその GUI ツールを中心に紹介してます。Day 18 はこのデータベース系イメージとしては最終回ですが、軽量ブロックチェーン実装である HATOYA イメージをデプロイする例を紹介します。
bird_shiro_hato



【イメージの概要】
厳密には「データベース」ではないのですが、「データを貯めるもの」という意味合いでこのカテゴリーに含めて紹介します。

HATOYA は2つの側面を持ったミドルウェアです:
(1)RESTful な JSON ドキュメントデータベース
(2)ブロックチェーン

開発者視点でみた HATOYA は(1)の JSON データのデータベースです。データベースの CRUD や、データベース内の(JSON)ドキュメントの CRUD といった操作がすべて REST API で提供されています。ここだけ見ると MongoDB や CouchDB に近い側面を持っています。

一方、運用者視点でみた HATOYA には(2)のブロックチェーン機能が内蔵されています。(1)で紹介した REST API の CRUD 操作の中で、R 以外の CUD が実行されると、その内容はすべてブロックチェーンの台帳に記録されます。つまりデータベースへの変更記録はすべてブロックチェーン台帳に自動的に記録されます。ブロックチェーン記録時にはカスタマイズ可能なマイニングルールが適用されるので、用途に応じてデータ改ざんの難易度とパフォーマンスのバランスをとることができます。HATOYA サーバーは1台でも運用可能ですが、サーバーを複数起動して(ブロックチェーンの)同期をとることも可能です。

HATOYA は軽量でいながらマイニングやハッシュポインタ、リオルグといったブロックチェーンの機能を実装し「一度格納したら管理者でも改竄不可」を実現しています。またデータ操作や複数サーバーの紐付け管理などに REST API が用意されていて、環境構築段階からエンジニアフレンドリーな操作性が提供されている、そんなブロックチェーン対応データベースです。

ちなみに Day 1 で紹介した hostname イメージ同様に、私が開発して公開しているコンテナイメージです。σ(^o^; イメージ自体の挙動として、デフォルトでは 4126 番ポートで REST API リクエストを待受けるよう作っていますが、この意味が分かる人は立派なオッサンだと思ってます。



【イメージのデプロイ】
まずはこちらのファイルを自分の PC にダウンロードしてください:
https://raw.githubusercontent.com/dotnsf/yamls_for_iks/main/hatoya.yaml

今回の HATOYA もシングルノード環境で利用する場合は特にパラメータ指定不要で、そのままデプロイすることができます。以下のコマンドを実行する前に Day 0 の内容を参照して ibmcloud CLI ツールで IBM Cloud にログインし、クラスタに接続するまでを済ませておいてください。

そして以下のコマンドを実行します:
$ kubectl apply -f hatoya.yaml

以下のコマンドで HATOYA 関連の Deployment, Service, Pod, Replicaset が1つずつ生成されたことと、サービスが 30126 番ポートで公開されていることを確認します:
$ kubectl get all

NAME                          READY   STATUS    RESTARTS   AGE
pod/hatoya-7886987799-6hkpc   1/1     Running   0          21s

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/hatoya       NodePort    172.21.53.119   <none>        4126:30126/TCP   21s
service/kubernetes   ClusterIP   172.21.0.1      <none>        443/TCP          27d

NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hatoya   1/1     1            1           22s

NAME                                DESIRED   CURRENT   READY   AGE
replicaset.apps/hatoya-7886987799   1         1         1       22s

この後に実際にサービスを利用するため、以下のコマンドでワーカーノードのパブリック IP アドレスを確認します(以下の例であれば 161.51.204.190):
$ ibmcloud ks worker ls --cluster=mycluster-free
OK
ID                                                       パブリック IP    プライベート IP   フレーバー   状態     状況    ゾーン   バージョン
kube-c3biujbf074rs3rl76t0-myclusterfr-default-000000df   169.51.204.190   10.144.185.144    free         normal   Ready   mil01    1.20.7_1543*

つまりこの時点で(上述の結果であれば)アプリケーションは http://169.51.204.190:30126/ で稼働している、ということになります。HATOYA はこの URL が管理ダッシュボードの URL になっているので早速実行してみます。ウェブブラウザを使って、アプリケーションの URL(上述の方法で確認した URL)にアクセスしてみます:
hatoya1


管理用ダッシュボードが表示され、この画面からデータベースを作成したり、そのデータベース内へのドキュメント追加・更新・削除といったオペレーションが可能です。変更系のオペレーションはすべて台帳にハッシュポインタチェーンで繋がれた形で記録され、データの改竄ができない形で記録されていることがわかります:
hatoya2



【YAML ファイルの解説】
YAML ファイルはこちらを使っています:
apiVersion: v1
kind: Service
metadata:
  name: hatoya
spec:
  selector:
    app: hatoya
  ports:
  - port: 4126
    protocol: TCP
    targetPort: 4126
    nodePort: 30126
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hatoya
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hatoya
  template:
    metadata:
      labels:
        app: hatoya
    spec:
      containers:
      - name: hatoya
        image: dotnsf/hatoya
        ports:
        - containerPort: 4126

Deployment 1つと、Service 1つ、環境変数の指定も不要で本シリーズで紹介する 30 個の中でも指折りにシンプルな YAML ファイルです。一応解説を加えておきます。アプリケーションそのものは 4126 番ポートで動作するように作られているため、NodePort 30126 番を指定して、外部からは 30126 番ポートでアクセスできるようにしています(NodePort として指定可能な番号の範囲は 30000 ~ 32767 です、指定しない場合は空いている番号がランダムに割り振られます)。また ReplicaSet は1つだけで作りました(データベースなので、別途クラスタ構成の準備をしない限りはこの数値だけを増やしてもあまり意味ないと思います)。


デプロイしたコンテナイメージを削除する場合はデプロイ時に使った YAML ファイルを再度使って、以下のコマンドを実行します。不要であれば削除しておきましょう:
$ kubectl delete -f hatoya.yaml


【紹介したイメージ】
https://hub.docker.com/r/dotnsf/hatoya


【紹介記録】
Dayカテゴリーデプロイ内容
0準備準備作業
1ウェブサーバーhostname
2Apache HTTP
3Nginx
4Tomcat
5Websphere Liberty
6データベースMySQL
7phpMyAdmin
8PostgreSQL
9pgAdmin4
10MongoDB
11Mongo-Express
12Redis
13RedisCommander
14ElasticSearch
15Kibana
16CouchDB
17CouchBase
18HATOYA
19プログラミングNode-RED
20Scratch
21Eclipse Orion
22Swagger Editor
23R Studio
24Jenkins
25アプリケーションFX
262048
27DOS Box
28VNC Server(Lubuntu)
29Drupal
30WordPress

この記事の続きです:
開発者視点で「理想的なブロックチェーン」とは?


開発開始から4週間、ブロックチェーン対応 RESTful データベース HATOYA がある程度動くようになった記念の Docker イメージを公開します。なお現時点で公開しているのは amd64 版の(x86 Linux 版の)Docker イメージです。

以下ではとりあえず開発用途として使うためのシングルノードで動かす場合の使い方を含めて紹介します(マルチノードで動かすこともできるのですが、、、その説明はまた別の機会に。興味ある方は連絡いただければ何か対応しますw)。


(追記)
マルチノード運用の記事を追加しました:
HATOYA をマルチノード運用して、ブロックチェーン情報を分散管理する


【HATOYA とは?】
HATOYA の特徴は大きくは以下の3点です:
(1) REST API でデータベースやその中のアイテムデータを CRUD(Create/Read/Update/Delete) するデータベースです。
(2) 変更を伴う API(読み取り以外の API)を実行すると、その内容は内蔵ブロックチェーンに自動記録されます。
(3) マルチノードで動かす場合、ブロックチェーン部分はマルチノード間で同期します(データベース部分は同期しません)。
 (3-1)データの破損時や環境引越し時などはマルチノード内の他ノードのブロックチェーンを使って、データベースやブロックチェーンのリストアが可能です。
 (3-2)ブロックチェーンネットワークへはプライベートネットワークからでも参加可能です(この場合、push 型の同期になります)


【事前準備】
x86 版の Docker 環境をインストールしておいてください。後述の公開イメージが amd64 アーキテクチャ用のため、Windows コンテナ版やラズパイコンテナ版ではなく、x86 Linux 用のものが必要です。

※ソースコード的にはラズパイ上でも動くことを確認しています。どなたかラズパイコンテナ用のイメージと併せてマルチアーキテクチャイメージにする具体的な方法をご存知の方がいたら教えていただけると助かります。


【Docker イメージ】
DockerHub 上に公開しました:
https://hub.docker.com/repository/docker/dotnsf/hatoya


2020080300


【起動方法】
イメージを DockerHub から pull して起動します。HATOYA はデフォルトで 4126 番ポートでリクエストを待ち受けるので、同じ 4126 番ポートで待ち受ける形で外部からもリクエストを受け付ける形にするのであれば、以下のように起動します:
$ docker pull dotnsf/hatoya

$ docker run -d -p 4126:4126 dotnsf/hatoya


(注 この方法で起動した場合、システム情報やデータ情報、ブロックチェーン情報は永続化されないため、コンテナを再起動するとデータを失ってしまいます。これを避けるためには以下のような(ボリュームを使った)起動パラメータを指定して、システムフォルダを永続化する必要があります)
$ docker run -d -v /tmp/.system:/tmp/.system -e SYSTEM_FOLDER=/tmp/.system -p 4126:4126 dotnsf/hatoya



これで 4126 番ポートで REST API の待受状態になります。同一ホストからのみ http://localhost:4126/doc を開くことで Swagger API ドキュメントを参照/実行することができます(Swagger API ドキュメントは他ホストからでも参照は可能ですが、実際に API を実行することは CORS 制約によりできません):
2020080101



【いくつかの API を実行してみる】
では Swagger API ドキュメントを使って、実際にいくつかの REST API を実行してみます。

※この節で書かれた内容が難しくてわからん、という方は下の「ダッシュボードを使う」を参照してください。同じ操作を GUI で行えます。

例えば、「現在のデータベース一覧」は GET /api/dbs という REST API で取得できます(Swagger API ドキュメント上では GET /dbs と表示されていますが、API は全て /api パス以下にあるため省略表記されています。以下同様):
2020080102


実際に実行するには Swagger API ドキュメントから "Try it out" を押した後に "Execute" を実行します。なおこの画面ではパラメータに token (トークン)を指定できるようになっていますが、今回はトークンを無効にして起動しているので指定する必要はありません(トークンがないと API が実行できないように設定することは可能です):
2020080103


"Execute" すると、実際に実行された内容を curl コマンドで実行した場合のコマンド内容(curl -X GET "http://localhost:4126/api/dbs" -H "accept: application/json")や、エンドポイント URL (http://localhost:4126/api/dbs)とともに実行結果が表示されます:
2020080104


ここでは実行結果は以下のようになっています。この実行結果の意味は実行は成功(status: true)し、データベースの一覧は空([])になっている、という内容でした:
{
  status: true,
  dbs: []
}

※注 HATOYA の REST API は(添付ファイル参照など)バイナリデータを返すもの以外は、実行結果は原則的に JSON データとなります。またその status 要素が true の時は成功、false の時は失敗を意味するよう統一されています。


なお、localhost 以外のホストから同じ API を実行する場合は curl コマンドを参照し、localhost 部分を IP アドレス(以下の例では xx.xx.xx.xx)に置き換えて実行することで実現できます:
$ curl -X GET "http://xx.xx.xx.xx:4126/api/dbs" -H "accept: application/json"

{
  status: true,
  dbs: []
}

この節の以下の説明では全て Swagger API ドキュメントを使って操作しますが、外部から実行する場合は同様にして操作する API と同じ curl コマンドを実行して試してみてください。


ではデータベースを1つ作成してみましょう。データベースの作成は POST /api/{_db} を使って行います:
2020080105


ここでも "Try it out" を押し、パラメータ _db に作成するデータベースの名前(以下の例では "mydb")を指定して "Execute" を実行します:
2020080106


すると実行結果(以下例では { status: true })が表示されます。成功したようです:
2020080107


改めてもう一度先程実行したデータベース一覧の API(GET /api/dbs)を実行します。すると先程空([])だった dbs の値が [ "mydb" ] となり、データベース作成が反映されたことが確認できます:
2020080108


では次に作成したデータベース mydb 内に1つアイテムを作成してみます。アイテムの新規作成 API は POST /api/{_db} で行います(下図では POST /api/{_db}/{_id} となっていますが、この {_id} 部分を指定しないと新規作成とみなすので、結局エンドポイントは POST /api/{_db} となります)。なおエンドポイントがデータベース作成時と同様ですが、ポストデータが存在する場合はアイテムの新規作成、存在しない場合はデータベースの新規作成とみなされます:
2020080101


ではこれまでと同様に一度 "Try it out" をクリックし、_db に "mydb" を指定後、送信 Body 内に保存したい内容の JSON オブジェクト(以下の例では { name: "K.Kimura", hobby: "Programming" })を記入して "Execute" を実行します:
2020080102


実行結果が以下のように( { status: true, id: "xxxxxx" } )表示されます。status: true となっているので処理は成功し、アイテムの id が割り振られて保存されました。その id 値も実行結果内に表示されています:
2020080103


実際にデータベース内にアイテムが格納されたことを確認してみます。データベース内のアイテム一覧を見るには GET /api/{_db} を実行します。この _db パラメータに "mydb" を指定して "Execute" します(なおこの API を実行する際に limit や offset を指定することも可能ですが、今回は使用せずに全データを表示します):
2020080104


こちらが実行結果({ status: true, items: [ "xxxxxx" ], limit: 0, offset: 0 })です。status: true が含まれているので処理は成功し、結果は items: [ "xxxxxx" ] という形で含められています。この items は指定したデータベースに含まれるアイテムの id の配列となっています。この時点では1つだけアイテムが含まれていることがわかります:
2020080105


ではこの id のアイテムが本当に先程入力したアイテムかどうかも確認してみます。特定のアイテムデータを取得するには GET /api/{_db}/{_id} を実行します。このパラメータのうち、_db は "mydb" 、_id は先程取得した items 配列の中に1つだけ含まれていた id の値を指定して "Execute" します:
2020080106


その実行結果がこちら({ status: true, item: { name: "K.Kimura", hobby: "Programming", id: "xxxxxx" } })です。先程の新規作成時に入力した内容が正しく記録されていました:
2020080107


今度はこのアイテムの内容を更新してみます。アイテムを更新する API は先程の新規作成時と同じ POST /api/{_db}/{_id} です({_id} を未指定時に新規作成とみなします)。この _db を "mydb" に、_id を先程指定した id 文字列値にして、body 部を適当に(下例では { name: "キムラ ケイ", hobby: "プログラミング" } と)して "Execute" します:
2020080108


再度アイテムの内容を確認する GET /api/{_db}/{_id} を、 _db に "mydb" 、_id に id 文字列値を指定して実行してみます。実行結果は { status: true, item: { name: "キムラ ケイ", hobby: "プログラミング", id: "xxxxxx" } } となり、正しくアイテムを更新することができました:
2020080109


今度はこのアイテムを削除してみます。アイテム削除の API は DELETE /api/{_db}/{_id} です。同様にして _db に "mydb" を、_id に id 文字列値を指定して "Execute" します:
2020080101


実行後に再度 GET /api/{_db}/{_id} を同じパラメータで実行しても、結果は { status: false } となり、実行に失敗することが確認できます。アイテムが消えてしまったので見つからない、という一覧の結果が確認できました:
2020080102


HATOYA にはこれらの基本的なアイテム CRUD 用 API 以外にも添付ファイルをアイテムとして登録する API があったり、複数アイテムをまとめて作成/更新/削除する、いわゆる「バルク処理」を行う API が存在してます。ここでは紹介しませんが、興味ある方は Swagger API ドキュメントを参照して試してみてください。


さて、ここまでが RESTful DB としての HATOYA の紹介でした。ここから先は HATOYA の特徴的な部分でもあるブロックチェーンプラットフォームとしての機能を紹介します。

ここまでの一連の処理の中で何度かシステムに変更を及ぼす API を実行しました。具体的には (1) mydb データベースの作成 (2) mydb データベースにアイテムを1件新規登録 (3) mydb データベースに登録したアイテムの内容を更新 (4) mydb データベースに登録したアイテムを削除 の計4回変更しています。これらの変更記録は全て HATOYA 内のブロックチェーンに自動登録されています。今度はブロックチェーンの中を参照する API を紹介します。

それが GET /api/ledgers です。早速実行してみよう・・と思うのですが、この API は実行時に serverid という HATOYA サーバーノードの id をパラメータ指定する必要があります。まずはこの serverid を取得しましょう:
2020080103


serverid 情報は GET /api/serverid で取得することができます。まずこの API を実行して、その実行結果に含まれる serverid 値を取り出します:
2020080104


GET /api/serverid 実行結果( { status: true, serverid: "zzzzzz" } )の "zzzzzz" 部分が、このサーバーノードの serverid 値です。今回の GET /api/ledgers 以外にも実行時にこの serverid 値をパラメータ指定する必要のある API がいくつかありますが、全てこの方法で確認可能です:
2020080105


では改めて、取得した serverid を指定して GET /api/ledgers を実行します:
2020080106


実行結果は以下のようになりました:
2020080107

{
  "status": true,
  "ledgers": [
    {
      "prev_id": null,
      "body": [
        {
          "serverid": 1594300581524,
          "method": "create_db",
          "db": "mydb"
        }
      ],
      "timestamps": [
        1596292186611
      ],
      "nonce": 34,
      "id": "0041cf982623372e59bafac8ee055f63b061fa702ea0364e6894fc5c453cd8ae0c0bf5e094892e8d34ac8d78229bcfb014f7641595bed4987545cf425b7e2d38"
    },
    {
      "prev_id": "0041cf982623372e59bafac8ee055f63b061fa702ea0364e6894fc5c453cd8ae0c0bf5e094892e8d34ac8d78229bcfb014f7641595bed4987545cf425b7e2d38",
      "body": [
        {
          "serverid": 1594300581524,
          "method": "create_item",
          "db": "mydb",
          "item": {
            "name": "K.Kimura",
            "hobby": "Programming",
            "id": "52ca8330-d405-11ea-a583-074fb3956e72"
          }
        }
      ],
      "timestamps": [
        1596292991723
      ],
      "nonce": 232,
      "id": "00ab2eddbec25b10560d8769c3d21a21efee0edb2f69e634da34fe1f326aae96a2d5346cff4ff794c88bc68fa3c45fcf73ba483b3c1ae1977f3b326efda3aeed"
    },
    {
      "prev_id": "00ab2eddbec25b10560d8769c3d21a21efee0edb2f69e634da34fe1f326aae96a2d5346cff4ff794c88bc68fa3c45fcf73ba483b3c1ae1977f3b326efda3aeed",
      "body": [
        {
          "serverid": 1594300581524,
          "method": "update_item",
          "db": "mydb",
          "item": {
            "name": "キムラ ケイ",
            "hobby": "プログラミング",
            "id": "52ca8330-d405-11ea-a583-074fb3956e72"
          }
        }
      ],
      "timestamps": [
        1596294186025
      ],
      "nonce": 427,
      "id": "0066ee6659b2ea71365ddc8b93a780e2d3684fc7f40d6c5f3ccec6835385f7863c1737f8eef239a0867440f2493d246166387082053f7cbb20aea468a8e74c7a"
    },
    {
      "prev_id": "0066ee6659b2ea71365ddc8b93a780e2d3684fc7f40d6c5f3ccec6835385f7863c1737f8eef239a0867440f2493d246166387082053f7cbb20aea468a8e74c7a",
      "body": [
        {
          "serverid": 1594300581524,
          "method": "delete_item",
          "db": "mydb",
          "id": "52ca8330-d405-11ea-a583-074fb3956e72"
        }
      ],
      "timestamps": [
        1596294559724
      ],
      "nonce": 88,
      "id": "006c91e3c08ded1791775aab6a0fa02b82cb842f5d2e6fba989d58b64444b5f0005ac115eb969b8491cad824ac6e5e924c4b8baa6567c04c7997b505a7891016"
    }
  ],
  "limit": 0,
  "offset": 0
}

実行結果の JSON オブジェクト内の ledgers が配列となっていて、その中にブロックチェーンの内容が含まれています。視認しやすいように上記では色をつけていますが、(1) mydb データベースの作成 (2) mydb データベースにアイテムを1件新規登録 (3) mydb データベースに登録したアイテムの内容を更新 (4) mydb データベースに登録したアイテムを削除 の4つが全て serverid 付きで登録されていることがわかります。しかも各オブジェクトには id と prev_id (と nonce)というキーがあり、これらがハッシュチェーンとなって繋がっていることで改ざんが困難なブロックチェーンとして機能しています。

つまり、この段階では HATOYA には mydb というデータベースが1つだけ存在していて、中身は何のアイテムデータが含まれていない状態になっていますが、これがデータベースが作られた直後ではなく、アイテムデータが作成され、変更され、削除された上での結果として何も含まれていない状態になっている、ということをブロックチェーンという技術が保証してくれていることになります。

これらのデータベースシステムへの変更作業が自動でブロックチェーンに登録される(そしてこのブロックチェーンは改ざんが困難なので変更履歴が保証される)ことが HATOYA の特徴の1つになっています。


【ダッシュボードを使う】
一つ上の節では Swagger API ドキュメントや REST API を使って HATOYA のデータベースを読み書きしたり、その結果生成されたブロックチェーンの様子を確認しました。これが理解しにくい、という人向けに簡易ダッシュボード機能も用意しています(ただしダッシュボードでは全ての REST API を実行できるわけではありません。例えば添付ファイルの読み書きやバルク API 実行は現在のダッシュボードからは行なえません)。

ダッシュボードへアクセスするにはブラウザでドキュメントルート(/)にアクセスします。localhost 上で実行している場合であれば http://localhost:4126/ へアクセスします。初回のみ Basic 認証を聞かれます。デフォルトではユーザー名 = username, パスワード = password です:
2020080202


※なお、この認証値は Docker コンテナ起動時の環境変数パラメータで変更可能です。例えばユーザー名 = user, パスワード = pass にするには以下のように起動します:
$ docker run -e BASIC_USERNAME=user -e BASIC_PASSWORD=pass -d -p 4126:4126 dotnsf/hatoya

正しい認証値を入力するとダッシュボードの画面が表示されます:
2020080201


画面左部分がメニューになっており、ブロックチェーン一覧や新規データベース作成はこちらから行えます。なおこの画面からブロックチェーン一覧を実行する場合、上述時に指定した serverid 値は自動的に指定されるので意識する必要はありません。なお serverid 値を確認したい場合は左上のハトマークにマウスカーソルを移動させると表示できます(シングルノード稼働の場合、(Config) と書かれた部分の環境設定は不要です、また config や ledgers という名前のデータベースを作成することはできません):
2020080203


データベースを作成すると (Config) の下に存在するデータベースが一覧で表示され、どれか1つを選択すると、そのデータベース内のアイテム一覧が表示したり、新規にアイテムを追加したり、編集/削除したりが行なえます。データベースの削除もこの画面から可能です:
2020080204


一通りの作業後に画面左の (Ledgers) を選択すると、その時点でのブロックチェーンの状態を確認することが可能です:
2020080205


↑UI 的にも全然イケてないダッシュボードですが、最小限の CRUD 操作を行ってブロックチェーンの確認までは行えるようになっています。Swagger API ドキュメントや curl の操作がよくわからない場合はこちらも併用してください。



【ブロックチェーンアプリケーション開発に】
HATOYA の全ての機能を紹介しているわけではないのですが、とりあえずこれらの情報を使うことでシングルノードの HATOYA を使ったブロックチェーン対応データストアが実現できると思っています。試験的にアプリケーションと組み合わせて使ってみていただいても構いませんし、HATOYA 対応アプリケーションを開発する用途であれば、その環境はシングルノードでも十分だと考えています。基本的には DB を REST で読み書き更新削除するだけでブロックチェーンに記録されるという特徴があるので、ブロックチェーンをほぼ意識することなくブロックチェーン対応アプリケーションが実装できると思っています(ちなみに拙作のマンホールマップ内でも使っています)。

今回紹介していませんが、本来は config 機能を使ってブロックチェーンネットワークを構築し、マルチノードでブロックチェーン部分を同期しながら運用することも想定しています。そこにもいくつかの特徴的な機能もあって我ながら面白い作品だと思っていますが、そのあたりについてはまた別の機会に。。


(このブログエントリは自作DBMS Advent Calendar 2020 の 12/3 にエントリーしています)


偉そうなブログタイトルになってしまいました、申し訳ない。


業務でも個人サービスでもブロックチェーンを使う機会があります。業務で携わることで世の中のお客様がどういった用途でどのようにブロックチェーンを使おうとしているのか、という貴重な情報を得ることができると同時に、企業内利用ゆえの様々な制約に直面することもあります。また個人サービスでもブロックチェーンを使うことで業務での必要十分な範囲にとらわれない(自分が挑戦してみたい)実装にチャレンジすることもできています。いろいろ恵まれた立場であることを実感しています。

一方で、この2つの立場でブロックチェーンを活用していると、「自分が理解してるブロックチェーン」と「実際のブロックチェーンプラットフォーム」との間に存在しているギャップが気になることもあります。このギャップは自分のブロックチェーンに対する理解がまだまだ足りないことに起因している可能性が大きいこともわかっていますが、それはつまり「ブロックチェーンは難しいもの」という先入観にも原因があるのかもしれません。

何を言いたいのかと言うと、自分のようなアプリケーション開発者の視点から考えるブロックチェーンはこういうものであってほしい(こういうものであってくれると使いやすいし、管理しやすい)んだけど、現実はそうではない、ということに何度か直面するのでした。具体的にいくつか挙げるとこのようなことです:


0 「ブロックチェーン」としての定義を満たしている

これは要件というよりも、後述する「ブロックチェーン」という用語への共通理解のための項目です。例えば日本では一般社団法人日本ブロックチェーン協会このような定義をしています。この中の「広義のブロックチェーン」で考えられるものが(広い意味での)ブロックチェーンであり、ここに定義された条件を満たしていれば、仮にサーバーノードを管理する権限を持っているような立場の人であっても、その中身を改ざんすることは困難になる、というものです。この「改ざんが困難」な特徴によって(法律などでルールを作らなくても、技術的に)記録内容の正当性を保証できるようになるもの、それがブロックチェーンだと理解しています。

で、特に開発者の視点ではブロックチェーンがここから下の要件を満たすものであってもらえるといろいろ嬉しいなあ、、と思っていまして、それを3つほど列挙させていただきます。


1 可能な限りブロックチェーンを意識せずにアプリケーション開発ができる

上述したようにブロックチェーンに関する最小限の知識はアプリケーション開発者が理解しているべきであると思いますが、一方でアプリケーション開発者がブロックチェーンを意識して開発すべきであるとは思いません。極端な言い方ですが、ブロックチェーンの存在すら知ることなく「開発者がデータベースを使ってアプリケーションを開発したら、データベースへのトランザクションは全て自動的にブロックチェーンに記録されて、改ざんが困難になったり、改ざんされてもすぐバレる」ようにできると理想的だと考えています。 要はブロックチェーンというインフラ部分をアプリケーション開発者の責任範囲から可能な限り分離したい、という意見です。


2 プライベートネットワークからもブロックチェーンに参加できる

ブロックチェーンネットワーク内に存在するノード間では台帳情報を分散管理します。したがって全てのノードがパブリックなネットワークに存在していて自由に同期をとることができることが理想ではあります。

一方、実際に企業のサービスがブロックチェーンネットワークに参加する場合、パブリックネットワークでの運用を前提とする時点で懸念が生じることがあります。比較対象として相応しいかどうかわかりませんが、例えば企業内で使っているデータベースをパブリッククラウド上に移行することですら問題になるのが現実で、そんな中で台帳情報を共有するブロックチェーンはパブリックでもオッケー、となるわけがありません。わがままにも聞こえますが「ネットワークはパブリックで構築してもいいが、うちが用意するノードはプライベートな状態で運用したい」のが本音となっているケースも散見します。

このわがままにも聞こえる懸念に対して「ブロックチェーンだからパブリックネットワークでないといけないのか?」という観点で考えるとどうでしょう? ある程度中身まで理解しているブロックチェーン技術者にとっては当然のようにパブリックネットワーク運用前提で考えてしまうかもしれませんが、ブロックチェーンを活用したいと考えている経営者からは厄介な制約に感じられてしまうものかもしれませんし、上述の「1 可能な限りブロックチェーンを意識せずにアプリケーション開発ができる」ようになると、アプリケーション開発者としてもネットワーク・トポロジーの制約を意識せずに開発できるようになるのが理想と考えています。 開発者と経営者、どちらもブロックチェーンを活用したいという想いは同じなのに、協力しあえないのはあまりに不幸な話と思えるため、それならなんらかの制約(※)が加わるにせよ、開発者側から歩み寄る形でプライベートネットワーク内からもブロックチェーンネットワークに参加して同期を取る方法を(技術的に不可能でないのだとしたら)検討する価値はあるのではないか、と考えています。そしてブロックチェーンそのものにそういった機能がはじめから付与されていれば歩み寄りも最小限で済むため理想的ではないかと思っています。

※具体的にはブロックチェーンをプライベート型やコンソーシアム型にせざるを得なくなる、といった制約です。


3 ブロックチェーンに全てのトランザクションが記録されるのであれば、例えばノードの引っ越し時やデータ破損時などにブロックチェーンからリストアできるはず

実は僕がブロックチェーンの存在や概要を知って、真っ先に思いついたブロックチェーンのメリットがこれでした。そもそも開発するサービス全体のうち、ブロックチェーンには何を(どの部分の情報を)記録するのかという問題があることは理解しています。例えばユーザー情報はブロックチェーンには記録せずに運用するけど、お金の動きに関わる部分だけはブロックチェーンに記録する、という考え方です。その意味ではサービス全体の情報がブロックチェーンに格納されるわけではないことは理解しておく必要があります。

でもブロックチェーンに記録されている情報に関してはそこに全トランザクションが記録されているわけだから、仮にデータ部分が破損したケースであっても、ブロックチェーンを最初からたどることで理論上は元通りに戻せるはず(つまりブロックチェーンからリストアできるはず)、だと思ったのでした。これができると運用者は楽だし、運用者が困っている面倒な部分の開発も楽になります。

ところが実際にそのようなリストア機能をもったブロックチェーンは自分の知る限りでは存在していません。もちろん実際のデータベースとの紐付け情報が必要だったり、どのサービスで使うデータに関するトランザクションなのか、といった情報も含めてブロックチェーンに記録されていないと実現できないことは理解できます。ブロックチェーン側のデータフォーマット仕様にも関わる話になってしまうと後からの変更や対応は難しいかもしれないなあ、といった事情もあるのでしょう。

この「ブロックチェーンからリストア」の実現について、色々面倒な制約があってできないのか、それともあまり重要視されていないのかわかりません。ただ自分のような開発者・運用者の視点からはこれがブロックチェーン側の機能の一部として実現できたらバックアップへの不安という心理的な負担を軽くことのできる開発プラットフォームが実現できて、それは開発にブロックチェーンを採用するメリットにもなり得るのではないか、、、と感じていたのでした。



そして、実はいま上述した3点を含む「開発者視点で考えた、使いやすいブロックチェーン」を実装中です。「開発者視点」と言いつつ、あくまで「自分の視点」ではありますけど(笑)。簡単に特徴を列挙するとこのような感じ:
  • 名前は HATOYA 、デフォルトの実行ポート番号は 4126
  • 表向きは REST API でデータベースやデータベース内のドキュメントを CRUD(Create/Read/Update/Delete 加えて Search も) 可能な JSON データベースシステム
  • データベースやドキュメントへの変更トランザクションは全て内蔵ブロックチェーンに自動記録
  • 複数ノードでブロックチェーンネットワークを作ると、参加するノードのブロックチェーン情報は1つにマージされる。つまりブロックチェーンネットワークで1つのブロックチェーンを共有管理する(データベース情報は共有しない)
  • ブロックチェーンネットワークにはプライベートネットワークからも参加可能(ただしその場合は push 型の同期処理となる)
  • サーバー引っ越し時や、データ破損時などにブロックチェーンからデータベースをリストアすることができる

なおスケーラビリティやアベイラビリティ、セキュリティといった非機能要件はまだ不十分な上、機能実装も現時点ではあくまで予定としているものではあるのですが、上述3点については実現にも目処が立っています:
20200724



現在は複数ノード環境での動作テストやデバッグ、このブロックチェーンを使ったツールやサンプルアプリなどを開発するのが中心の段階に至っています(つまり上述の3点の特徴含めてある程度は動く状態になっています):
2020072400


近い将来になんらかの形で公開予定です。本来アプリケーション開発とは利用者の観点を重要視するべきで、そこを開発者観点で作ることに異論を唱える人が存在するであろうことも理解しています。ただブロックチェーンってそもそもエンドユーザーが意識するべきものではないし、「ブロックチェーンの利用者」が誰なのかを考えると、それは「開発者」であるという視点があってもいいのかな、と。そう考えた時の答の1つになれればいいと思っています。あと深く勉強しなくてもすぐに実装が開始できるので、PoC 的な用途には向いているんじゃないかなあ、とも。

まだ動くものを公開していないので感想を持ちにくいということも理解していますが、こういった考え方で作られるブロックチェーンへの賛同や反論などありましたら、傷が浅いうちに(笑)ぜひ聞いておきたいです。コメント等でご意見いただければと。


(追記 公開しました)
HATOYA の Docker イメージを公開
HATOYA をマルチノード運用して、ブロックチェーン情報を分散管理する

マンホールマップの新バージョンを開発中です。暇を見つけて開発しているので、作業が進む時期も進まない時期もあったりしていましたが、とりあえず最低限の機能の実装はできつつあり、だんだんアプリケーションとしての形になってきました。まだ開発中なので、画面は変更する可能性もありますが、一部の機能をチラ見せしつつ予告編のような形で紹介します。


【新バージョンのテーマ】
ずばり「地図へのこだわり」と「技術の無駄遣い」です。現行版もおなじテーマで開発しており、マップのネーミング通り、地図機能にこだわりつつも、人工知能など比較的あたらしい技術要素を含んだりしていましたが、自分的にはそろそろ更に新しい技術テーマにも挑戦したいと考えていました。既存バージョンからの機能拡張では済まないような、基盤よりの新技術テーマにも挑戦したかったので、新バージョンという形でまったくゼロからのスクラッチ開発に取り組みました。

ちなみに開発言語は現行版が Java でしたが、新バージョンでは Node.js を使います。過去のマンホールマップのプラットフォームは Google App Engine だったり、J2SE だったりしましたが、開発言語はずっと Java を使っていました。その意味でも全くのゼロからの作り直しとなります。


【新バージョンの技術目標】
大きなものは以下の3つです:
- レスポンシブ対応を含めた新ユーザーインターフェース
- ハイブリッドクラウド化
- ブロックチェーン対応

「レスポンシブ対応」とは「PC ブラウザでもスマホブラウザでも、使っているデバイスの画面サイズにあわせてそれなりに使えるようにする」ことを意味しています。現行版も一応スマホ対応していますが、これはスマホ用の画面を PC 用とは別に作っていて、「スマホの人はこっちのページ」という対応でした。 この方法はメンテナンス時に互いの影響を少なくすることはできますが、2つのアプリを同時に開発するような側面もあって、開発する側としては面倒なものでもありました(現実的にはスマホからは使えない機能も多くあります)。 今回ははじめからレスポンシブ化を目指して開発を進めています。今の所 100% レスポンシブ対応できるようになるかどうかは微妙な予感がしていますが(苦笑)、数カ所を除いてなんとかがんばる予定です(←既に数箇所諦めています (^^;)

なお、現在開発中の画面の一部を公開します:
2019091601


2019091602


このカレンダー機能は実は現行版にも含まれていますが「隠し機能」的な位置づけでした。新バージョンではメニューから表示できるようにします:
2019091603


ゲーム要素にも新しい画面を取り入れる予定です。これは「アタック25」モードのページ:
2019091604



次に「ハイブリッドクラウド化」。まず現行版は私の自宅にある ThinkPad 内にアプリケーションサーバーとデータベースサーバーをインストールして動いています。要は(一部の機能は外部APIを使っていますが)ほとんどを自宅内のリソースで提供しています。クラウド時代をある意味で逆行しているものです(ただ個人開発者としては今でもこの形のアドバンテージはあると思っています)。これを一部パブリッククラウドに移して(一部は自宅に残して)コンテナ化するようなハイブリッドシステムを構築する方向で進めています。詳しいシステム構成は後述します。

最後の「ブロックチェーン対応」、これは改ざんが困難な仕組みである「ブロックチェーン」の技術を使い、利用者がマンホールマップにアップロードしたデータが正しいものである(改ざんされていないものである)ことを保証する仕組みを提供するつもりです。


【新バージョンのシステム構成(予定)】
上記の要素を取り入れ、マンホールマップのシステム構成は現行のこの形から、
2019091501


最終的にはこのような形に変更する予定です。今まで以上に公私混同具合が大きくなります(苦笑):
2019091502
(↑ブロックチェーン部分のネットワーク構成は面倒なのでかなり省略)


なお、データベースサーバーのみ独立して自宅サーバーで稼働し続ける形になりますが、これもいずれはパブリッククラウド化を考えています(当面自宅に残す理由はコストパフォーマンスの問題です。クラウドのデータベースサービスは高い・・)。


リリースを優先する意志もあるので、新バージョン公開段階でどこまで実装できているかはまだ不確定要素もありますが、この最終形を目指して開発を進めていくつもりです。なお UI はまだ変更の可能性が高いのですが、ハイブリッドクラウド化とブロックチェーン対応は試験的にはもう動いていて、技術的な目処はついています。


【新バージョンの公開予定】
新バージョンは現時点では令和元年11月2日に開催予定の「マンホールナイト11」にてお披露目予定です。可能であれば(そして大きなトラブルがなければ)この日に前後する形でシステム公開するつもりでいます。




 

ブロックチェーンの特徴の1つに「対改ざん性(一度格納した情報は、たとえ管理者権限を持っている人でも改ざんができない)」があります。その改ざんを困難にするためのマイニングと呼ばれる仕組みをゲーム感覚で学べるサービスを作ってみました:
https://dotnsf.github.io/nonce/
2019012800


少しだけ補足すると、これはブロックチェーンのマイニングの仕組みをシミュレーションするサービスです。実際のマイニングよりもかなりシンプルなマイニング作業を実際に行いながら、nonce やハッシュの仕組みを理解し、ブロックチェーンがなぜ改ざんできない仕組みと言われているのかを遊びながら理解することを目的に作りました。

なお、画面含めて今後変更が加わる可能性があることを申し上げておきます。以下は 2019/01/28 時点でのサービス内容をもとに紹介しています。


ではチュートリアルっぽく、このサービスを紹介していきます。まず上記 URL に(PC の)ウェブブラウザでアクセスすると、以下のような画面が表示されます(スマホのブラウザで見えないことはないと思いますが、情報量の多いページで横スクロールも併用する必要があり、現状スマホからはかなり使いにくいと思っています。どうしてもスマホで利用する場合は画面を横にしてお使いください):
2019012800


この画面は目的別のパーツに分かれています。ここからは各パーツの意味も含めて順に紹介していきます:
2019012801



#パーツ意味
1オプションの内容このゲームのパラメータ(この図では初期値のまま Mining Level = 1 と Goal = 3 となっている)
2ブロック番号何番目のブロックかを示す数値。
上図の場合、1なのでこれが最初のブロック
3body 値これからブロックチェーンに格納したいと思っている内容
4nonce 値整数値(詳しくは後述、最初は1)
5hash 値body と nonce(ブロック番号が2以上の場合は1つ前のブロックに格納されたハッシュ値も)から計算したハッシュ値
6格納されたブロック上記の nonce 値とハッシュ値の値が確定してブロックチェーンに格納されたブロック(この図では空なので、まだ格納していない)


↑の表や意味は後でもう一度確認するとして、まずは一度ゲームを進めてみます。

その前に1つだけ、ゲームスタート時点で hash 値のボタンが上図のように赤ではなく、稀に緑になっている可能性があります。その場合は(本当はゲーム的にはすごくラッキーなんですが・・・)hash 値のボタンが赤くなるまで何度か nonce 値の、初期状態で1と書かれているボタンを押してください(ボタンを押すたびに数値が増えていきますが、とりあえずは無視してください)。以下の説明では hash 値のボタンが赤で始まることを想定しています。このボタンの色の意味は後述します。


【ゲームの目的】
一定の数(オプション Goal の値、デフォルトでは3)のブロックをブロックチェーンに格納することが目的です。なるべく短い時間でブロックチェーンを完成させましょう。

ブロックはマイニングルールと呼ばれるある条件を満たせばブロックチェーンに格納することができます。マイニングルールを満たしていないブロックはブロックチェーンに格納できません。その条件を満たすよう、注意しながら操作し、条件を満たしたタイミングでブロックチェーンに格納します。これを一定の数繰り返すことでゲームをクリアできます。


【ゲームの遊び方】
ややこしい話は後回しにして、とりあえずゲームとしての遊び方を紹介します。すごくシンプルです。

nonce 値のボタンを1回押すごとに nonce 値は1つ増え、同時に hash 値が変化します:
2019012802


そして nonce 値を増やしていくと hash 値がある条件を満たした時にボタンは緑色になります
2019012803


そして hash 値のボタンを緑色の時に押すとブロックがブロックチェーンに格納されます。なお、赤の時に押してもマイニングルールを満たしていないためブロックは格納されません。以下のようなメッセージが表示されます。このままマイニングを続ける場合は「キャンセル」を選択してください:
2019012809


表の一番右の列にブロックが格納されます(通常、この時点でブロックは暗号化されて格納されますが、このサービスでは暗号化せずにそのままマイニングしてそのまま格納しています)。1つのブロックが格納されると、次のブロックが下段に表示されます。このブロックも同様に hash 値のボタンが緑になるまで nonce 値のボタンを押し、緑になったら押してブロックチェーンに格納します:
2019012804


これを繰り返して、ブロックをブロックチェーンに格納していきます:
2019012806


一定数(デフォルトでは3)のブロックがブロックチェーンに格納できたらゲームクリアとなります。ゲームクリア時にゲーム開始からクリアまでかかった時間と合わせてゲームクリアの情報が表示されます:
2019012807


なお途中で赤のボタンを押して、「自動計算しますか?」の問いに「OK」を選択するとギブアップしたことになります。ギブアップすると hash 値のボタンを緑にすることはできますが、クリア時に「途中で自動計算を使いました」というメッセージが表示されます):
2019012808


また hash 値のボタンがいちど緑になった後、そのボタンを押す前に nonce 値のボタンを押してしまった場合、nonce 値を戻すことはできません。そのまま更に nonce 値を1つずつ増やして、再度マイニングルールを満たす nonce 値を探す必要があります。


と、こんな感じのゲーム感覚でマイニングを体験するサイトです。サービスそのものは Github Pages を使って、(CDN 以外は)1枚の HTML で作りました:
https://github.com/dotnsf/nonce/tree/gh-pages


本当は別の機会にちゃんと説明したいのですが、長くなりそうなのでシンプルに「なぜブロックチェーンに記録されたデータの改ざんが難しいのか」を説明しておきます。要するに記録したデータに対して、マイニングというそれなりに CPU パワーを必要とする処理を施した上でのチェックビットのような仕組みを加え、かつそのマイニング結果を前後のブロックに持たせてつなげる(「ハッシュポインタ」)ことが対改ざん性としての鍵になっているのです(更に実際にはデータに暗号化を加えることもあります)。 この仕組みによって、記録したデータを単純に(例えば振込額を増やすとか、振込先を変えるとか)書き換えるとその hash 値が変わり、記録内容とは異なってしまうので、書き換えたことがすぐにバレてしまいます。書き換えをごまかすためには hash 値を再計算する必要があるのですが、マイニングルールを満たす条件があるため、ここでそれなりの処理能力が必要になります。更に1つのブロックのマイニングが完了して書き換えに成功したとしても、次のブロックは1つ前の(つまり今書き換えたばかりの)ブロックの hash 値を持っているので、やはりここでも書き換えがバレてしまいます。これをごまかすためにはこのブロックの hash も書き換える必要があり、ここを書き換えると再度マイニングが必要になり・・・ とブロックチェーンの最後のブロックまで全てマイニングし直して書き換える必要がでてきてしまいます。マイニングルールが1桁とか2桁程度であればまだ処理できるかもしれませんが、実際の仮想通貨取引では 10 桁以上のマイニングルールが課せられており、全てのブロックを書き換えることは事実上不可能とされているのでした。



このサービスではマイニングが完了したブロックを仮想的なブロックチェーンに格納していますが、近い将来にこのページの HTML を改良して認証を加え、自動計算なしでクリアしたら本当にその記録をブロックチェーンに格納する、というものをリリースする予定です。お楽しみに。


このページのトップヘ