Open9

WeaveDBの中身を暴きたい

inaridiyinaridiy

ツイッターを見ているとWeaveDBというArweave上にファイルを保管する完全に分散的なデータベースが登場していた。
ついに来たかと思いつつ本当に動作するのか、実用的に使えるのか気になったのでソースコードを読んで、適当にメモって行く予定。

https://weavedb.asteroid.ac/

inaridiyinaridiy

大きくリポジトリは大きく下の三つに分かれていた。

  1. src下のメインのコード
  2. sdk下のメインのコードにアクセスするコード
  3. console下の管理?に使うコード

まず、SDK側から読んでいく、気になったNPMパッケージがいくつかあった。
ramda 関数型プログラミングのためのユーティリティセット
arweave Arweaveの公式クライアント
warp-contracts なんか複雑そうだがArweaveの開発環境?らしい。SmartWeaveという開発環境?の実装らしい。ちょっと面白そうなので詳しく調べてみる

inaridiyinaridiy

warp-contractsとはなんなのか

まず、warp-contractsはSmartWeaveというものの実装らしいので、それについて調べる

Arweaveプロトコルに基づくシンプルでスケーラブルなスマートコントラクト

遅延評価を使用して、契約実行の負担をネットワークノードからスマートコントラクトユーザーに移します。現在、SmartWeaveはJavaScriptをサポートしており、クライアントの修正されていない実行エンジンを使用しています。
Readmeを翻訳したもの。

この文面を素直に受け止めると画期的な気がするが、スマコンユーザーが契約実行をするがよくわからない。

inaridiyinaridiy

SmartWeaveについてもっと詳しく(しかも日本語で)Arweaveの公式サイトに載っていた。

ユーザがSmartWeaveコントラクトを操作すると、有効な状態遷移の連鎖の最後に到達するまで、dApp上の各トランザクションを評価します。コントラクトの終わりに到達すると、ユーザーはコントラクトへの呼び出しを評価し、結果として得られた状態遷移をArweaveネットワークに書き込みます。このプロセスは継続的に繰り返され、新しいユーザーはお互いのトランザクションを継続的に検証し、自分の状態遷移を追加していきます。

この仕組みは効率的なきがするけど、どうやってコンセンサスを形成するのかわからない。
なんにせよ、このSmartWeaveがWeaveDBの根幹を担ってる気がする。

inaridiyinaridiy

sdkを読み進めていると、読み取り系のメソッドはコントラクトを直に読んでて、
書き込み系のメソッドは署名とかいろいろしてるっぽい。
書き込む際は内部でEIP712の署名を作成して、書き込むっぽい。
条件によって書き込むときにdryWrite bundleInteraction writeInteractionなどのコントラクトメソッドを叩いている。
なんのメソッドかわからないので、ぼちぼちWeave本体のコードをよむ。

inaridiyinaridiy

WeaveDBのメインのコードを読んでたら、sdkとの間に挟まってるwrap-contractsの機能が想ったより重かった。
なので、一旦wrap-contractsのドキュメントを軽く読む

inaridiyinaridiy

デプロイスクリプト2

const fs = require("fs/promises");
const path = require("path");

const Arweave = require("arweave");
const { WarpNodeFactory } = require("warp-contracts");

const srcTxId = "G7aMmk1Fux6Dqw7M7CFoNQf5KZV2J1UCuZjfEn_VFVM";

const deploy = async () => {
  const arweave = Arweave.init({
    host: "arweave.net",
    port: 443,
    protocol: "https",
  });
  //@ts-ignore
  const warp = WarpNodeFactory.memCachedBased(arweave).useWarpGateway().build();

  const wallet = JSON.parse(
    await fs.readFile(
      path.join(__dirname, ".wallets/wallet-mainnet.json"),
      "utf-8"
    )
  );
  const walletAddress = await arweave.wallets.jwkToAddress(wallet);

  const stateFromFile = JSON.parse(
    await fs.readFile(
      path.join(__dirname, "../dist/warp/initial-state.json"),
      "utf8"
    )
  );

  const initialState = {
    ...stateFromFile,
    ...{
      owner: walletAddress,
    },
  };

  const res = await warp.createContract.deployFromSourceTx(
    {
      wallet,
      initState: JSON.stringify(initialState),
      srcTxId,
    },
    true
  );
  console.log(res);
};

deploy()
  .catch(console.error)
  .finally(() => process.exit(0));

inaridiyinaridiy

weavedbの仮の方ていぎ

src/types/weavedb.d.ts
/* eslint-disable */

interface Window {
  ethereum: any | undefined;
}
declare let window: Window;

declare module "weavedb-sdk" {
  export interface ArweaveConfig {
    host?: string;
    protocol?: string;
    port?: string | number;
    timeout?: number;
    logging?: boolean;
    network?: string;
  }

  export type SigningFunction = (tx: Transaction) => Promise<void>;

  export interface ArWallet {
    kty: string;
    e: string;
    n: string;
    d?: string;
    p?: string;
    q?: string;
    dp?: string;
    dq?: string;
    qi?: string;
  }

  export type WarpSigner = SigningFunction | ArweaveWallet | "use_wallet";

  export interface EthWallet {
    wallet: string;
    privateKey: string;
  }

  export interface WeaveDBConfig {
    arweave?: ArweaveConfig;
    contractTxId: string; // maybe
    wallet: WarpSigner;
    name: string;
    version: string;
    EthWallet?: string | EthWallet; //maybe
    web3?: any;
  }

  export type OP = {
    //I don't know what this is
    __op: string;
  } & any;

  export default class SDK {
    constructor(config: WeaveDBConfig);
    cget<T = any>(path: string, ...query: string[][]): Promise<T>;
    add<T = any>(data: T, path: string, user: EthWallet): Promise<void>;
    update<T = any>(
      data: T,
      path: string,
      id: string,
      user: EthWallet
    ): Promise<void>;
    delete(path: string, id: string, user: EthWallet): Promise<void>;
    ts(): OP;
    signer(): OP;
    createTempAddress(
      address: string
    ): Promise<{ tx: any; identity: any; err: any }>;
  }
}