Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ANS (NEP#0006) #1193

Merged
merged 5 commits into from
Aug 20, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions runtime/runtime/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,30 @@ use std::sync::Arc;
/// Number of epochs it takes to unstake.
const NUM_UNSTAKING_EPOCHS: BlockIndex = 3;

fn cost_per_block(
account_id: &AccountId,
account: &Account,
runtime_config: &RuntimeConfig,
) -> u128 {
evgenykuzyakov marked this conversation as resolved.
Show resolved Hide resolved
let account_length_cost_per_block = if account_id.len() > 10 {
0
} else {
runtime_config.account_length_baseline_cost_per_block
/ 3_u128.pow(account_id.len() as u32 - 2)
};

let storage_cost_per_block = (total_account_storage(account_id, account) as u128)
* runtime_config.storage_cost_byte_per_block;

account_length_cost_per_block + storage_cost_per_block
}

/// Returns true if the account has enough balance to pay storage rent for at least required number of blocks.
/// Validators must have at least enough for `NUM_UNSTAKING_EPOCHS` * epoch_length of blocks,
/// regular users - `poke_threshold` blocks.
pub(crate) fn check_rent(
account_id: &AccountId,
account: &mut Account,
account: &Account,
runtime_config: &RuntimeConfig,
epoch_length: BlockIndex,
) -> bool {
Expand All @@ -38,9 +56,8 @@ pub(crate) fn check_rent(
} else {
runtime_config.poke_threshold
};
let buffer_amount = (buffer_length as u128)
* (total_account_storage(account_id, account) as u128)
* runtime_config.storage_cost_byte_per_block;
let buffer_amount =
(buffer_length as u128) * cost_per_block(account_id, account, runtime_config);
account.amount >= buffer_amount
}

Expand All @@ -49,11 +66,10 @@ pub(crate) fn apply_rent(
account_id: &AccountId,
account: &mut Account,
block_index: BlockIndex,
config: &RuntimeConfig,
runtime_config: &RuntimeConfig,
) {
let charge = ((block_index - account.storage_paid_at) as u128)
* (total_account_storage(account_id, account) as u128)
* config.storage_cost_byte_per_block;
* cost_per_block(account_id, account, runtime_config);
account.amount = account.amount.saturating_sub(charge);
account.storage_paid_at = block_index;
}
Expand Down
4 changes: 4 additions & 0 deletions runtime/runtime/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ pub struct RuntimeConfig {
pub transaction_costs: RuntimeFeesConfig,
/// Config of wasm operations.
pub wasm_config: Config,
/// The baseline cost to store account_id of short length per block.
/// The original formula in NEP#0006 is `1,000 / (3 ^ (account_id.length - 2))` for cost per year.
/// This value represents `1,000` above adjusted to use per block.
pub account_length_baseline_cost_per_block: Balance,
}

pub fn safe_gas_to_balance(
Expand Down
38 changes: 23 additions & 15 deletions test-utils/testlib/src/standard_test_cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -825,35 +825,43 @@ pub fn test_unstake_while_not_staked(node: impl Node) {
}

/// Account must have enough rent to pay for next `poke_threshold` blocks.
/// `bob.near` is not wealthy enough.
pub fn test_fail_not_enough_rent(node: impl Node) {
let node_user = node.user();
let _ = node_user.create_account(
alice_account(),
eve_dot_alice_account(),
node.signer().public_key(),
10,
);
let transaction_result = node_user.send_money(eve_dot_alice_account(), alice_account(), 10);
let mut node_user = node.user();
let account_id = bob_account();
let signer = Arc::new(InMemorySigner::from_seed(&account_id, &account_id));
node_user.set_signer(signer);
let transaction_result = node_user.send_money(account_id, alice_account(), 10);
assert_eq!(transaction_result.status, FinalTransactionStatus::Failed);
assert_eq!(transaction_result.transactions.len(), 1);
}

/// Account must have enough rent to pay for next 4 x `epoch_length` blocks (otherwise can not stake).
pub fn test_stake_fail_not_enough_rent(node: impl Node) {
fn test_stake_fail_not_enough_rent_with_balance(node: impl Node, initial_balance: Balance) {
let node_user = node.user();
let _ = node_user.create_account(
let new_account_id = "b0b_near".to_string();
let transaction_result = node_user.create_account(
alice_account(),
eve_dot_alice_account(),
new_account_id.clone(),
node.signer().public_key(),
134_000_000_000_000_010,
initial_balance,
);
let transaction_result =
node_user.stake(eve_dot_alice_account(), node.signer().public_key(), 5);
assert_eq!(transaction_result.status, FinalTransactionStatus::Completed);
assert_eq!(transaction_result.transactions.len(), 2);
let transaction_result = node_user.stake(new_account_id.clone(), node.signer().public_key(), 5);
assert_eq!(transaction_result.status, FinalTransactionStatus::Failed);
assert_eq!(transaction_result.transactions.len(), 2);
}

pub fn test_delete_account(node: impl Node) {
pub fn test_stake_fail_not_enough_rent_for_storage(node: impl Node) {
test_stake_fail_not_enough_rent_with_balance(node, 134_000_000_000_000_010);
}

pub fn test_stake_fail_not_enough_rent_for_account_id(node: impl Node) {
test_stake_fail_not_enough_rent_with_balance(node, TESTING_INIT_BALANCE * 2);
}

pub fn test_delete_account_low_balance(node: impl Node) {
let node_user = node.user();
// There is some data attached to the account.
assert!(node_user.view_state(&bob_account(), b"").unwrap().values.len() > 0);
Expand Down
53 changes: 47 additions & 6 deletions tests/test_cases_runtime.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#[cfg(test)]
mod test {
use near::config::TESTING_INIT_BALANCE;
use near::GenesisConfig;
use near_primitives::serialize::to_base64;
use near_primitives::utils::key_for_data;
Expand All @@ -16,7 +17,29 @@ mod test {
let mut genesis_config =
GenesisConfig::legacy_test(vec![&alice_account(), &bob_account(), "carol.near"], 1);
// Set expensive state rent and add alice more money.
genesis_config.runtime_config.storage_cost_byte_per_block = 100_000_000_000_000;
genesis_config.runtime_config.storage_cost_byte_per_block = TESTING_INIT_BALANCE / 10;
genesis_config.runtime_config.poke_threshold = 10;
match &mut genesis_config.records[0][0] {
StateRecord::Account { account, .. } => account.amount = 10_000_000_000_000_000_000,
_ => {}
}
genesis_config.records[0].push(StateRecord::Data {
key: to_base64(&key_for_data(&bob_account(), b"test")),
value: to_base64(b"123"),
});
RuntimeNode::new_from_genesis(&alice_account(), genesis_config)
}

fn create_runtime_with_expensive_account_length() -> RuntimeNode {
let mut genesis_config =
GenesisConfig::legacy_test(vec![&alice_account(), &bob_account(), "carol.near"], 1);
// Set expensive account length rent and add alice more money.
// `bob.near` has 8 characters. Cost per block is `base / (3^6)`.
// Need to have balance as least `10 * base / (3^6)`, so if we put `base` at least 73
// it would be enough to delete bob's account.
genesis_config.runtime_config.account_length_baseline_cost_per_block =
73 * TESTING_INIT_BALANCE;
genesis_config.runtime_config.storage_cost_byte_per_block = 1;
genesis_config.runtime_config.poke_threshold = 10;
match &mut genesis_config.records[0][0] {
StateRecord::Account { account, .. } => account.amount = 10_000_000_000_000_000_000,
Expand Down Expand Up @@ -240,21 +263,39 @@ mod test {
}

#[test]
fn test_fail_not_enough_rent_runtime() {
fn test_fail_not_enough_rent_for_storage_runtime() {
let node = create_runtime_with_expensive_storage();
test_fail_not_enough_rent(node);
}

#[test]
fn test_stake_fail_not_enough_rent_runtime() {
fn test_stake_fail_not_enough_rent_for_storage_runtime() {
let node = create_runtime_with_expensive_storage();
test_stake_fail_not_enough_rent(node);
test_stake_fail_not_enough_rent_for_storage(node);
}

#[test]
fn test_delete_account_runtime() {
fn test_delete_account_for_storage_runtime() {
let node = create_runtime_with_expensive_storage();
test_delete_account(node);
test_delete_account_low_balance(node);
}

#[test]
fn test_fail_not_enough_rent_for_account_id_runtime() {
let node = create_runtime_with_expensive_account_length();
test_fail_not_enough_rent(node);
}

#[test]
fn test_stake_fail_not_enough_rent_for_account_id_runtime() {
let node = create_runtime_with_expensive_account_length();
test_stake_fail_not_enough_rent_for_account_id(node);
}

#[test]
fn test_delete_account_for_account_id_runtime() {
let node = create_runtime_with_expensive_account_length();
test_delete_account_low_balance(node);
}

#[test]
Expand Down