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

# はじめに

前回はLibraCoin Moduleをみていきました。
[Libra開発言語 MOVEのLibraCoinモジュール](https://qiita.com/YosukeAramaki/items/dbbe35e507ada2b97637)

今回はもう一つのよく使われるModuleである`LibraAccount`をみていきます。

# LibraAccount

`LibraAccount`モジュールコードはこんな感じです。

```rust
module LibraAccount {
    import 0x0.LibraCoin;
    import 0x0.Hash;
    import 0x0.U64Util;
    import 0x0.AddressUtil;
    import 0x0.BytearrayUtil;

    resource T {
        authentication_key: bytearray,
        balance: LibraCoin.T,
        delegated_key_rotation_capability: bool,
        delegated_withdrawal_capability: bool,
        received_events: Self.EventHandle<Self.ReceivedPaymentEvent>,
        sent_events: Self.EventHandle<Self.SentPaymentEvent>,
        sequence_number: u64,
        event_generator: Self.EventHandleGenerator,
    }

    resource WithdrawalCapability {
        account_address: address,
    }

    resource KeyRotationCapability {
        account_address: address,
    }

    struct SentPaymentEvent {
        amount: u64,
        payee: address,
        metadata: bytearray,
    }

    struct ReceivedPaymentEvent {
        amount: u64,
        payer: address,
        metadata: bytearray,
    }

    resource EventHandleGenerator {
        counter: u64,
    }

    resource EventHandle<T: unrestricted> {
        counter: u64,
        guid: bytearray,
    }

    public deposit(payee: address, to_deposit: LibraCoin.T) acquires T {
        Self.deposit_with_metadata(move(payee), move(to_deposit), h"");
        return;
    }

    public deposit_with_metadata(
        payee: address,
        to_deposit: LibraCoin.T,
        metadata: bytearray
    ) acquires T {
        Self.deposit_with_sender_and_metadata(
            move(payee),
            get_txn_sender(),
            move(to_deposit),
            move(metadata)
        );
        return;
    }

    deposit_with_sender_and_metadata(
        payee: address,
        sender: address,
        to_deposit: LibraCoin.T,
        metadata: bytearray
    ) acquires T {
        let deposit_value: u64;
        let payee_account_ref: &mut Self.T;
        let sender_account_ref: &mut Self.T;

        deposit_value = LibraCoin.value(&to_deposit);
        assert(copy(deposit_value) > 0, 7);

        sender_account_ref = borrow_global_mut<T>(copy(sender));
        Self.emit_event<Self.SentPaymentEvent>(
            &mut move(sender_account_ref).sent_events,
            SentPaymentEvent {
                amount: copy(deposit_value),
                payee: copy(payee),
                metadata: copy(metadata)
            },
        );

        payee_account_ref = borrow_global_mut<T>(move(payee));
        LibraCoin.deposit(&mut copy(payee_account_ref).balance, move(to_deposit));
        Self.emit_event<Self.ReceivedPaymentEvent>(
            &mut move(payee_account_ref).received_events,
            ReceivedPaymentEvent {
                amount: move(deposit_value),
                payer: move(sender),
                metadata: move(metadata)
            }
        );
        return;
    }

    public mint_to_address(payee: address, amount: u64) acquires T {
        if (!exists<T>(copy(payee))) {
            Self.create_account(copy(payee));
        }

        Self.deposit(move(payee), LibraCoin.mint_with_default_capability(move(amount)));
        return;
    }

    withdraw_from_account(account: &mut Self.T, amount: u64): LibraCoin.T {
        let to_withdraw: LibraCoin.T;

        to_withdraw = LibraCoin.withdraw(&mut move(account).balance, copy(amount));
        return move(to_withdraw);
    }

    public withdraw_from_sender(amount: u64): LibraCoin.T acquires T {
        let sender_account: &mut Self.T;

        sender_account = borrow_global_mut<T>(get_txn_sender());
        if (*&copy(sender_account).delegated_withdrawal_capability) {
            // The sender has delegated the privilege to withdraw from her account elsewhere--abort.
            abort(11);
        } else {
            return Self.withdraw_from_account(move(sender_account), move(amount));
        }
    }

    public withdraw_with_capability(
        cap: &Self.WithdrawalCapability, amount: u64
    ): LibraCoin.T acquires T  {
        let account: &mut Self.T;

        account = borrow_global_mut<T>(*&move(cap).account_address);
        return Self.withdraw_from_account(move(account), move(amount));
    }

    public extract_sender_withdrawal_capability(): Self.WithdrawalCapability acquires T {
        let sender: address;
        let sender_account: &mut Self.T;
        let delegated_ref: &mut bool;

        sender = get_txn_sender();
        sender_account = borrow_global_mut<T>(copy(sender));
        delegated_ref = &mut move(sender_account).delegated_withdrawal_capability;
        if (*copy(delegated_ref)) {
            // We already extracted the unique withdrawal capability for this account.
            abort(11);
        } else {
            *move(delegated_ref) = true; // ensure uniqueness of the capability
            return WithdrawalCapability { account_address: move(sender) };
        }
    }

    public restore_withdrawal_capability(cap: Self.WithdrawalCapability) acquires T {
        let account_address: address;
        let account: &mut Self.T;

        WithdrawalCapability { account_address } = move(cap);
        account = borrow_global_mut<T>(move(account_address));
        *(&mut move(account).delegated_withdrawal_capability) = false;

        return;
    }

    public pay_from_capability(
        payee: address,
        cap: &Self.WithdrawalCapability,
        amount: u64,
        metadata: bytearray
    ) acquires T {
        if (!exists<T>(copy(payee))) {
            Self.create_account(copy(payee));
        }
        Self.deposit_with_sender_and_metadata(
            move(payee),
            *&copy(cap).account_address,
            Self.withdraw_with_capability(move(cap), move(amount)),
            move(metadata),
        );
        return;
    }

    public pay_from_sender_with_metadata(
        payee: address,
        amount: u64,
        metadata: bytearray
    ) acquires T {
        if (!exists<T>(copy(payee))) {
            Self.create_account(copy(payee));
        }
        Self.deposit_with_metadata(
            move(payee),
            Self.withdraw_from_sender(move(amount)),
            move(metadata)
        );
        return;
    }

    public pay_from_sender(payee: address, amount: u64) acquires T {
        Self.pay_from_sender_with_metadata(move(payee), move(amount), h"");
        return;
    }

    rotate_authentication_key_for_account(account: &mut Self.T, new_authentication_key: bytearray) {
        *(&mut move(account).authentication_key) = move(new_authentication_key);
        return;
    }

    public rotate_authentication_key(new_authentication_key: bytearray) acquires T {
        let sender_account: &mut Self.T;

        sender_account = borrow_global_mut<T>(get_txn_sender());
        if (*&copy(sender_account).delegated_key_rotation_capability) {
            // The sender has delegated the privilege to rotate her key elsewhere--abort
            abort(11);
        } else {
            // The sender has retained her key rotation privileges--proceed.
            Self.rotate_authentication_key_for_account(
                move(sender_account),
                move(new_authentication_key)
            );
            return;
        }
    }

    public rotate_authentication_key_with_capability(
        cap: &Self.KeyRotationCapability,
        new_authentication_key: bytearray,
    ) acquires T  {
        Self.rotate_authentication_key_for_account(
            borrow_global_mut<T>(*&move(cap).account_address),
            move(new_authentication_key)
        );
        return;
    }

    public extract_sender_key_rotation_capability(): Self.KeyRotationCapability acquires T {
        let sender: address;
        let delegated_ref: &mut bool;

        sender = get_txn_sender();
        delegated_ref = &mut borrow_global_mut<T>(copy(sender)).delegated_key_rotation_capability;
        if (*copy(delegated_ref)) {
            // We already extracted the unique key rotation capability for this account.
            abort(11);
        } else {
            *move(delegated_ref) = true; // ensure uniqueness of the capability
            return KeyRotationCapability { account_address: move(sender) };
        }
    }

    public restore_key_rotation_capability(cap: Self.KeyRotationCapability) acquires T {
        let account_address: address;
        let account: &mut Self.T;

        KeyRotationCapability { account_address } = move(cap);
        account = borrow_global_mut<T>(move(account_address));
        // extract_sender_key_rotation_capability again
        *(&mut move(account).delegated_key_rotation_capability) = false;

        return;
    }

    public create_account(fresh_address: address) {
        let generator: Self.EventHandleGenerator;

        generator = EventHandleGenerator {counter: 0};
        Self.save_account(
            copy(fresh_address),
            T {
                authentication_key: AddressUtil.address_to_bytes(copy(fresh_address)),
                balance: LibraCoin.zero(),
                delegated_key_rotation_capability: false,
                delegated_withdrawal_capability: false,
                received_events: Self.new_event_handle_impl<Self.ReceivedPaymentEvent>(&mut generator, copy(fresh_address)),
                sent_events: Self.new_event_handle_impl<Self.SentPaymentEvent>(&mut generator, move(fresh_address)),
                sequence_number: 0,
                event_generator: move(generator),
            }
        );
        return;
    }

    public create_new_account(fresh_address: address, initial_balance: u64) acquires T {
        Self.create_account(copy(fresh_address));
        if (copy(initial_balance) > 0) {
            Self.pay_from_sender(move(fresh_address), move(initial_balance));
        }
        return;
    }

    native save_account(addr: address, account: Self.T);

    balance_for_account(account: &Self.T): u64 {
        let balance_value: u64;
        balance_value = LibraCoin.value(&move(account).balance);
        return move(balance_value);
    }

    public balance(addr: address): u64 acquires T {
        return Self.balance_for_account(borrow_global<T>(move(addr)));
    }

    sequence_number_for_account(account: &Self.T): u64 {
        return *(&move(account).sequence_number);
    }

    public sequence_number(addr: address): u64 acquires T {
        return Self.sequence_number_for_account(borrow_global<T>(move(addr)));
    }

    public delegated_key_rotation_capability(addr: address): bool acquires T {
        return *&(borrow_global<T>(move(addr))).delegated_key_rotation_capability;
    }

    public delegated_withdrawal_capability(addr: address): bool acquires T {
        return *&(borrow_global<T>(move(addr))).delegated_withdrawal_capability;
    }


    public withdrawal_capability_address(cap: &Self.WithdrawalCapability): &address {
        return &move(cap).account_address;
    }

    public key_rotation_capability_address(cap: &Self.KeyRotationCapability): &address {
        return &move(cap).account_address;
    }

    public exists(check_addr: address): bool {
        return exists<T>(move(check_addr));
    }

    prologue(
        txn_sequence_number: u64,
        txn_public_key: bytearray,
        txn_gas_price: u64,
        txn_max_gas_units: u64,
    ) acquires T {
        let transaction_sender: address;
        let sender_account: &mut Self.T;
        let imm_sender_account: &Self.T;
        let max_transaction_fee: u64;
        let balance_amount: u64;
        let sequence_number_value: u64;

        transaction_sender = get_txn_sender();

        assert(exists<T>(copy(transaction_sender)), 5);

        sender_account = borrow_global_mut<T>(copy(transaction_sender));

        assert(
            Hash.sha3_256(move(txn_public_key)) == *(&copy(sender_account).authentication_key),
            2
        );

        max_transaction_fee = move(txn_gas_price) * move(txn_max_gas_units);
        imm_sender_account = freeze(copy(sender_account));
        balance_amount = Self.balance_for_account(move(imm_sender_account));
        assert(move(balance_amount) >= move(max_transaction_fee), 6);

        sequence_number_value = *(&mut move(sender_account).sequence_number);
        assert(copy(txn_sequence_number) >= copy(sequence_number_value), 3);
        assert(move(txn_sequence_number) == move(sequence_number_value), 4);
        return;
    }

    epilogue(
        txn_sequence_number: u64,
        txn_gas_price: u64,
        txn_max_gas_units: u64,
        gas_units_remaining: u64
    ) acquires T {
        let sender_account: &mut Self.T;
        let transaction_fee_account: &mut Self.T;
        let imm_sender_account: &Self.T;
        let transaction_fee_amount: u64;
        let transaction_fee: LibraCoin.T;

        sender_account = borrow_global_mut<T>(get_txn_sender());

        transaction_fee_amount =
            move(txn_gas_price) * (move(txn_max_gas_units) - move(gas_units_remaining));
        imm_sender_account = freeze(copy(sender_account));
        assert(
            Self.balance_for_account(move(imm_sender_account)) >= copy(transaction_fee_amount),
            6
        );
        transaction_fee =
            Self.withdraw_from_account(
                copy(sender_account),
                move(transaction_fee_amount)
            );

        *(&mut move(sender_account).sequence_number) = move(txn_sequence_number) + 1;
        transaction_fee_account = borrow_global_mut<T>(0xFEE);
        LibraCoin.deposit(&mut move(transaction_fee_account).balance, move(transaction_fee));
        return;
    }

    fresh_guid(counter: &mut Self.EventHandleGenerator, sender: address): bytearray {
        let count: &mut u64;
        let count_bytes: bytearray;
        let preimage: bytearray;
        let sender_bytes: bytearray;

        count = &mut move(counter).counter;
        sender_bytes = AddressUtil.address_to_bytes(move(sender));

        count_bytes = U64Util.u64_to_bytes(*copy(count));
        *move(count) = *copy(count) + 1;

        preimage = BytearrayUtil.bytearray_concat(move(count_bytes), move(sender_bytes));

        return move(preimage);
    }

    new_event_handle_impl<T: unrestricted>(counter: &mut Self.EventHandleGenerator, sender: address): Self.EventHandle<T> {
        return EventHandle<T> {counter: 0, guid: Self.fresh_guid(move(counter), move(sender))};
    }

    public new_event_handle<T: unrestricted>(): Self.EventHandle<T> acquires T {
        let sender_account_ref: &mut Self.T;
        let sender_bytes: bytearray;
        sender_account_ref = borrow_global_mut<T>(get_txn_sender());
        return Self.new_event_handle_impl<T>(&mut (move(sender_account_ref)).event_generator, get_txn_sender());
    }

    public emit_event<T: unrestricted>(handle_ref: &mut Self.EventHandle<T>, msg: T) {
        let count: &mut u64;
        let guid: bytearray;

        guid = *&copy(handle_ref).guid;
        count = &mut move(handle_ref).counter;

        Self.write_to_event_store<T>(move(guid), *copy(count), move(msg));
        *move(count) = *copy(count) + 1;
        return;
    }

    native write_to_event_store<T: unrestricted>(guid: bytearray, count: u64, msg: T);

    public destroy_handle<T: unrestricted>(handle: Self.EventHandle<T>) {
        let guid: bytearray;
        let count: u64;
        EventHandle<T> { count, guid } = move(handle);
        return;
    }
}
```

## resource T

全てのアカウントは`resource T`を持っています。
`delegated_key_rotation_capability`と`delegated_withdrawal_capability`は誰かにアカウントの権限を委ねるものでERC20のallowanceに近いものだと思われます。その他のbalance, event, sequence_numberなどは他のDapp開発でもよく見るものですね。
```

resource T {
    authentication_key: bytearray,
    balance: LibraCoin.T,
    delegated_key_rotation_capability: bool,
    delegated_withdrawal_capability: bool,
    received_events: Self.EventHandle<Self.ReceivedPaymentEvent>,
    sent_events: Self.EventHandle<Self.SentPaymentEvent>,
    sequence_number: u64,
    event_generator: Self.EventHandleGenerator,
}
```


## WithdrawalCapability, KeyRotationCapability

上記で説明した`delegated_key_rotation_capability`と`delegated_withdrawal_capability`のためのresourceです。指定した`address`にアカウントの権限を譲渡できるようになっています。

```rs
resource WithdrawalCapability {
    account_address: address,
}

resource KeyRotationCapability {
    account_address: address,
}
```

## Event

```rust
struct SentPaymentEvent {
    amount: u64,
    payee: address,
    metadata: bytearray,
}

struct ReceivedPaymentEvent {
    amount: u64,
    payer: address,
    metadata: bytearray,
}

resource EventHandleGenerator {
    counter: u64,
}

resource EventHandle<T: unrestricted> {
    counter: u64,
    guid: bytearray,
}
```

・Eventは`SentPaymentEvent`や`ReceivedPaymentEvent`のように`struct`で持つこどができるようです。

・`EventHandleGenerator`はこのモジュール内のイベントに対してアドレス毎にIDを与えるためのもので、`EventHandle<T: unrestricted>`はイベント毎にIDを与えて管理するものと考えられます。（理解することが難しく断言はできませんが、、）

# create_account

Libra accountを作るための関数です。

```rust
public create_account(fresh_address: address) {
    let generator: Self.EventHandleGenerator;

    generator = EventHandleGenerator {counter: 0};
    Self.save_account(
        copy(fresh_address),
        T {
            authentication_key: AddressUtil.address_to_bytes(copy(fresh_address)),
            balance: LibraCoin.zero(),
            delegated_key_rotation_capability: false,
            delegated_withdrawal_capability: false,
            received_events: Self.new_event_handle_impl<Self.ReceivedPaymentEvent>(&mut generator, copy(fresh_address)),
            sent_events: Self.new_event_handle_impl<Self.SentPaymentEvent>(&mut generator, move(fresh_address)),
            sequence_number: 0,
            event_generator: move(generator),
        }
    );
    return;
}

native save_account(addr: address, account: Self.T);
```

・引数が`fresh_address: address`となってるので、外で作成したアドレスは渡しているようです。どこかにアドレスを作成する関数があると思ったのですが、見つけることができませんでした。引き続き調査します。

・`generator = EventHandleGenerator {counter: 0};`でアカウントの`EventHandleGenerator`に設定しています。

・`Self.save_account( ... );`で作成したいアドレスに紐づけて`resource T`を保存してます。

# deposit

`to_deposit`分のLibra Coinを`payee`のアドレスに送るための3つの関数です。Libra coinを送るためには`deposit`、`deposit_with_metadata`、`deposit_with_sender_and_metadata`の関数が必要になります。

```rs
public deposit(payee: address, to_deposit: LibraCoin.T) acquires T {
    Self.deposit_with_metadata(move(payee), move(to_deposit), h"");
    return;
}

public deposit_with_metadata(
    payee: address,
    to_deposit: LibraCoin.T,
    metadata: bytearray
) acquires T {
    Self.deposit_with_sender_and_metadata(
        move(payee),
        get_txn_sender(),
        move(to_deposit),
        move(metadata)
    );
    return;
}

deposit_with_sender_and_metadata(
    payee: address,
    sender: address,
    to_deposit: LibraCoin.T,
    metadata: bytearray
) acquires T {
    let deposit_value: u64;
    let payee_account_ref: &mut Self.T;
    let sender_account_ref: &mut Self.T;

    deposit_value = LibraCoin.value(&to_deposit);
    assert(copy(deposit_value) > 0, 7);

    sender_account_ref = borrow_global_mut<T>(copy(sender));
    Self.emit_event<Self.SentPaymentEvent>(
        &mut move(sender_account_ref).sent_events,
        SentPaymentEvent {
            amount: copy(deposit_value),
            payee: copy(payee),
            metadata: copy(metadata)
        },
    );

    payee_account_ref = borrow_global_mut<T>(move(payee));
    LibraCoin.deposit(&mut copy(payee_account_ref).balance, move(to_deposit));

　　Self.emit_event<Self.ReceivedPaymentEvent>(
        &mut move(payee_account_ref).received_events,
        ReceivedPaymentEvent {
            amount: move(deposit_value),
            payer: move(sender),
            metadata: move(metadata)
        }
    );
    return;
}
    
```

・Libra coinを送る相手のアドレス`payee: address`と、送る量`to_deposit: LibraCoin.T`を引数として渡しています。

・Metadataを含めない場合は`deposit`の関数を呼びその中でからのMetadataを作ってもらい、Metadataを含めたい場合には`deposit_with_metadata`関数で引数を渡す形になっているようです。最終的に全て`deposit_with_sender_and_metadata`に行き着きますがオフションを増やして使いやす区しているようです。

・`deposit_value = LibraCoin.value(&to_deposit);`で送金者の送金分のLibra Coinを取得しています。LibraCoinモジュールの`Value`関数は以下になります

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

・`sender_account_ref = borrow_global_mut<T>(copy(sender));`で送信者の`resource T`を取得しています。
リソースを取得する際は`borrow_global<リソース名>(アドレス)`で取得できるようです。

＊その後`Self.emit_event<>`でイベントを発生させていますが、イベントは次の項目で記載します。

・同じように`payee_account_ref = borrow_global_mut<T>(move(payee));`で受信者の`resource T`を取得しています。

・`LibraCoin.deposit(&mut copy(payee_account_ref).balance, move(to_deposit));`でLibra Coinが送金されます。[前回の記事](https://qiita.com/YosukeAramaki/items/dbbe35e507ada2b97637)でも説明したように、`LibraCoin.deposit`では2つのLibraCoinのバランスを合わせる形になっているので、受信者のLibraCoinのバランスと送金がくのバランスが引数として渡されています。


# event

LibraCoinを送金した際のイベントです。

```rs

・・・
Self.emit_event<Self.SentPaymentEvent>(
    &mut move(sender_account_ref).sent_events,
    SentPaymentEvent {
        amount: copy(deposit_value),
        payee: copy(payee),
        metadata: copy(metadata)
    },
);
・・・

// Emit an event with payload `msg` by using handle's key and counter. Will change the payload from bytearray to a
    // generic type parameter once we have generics.
public emit_event<T: unrestricted>(handle_ref: &mut Self.EventHandle<T>, msg: T) {
    let count: &mut u64;
    let guid: bytearray;

    guid = *&copy(handle_ref).guid;
    count = &mut move(handle_ref).counter;

    Self.write_to_event_store<T>(move(guid), *copy(count), move(msg));
    *move(count) = *copy(count) + 1;
    return;
}

// Native procedure that writes to the actual event stream in Event store
// This will replace the "native" portion of EmitEvent bytecode
native write_to_event_store<T: unrestricted>(guid: bytearray, count: u64, msg: T);
```

・`Self.emit_event<Self.SentPaymentEvent>( ... ）` でeventを発火する関数を叩いています。引数には`resource T`の`sent_events: Self.EventHandle<Self.SentPaymentEvent>`と`SentPaymentEvent`のstructを渡しています。

・`write_to_event_store`でeventがstoreされる場所に書き込まれているようです。おそらくこの関数の中で`EventHandle`と`SentPaymentEvent`は紐付けられてると思います。この辺りの書き方はかなり難しいですね、、全然理解できていません。
`write_to_event_store`の引数に、EventHandle`guid, count`と`SentPaymentEvent`そのものである`msg`が渡されてるので、イベントを参照する際は`count`を使ってできるのではないかと思います。

Eventの発行に関わるrerource, structをまとめてみました。Solidityのイベントと比べるとかなり複雑です。

```
resource T {
    ...
    sent_events: Self.EventHandle<Self.SentPaymentEvent>,
    ...
    event_generator: Self.EventHandleGenerator,
}


struct SentPaymentEvent {
    amount: u64,
    payee: address,
    metadata: bytearray,
}

resource EventHandle<T: unrestricted> {
    counter: u64,
    guid: bytearray,
}

```

# withdraw

deposit関係の関数では「`to_deposit`分のLibra Coinを`payee`のアドレスに送る」と説明しました。LibraAccountにおいてwithdraw関数は`to_deposit`のLibracoinを作る関数になります。

```rs
withdraw_from_account(account: &mut Self.T, amount: u64): LibraCoin.T {
    let to_withdraw: LibraCoin.T;

    to_withdraw = LibraCoin.withdraw(&mut move(account).balance, copy(amount));
    return move(to_withdraw);
}

public withdraw_from_sender(amount: u64): LibraCoin.T acquires T {
    let sender_account: &mut Self.T;

    sender_account = borrow_global_mut<T>(get_txn_sender());
    if (*&copy(sender_account).delegated_withdrawal_capability) {
        abort(11);
    } else {
        return Self.withdraw_from_account(move(sender_account), move(amount));
    }
}
```

`withdraw_from_sender`→`withdraw_from_account`の順番で処理が行われます。

・`sender_account = borrow_global_mut<T>(get_txn_sender());`で関数の実行者に紐付く`resource T`を参照しています。`get_txn_sender()`で関数の実行者のアドレスを取得することができます。Solidityでいうと`msg.sender`を同じ扱いになります。

・条件分岐は`resource T`の`delegated_withdrawal_capability`で判断されています。`delegated_withdrawal_capability`が`true`の場合、つまりLibraCoinの引き出し機能を関数実行者が誰かに譲っていることになるのでエラーになります。`delegated_withdrawal_capability`が`true`の場合に`withdraw_from_account`が実行されます。

・`withdraw_from_account`では、自分のLibraCoinのバランスから`amount`分だけ引き離した`LibraCoin.T`を作成しています。関数の中では`LibraCoin`モジュールの`withdraw`が使われています。


## Delegated Withdrawal Capability

今まで`delegated_withdrawal_capability`が出てきていましたが、それを扱う関数になります。
`extract_sender_withdrawal_capability`は自分のリブラアカウントからの引き出し権限を手放す関数で、`restore_withdrawal_capability`で手放した引き出し権限をまた手元に戻す関数になっています。

```
public extract_sender_withdrawal_capability(): Self.WithdrawalCapability acquires T {
    let sender: address;
    let sender_account: &mut Self.T;
    let delegated_ref: &mut bool;

    sender = get_txn_sender();
    sender_account = borrow_global_mut<T>(copy(sender));
    delegated_ref = &mut move(sender_account).delegated_withdrawal_capability;
    if (*copy(delegated_ref)) {
        abort(11);
    } else {
        *move(delegated_ref) = true; 
        return WithdrawalCapability { account_address: move(sender) };
    }
}

public restore_withdrawal_capability(cap: Self.WithdrawalCapability) acquires T {
    let account_address: address;
    let account: &mut Self.T;

    WithdrawalCapability { account_address } = move(cap);
    account = borrow_global_mut<T>(move(account_address));
    *(&mut move(account).delegated_withdrawal_capability) = false;

    return;
}

resource WithdrawalCapability {
    account_address: address,
}
```

`extract_sender_withdrawal_capability`では`delegated_withdrawal_capability`をtrueにしてresourceの`WithdrawalCapability`に関数実行者のアドレスを渡しています。
`WithdrawalCapability`に紐付いているアカウントはLibraCoinの引き出しが可能になると説明されていましたが、`extract_sender_withdrawal_capability`ではLibraCoinの引き出し権限がどのアドレスにも移動していないため、このresourceがなんのために用意されているresourceなのか理解できませんでした。ただ、`LibraAccount`のモジュールには`WithdrawalCapability`のアドレスを移動する関数がなかったのですが、別のモジュールで`WithdrawalCapability`のアドレスを移動させる関数があるかもしれません。要調査です。

`restore_withdrawal_capability`関数では`WithdrawalCapability`を削除して、`delegated_withdrawal_capability`を`false`にしています。
・`WithdrawalCapability { account_address } = move(cap);`で`WithdrawalCapability`を削除しているようですが、引数に`cap: Self.WithdrawalCapability`を渡しており、引数をそのまま`move`することにより削除しているようです。
・この関数の中の`account_address`が何のアドレスなのかわかりませんでした、、、どこから持って来るのだろう？`Self.WithdrawalCapability`ってなってるから自分のアドレス？？

## 終わりに

LibraAccountモジュールの主要な関数を見ていきました。LibraはEthereumと同様にアカウントベースのステート管理が採用されていますが、コードを見ているとステートの管理はMOVE言語の特徴によってBitcoinのUTXOに似ているなと感じました。
２つのデフォルトモジュールの中身を見ましたがrustと似た言語であるMOVEのコードの特徴を掴むのはなかなか難しいです。