♾️

【Internet Computer】100%オンチェーンの Web アプリを作る

2025/01/26に公開

Internet Computer(ICP) とは、世界のソフトウェアをスマートコントラクトに置き換えることをビジョンに掲げるパブリックブロックチェーンです。
Ethereum など他のチェーンよりも高速など様々な特徴もありますが、今回は特徴の一つであるウェブスタックをホストできるという部分に注目します。これを使うとブロックチェーンのみで、つまり100%オンチェーンで Web アプリをホストすることが可能です。

本記事では最初に実際に ICP で Web サイトをホストする手順を紹介し、その後に各手順で具体的にどのようなことが行われているかを確認していきます。

100%オンチェーン Web サイトの構築

サンプルコードを利用して、ICP 上で React アプリケーションを動かしてみましょう。

開発環境整備とデプロイの手順

1.ツールのインストール

dfx
開発には dfx と呼ばれる SDK を利用するため、まずはこれをインストールします。

sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)"

上記を実行後、こちらのコマンドを実行して数字が表示されれば正常にインストールが環境しています。

dfx --version

Node.js
Node.js も開発に必要です。本記事ではバージョン 20 で開発を行いました。

2.プロジェクトの作成

先ほどインストールした dfx を利用してプロジェクトの雛形を作成します。以下のコマンドを実行すると対話的に設定を進めることができます。

dfx new hello

本記事ではバックエンドの言語には Rust、フロントエンドのフレームワークには React を選択しました。
※ Rust を選択した場合は Rust 開発に必要なツールのインストールも必要です。

すべて完了するとロゴが表示されます。鮮やかですね。

3.デプロイ

今回はバックエンド、フロントエンドのソースコードはそのままでデプロイをしていきます。
ますは ICP ネットワークをローカル環境で走らせます。

dfx start --background

その後デプロイを行います.

dfx deploy

4.アプリケーション起動

npm start

でアプリケーションを立ち上げ、ブラウザで http://localhost:3000/ にアクセスするとこのような画面が表示されます。
フォームに好きな名前を入れてボタンを押すと挨拶メッセージが表示されます。

アプリのソースコード

バックエンド

バックエンドのソースコードは /hello/src/hello_backend/src/lib.rs に記述されています。先ほど確認したようにパラメータとして受け取った文字列を定型文に埋め込み返すだけの簡単な処理です。

lib.rs
#[ic_cdk::query]
fn greet(name: String) -> String {
    format!("Hello, {}!", name)
}

フロントエンド

フロントエンドのソースコードは /hello/src/hello_frontend/ にあります。hell_backend のインポート以外は通常の React のコードと違いはありません。

App.js
import { useState } from 'react';
import { hello_backend } from 'declarations/hello_backend';

function App() {
  const [greeting, setGreeting] = useState('');

  function handleSubmit(event) {
    event.preventDefault();
    const name = event.target.elements.name.value;
    hello_backend.greet(name).then((greeting) => {
      setGreeting(greeting);
    });
    return false;
  }

  return (
    <main>
      <img src="/logo2.svg" alt="DFINITY logo" />
      <br />
      <br />
      <form action="#" onSubmit={handleSubmit}>
        <label htmlFor="name">Enter your name: &nbsp;</label>
        <input id="name" alt="Name" type="text" />
        <button type="submit">Click Me!</button>
      </form>
      <section id="greeting">{greeting}</section>
    </main>
  );
}

export default App;

ICP に登場する概念

ここからは先ほど構築した Web サイトは ICP 上でどのように動いているのかを確認していきますが、その前にまずは ICP 特有の概念から確認します。

principal

ICP ネットワーク上でユーザーや後述の canister を識別するための一意の ID です。(principal id と書く場合もあります。)Ethereum のウォレットアドレスやコントラクトアドレスに近い概念です。principal は canister をデプロイしたり、canister とやりとりをする際などに必要となります。
次のコマンドで現在のユーザーの principal を確認することができます。

dfx identity get-principal

canister の principal id は次のコマンドで確認できます。これは先ほどの章で構築した canister の principal id を確認するコマンドです。

dfx canister id hello_backend

canister

canister は Ethereum で言うところのスマートコントラクトに近いものです。canister を構成する機能
canister 上では WebAssembly(wasm) を動かすことができます。canister 自体は wasm の入れ物のようなもので、ユーザーが動かしたい wasm プログラムを canister にインストールすることで、canister をスマートコントラクトのように機能させることができます。
ストレージとしては wasm memory と stable memory の2種類があり、stable memory の方が永続性が高い分操作コストもやや高めです。canister も cycle を保有することが可能です。canister の cycle の利用や wasm プログラムのインストールなどは controllers に登録された principal のみが可能となります。

ウォレット

ICP のウォレットは canister の一種です。ウォレット自体は canister であり、ユーザーが管理者としてウォレット(canister)の conroller として登録されています。
現在のユーザーのウォレットID、つまり canister の principal は次のコマンドで取得することができます。

dfx identity get-wallet

ウォレットの cycle 残高は次のコマンドで取得することができます。

dfx wallet balance

dfx deploy で行われていること

先ほど実行した dfx deploy が行なっていることを具体的に確認してきましょう。dfx deploy は次の3つのコマンドで行う処理をまとめて行なっています。

dfx canister create --all
dfx build
dfx canister install --all

1つずつ確認していきましょう。

canister の作成(dfx canister create --all)

まず、dfx canister create --all で canister を作成しています。先ほど紹介したように canister は wasm のプログラムを入れるための箱のようなもので、このコマンドを実行すると、ネットワークに canister id を登録します。登録する canister id は dfx.json に依存します。先ほどの例であれば以下のようになっているため、hello_backend と hello_frontend の2つの canister id を登録します。

dfx.json
{
  "canisters": {
    "hello_backend": {
      "candid": "src/hello_backend/hello_backend.did",
      "package": "hello_backend",
      "type": "rust"
    },
    "hello_frontend": {
      "dependencies": [
        "hello_backend"
      ],
      "source": [
        "src/hello_frontend/dist"
      ],
      "type": "assets",
      "workspace": "hello_frontend"
    }
  },
  "defaults": {
    "build": {
      "args": "",
      "packtool": ""
    }
  },
  "output_env_file": ".env",
  "version": 1
}

wasm へのコンパイル(dfx build)

続いて、canister 上で動かすプログラムを生成します。dfx build を実行すると、wasm モジュールを作成します。作成の対象となるのは dfx.json で canisters キー以下に指定されているものです。

canister に wasm をインストール(dfx canister install --all)

最後に wasm モジュールを作成した canister にインストールします。今回の場合は Rust ファイルをコンパイルした wasm モジュールがhello_backend canister にインストールされ、React で構築した一連の js ファイルが wasm モジュールにコンパイルされ hello_frontend canister にインストールされています。


本記事では ICP で Web アプリをホストする方法と、その仕組みついて紹介しました。現在の ICP でホストできるのは静的なウェブアセットのみですが、ブロックチェーンだけで Web アプリを構築できるのはほかのメジャーなブロックチェーンにはない機能で面白いと思い記事にしました。
ICP はアウトバウンドの HTTP 通信をネイティブでサポートしていたりと、他にも面白い機能があります。(Ethereum など多くのブロックチェーンはアウトバウンドの HTTP 通信はネイティブにはサポートしておらず、オラクルなどオフチェーンのシステムを利用して実現されています。)
ICP に興味を持たれた方はぜひこちらの URL から公式ドキュメントを覗いてみてください。

https://internetcomputer.org/

Discussion