How could I understand the keyword "acquires"?

Question: How could I understand “acquires” in the code? For example, in the following code, I can’t understand the “acquires” in line 4 and " _ = move(x); " in line 7.

module A {
    resource T1{v: u64}

    public test(addr: address) acquires T1 {
        let x: &mut Self.T1;
        x = borrow_global<T1>(get_txn_sender());
        _ = move(x);
        Self.acquires_t1();

        return;
    }

    acquires_t1() acquires T1 {
        let v: u64;
        T1 { v } = move_from<T1>(get_txn_sender());
        return;
    }
}

How could I understand “acquires” in the code?

This is an annotation that tells the bytecode verifier which types a procedure (or its callees) may borrow or move from global state. These annotations allow the bytecode verifier to ensure that there are no dangling references to values in global state. The docs for the bytecode verifier that uses these annotations might be helpful.

From a programmer perspective, this means that you must add acquires T for any procedure that

  • performs borrow_global<T>
  • performs move_from<T>
  • calls a procedure annotated with acquires T

and " _ = move(x); " in line 7.

This code moves the value stored in x to the stack and then pops the stack. Functionally, it is destroying the global reference to a T1 stored in X to allow a call to acquires_t1() to proceed successfully. If the reference in x still existed at the time acquires_t1() was called, it would cause a dangling reference inside of acquires_t1() (and the bytecode verifier would complain).

1 Like

Thank you very much for your answer. Besides, I have two questions to ask.

  1. Does the builtin create_account create only the LibraAccount? Can I use create_account to create the account resources which is defined by myself?
  2. Does sending a resource to the address which calls the procedure with move_to_sender mean that the resource corresponds to that address in the block chain and can be read or modified later by move_from or borrow_global?
  1. Does the builtin create_account create only the LibraAccount?

Yes, create_account(addr) creates and publishes a LibraAccount.T resource under addr.

Can I use create_account to create the account resources which is defined by myself?

No. Resources defined in custom modules must be created using the Pack bytecode (in IR, the Pack syntax is T { f: 6 } to create a resource named T with an integer field f). Once a resource of type T has been created, it can added to the sender’s address using move_to_sender<T>.

Does sending a resource to the address which calls the procedure with move_to_sender mean that the resource corresponds to that address in the block chain and can be read or modified later by move_from or borrow_global ?

Yes (if I understand correctly). If I run move_to_sender<T> in a transaction sent by address adr, that resource can later be accessed via move_from<T>(addr) and borrow_global<T>(addr) (even if these accesses occur in a transaction not sent by addr).

Thank you very much for your answer, which helps me a lot!

I’m sorry to disturb you, but I have some more questions to ask:

Question 1:
Does a resource have to be destroyed after it is created? If so, in what ways can I destroy resources? In the code, I find several ways to destroy the resource and compile the code successfully:
(1) _ = move(x) ;
(2) move_to_sender and so on ;
(3)Define the procedure of resource destruction as below:

public destroy_t(t: Self.T) {
            let money: LibraCoin.T;
            T{ money } = move(t);
            LibraCoin.destroy_zero(move(money));
            return;
        }

Are there other ways to destroy resources?

Question 2:
I noticed that Libra has an administrator account. What privileges does that account have?

Question 3:
Is the way to release the borrow the same as the way to destroy the resource?

Question 4:
I noticed that some functions have a modifier native . What does this modifier mean?

Question 5:
I notice that the function originally named borrow_global has been renamed borrow_global_mut . Does borrow_global still exist?

Does a resource have to be destroyed after it is created

A created resource must either be destroyed inside the module that created it via Unpack (e.g., the T{ money } = move(t); syntax in your example) or published to global state via move_to_sender (either directly, or as a field of a different published resource).

(1) _ = move(x) ;

This should not work when x is a resource type. It moves x to the stack and then pops it, but resources cannot be popped.

(3)Define the procedure of resource destruction as below:

Note that this code will fail at runtime unless value field of money is zero.

I noticed that Libra has an administrator account. What privileges does that account have?

There is an “association” account 0xA550C that has the privilege to mint and burn Libra.

I noticed that some functions have a modifier native . What does this modifier mean?

This means that the body of the procedure is implemented in Rust instead of Move. Only standard library procedures can be native; procedures in user-submitted modules with this annotation will not work.

I notice that the function originally named borrow_global has been renamed borrow_global_mut . Does borrow_global still exist?

Yes, borrow_global<T>(addr) now returns an immutable reference to the T resource stored at addr. borrow_global_mut<T>(addr) returns a mutable reference instead.