0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Blockchain(ブロックチェーン)Advent Calendar 2021

Day 6

Lisk SDKでブロックチェーンアプリケーションを作る

Last updated at Posted at 2021-12-05

はじめに

Liskって名前を知っていてもLisk SDKに触れたことがある人は少ないのでは、ということで今回記事にしました。
丁度いいところに、アドベントカレンダーもあったしね。
構築手順を書いているので少し長いですがお付き合いください。

Lisk SDK ってなんぞ

ブロックチェーンとその上で動作するアプリケーション(ブロックチェーンアプリケーション)を構築するための開発キット。
これを使用して作成されたアプリケーションは独自のブロックチェーン上で動作します。
また、将来的にサイドチェーンとしてLiskにつながることが可能になります。
なお、JavaScript、TypeScriptで構築できるので非常に楽。(いや、ホントに)

サイドチェーンとしてLiskと繋ぐ部分(インターオペラビリティ周り)はまだ開発中
サイドチェーンとなる必要がないなら、今のままでも新プロジェクトとして立ち上げられるくらいの実力有り

推奨構築環境

OS

  • ubuntu
    18.04 (LTS)
    20.04 (LTS)

  • mac
    10.13 (High Sierra)
    10.14 (Mojave)
    10.15 (Catalina)
    11.04 (Big Sur)

Windowsは残念ながら...

RAM

2GB以上

Node.js

v12.22.7 (NPM version 6.14.15)

v12系以外を使用するとうまく動作しません

構築方法

1. 下準備

とりあえずこの辺いれとく

sudo apt-get install curl wget tar unzip zip ntp jq git build-essential

2. Lisk Commander の導入

npm i -g lisk-commander

とても便利なCLI
入れなくても構築できるが、入れておいて損はなし
今回の記事では入れている前提

3. ブロックチェーンアプリケーションのプロジェクトを作成

lisk init omikuji

lisk init [アプリケーション名]

4. 作成したプロジェクトを開く

以降 Visual Studio Codeなどを使って作業するのをおすすめ

1_init.png

5. 便利なプラグインの追加

以下のファイルを編集

/src/app/plugin.ts
import { Application, HTTPAPIPlugin } from 'lisk-sdk';    // add HTTPAPIPlugin
import { DashboardPlugin } from '@liskhq/lisk-framework-dashboard-plugin';    // add DashboardPlugin

// @ts-expect-error Unused variable error happens here until at least one module is registered
export const registerPlugins = (app: Application): void => {
    app.registerPlugin(HTTPAPIPlugin);    // register HTTPAPIPlugin
    app.registerPlugin(DashboardPlugin);    // register DashboardPlugin
};

HTTPAPIPlugin:ブラウザ等でAPIをたたきたいときに必須
DashboardPlugin:構築したアプリケーションの動作確認に使用できるので開発中は入れておくと便利

6. モジュールとアセットの作成

Lisk SDKではモジュールとアセットで1つのトランザクションとして扱われます。

Lisk標準のモジュール・アセット(一部抜粋)

moduleID assetID name
2 0 token:transfer
5 0 dpos:register
5 1 dpos:vote
5 2 dpos:unlock

今回はおみくじを作りたいので、以下のような感じにします。

moduleID assetID name
3535 0 omikuji:pull

ということで

lisk generate:module omikuji 3535
lisk generate:asset omikuji pull 0

lisk generate:module [モジュール名] [モジュールID]
lisk generate:asset [モジュール名] [アセット名] [アセットID]

ここまでやると以下のファイルが作成されます。

/src/app/modules/omikuji/omikuji_module.ts
/* eslint-disable class-methods-use-this */
import {
    AfterBlockApplyContext,
    AfterGenesisBlockApplyContext, BaseModule,
    BeforeBlockApplyContext, TransactionApplyContext
} from 'lisk-sdk';
import { PullAsset } from "./assets/pull_asset";

export class OmikujiModule extends BaseModule {

    ......

    public name = 'omikuji';
    public transactionAssets = [new PullAsset()];
    public events = [
        // Example below
        // 'omikuji:newBlock',
    ];
    public id = 3535;

    ......

    public async afterGenesisBlockApply(_input: AfterGenesisBlockApplyContext) {
        // Get any data from genesis block, for example get all genesis accounts
        // const genesisAccounts = genesisBlock.header.asset.accounts;
    }
}

/src/app/modules/omikuji/asset/pull_asset.ts
import { BaseAsset, ApplyAssetContext, ValidateAssetContext } from 'lisk-sdk';

export class PullAsset extends BaseAsset {
    public name = 'pull';
    public id = 0;

    // Define schema for asset
    public schema = {
        $id: 'omikuji/pull-asset',
        title: 'PullAsset transaction asset for omikuji module',
        type: 'object',
        required: [],
        properties: {},
    };

    public validate({ asset }: ValidateAssetContext<{}>): void {
        // Validate your asset
    }

    // eslint-disable-next-line @typescript-eslint/require-await
    public async apply({ asset, transaction, stateStore }: ApplyAssetContext<{}>): Promise<void> {
        throw new Error('Asset "pull" apply hook is not implemented.');
    }
}

また、以下のファイルに作成されたモジュールが自動で追加されます。

/src/app/modules.ts
import { Application } from 'lisk-sdk';
import { OmikujiModule } from "./modules/omikuji/omikuji_module";    // add

// @ts-expect-error Unused variable error happens here until at least one module is registered
export const registerModules = (app: Application): void => {
    app.registerModule(OmikujiModule);    // add
};

7. モジュールとアセットの編集

あとは自由にどんなアプリケーションにしたいか考えながら作成されたファイルを編集していきます。
今回のおみくじは以下のような仕様

  • 1LSK以上つかってお願いすると「大吉、吉、中吉、小吉、末吉、凶、大凶」を教えてくれる
  • 1LSK未満だと神様に怒られる
  • おみくじの結果はアカウント情報のomikuji.kekkaに設定される
  • おみくじを引く際のパラメータは {"onegai": 数値}
/src/app/modules/omikuji/omikuji_module.ts
/* eslint-disable class-methods-use-this */
import {
    AfterBlockApplyContext,
    AfterGenesisBlockApplyContext, BaseModule,
    BeforeBlockApplyContext, TransactionApplyContext
} from 'lisk-sdk';
import { PullAsset } from "./assets/pull_asset";

export class OmikujiModule extends BaseModule {
    public actions = {};
    public reducers = {};
    public name = 'omikuji';
    public transactionAssets = [new PullAsset()];
    public events = [];
    public id = 3535;

    // アカウント情報に追加するおみくじの結果を設定するためのスキーマの設定
    public accountSchema = {
        type: 'object',
        properties: {
            kekka: {
                fieldNumber: 1,
                dataType: 'string',
                maxLength: 64,
            },
        },
        default: {
            kekka: '',
        },
    };

    // Lifecycle hooks
    public async beforeBlockApply(_input: BeforeBlockApplyContext) {}
    public async afterBlockApply(_input: AfterBlockApplyContext) {}
    public async beforeTransactionApply(_input: TransactionApplyContext) {}
    public async afterTransactionApply(_input: TransactionApplyContext) {}
    public async afterGenesisBlockApply(_input: AfterGenesisBlockApplyContext) {}
}
/src/app/modules/omikuji/asset/pull_asset.ts
import { BaseAsset, ApplyAssetContext, ValidateAssetContext, cryptography } from 'lisk-sdk';

export class PullAsset extends BaseAsset {
    public name = 'pull';
      public id = 0;

    // トランザクション実行時のパラメータ用のスキーマを設定
    public schema = {
        $id: 'omikuji/pull-asset',
        title: 'PullAsset transaction asset for omikuji module',
        type: 'object',
        required: ["onegai"],
        properties: {
            onegai: {
                dataType: 'uint64', 
                fieldNumber: 1,
            },
        },
    };

    public validate({ asset }: ValidateAssetContext<{onegai:BigInt}>): void {
        // 1LSK未満なら怒る
        if (asset.onegai < BigInt(100000000)) {
            throw new Error(
                '1LSKくらいはらうのじゃー'
            );
        }
    }

    // eslint-disable-next-line @typescript-eslint/require-await
    public async apply({ asset, transaction, reducerHandler, stateStore }: ApplyAssetContext<{onegai:BigInt}>): Promise<void> {
        // おみくじ
        const kuji = ["大吉","","中吉","小吉", "末吉", "", "大凶"];
        const kekka = kuji[parseInt(cryptography.bufferToHex(transaction.id).slice(0, 8), 16) % kuji.length];

        // 送信者の情報に結果を設定
        const senderAddress = transaction.senderAddress;
        const senderAccount:{address: Buffer, omikuji:{kekka:string}} = await stateStore.account.get(senderAddress);
        senderAccount.omikuji.kekka = kekka;    
        stateStore.account.set(senderAccount.address, senderAccount);

        // 送信者の残高からお願いに使ったLSKを減らす
        await reducerHandler.invoke("token:debit", {
            address: senderAddress,
            amount: asset.onegai,
        });
    }
}

8. Genesis Blockの作成準備

./bin/run genesis-block:create --output config/default

A genesis_block file already exists at the given location. Do you want to overwrite it?と聞かれたらy
すると、config/defaultに以下のファイルが生成されます

  • accounts.json
  • forging_info.json
  • genesis_block.json
  • password.json

config/default内の各JSONファイル(config.jsonを含む)はGitHubなどで公開する際は十分気をつけましょう。
ここで生成されるファイルを公開し、そのままアプリをリリースしようものなら大変なことになります。
絶対にアカウントのパスフレーズや、パスワードは公開してはいけません。

2_create-genesis.png

forging_info.jsonおよびpassword.jsonの内容をconfig.jsonに反映

tmp=$(mktemp)
jq '.forging.delegates = input' config/default/config.json config/default/forging_info.json > "$tmp" && mv "$tmp" config/default/config.json
jq '.forging += input' config/default/config.json config/default/password.json > "$tmp" && mv "$tmp" config/default/config.json

9. ブロックチェーンアプリケーションの起動

./bin/run start --api-ws

10. 動作確認

ブラウザから http://localhost:4005 にアクセス
3_dashboad.png

Send transactionから作成したモジュール・アセットを選択して「Submit」
9_omikuji.png

動作確認の際は accounts.json に記載されているものを使用すると楽
なお、1LSK = 100,000,000

成功するとこんな感じ
1.png

onegai を 1LSK未満にすると怒られる
10_kekka_fail.png

HttpAPIで成功した際のトランザクションIDを確認してみる
2.png

HttpAPIでおみくじの結果(送信者のアカウント情報)を確認してみる
3.png

「大凶」だったみたいです...

ということで

SDKの機能の1割も使ってないんじゃないかというくらい簡単なアプリでしたがなんとなくイメージは掴めたでしょうか?
必要スペックも低い、ブロックチェーン構築も楽、アプリケーションの実装も楽
言うことなしだと思いません!?
ぜひ触ってみてくださいね!

なお、開発で困ったことがあれば Lisk 公式Discord や、Lisk Dev forum で尋ねるとコミュニティの誰かがきっと助けてくれます!

また、ブロックチェーンアプリケーションを本格的なプロジェクトとして構築していくのであればGrant Programに応募してみてはどうでしょうか?

読んでいただきありがとうございました!
お疲れ様でした!

補足:ブロックチェーンアプリケーションの削除

rm -rf ~/.lisk/[アプリケーション名]

データのみ消す場合は

rm -rf ~/.lisk/[アプリケーション名]/data
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?