JS Hooksの第一歩
XRPL Hooks
XRPL Hooksは、分散型のL1ブロックチェーンXRP LedgerのXRP Ledgerプロトコルで利用可能なスマートコントラクト機能です。Hooks機能は現時点ではXRPLと同じXRPLプロトコルベースのネットワークであるXahau Networkでのみ利用可能です。
XRP LedgerメインネットへのHooks機能統合へも取り組まれており、Hooks機能はXRPLのスマートコントラクト機能として利用可能となることが期待されています。
JS Hooks
これまでHooksはWASMベースで記述されており、ほとんどがCで記述されていました。しかし、C言語は非常に効率的である一方で、多くの開発者にとっては学習コストが高い言語であるため、開発者がHooksを利用する際には学習コストが高いという問題がありました。
しかし、Hooks機能の次の一歩として、JavaScriptエンジンQuickJSを利用しJavaScript/TypeScriptでHooksを記述可能にする機能の開発が進められています。
これはAssemblyScriptではなく、JavaScript/TypeScriptの標準的な構文を利用することが可能であり、Hooksの開発をより簡単にすることが期待されています。
現在はXahau NetworkのJSHookテストネットで利用可能です。
試してみる
hooks-toolkit-cliを利用することで、簡単にJS Hooksを試してみることができます。
準備
CLIツールをインストールします。
npm i -g @transia/hooks-toolkit-cli
インストールしたCLIツールのinitコマンドを使ってプロジェクトを作成します。
js
は利用言語の指定、jshooks-test
はプロジェクト名です。
js
の箇所をc
とすることで既存のC言語ベースでHooksプロジェクトを作成することも可能です。
hooks-toolkit-cli init js jshooks-test
プロジェクトのディレクトリへ移動します。
cd jshooks-test
依存関係をインストールします
npm i
ここまでの作業が完了しているとcontracts
ディレクトリにbase.ts
というファイルが存在していると思います。
このファイルがHookのエントリーポイントとなります。
import { SUCCESS } from "jshooks-api";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Hook = (arg: number) => {
trace("Base.c: Called.", 0, false);
return accept("base: Finished.", SUCCESS);
};
export { Hook };
Hook APIについて
JSHooksを含むHooks機能ではHook APIを利用し、ネットワークの情報を取得したり、Stateを追加/更新/削除、Hook内からトランザクションを送信したりすることが可能です。
Hook APIドキュメント (執筆時点でJSHooks向けのドキュメントはまだ存在しませんが、C言語向けのドキュメントを参考にすることができます。)
JSHooksのHook APIの型情報はこちらで確認することができます。
Hook開発
contracts/base.ts
ファイルを編集し、Hookのロジックを記述します。
今回はシンプルなHookとして、Hookが呼び出されるたびにカウントアップするHookを作成します。
import { assert, encodeString, fallback, SUCCESS } from 'jshooks-api'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Hook = (arg: number) => {
trace("Counter: Called.", 0)
const key = encodeString('COUNT')
const value = fallback(state(key))
let count: number
if (value === undefined)
count = 0
else
count = value[0]
if (count === 0xFF)
count = 0
trace("Counter: prev", count);
count++
trace("Counter: next", count);
assert(state_set([count], key))
accept(`Counter: Finished.: ${count}`, SUCCESS)
}
export { Hook }
コードを見ていきましょう
ログの出力
trace("Counter: Called.", 0);
trace()
はHookAPIであり、サーバ上にログを出力します。第一引数にはログメッセージ、第二引数には任意の値を指定します。
Stateの取得
const key = encodeString('COUNT')
const value = fallback(state(key))
state
はHookAPIであり引数で指定したキーに対応するStateを取得します。
encodeString
はjshooks-apiライブラリで提供されているヘルパー関数であり、文字列をバイト列にエンコードします。
fallback
も同じくヘルパー関数であり、引数のデータが存在する場合はそのデータを返し、存在しない場合はundefined
を返します。 (引数の結果が-5
(DOESNT_EXIST
)以外のエラーコードの場合はrollback処理を行い、Hook処理を終了します。)
つまりCOUNT
を持つState情報を取得し、valueに格納している処理になります。
Counter処理
let count: number
if (value === undefined)
count = 0
else
count = value[0]
if (count === 0xFF)
count = 0
trace("Counter: prev", count);
count++
trace("Counter: next", count);
JavaScriptによる基本的な処理です。Stateが存在しなかった場合は0を、存在した場合はStateの値を取得します。
Stateの値が0xFF
の場合は0にリセットし、その後カウンターをインクリメントします。
Stateの更新
assert(state_set([count], key))
accept(`Counter: Finished.: ${count}`, SUCCESS)
state_set
はHookAPIであり、引数で指定した値を指定したキーに対応するStateにセットします。
accept
はHookAPIであり、Hook処理を正常終了します。
assert
はjshooks-apiライブラリで提供されているヘルパー関数であり、引数の結果が成功の場合はその値を返し、失敗の場合はrollback処理を行い、Hook処理を終了します。
つまり、更新したカウンターの値をCOUNT
というキーでStateにセットし、Hook処理を正常終了する処理になります。
Hookのビルド
次のコマンドでHookをビルドします。
npm run build
実行後build
ディレクトリにbase.bc
が生成されます。これがHookの実態となるバイナリファイルです。
テストネットでのHookのデプロイ & 実行
次のコマンドでHookをデプロイおよびHookの呼び出しを行います。
npm run deploy
テストネットを利用しているため、10秒程度時間がかかります。
処理が終了すると、次のようなHookの結果が表示されCounterが動作していることがわかると思います。
Counter: Finished.: 1
続けて何度か実行するとCounterがインクリメントされていくことも確認できます。
まとめ
JSHooksはXRP LedgerのHooks機能をJavaScript/TypeScriptで利用可能にすることで、より多くの開発者がHooksを利用しやすくすることを目指しています。
現時点では、JSHooks関連のドキュメントは十分とは言えませんが、Hooks機能の最先端を試してみて、Hooks開発に興味を持つきっかけになればと思います。
XRPL JapanのDiscordサーバがありますので、こちらもぜひご参加ください!
技術的な質問などあれば開発者チャンネルにてお答えします!
私のX/Twitterアカウントはこちら!
Discussion