はじめに
前回はModuleのローカルへのデプロイ方法を説明しました。
Libra開発言語 Moveをローカルで使ってみる
今回はLibraにデフォルトでデプロイされているModuleを説明していきます。
Module / Resource とは?
前回から出ている Module/Resource の説明を簡単にします。
Libraはアカウントベースデータモデルを採用しており、account addressにMove modulesとresourcesを紐ずけてデータの管理を行います。
Moduleはcodeを保存してあるもので、Resourceはデータを管理しているものです。
そして1つのaddressに1つのModule, resourceを持つことができます。(*プログラム次第で2つ以上持つことも可能です)
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 Module
のresource 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 = *©(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 = *©(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 = *©(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_ref
とmarket_cap_total_value
はこれ以降使わないのでmoveで所有権を以降してます。
・return T{value: move(value)};
でmintしたLibra coinを渡しています。
・mintの関数では&
や*
が出ていましたがC言語で使われるものらしいので下記の記事を参考にしてみてください。mintの関数だと&market_cap.total_value
は&u64
で*&market_cap.total_value
はu64
になります。
【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
モジュールの中身をみていこうと思います。