今週のざっくばらん
OS作りの楽しさ
ブロックチェーン向けの画像データを保存するAssetStore から始まった私のプロジェクトですが、フルオーンチェーンNFTを実現するために、描画ライブラリ、フォント、テキスト処理などを次々に実装していると、「OS(オペレーティング・システム)」を作っているような気分になります。
パソコンの場合、一番下のレイヤーにBIOSがありますが、そこで出来ることは非常に限定的です。その上に、CP/M、MS-DOS、Windows、Linux、MacOSなどのさまざまなOSが作られ、アプリケーションの開発環境が整備され、今の「誰でも使えるパソコン」の形になっているのです。
Ethereumブロックチェーンの場合、一番下のレイヤーにEVM(Ethereum Virtual Machine)がありますが、これはBIOSに相当する非常にシンプルなもので、その上でアプリケーションを作るのは一苦労です。
OpenZeppelinは、そんなニーズに応えるために誕生したオープンソースなライブラリで、現在作られているスマートコントラクトの大半が、これを活用しています。
しかし、OpenZeppelinは、主に通常のNFT(ERC721)や暗号通貨(ERC20)の実装を助けるために作られたライブラリであり、私がやっているような「オンチェーン描画」のことは全く考慮されていません。つまり、MS-DOSのような存在なのです。
私が作っている描画ライブラリは、さらにその上のGUIレイヤーで、WindowsやMacOSのグラフィック・スレイヤーに相当するものなのです。
今回のプロジェクトが妙に楽しいのは、EVMがまだグラフィックス・レイヤーを持たない未熟なOSであるにも関わらず、ほとんどライバルがおらず、まるで私のために用意されていた仕事のように感じられるからなのだと思います。
エンジニアの生産性の話
エンジニアの生産性は、普通のエンジニアと超優秀なエンジニアとの間に20倍以上の開きがある、という話を聞いたことがある人もいると思いますが、それを示す良い例に遭遇したので紹介します。
「フルオーンチェーンNFT」プロジェクトの一環として、フォントをブロックチェーン上にデプロイする仕組みを作ったのですが、フォントデータをブロックチェーン上に書き込むのにはガス代がかかるため、それを低減する手法を開発しています。
データ形式は、AssetStore向けに開発した圧縮方式を採用していますが、家紋のように、ミントした人にガス代を負担してもらう方式は使いたくなかったので、スマートコントラクト自身に圧縮済みのフォントデータを埋め込む方法を採用することにしました。
CyberBrokersというプロジェクトの開発リーダーだった人のインタビューをpodcastで聞いた際に、データはスマートコントラクト自身に埋め込んだ方が、storageと呼ばれるデータベースに格納するよりもずっと安いと聞いていたからです。
そこで、最初に実装したのは、以下のような形です。mapping(uint => bytes) fonts;
...
bytes constant font_A = "\x4d\x60\xe9\x14...";
bytes constant font_B = "\x4d\x60\xe5\xc2...";
...
fonts[0x41] = font_A; // 0x41:"A"
fonts[0x42] = font_B; // 0x42:"B"
フォントはちゃんと表示されるようになったのですが、予想していたよりもガス代が高いのです。
そこで色々と調べた結果、fonts[0x41] = font_A;
とfont_Aを参照した時点で、フォントデータがスマートコントラクトからstorageにコピーされてしまっており、そこにガス代がかかっていることが判明しました。コントラクト中のデータは特別な形で格納されており、このように参照した際に、memoryやstorageにコピーされる仕様になっているのです。
そこで何とか解決しようと色々と工夫したのですが答えが見つからず、StackoverflowやRedditのSolidityコミュニティに質問を投げてみましたが、解答はありません。
Solidityの開発者の数はまだ少ない上に、本格的なプログラミングをしている中上級者はごくわずかなので、こんなに難しい質問に答えが返って来なくて当然といえば当然です。
そこで最初に試みたのは、mappingを使わず、フォントへのリクエストが外から入った際に、バイナリーサーチで適切なデータを見つけてそれを返す、という方式です。
一応動くようになったし、ガス代も節約出来ましたが、コードが非常に読みにくくて大きく、メンテナンスもしにくくなってしまいました。...
} else if (key < 0x60) {
if (key < 0x50) {
if (key < 0x48) {
if (key < 0x44) {
if (key < 0x42) {
path = (key == 0x40) ? font_at: font_A;
この記事は約
NaN 分で読めます(
NaN 文字 / 画像
NaN
枚)