Skip to content

Instantly share code, notes, and snippets.

@nuuneoi
Created June 19, 2019 05:04
Show Gist options
  • Save nuuneoi/c2d7a6effa0ae7bd2172181edd156610 to your computer and use it in GitHub Desktop.
Save nuuneoi/c2d7a6effa0ae7bd2172181edd156610 to your computer and use it in GitHub Desktop.
// The module for the account resource that governs every Libra account
module LibraAccount {
import 0x0.LibraCoin;
import 0x00.Hash;
// Every Libra account has a LibraLibraAccount.T resource
resource T {
// The coins stored in this account
balance: R#LibraCoin.T,
// The current authentication key.
// This can be different than the key used to create the account
authentication_key: bytearray,
// The current sequence number.
// Incremented by one each time a transaction is submitted
sequence_number: u64,
// TEMPORARY the current count for the number of sent events for this account
// The events system is being overhauled and this will be replaced
sent_events_count: u64,
// TEMPORARY the current count for the number of received events for this account
// The events system is being overhauled and this will be replaced
received_events_count: u64
}
// Message for sent events
struct SentPaymentEvent {
// The address that was paid
payee: address,
// The amount of LibraCoin.T sent
amount: u64,
}
// Message for received events
struct ReceivedPaymentEvent {
// The address that sent the coin
payer: address,
// The amount of LibraCoin.T received
amount: u64,
}
// Creates a new LibraLibraAccount.T
// Invoked by the `create_account` builtin
make(auth_key: bytearray): R#Self.T {
let zero_balance: R#LibraCoin.T;
zero_balance = LibraCoin.zero();
return T {
balance: move(zero_balance),
authentication_key: move(auth_key),
sequence_number: 0,
sent_events_count: 0,
received_events_count: 0,
};
}
// Deposits the `to_deposit` coin into the `payee`'s account
public deposit(payee: address, to_deposit: R#LibraCoin.T) {
let deposit_value: u64;
let payee_account_ref: &mut R#Self.T;
let sender: address;
let sender_account_ref: &mut R#Self.T;
let sent_event: V#Self.SentPaymentEvent;
let received_event: V#Self.ReceivedPaymentEvent;
// Check that the `to_deposit` coin is non-zero
deposit_value = LibraCoin.value(&to_deposit);
assert(copy(deposit_value) > 0, 7);
// Load the sender's account
sender = get_txn_sender();
sender_account_ref = borrow_global<T>(copy(sender));
// Log a send event
sent_event = SentPaymentEvent { payee: copy(payee), amount: copy(deposit_value) };
// TEMPORARY The events system is being overhauled and this will be replaced by something
// more principled in the future
emit_event(&mut move(sender_account_ref).sent_events_count, b"73656E745F6576656E74735F636F756E74", move(sent_event));
// Load the payee's account
payee_account_ref = borrow_global<T>(move(payee));
// Deposit the `to_deposit` coin
LibraCoin.deposit(&mut copy(payee_account_ref).balance, move(to_deposit));
// Log a received event
received_event = ReceivedPaymentEvent { payer: move(sender), amount: move(deposit_value) };
// TEMPORARY The events system is being overhauled and this will be replaced by something
// more principled in the future
emit_event(&mut move(payee_account_ref).received_events_count, b"72656365697665645F6576656E74735F636F756E74", move(received_event));
return;
}
// mint_to_address can only be called by accounts with MintCapability (see LibraCoin)
// and those account will be charged for gas. If those account don't have enough gas to pay
// for the transaction cost they will fail minting.
// However those account can also mint to themselves so that is a decent workaround
public mint_to_address(payee: address, amount: u64) {
let mint_capability_ref: &R#LibraCoin.MintCapability;
let coin: R#LibraCoin.T;
let payee_account_ref: &mut R#Self.T;
let payee_exists: bool;
let sender: address;
// Mint the coin
mint_capability_ref = LibraCoin.borrow_sender_mint_capability();
coin = LibraCoin.mint(copy(amount), move(mint_capability_ref));
// Create an account if it does not exist
payee_exists = exists<T>(copy(payee));
if (!move(payee_exists)) {
sender = get_txn_sender();
Self.create_new_account(copy(payee), 0);
}
// Load the `payee`'s account and deposit the minted `coin`
payee_account_ref = borrow_global<T>(move(payee));
LibraCoin.deposit(&mut move(payee_account_ref).balance, move(coin));
return;
}
// Helper to withdraw `amount` from the given `account` and return the resulting LibraCoin.T
withdraw_from_account(account: &mut R#Self.T, amount: u64): R#LibraCoin.T {
let to_withdraw: R#LibraCoin.T;
to_withdraw = LibraCoin.withdraw(&mut move(account).balance, copy(amount));
return move(to_withdraw);
}
// Withdraw `amount` LibraCoin.T from the transaction sender's account
public withdraw_from_sender(amount: u64): R#LibraCoin.T {
let sender: address;
let sender_account: &mut R#Self.T;
let to_withdraw: R#LibraCoin.T;
// Load the sender
sender = get_txn_sender();
sender_account = borrow_global<T>(move(sender));
// Withdraw the coin
to_withdraw = Self.withdraw_from_account(move(sender_account), move(amount));
return move(to_withdraw);
}
// Withdraw `amount` LibraCoin.T from the transaction sender's account and send the coin
// to the `payee` address
// Creates the `payee` account if it does not exist
public pay_from_sender(payee: address, amount: u64) {
let to_pay: R#LibraCoin.T;
let payee_exists: bool;
payee_exists = exists<T>(copy(payee));
if (move(payee_exists)) {
to_pay = Self.withdraw_from_sender(move(amount));
Self.deposit(move(payee), move(to_pay));
} else {
Self.create_new_account(move(payee), move(amount));
}
return;
}
// Rotate the transaction sender's authentication key
// The new key will be used for signing future transactions
public rotate_authentication_key(new_authentication_key: bytearray) {
let sender: address;
let sender_account: &mut R#Self.T;
sender = get_txn_sender();
sender_account = borrow_global<T>(move(sender));
*(&mut move(sender_account).authentication_key) = move(new_authentication_key);
return;
}
// Creates a new account at `fresh_address` with the `initial_balance` deducted from the
// transaction sender's account
public create_new_account(fresh_address: address, initial_balance: u64) {
create_account(copy(fresh_address));
if (copy(initial_balance) > 0) {
Self.pay_from_sender(move(fresh_address), move(initial_balance));
}
return;
}
// Helper to return u64 value of the `balance` field for given `account`
balance_for_account(account: &R#Self.T): u64 {
let balance_value: u64;
balance_value = LibraCoin.value(&move(account).balance);
return move(balance_value);
}
// Return the current balance of the LibraCoin.T in LibraLibraAccount.T at `addr`
public balance(addr: address): u64 {
let payee_account: &mut R#Self.T;
let imm_payee_account: &R#Self.T;
let balance_amount: u64;
payee_account = borrow_global<T>(move(addr));
imm_payee_account = freeze(move(payee_account));
balance_amount = Self.balance_for_account(move(imm_payee_account));
return move(balance_amount);
}
// Helper to return the sequence number field for given `account`
sequence_number_for_account(account: &R#Self.T): u64 {
return *(&move(account).sequence_number);
}
// Return the current sequence number at `addr`
public sequence_number(addr: address): u64 {
let account_ref: &mut R#Self.T;
let imm_ref: &R#Self.T;
let sequence_number_value: u64;
account_ref = borrow_global<T>(move(addr));
imm_ref = freeze(move(account_ref));
sequence_number_value = Self.sequence_number_for_account(move(imm_ref));
return move(sequence_number_value);
}
// Checks if an account exists at `check_addr`
public exists(check_addr: address): bool {
let is_present: bool;
is_present = exists<T>(move(check_addr));
return move(is_present);
}
// The prologue is invoked at the beginning of every transaction
// It verifies:
// - The account's auth key matches the transaction's public key
// - That the account has enough balance to pay for all of the gas
// - That the sequence number matches the transaction's sequence key
prologue() {
let transaction_sender: address;
let transaction_sender_exists: bool;
let sender_account: &mut R#Self.T;
let imm_sender_account: &R#Self.T;
let sender_public_key: bytearray;
let public_key_hash: bytearray;
let gas_price: u64;
let gas_units: u64;
let gas_fee: u64;
let balance_amount: u64;
let sequence_number_value: u64;
let transaction_sequence_number_value: u64;
transaction_sender = get_txn_sender();
// FUTURE: Make these error codes sequential
// Verify that the transaction sender's account exists
transaction_sender_exists = exists<T>(copy(transaction_sender));
assert(move(transaction_sender_exists), 5);
// Load the transaction sender's account
sender_account = borrow_global<T>(copy(transaction_sender));
// Check that the transaction's public key matches the account's current auth key
sender_public_key = get_txn_public_key();
public_key_hash = Hash.sha3_256(move(sender_public_key));
assert(move(public_key_hash) == *(&copy(sender_account).authentication_key), 2);
// Check that the account has enough balance for all of the gas
gas_price = get_txn_gas_unit_price();
gas_units = get_txn_max_gas_units();
gas_fee = move(gas_price) * move(gas_units);
imm_sender_account = freeze(copy(sender_account));
balance_amount = Self.balance_for_account(move(imm_sender_account));
assert(move(balance_amount) >= move(gas_fee), 6);
// Check that the transaction sequence number matches the sequence number of the account
sequence_number_value = *(&mut move(sender_account).sequence_number);
transaction_sequence_number_value = get_txn_sequence_number();
assert(copy(transaction_sequence_number_value) >= copy(sequence_number_value), 3);
assert(move(transaction_sequence_number_value) == move(sequence_number_value), 4);
return;
}
// The epilogue is invoked at the end of transactions.
// It collects gas and bumps the sequence number
epilogue() {
let transaction_sender: address;
let sender_account: &mut R#Self.T;
let imm_sender_account: &R#Self.T;
let gas_price: u64;
let gas_units_remaining: u64;
let starting_gas_units: u64;
let gas_fee_amount: u64;
let balance_amount: u64;
let gas_fee: R#LibraCoin.T;
let transaction_sequence_number_value: u64;
transaction_sender = get_txn_sender();
// Load the transaction sender's account
sender_account = borrow_global<T>(copy(transaction_sender));
// Charge for gas
gas_price = get_txn_gas_unit_price();
starting_gas_units = get_txn_max_gas_units();
gas_units_remaining = get_gas_remaining();
gas_fee_amount = move(gas_price) * (move(starting_gas_units) - move(gas_units_remaining));
imm_sender_account = freeze(copy(sender_account));
balance_amount = Self.balance_for_account(move(imm_sender_account));
assert(move(balance_amount) >= copy(gas_fee_amount), 6);
gas_fee = Self.withdraw_from_account(copy(sender_account), move(gas_fee_amount));
LibraCoin.TODO_REMOVE_burn_gas_fee(move(gas_fee));
// Bump the sequence number
transaction_sequence_number_value = get_txn_sequence_number();
*(&mut move(sender_account).sequence_number) = move(transaction_sequence_number_value) + 1;
return;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment