インターネットコンピューター(Dfinity)の概要と、その技術を触ってみた(フロントエンドVue.js)
はじめに
2021年5月7日、インターネットコンピューター(Dfinity)のローンチイベントが開催されました。
イベント開始の日本時間は2021年5月8日午前2時でしたので、筆者も序盤だけ少し参加して、もしかすると「この瞬間が時代の転換点になるかもしれない」と少し感銘を受けて寝ました。ちなみにこのnoteのトップ画像は、ローンチイベントで何個か入手した画像の内の一つです。
前回までのnoteでは、Solanaのブロックチェーン技術を使うために簡単なプログラムを実行してdevnetにデプロイしましたが、今回のnoteでは、同様にインターネットコンピューター(Dfinity)のブロックチェーン技術を使うために簡単なプログラムを書いていきたいと思います。
インターネットコンピューターとは
インターネットコンピューターは、ソフトウェアを実行するための分散型のプラットフォームで、Amazon Web Services(AWS)、Google Cloud Platform(GCP)、Microsoft Azureなどのパブリッククラウドプロバイダー、またはプライベートによって内部的に管理されるプライベートクラウドをブロックチェーンに置き換えようとするプロジェクトです。
例えば、AWSもサーバーは世界中に分散していますが、Amazonという単一の企業が運営しています。昨今Etheriumで流行したDeFi取引や、NFTのようなDappsはブロックチェーン上で非中央集権的に動作しますが、それを展開するフロントエンドアプリケーションが従来どおりAWS等で運用されていると、完全に分散しているとは言えないよねということです。
また、インターネットコンピューターは単一の企業が運営する、物理的な場所に存在する物理的なハードウェアではなく、世界中の独立したデータセンターが提供するコンピューティングリソースを組み合わせてインターネット上で動き続ける分散型プラットフォームであるため、誰にも改ざんされず、止まることなく動き続けます。
そのため、開発者は下記のような事項を気にすることなく、アプリケーションの開発に集中することがでいます。
・物理または仮想ネットワーク構成要件
・負荷分散サービス
・ファイアウォール、ネットワークトポロジ、またはポート管理
・データベースの構成と保守
・ストレージボリュームとデバイス
そして、Dfinityは、アプリケーション開発者が従来のAWSのようなプラットフォームを使用せず、またデータベース等を使わずにDappsを構築することができるプラットフォームを目指しています。
また、Dfinityは、2020年1月にLinkedInの分散型バージョンであるLinkedUp、2020年6月にはTikTokの分散型バージョンであるCanCanを発表しています。ソースコードも下記のリンク先から閲覧することが出来ます。
インターネットコンピューターの今後の展望について、下記リンク先のロードマップから抜粋すると以下のとおりです。
5年後
テクノロジーに関心のある全ての人がインターネットコンピューターについて耳にし、その性能と目的が広く理解されるようになり、多くの起業家や開発チームが従来のITを使用するのではなくインターネットコンピューター上にサービスを構築することを選択する。
10年後
インターネットコンピューターでスマートコントラクトを作成せずに卒業するコンピューターサイエンスの学生はほとんどいない。
20年後
グローバル社会のインフラストラクチャ、システム、及び、サービスの多くは、オープンで止められない、改ざん不能のインターネットコンピューターブロックチェーンネットワーク上で実行される。
インターネットコンピューターが普及すれば、Webアプリケーションを公開するのに誰かが管理するサーバーを使うことなく、また、開発者はインフラのことを考えずにアプリケーション開発に集中できるため、大幅なコスト削減になり、また、完全に分散化されたWebサービスが実現すれば、プラットフォームに対して利用料を支払うという概念が無くなるかもしれません。
もうこれだけでわくわくしているのは筆者だけでしょうか。今回のnoteではそんなインターネットコンピューターの入り口だけ、少し触ってみたいと思います。
インターネットコンピューターの技術
もう少し踏み込んで、技術的な説明をします。下記は、基本的には、公式のドキュメントを参考に記載しています。
まず、インターネットコンピューターのネットワークを構成する各サブネットワークは、インターネットコンピュータプロトコルのソフトウェアを実行するいくつかの個別のマシン(ノードと呼ばれるコンピュータ)で構成されています。各ノードで実行されるソフトウェアは、サブネットワーク内のすべてのノード間で状態と計算を複製することから、「replica」と呼ばれます。
前述したように、インターネットコンピューターは、単一の民間企業によって運営されていません。その代わりに、分散型ガバナンスシステム(「Network Nervous System(NNS)」と呼ばれます。)を介して、複数のコンピューターを1つの仮想マシンのように動作します。
他方、この仮想マシンを構成する個々のノードとなるコンピューターは、一つのデータセンターではなく、様々な場所の世界中のデータセンターのサブネットワークに組み込まれて分散されます。この世界中のデータセンターは、インターネットコンピューターに貢献することに対して報酬を受け取ることができます。
開発者がインターネットコンピューターで実行されるアプリケーションのソースコードを作成するときは、ソースコードをWebAssemblyモジュールにコンパイルします。コンパイルしたWebAssemblyモジュールをインターネットコンピューターの「replica」にデプロイすると、プログラムは「canister」と呼ばれる概念的な計算ユニット内で実行されます。
また、WebAssemblyモジュールがデプロイされると、ブラウザーなどのフロントエンドから「canister」のエントリーポイントを呼び出してアクセスすることができます。「canister」のエントリーポイントを呼び出す方法には2種類あります。
Query calls
ユーザーが「canister」の現在の状態を照会したり、「canister」の状態を変更せずに操作する関数を呼び出したりできるようにします。
非同期処理ではないため、すぐにレスポンスが返ります。
結果を検証するためのコンセンサスを必要としない任意のノードに対して呼び出すことができますが、単一ノードからの応答は信頼できない可能性があるため、セキュリティとパフォーマンスの間にはトレードオフの関係があります。
基本的に、プログラムは「Query calls」を使用して読み取り専用の操作を実行します。
Update calls
ユーザーが「canister」の状態を変更し、変更を永続化できるようにします。
この処理は、コンセンサスを通過する必要があるため、「canister」の状態の変更には時間がかかる場合があることから、非同期でレスポンスが返ります。
サブネット内の「replica」の3分の2が結果に同意するコンセンサスが必要となるため、セキュリティとパフォーマンスの間にはトレードオフの関係があります。
呼び出された「canister」は、他の「canister」によって公開されている関数を呼び出すことができます
インターネットコンピューターのプラットフォームは、アプリケーションを設計、構築、デプロイを簡素化するためのフレームワークを提供していますが、このフレームワークで重要なのは、新しい汎用プログラミング言語の「Motoko」です。「Motoko」は、JavaScriptライクな言語で、JavaScriptを知っている人なら誰でもすぐに習得できるモダンで使いやすい言語として開発されました。
「Motoko」は、アクターベースのプログラミングモデルを使用しており、各「canister」には1つのアクターオブジェクトのコンパイル済みコードが含まれています。各「canister」にはインターフェースの説明やフロントエンドのassetsが含まれており、複数の「canister」を含むプロジェクトを作成することができますが、各「canister」に含めることができるアクターは1つだけです。
また、「Motoko」のコードをコンパイルすると、WebAssemblyモジュールになります。そのため、「Motoko」を使用すると、高水準の言語を使用して安全なアプリケーションを提供しながら、軽量なWebAssemblyモジュールにコンパイルすることができます。
Dfinityの環境構築
それでは、実際にDfinityのインターネットコンピューターを触ってみたいと思います。
今回のnoteでは、バックエンドがDfinityのインターネットコンピューター、フロントエンドはVue.jsを使って、アプリケーションを実行します。前回noteのSolanaの時もそうでしたが、筆者は基本的にローカルPCの環境を汚したくないため、Dockerで環境をつくります。
それでは、デスクトップに好きな名前でフォルダを作り、以下のようにフォルダとファイルを作成します。
$ tree
.
├── Dockerfile
├── README.md
├── docker-compose.yml
└── docker-sync.yml
0 directories, 4 files
次に、それぞれのファイルに以下のように記載します。
docker-compose.yml
version: '3'
volumes:
icp-vue-dapp-sync:
external: true
services:
dapp:
# Dockerfileの場所
build: .
# ホストOSとコンテナ内でソースコードを共有
volumes:
- icp-vue-dapp-sync:/usr/src/app
environment:
- HOST=0.0.0.0
# コンテナ内部の3000を外部から8080でアクセス
ports:
- "8080:3000"
tty: true
docker-sync.yml
version: '2'
syncs:
icp-vue-dapp-sync:
src: '.'
Dockerfile
FROM node:14
RUN mkdir -p /usr/src/app
ENV HOME=/usr/src/app
WORKDIR $HOME
ADD . $HOME
WORKDIR $HOME
それでは、Dockerをビルドして、Docker内に入ります。
$ docker volume create --name=icp-vue-dapp-sync
$ docker-sync start
$ docker-compose up -d --build
$ docker-compose exec dapp /bin/bash
中に入ったら、下記のコマンドで、DfinityのSDKをインストールします。
$ sh -ci "$(curl -fsSL https://sdk.dfinity.org/install.sh)"
インストールの途中で下記のように聞かれますので、 `y` と入力してEnterを押します。
Do you agree and wish to install the DFINITY Canister SDK [y/N]?
y
info: Version found: 0.6.26
info: Creating uninstall script in ~/.cache/dfinity
info: uninstall path=/usr/src/app/.cache/dfinity/uninstall.sh
info: Checking for latest release...
Will install in: /usr/local/bin
info: Installed /usr/local/bin/dfx
すると、DfinityのSDKがインストールできました。念の為にバージョンを確認します。
$ dfx --version
dfx 0.6.26
Dfinityのプロジェクトを作成する
それでは、早速プロジェクトを作成します。
$ dfx new hello
上記のコマンドを実行すると、helloというプロジェクトが作成されます。新たにhelloというディレクトリが作成され、下記のようなディレクトリ構成になっているかと思います。
$ cd hello
$ tree -L 3
.
├── README.md
├── dfx.json
├── node_modules
│ ├── ...
│ ...
├── package-lock.json
├── package.json
├── src
│ ├── hello
│ │ └── main.mo
│ └── hello_assets
│ ├── assets
│ └── public
└── webpack.config.js
srcディレクトリ以下に、アプリケーションに必要なソースコードを作成します。dfx.jsonファイルが、プロジェクトのオプションを設定するための構成ファイルです。
ここで、後ほどローカルネットワークを起動しますが、Dockerを使っている関係で、少し設定を変えておきます。dfx.jsonというファイルが作成されたかと思いますので、以下のようにコードを修正します。
dfx.json
{
...(省略)
"networks": {
"local": {
// "bind": "127.0.0.1:8000" // コメントアウト
"bind": "0.0.0.0:3000", // 追加
"type": "ephemeral"
}
},
"version": 1
}
Vue.jsの環境を構築する
次に、Vue.jsのセットアップをします。下記のコマンドで必要なパッケージをインストールします。Vue.jsについては、下記の公式ドキュメントや書籍、他の記事が充実しているため、このnoteでは特に解説しません。
$ npm i vue vue-loader vue-template-compiler
そして、hello_assets/public/index.jsファイルを以下のように修正します。
hello_assets/public/index.js
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: (h) => h(App)
}).$mount('#app')
また、hello_assets/publicディレクトリにApp.vueファイルを作成して、以下のように記載します。
hello_assets/public/App.vue
<template>
<div id="app">
<div>{{ greeting || 'インターネットコンピューターからのメッセージを読込中...' }}</div>
</div>
</template>
<script>
import hello from 'ic:canisters/hello';
export default {
data: () => {
return {
greeting: '',
};
},
created() {
hello.greet(window.prompt("名前を入力してください:")).then(greeting => {
this.greeting = greeting;
});
}
}
</script>
そして、webpack.config.jsファイルに下記を追記します。
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader"); // これを追加
const dfxJson = require("./dfx.json");
...(省略)
function generateWebpackConfigForCanister(name, info) {
...(省略)
plugins: [
new VueLoaderPlugin() // これを追加
],
};
}
...(省略)
これで、Vue.jsを使用する準備が整いました。
ローカルネットワークにアプリケーションをデプロイする
それでは、インターネットコンピューターのローカルネットワークを起動します。新らしいターミナルの画面をもう1画面開いて、Dockerに入ります。(2つのターミナルでDockerに入ります。)
新たに開いたターミナル画面のDocker内で、下記のコマンドを実行します。
$ cd hello
$ dfx start
そして、ブラウザで「http://localhost:8080/」にアクセスして、「Could not find the canister ID to use. Please provide one in the query parameters.」と表示されれば、起動に成功しています。
次に、ローカルネットワークにアプリケーションをデプロイします。まず、ローカルネットワークを起動した状態で下記のコマンドを実行します。
$ dfx canister create --all
上記のコマンドを実行すると、プロジェクト内の「canister」に一意のIDを登録します。
今回のhelloプロジェクトのsrcディレクトリには、 `hello` ディレクトリと `hello_assets` ディレクトリがあり、 `hello` ディレクトリはメインプログラム用、`hello_assets` ディレクトリはフロントエンド用のディレクトリで、それぞれ「canister」の名前になります。「canister」は、dfx.jsonファイルのcanisterで定義されています。
dfx.json
{
"canisters": {
"hello": {
"main": "src/hello/main.mo",
"type": "motoko"
},
"hello_assets": {
"dependencies": [
"hello"
],
"frontend": {
"entrypoint": "src/hello_assets/public/index.js"
},
"source": [
"src/hello_assets/assets",
"dist/hello_assets/"
],
"type": "assets"
}
},
...(省略)
}
また、ローカルネットワークの「canister」のIDは、.dfx/local/canister_ids.jsonファイルに保存されます。
.dfx/local/canister_ids.json
{
"hello": {
"local": "rrkah-fqaaa-aaaaa-aaaaq-cai"
},
"hello_assets": {
"local": "ryjl3-tyaaa-aaaaa-aaaba-cai"
}
}
なお、いちいち上記のファイルを確認しなくとも、下記のコマンドで「canister」のIDを確認することができます。
$ dfx canister id hello_assets
ryjl3-tyaaa-aaaaa-aaaba-cai
次に、Vue.jsのプログラムを、WebAssemblyモジュールにコンパイルするために、下記のコマンドを実行します。
$ dfx build
上記のコマンドを実行することで、.dfx/local/canistersディレクトリにWebAssemblyモジュールと関連するアプリケーションファイルが生成されます。
ここまでできたら、最後にローカルネットワークにデプロイを行います。デプロイを行うためには、下記のコマンドを実行します。
$ dfx canister install --all
なお、2回目以降デプロイする時は、以下のようにコマンドを実行することで、ファイルをアップグレードすることができます。
$ dfx build hello_assets
$ dfx canister install hello_assets --mode upgrade
ローカルネットワークにアプリケーションをデプロイする手順はこれだけです。次に、登録したアプリケーションをブラウザで確認します。ブラウザでフロントエンドにアクセスするためには、ブラウザで「http://localhost:8080/?canisterId=<CANISTER_ID>」にアクセスします。
「canister」のIDの確認方法は先程記載した通りです。ブラウザで「http://localhost:8080/?canisterId=ryjl3-tyaaa-aaaaa-aaaba-cai」にアクセスすると、フロントエンドにアクセスできます。
「名前を入力してください」という入力欄が表示され、適当な文字列を送信したら、ブラウザの画面が「インターネットコンピューターからのメッセージを読込中...」と表示され、その後表示が入力した文字列に変わったのではないでしょうか。
まとめ
いかがだったでしょうか。今回は、Dfinityのインターネットコンピューターを簡単に触ってみました。Webアプリケーションをブロックチェーンでデプロイできるようになるという感覚が新鮮でした。
近いうちに、Dfinityが公開しているTikTokやLinkedInのアプリケーションも少し触ってみたいと思っているので、次回以降のnoteで書きたいと思います。
この記事が気に入ったらサポートをしてみませんか?