ICP × Deno - Agent of Cloud 3.0
はじめに
分散型クラウド『Internet Computer (IC)』上に配置されたCanisterに対して、Denoからアクセスするエージェントのサンプルを解説します。
近年Denoのnpmモジュール互換性がだいぶ高くなってきていて、先日ついにDeno2がリリースされましたので、改めてICP関連のnpmパッケージが動作するようになったか試したところ見事に動作しました。
Node.js使えばいいじゃんと言われてしまえばその通りなのですが、私個人が感じるDenoの魅力の一つは、Node.jsのようなパッケージ管理をしなくても、小さい規模のプログラムならさっと書いて簡単に動かせるという点です。
それなりのプログラムをつくる場合にはdeno.json
やpackage.json
でパッケージ管理する方がよいですが、外部パッケージを使用するTypeScriptのプログラムをその場で書いて実行できるシンプルさと快適さは、まさに『10 Things I Regret About Node.js』に通じるものでしょう。
Denoのランタイム実行環境は明示的にネットワークやファイルアクセス許可を求める、という点も安心して利用できる素晴らしい仕組みだと考えています。
参考情報
サンプル概要
呼び出し元のPrincipalを返すだけの単純なCanisterをRustで作成します。
作成したCanisterに対して、TypeScriptで書いたプログラムからアクセスします。
Canister開発
Canister Development Kit (CDK)を用いてCanisterを開発します。
1. Canisterプロジェクト作成
dfx new
コマンドでCanisterを作成します。
(1) Backend language
開発に使用するプログラミング言語は本記事ではRustを選択します。
$ dfx new whoami
? Select a backend language: ›
Motoko
❯ Rust
Python (Kybra)
Typescript (Azle)
(2) Frontend framework
今回のサンプルではBackendのみ用意しますのでFrontendはどれを選択しても構いません。
? Select a frontend framework: ›
SvelteKit
React
Vue
Vanilla JS
No JS template
❯ None
(3) Extra Features
今回のサンプルでは不要です。
? Add extra features (space to select, enter to confirm) ›
⬚ Internet Identity
⬚ Bitcoin (Regtest)
2. Canisterプログラム修正
whoami
という公開インタフェースを定義し、それに合わせてRustプログラムを修正します。
service : {
"whoami": () -> (text) query;
}
#[ic_cdk::query]
fn whoami() -> String {
return ic_cdk::caller().to_string();
}
3. Local Canister実行環境への配備
$ cd whoami
$ dfx start --background --clean
$ dfx deploy
4. dfxコマンドによる動作確認
まずはdfx canister call
コマンドでCanisterの動作を確認してみます。
--identity
オプションで呼び出し元を切り替えることができます。
$ dfx canister call --identity anonymous whoami_backend whoami
("2vxsx-fae")
※2vxsx-faeは匿名を表すPrincipalです。
Denoエージェント開発
ローカルの開発環境にCanisterを配置しましたので、まずはそこにアクセスするエージェントを書いてみます。
1. Clientプログラム作成
プログラム例を記載します。
importしたActor
、HttpAgent
、IDL
の関係性や、Canisterを呼び出すためのコードなど分かりづらいですが、今はあまり気にすることはないでしょう。
1つのソースファイルに収めるようためにあえて記述していますが、dfx generate
コマンドを使えば、公開インターフェースに対応した呼び出し部分のJavaScriptコードは生成できます。
import { Actor, HttpAgent } from 'npm:@dfinity/agent';
import type { IDL } from 'npm:@dfinity/candid';
// Config
const host = "http://127.0.0.1:4943"; //for Local; for IC:"https://icp-api.io"
const shouldFetchRootKey = true; // Fetch root key for certificate validation during development
const canisterId = "bkyz2-fmaaa-aaaaa-qaaaq-cai"; // for Local
// Interface
export const idlFactory: IDL.InterfaceFactory = ({ IDL }) => {
return IDL.Service({
'whoami' : IDL.Func([], [IDL.Text], ['query']),
});
};
const agent = await HttpAgent.create({
host,
shouldFetchRootKey
});
const backend = Actor.createActor(idlFactory, {
agent,
canisterId,
});
const result = await backend.whoami();
console.log(result);
2. 匿名Identityでのアクセス
まずは匿名Identityで実行してみます。
$ deno run --allow-net=127.0.0.1 main.ts
2vxsx-fae
3. 特定Identityでのアクセス
以下の3箇所を修正することで、secp256k1 ECDSA秘密鍵に紐づいたIdentityでアクセスできます。
(1) Secp256k1KeyIdentityを取り込む
import { Secp256k1KeyIdentity } from 'npm:@dfinity/identity-secp256k1';
(2) Identityの作成
たとえば、Seed phraseからSecp256k1秘密鍵を復元するには、Secp256k1KeyIdentity
モジュールのfromSeedPhrase()
関数を利用するとよいでしょう。
その他、pemファイルから復元するなどの関数も用意されているようです。
const seed = "test test test test test test test test test test test test";
const identity = Secp256k1KeyIdentity.fromSeedPhrase(seed);
※Secp256k1のSeed phraseはdfx identity new <名前>
を実行することでも生成できます。取り扱いにはくれぐれもご注意ください。
(3) Identityを指定したAgent作成
const agent = await HttpAgent.create({
host,
shouldFetchRootKey,
identity
});
(4) プログラム実行
$ deno run --allow-net=127.0.0.1 main.ts
rwbxt-jvr66-qvpbz-2kbh3-u226q-w6djk-b45cp-66ewo-tpvng-thbkh-wae
Playgroundの利用
Local Canisterではなく、実際のInternet Computerにアクセスしたい場合、本番環境にCanisterに配備するとコストがかかります。
そのため、20分間だけCanisterを配備して試用できるPlayground環境を使うとよいでしょう。
1. Playgroundへの配置
dfx deploy
コマンドに--playgound
オプションを指定します。配備する際に--identity XXXXX
でdefault
以外のIdentityを指定するとよいでしょう。
$ dfx deploy --playground --identity XXXXX
Deploying all canisters.
Reserving canisters in playground...
Reserved canister 'whoami_backend' with id 4k2wq-cqaaa-aaaab-qac7q-cai with the playground.
Building canisters...
︙
Installing canisters...
Installing code for canister whoami_backend, with canister ID 4k2wq-cqaaa-aaaab-qac7q-cai
Something is installed in canister 4k2wq-cqaaa-aaaab-qac7q-cai. Assuming new code is installed.
Deployed canisters.
URLs:
Backend canister via Candid interface:
whoami_backend: https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.icp0.io/?id=4k2wq-cqaaa-aaaab-qac7q-cai
2. Playgroundへのアクセス
プログラム中のhost
やCanisterId
などを修正します。
canisterId
には、dfx deploy --playground
を実行した際に出力された値を指定します。
const host = "https://icp-api.io"
const shouldFetchRootKey = false;
const canisterId = "4k2wq-cqaaa-aaaab-qac7q-cai"; // CanisterId in Playground
deno run
コマンドを実行する際、リクエスト先が変わりますので--allow-net
で許可するホストを変更します。
$ deno run --allow-net=icp-api.io main.ts
rwbxt-jvr66-qvpbz-2kbh3-u226q-w6djk-b45cp-66ewo-tpvng-thbkh-wae
将来の展望
どうしてDenoを使ってInternet ComputerのCanisterにアクセスする記事を書いたのか。
『ICP × Deno』という組み合わせは単なる自分の好みにすぎません。
Denoの視点から見ればDeno Deployをはじめ便利にWebサービスを配置できる環境がありますので、技術的にまだ成熟しているとは言えない分散型クラウドを利用するケースはまだ多くないと思います。
また、Internet Computerの視点で考えれば、Node.jsやRust、Motokoを利用するのが一般的かと思いますので、あまりDenoを利用することはないのかもしれません。
今後ますますAzure、Google Cloud、AWSをはじめ商用Cloudが活用されていくのは間違いないでしょう。その一方で、この流れとうまく棲み分けをしながら、特定ベンダーには依存せずオープンな仕組みで実現された分散型コンピューティングや分散型ストレージ、いわゆる『Cloud 3.0』が適材適所で使われていくと考えています。
Cloud 3.0を謳っているInternet Computerには、『Chain-Key Cryptography』と呼ばれる暗号技術によって実現する『しきい値署名』、BTCやEthereumなど異なるBlockchain間を結ぶ『Chain Fusion』、『VetKD (Verifiably Encrypted Threshold Key Derivation)』によるEnd-to-end encryptionの実現など様々な特徴があります。
今後Internet Computer上に自分で開発したCanisterや一般公に開されたCanisterなどが数多く配置されていく未来を予想すると、こうしたCanisterを組み合わせたサービスを実現するために、CLIからCanisterにアクセスしたり、Internet Computerの外部にあるシステムからCanisterにアクセスするケースも増えてくるでしょう。
今回のサンプルで感じたのは、現状Canisterを呼び出すだけなのによく分からないコードをたくさん書く必要がある点です。このような泥臭い部分はモジュール内に隠蔽化し、JSRのようなリポジトリから、importしてすぐ使えるようになれば便利になっていくでしょう。
Discussion