LoginSignup
1
1

More than 3 years have passed since last update.

Libra開発言語 MOVEのLibraCoinモジュール

Posted at

はじめに

前回はModuleのローカルへのデプロイ方法を説明しました。
Libra開発言語 Moveをローカルで使ってみる

今回はLibraにデフォルトでデプロイされているModuleを説明していきます。

Module / Resource とは?

前回から出ている Module/Resource の説明を簡単にします。
Libraはアカウントベースデータモデルを採用しており、account addressにMove modulesとresourcesを紐ずけてデータの管理を行います。

Moduleはcodeを保存してあるもので、Resourceはデータを管理しているものです。
そして1つのaddressに1つのModule, resourceを持つことができます。(*プログラム次第で2つ以上持つことも可能です)

スクリーンショット 2020-01-19 15.53.41.png

White paperでは上記のようにModuleとRecourseの管理が図示されていました。
0x56...CurrencyというModuleを公開しておりその中にTというResourceが含まれています。そして他のアカウントが'Currency Module'にあるTをアカウントアドレスに紐ずけて保持できたり、'Currency Module'で定義されている関数を使用できたりします。

LibraCoin Module

では早速LibraCoin Moduleの中身を見ていきます。

Libra Coinの発行や残高などはこのLibraCoin Moduleで管理されています。
例えば、Libra Coinの残高管理の場合は、LibraCoin Moduleresource Tをアドレスに紐付けて所有されることで管理されています。

下記が`LibraCoinの全体です。

//LibraCoin.mvir
module LibraCoin {
    resource T {
        value: u64,
    }

    resource MintCapability {
        _dummy: bool 
    }

    resource MarketCap {
        total_value: u128,
    }

    public mint_with_default_capability(amount: u64): Self.T acquires MintCapability, MarketCap {
        return Self.mint(move(amount), borrow_global<MintCapability>(get_txn_sender()));
    }

    public mint(value: u64, capability: &Self.MintCapability): Self.T acquires MarketCap {
        let market_cap_ref: &mut Self.MarketCap;
        let market_cap_total_value: u128;

        _ = move(capability);

        market_cap_ref = borrow_global_mut<MarketCap>(0xA550C18);
        market_cap_total_value = *&copy(market_cap_ref).total_value;
        *(&mut move(market_cap_ref).total_value) = move(market_cap_total_value) + to_u128(copy(value));

        return T{value: move(value)};
    }

    public initialize() {
        assert(get_txn_sender() == 0xA550C18, 1);

        move_to_sender<MintCapability>(MintCapability{ _dummy: true });
        move_to_sender<MarketCap>(MarketCap { total_value: 0u128 });

        return;
    }

    public market_cap(): u128 acquires MarketCap {
        return *&(borrow_global<MarketCap>(0xA550C18)).total_value;
    }

    public zero(): Self.T {
        return T{value: 0};
    }

    public value(coin_ref: &Self.T): u64 {
        return *&move(coin_ref).value;
    }

    public split(coin: Self.T, amount: u64): Self.T * Self.T {
        let other: Self.T;
        other = Self.withdraw(&mut coin, move(amount));
        return move(coin), move(other);
    }

    public withdraw(coin_ref: &mut Self.T, amount: u64): Self.T {
        let value: u64;

        value = *(&mut copy(coin_ref).value);
        assert(copy(value) >= copy(amount), 10);

        *(&mut move(coin_ref).value) = move(value) - copy(amount);
        return T{value: move(amount)};
    }

    public join(coin1: Self.T, coin2: Self.T): Self.T  {
        Self.deposit(&mut coin1, move(coin2));
        return move(coin1);
    }


    public deposit(coin_ref: &mut Self.T, check: Self.T) {
        let value: u64;
        let check_value: u64;

        value = *(&mut copy(coin_ref).value);
        T { value: check_value } = move(check);
        *(&mut move(coin_ref).value)= move(value) + move(check_value);
        return;
    }

    public destroy_zero(coin: Self.T) {
        let value: u64;
        T { value } = move(coin);
        assert(move(value) == 0, 11);
        return;
    }

}

よく使いそうなところを見ていきます。
自分もRustに触れるのは初めてなのでどこまで正確に説明できるかわかりませんが頑張ります。

resource T

resource T {
    value: u64,
}

上記でも述べたようにこのresource Tをaddressに紐付けてLibra coinの残高管理を行なっています。
例えば、0x123のaddressが100 Libra coin持っているとしたら、0x123.LibraCoin.T = 100となります。

resource MintCapability

resource MintCapability {
    _dummy: bool 
}

Libra associationのみにLibraCoin.mintの関数を使用権限を与えるために使われるものらしいです。mintの際に見てみます。

resource MarketCap

resource MarketCap {
    total_value: u128,
}

MarketCapはおそらくLibra coinの総発行量を示しています。

mint

public mint(value: u64, capability: &Self.MintCapability): Self.T acquires MarketCap {
    let market_cap_ref: &mut Self.MarketCap;
    let market_cap_total_value: u128;

    _ = move(capability);

    assert(copy(value) <= 1000000000 * 1000000, 11); 
    market_cap_ref = borrow_global_mut<MarketCap>(0xA550C18);
    market_cap_total_value = *&copy(market_cap_ref).total_value;
    *(&mut move(market_cap_ref).total_value) = move(market_cap_total_value) + to_u128(copy(value));

    return T{value: move(value)};
}

acquiresと関数の引数の後につける理由はbytecode verifierに伝える際に必要になるそうです。下記のリンクで詳しく説明してあったのでわかるたいたら教えていただきたいです、、、
How could I understand the keyword “acquires”?

market_cap_ref = borrow_global_mut<MarketCap>(0xA550C18);では0xA550C18に紐付いてるresource MarketCapを参照しています。0xA550C18はLibLibのアドレスだと思われます。

market_cap_total_value = *&copy(market_cap_ref).total_value;でLibraCoinのそう発行料を取得しています。
market_cap_refはこれ以降も使うのでcopy()で指定しており、*&で間接参照しているみたいです。

*(&mut move(market_cap_ref).total_value) = move(market_cap_total_value) + to_u128(copy(value));で今回のmintした量を総発行料に追加しています。
to_u128でu64だった引数をu126に変換できるみたいですね。valueは最後に使うのでcopyで、market_cap_refmarket_cap_total_valueはこれ以降使わないのでmoveで所有権を以降してます。

return T{value: move(value)};でmintしたLibra coinを渡しています。

・mintの関数では&*が出ていましたがC言語で使われるものらしいので下記の記事を参考にしてみてください。mintの関数だと&market_cap.total_value&u64*&market_cap.total_valueu64になります。
【C言語ポインタ】&(アンパサンド)や*(アスタリスク)の使い分け

withdraw

public withdraw(coin_ref: &mut Self.T, amount: u64): Self.T {
    let value: u64;

    value = *(&mut copy(coin_ref).value);
    assert(copy(value) >= copy(amount), 10);

    *(&mut move(coin_ref).value) = move(value) - copy(amount);
    return T{value: move(amount)};
}

withdrawは所有しているLibra coinを分割するための関数です。下記のサイトの2つのアドレスに同時に送金されるときに使われるみたいです。Moveにはrustと同じように所有権が存在しているので送金するときもその所有権の概念が、同時に送金する際に絡むのかなと思いました。
Getting Started With Move

value = *(&mut copy(coin_ref).value);で自分のLibra coin残高を取得して、次の文で分割する数より多いか確かめています。

*(&mut move(coin_ref).value) = move(value) - copy(amount);で合計残高からamountを引いています。
move(coin_ref).valueは書き換えられているためmutをつけてると思われます。

public deposit(coin_ref: &mut Self.T, check: Self.T) {
    let value: u64;
    let check_value: u64;

    value = *(&mut copy(coin_ref).value);
    T { value: check_value } = move(check);
    *(&mut move(coin_ref).value)= move(value) + move(check_value);
    return;
}

depositは2つのコインを合わせる関数です。これは、Libra coinを受け取るときに使われる関数だと思います。「自分のcoin」と「受け取るcoin」を合わせる形でLibra coinを受け取るとき仕組みになっています。ややこしいですが、これもrustの特徴によるものでしょう。

T { value: check_value } = move(check);check_valueがcheckと同じ数のLibra coinになります。checkは引数で`Self.Tと指定されてるのでresource`の形で渡されます。

*(&mut move(coin_ref).value)= move(value) + move(check_value);で関数を呼び出す前の残高であるvalueと送金されたcheck_valueを合わせて新しい残高に更新しています。

終わりに

MoveのデフォルトモジュールであるLibraCoinの仕組みをみました。MoveはRustを参考にした言語で、所有権の概念や言語特有の縛りなどがあり初学者には少々難しく感じました。

次回はLibraAccountモジュールの中身をみていこうと思います。

1
1
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
1
1