Good questions! Here is a longer writeup on the rationale and plan for move_to
. I think this answers most of your specific questions except for one, which I’ll answer here:
Again - how do these methods work ( create_child_vasp_account
as example) and does it mean that I can use any address in account creation?
Yes. This was actually true before–you can use any address in account creation, but you’ll only be able to send transactions from the address if you uses a key to derive it.
Will it actually create a new account?
Yes. Move itself does not have any notion of “accounts”; the fact that every nonempty address has a LibraAccount
resource is a Libra-specific convention. It would be possible to use a different approach.
If so, can I move my resources to it?
Yes, but only if you can send a transaction from the account.
The Move Signer Type
This is a more in-depth explanation of the new signer
type originally proposed in https://github.com/libra/libra/issues/3679.
Overview
Old way
move_to_sender<R>(r: R) // publish R under the transaction sender's address
Transaction::sender(): address // return the transaction sender's address
New way
// signer is a native (like u64, bool, etc.) resource type with one field (addr)
move_to<R>(&signer, R) // publish R under signer.addr
Signer::address_of(&signer): address // return signer.addr
Where do you get a signer?
// If a transaction script declares a `&signer` argument, the Move VM creates a
// signer resource whose addr is the sender address of the current transaction.
// It then passes a reference to that `signer` into the tx script.
// A script need not have a &signer argument, but if it does have a &signer, it must
// be the first argument. A script can have at most one &signer (for now).
fun main(account: &signer, arg1: u64, arg2: address, ...) {
Migration
- Short term (next week): all uses of
move_to_sender
and Transaction::sender
eliminated from Move standard library
- Medium term (next ~month): eliminate all uses in Move lang tests, work with Prover team to eliminate all uses in prover tests
- Long term (but hopefully before V0 launch): eliminate
Transaction::sender
native function, remove move_to_sender
source syntax and bytecode instruction
Why
Simplified Account Creation
- Bootstrapping problem: need to add a
LibraAccount::T
resource (and other resources) to an address in order to create an account there.
- But transaction to create account at address A must be sent from some existing account at A’ != A.
-
move_to_sender
can only put resources under sender A’. And can’t send from A because it doesn’t exist yet. We’re stuck.
- Old solution:
native fun save_account(address, resource1, resource2, ...)
- Where
resource1
and resource2
are the resources that all account types require
- Becomes very cumbersome with the variety of account role in whitepaper V2 Libra: ParentVASP, ChildVASP, Association, DesignatedDealer, Validator, and so on.
- For all role-specific resources, use a two transaction publish-approve pattern: account publishes (e.g.)
ChildVASP
resource, relevant authority (e.g., ParentVASP
) approves by flipping flag to true in separate tx.
- Account setup is (at least!) three steps: create account, publish, approve
- New solution:
signer
+ two private native functions
fun create_signer(address): signer
fun destroy_signer(signer)
- This allows single-tx creation and initialization with relevant resources. There is a separate function like the example below for each account role
let s = create_signer(address_to_create)
move_to(&s, resource1) // if resource1 declared in LibraAccount
M1::publish(&s); // to publish resource declared in M1
s = M2::publish(s);
// ^ to publish resource declared in M2 that should only be created by LibraAccount
destroy_signer(s);
Explicit representation of sender authority
In the old Move, there is problem that many folks have pointed out/been troubled by (more details here): a function signature doesn’t give you any hint about whether it is going to read the sender address or use your sender authority. As an extreme example:
fun some_innocent_looking_thing() {
// steals all your money and sends it to 0x101
LibraAccount:pay_from_sender(0x101, LibraAccount::balance(Transaction::sender()));
}
This is a odd/unfortunate in a language whose purpose is to make everything explicit.
The introduction of signer
does not completely solve this problem, but it helps. All code (except the special account creation code mentions) must be written in a functional style that threads the &signer
from the transaction script through to any code that wants to look at/use it. That means the function above must now look like:
fun some_innocent_looking_thing(account: &signer) { // could
// steals all your money and sends it to 0x101
LibraAccount:pay_from(account, 0x101, LibraAccount::balance(account));
}
This helps because functions that don’t take a signer
are guaranteed[1] not to change the sender’s account state. Functions that do require more scrutiny, and we may want to encourage design patterns to make things even more explicit (e.g., pay_from
might require a WithdrawCapability
instead of a &signer
).
Multi-sender transactions scripts aka atomic scripts (future work)
There are many compelling applications of multi-sender transactions: atomic exchange of currencies, escrow without trusted third party or a complex contract, sending a transaction from one account with a different account paying for gas, cleaner implementation of the travel rule protocol, and so on. It is not clear how to implement this in the presence of move_to_sender
or Transaction::sender
; which sender the code is talking about will always be ambiguous. However, signer
suggests a very simple solution to this problem:
// Extend the transaction format to accept multiple sender addresses +
// multiple signatures. The VM can check signatures and create a &signer for each
fun main(account1: &signer, account2: &signer, account3: &signer, ...)
There are some minor logistical issues to work out (deciding how/if to split gas fees, which sequence numbers get incremented, etc.), but overall the path forward seems clear.
[1] Unless they happen to hardcode the sender’s address