Skip to content

Commit

Permalink
Merge pull request #139 from dfinance/meta-balance
Browse files Browse the repository at this point in the history
set account balance via meta
  • Loading branch information
mkurnikov authored Nov 6, 2020
2 parents d966055 + 7901f0a commit 6da812a
Show file tree
Hide file tree
Showing 8 changed files with 483 additions and 9 deletions.
9 changes: 8 additions & 1 deletion executor/src/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use vm::file_format::{CompiledScript, FunctionDefinitionIndex};

use crate::explain::{explain_effects, explain_error, StepExecutionResult};
use crate::meta::ExecutionMeta;
use crate::oracles::{oracle_coins_module, time_metadata};
use crate::oracles::{oracle_coins_module, time_metadata, coin_balance_metadata};
use move_vm_runtime::logging::NoContextLog;
use crate::session::ConstsMap;

Expand Down Expand Up @@ -168,6 +168,7 @@ pub fn execute_script(
let mut ds = data_store.clone();
let ExecutionMeta {
signers,
accounts_balance,
oracle_prices,
current_time,
aborts_with,
Expand All @@ -193,6 +194,12 @@ pub fn execute_script(
ds.resources
.insert((std_addr, price_tag), lcs::to_bytes(&val).unwrap());
}
for (account, coin, val) in accounts_balance {
ds.resources.insert(
(account, coin_balance_metadata(&coin)),
lcs::to_bytes(&val).unwrap(),
);
}

let res = execute_script_with_runtime_session(
&ds,
Expand Down
18 changes: 18 additions & 0 deletions executor/src/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ fn split_signers(s: &str) -> Vec<AccountAddress> {
#[derive(Debug, Default, Clone)]
pub struct ExecutionMeta {
pub signers: Vec<AccountAddress>,
pub accounts_balance: Vec<(AccountAddress, String, u128)>,
pub oracle_prices: Vec<(StructTag, u128)>,
pub current_time: Option<u64>,
pub aborts_with: Option<u64>,
Expand All @@ -34,6 +35,23 @@ impl ExecutionMeta {
let (key, val) = split_around(&comment, ":");
match key {
"signers" => self.signers = split_signers(val),
"balance" => {
if !val.contains(' ') {
eprintln!("Invalid balance doc comment: {}", comment);
return;
}
let (address, balance) = split_around(val, " ");
if !balance.contains(' ') {
eprintln!("Invalid balance doc comment: {}", comment);
return;
}
let (coin, num) = split_around(balance, " ");
self.accounts_balance.push((
AccountAddress::from_hex_literal(address).unwrap(),
coin.to_string(),
num.parse().unwrap(),
));
}
"price" => {
if !val.contains(' ') {
eprintln!("Invalid ticker price doc comment: {}", comment);
Expand Down
20 changes: 16 additions & 4 deletions executor/src/oracles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use move_core_types::account_address::AccountAddress;
const COIN_MODULE: &str = "Coins";
const PRICE_STRUCT: &str = "Price";

const ACCOUNT_MODULE: &str = "Account";
const ACCOUNT_BALANCE_STRUCT: &str = "Balance";

const XFI_MODULE: &str = "XFI";
const XFI_RESOURCE: &str = "T";

Expand All @@ -20,15 +23,15 @@ fn currency_type(curr: &str) -> TypeTag {
if curr == XFI_MODULE {
TypeTag::Struct(StructTag {
address: CORE_CODE_ADDRESS,
name: Identifier::new(XFI_RESOURCE).expect("Valid currency name."),
module: Identifier::new(XFI_MODULE).expect("Valid module name."),
name: Identifier::new(XFI_RESOURCE).expect("Valid currency name."),
type_params: vec![],
})
} else {
TypeTag::Struct(StructTag {
address: CORE_CODE_ADDRESS,
name: Identifier::new(curr).expect("Valid currency name."),
module: Identifier::new(COIN_MODULE).expect("Valid module name."),
name: Identifier::new(curr).expect("Valid currency name."),
type_params: vec![],
})
}
Expand All @@ -43,17 +46,26 @@ pub fn oracle_coins_module() -> ModuleId {
pub fn oracle_metadata(first: &str, second: &str) -> StructTag {
StructTag {
address: CORE_CODE_ADDRESS,
name: Identifier::new(PRICE_STRUCT).expect("Valid struct name."),
module: Identifier::new(COIN_MODULE).expect("Valid module name."),
name: Identifier::new(PRICE_STRUCT).expect("Valid struct name."),
type_params: vec![currency_type(first), currency_type(second)],
}
}

pub fn time_metadata() -> StructTag {
StructTag {
address: CORE_CODE_ADDRESS,
name: Identifier::new("CurrentTimestamp").expect("Valid module name."),
module: Identifier::new("Time").expect("Valid module name."),
name: Identifier::new("CurrentTimestamp").expect("Valid module name."),
type_params: vec![],
}
}

pub fn coin_balance_metadata(currency: &str) -> StructTag {
StructTag {
address: CORE_CODE_ADDRESS,
module: Identifier::new(ACCOUNT_MODULE).expect("Valid module name."),
name: Identifier::new(ACCOUNT_BALANCE_STRUCT).expect("Valid module name."),
type_params: vec![currency_type(currency)],
}
}
51 changes: 48 additions & 3 deletions executor/tests/test_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use move_executor::execute_script;
use move_executor::explain::{AddressResourceChanges, ResourceChange};
use lang::compiler::ConstPool;
use lang::compiler::file::MoveFile;
use resources::assets_dir;
use resources::{assets_dir, stdlib_path, modules_path};
use lang::compiler::error::CompilerError;

fn script_path() -> String {
Expand All @@ -19,11 +19,11 @@ fn module_path(name: &str) -> String {
}

pub fn stdlib_mod(name: &str) -> MoveFile {
MoveFile::load(assets_dir().join("stdlib").join(name)).unwrap()
MoveFile::load(stdlib_path().join(name)).unwrap()
}

pub fn modules_mod(name: &str) -> MoveFile {
MoveFile::load(assets_dir().join("modules").join(name)).unwrap()
MoveFile::load(modules_path().join(name)).unwrap()
}

#[test]
Expand Down Expand Up @@ -962,3 +962,48 @@ script {
ResourceChange("0x2::Record::T".to_string(), Some("[U8(11)]".to_string()))
);
}

#[test]
fn test_set_balance_via_meta() {
let _pool = ConstPool::new();
let text = r#"
/// signers: 0x1
script {
use 0x1::Dfinance;
use 0x1::Coins::ETH;
fun register_coins(standard_account: &signer) {
Dfinance::register_coin<ETH>(standard_account, b"eth", 18);
}
}
/// signers: 0x2
/// balance: 0x2 eth 100
script {
use 0x1::Account;
use 0x1::Coins::ETH;
fun main(s: &signer) {
assert(Account::balance<ETH>(s) == 100, 101);
}
}
"#;

execute_script(
MoveFile::with_content(script_path(), text),
vec![
stdlib_mod("coins.move"),
stdlib_mod("event.move"),
stdlib_mod("dfinance.move"),
stdlib_mod("signer.move"),
stdlib_mod("account.move"),
],
"libra",
"0x1",
vec![],
)
.unwrap()
.last()
.unwrap()
.effects();
}
203 changes: 203 additions & 0 deletions resources/assets/stdlib/account.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
address 0x1 {

/// Account is the access point for assets flow. It holds withdraw-deposit handlers
/// for generic currency <Token>. It also stores log of sent and received events
/// for every account.
module Account {

use 0x1::Dfinance;
use 0x1::Signer;
use 0x1::Event;

const ERR_ZERO_DEPOSIT: u64 = 7;

/// holds account data, currently, only events
resource struct T {}

resource struct Balance<Token> {
coin: Dfinance::T<Token>
}

/// Message for sent events
struct SentPaymentEvent {
amount: u128,
denom: vector<u8>,
payee: address,
metadata: vector<u8>,
}

/// Message for received events
struct ReceivedPaymentEvent {
amount: u128,
denom: vector<u8>,
payer: address,
metadata: vector<u8>,
}

/// Init wallet for measurable currency, hence accept <Token> currency
public fun accept<Token>(account: &signer) {
move_to<Balance<Token>>(account, Balance { coin: Dfinance::zero<Token>() })
}

public fun has_balance<Token>(payee: address): bool {
exists<Balance<Token>>(payee)
}

public fun has_account(payee: address): bool {
exists<T>(payee)
}

public fun balance<Token>(account: &signer): u128 acquires Balance {
balance_for<Token>(Signer::address_of(account))
}

public fun balance_for<Token>(addr: address): u128 acquires Balance {
Dfinance::value(&borrow_global<Balance<Token>>(addr).coin)
}

public fun deposit_to_sender<Token>(
account: &signer,
to_deposit: Dfinance::T<Token>
) acquires Balance {
deposit<Token>(
account,
Signer::address_of(account),
to_deposit
)
}

public fun deposit<Token>(
account: &signer,
payee: address,
to_deposit: Dfinance::T<Token>
) acquires Balance {
deposit_with_metadata<Token>(
account,
payee,
to_deposit,
b""
)
}

public fun deposit_with_metadata<Token>(
account: &signer,
payee: address,
to_deposit: Dfinance::T<Token>,
metadata: vector<u8>
) acquires Balance {
deposit_with_sender_and_metadata<Token>(
account,
payee,
to_deposit,
metadata
)
}

public fun pay_from_sender<Token>(
account: &signer,
payee: address,
amount: u128
) acquires Balance {
pay_from_sender_with_metadata<Token>(
account, payee, amount, b""
)
}

public fun pay_from_sender_with_metadata<Token>(
account: &signer,
payee: address,
amount: u128,
metadata: vector<u8>
)
acquires Balance {
deposit_with_metadata<Token>(
account,
payee,
withdraw_from_sender<Token>(account, amount),
metadata
)
}

fun deposit_with_sender_and_metadata<Token>(
sender: &signer,
payee: address,
to_deposit: Dfinance::T<Token>,
metadata: vector<u8>
) acquires Balance {
let amount = Dfinance::value(&to_deposit);
assert(amount > 0, ERR_ZERO_DEPOSIT);

let denom = Dfinance::denom<Token>();

// add event as sent into account
Event::emit<SentPaymentEvent>(
sender,
SentPaymentEvent {
amount, // u64 can be copied
payee,
denom: copy denom,
metadata: copy metadata
},
);

// there's no way to improve this place as payee is not sender :(
if (!has_balance<Token>(payee)) {
create_balance<Token>(payee);
};

if (!has_account(payee)) {
create_account(payee);
};

let payee_balance = borrow_global_mut<Balance<Token>>(payee);

// send money to payee
Dfinance::deposit(&mut payee_balance.coin, to_deposit);
// update payee's account with new event
Event::emit<ReceivedPaymentEvent>(
sender,
ReceivedPaymentEvent {
amount,
denom,
metadata,
payer: Signer::address_of(sender)
}
)
}

public fun withdraw_from_sender<Token>(
account: &signer,
amount: u128
): Dfinance::T<Token> acquires Balance {
let balance = borrow_global_mut<Balance<Token>>(Signer::address_of(account));

withdraw_from_balance<Token>(balance, amount)
}

fun withdraw_from_balance<Token>(balance: &mut Balance<Token>, amount: u128): Dfinance::T<Token> {
Dfinance::withdraw(&mut balance.coin, amount)
}

fun create_balance<Token>(addr: address) {
let sig = create_signer(addr);

move_to<Balance<Token>>(&sig, Balance {
coin: Dfinance::zero<Token>()
});

destroy_signer(sig);
}

/// keep this function, we may use T in the future
fun create_account(addr: address) {
let sig = create_signer(addr);

move_to<T>(&sig, T { });

destroy_signer(sig);
}

native fun create_signer(addr: address): signer;
native fun destroy_signer(sig: signer);
}
}
Loading

0 comments on commit 6da812a

Please sign in to comment.