Created
June 19, 2019 05:04
-
-
Save nuuneoi/c2d7a6effa0ae7bd2172181edd156610 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) == *(©(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