diff --git a/Cargo.toml b/Cargo.toml index 9f977f870..e00378ae2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -347,8 +347,3 @@ incremental = true # # This will be removed once our pull requests land. [patch.crates-io] serde-reflection = { git = "https://github.com/aptos-labs/serde-reflection", rev = "839aed62a20ddccf043c08961cfe74875741ccba" } - -[env] -# set coin name for diem dependencies -RUST_DIEM_COIN_MODULE = "gas_coin" -RUST_DIEM_COIN_NAME = "LibraCoin" \ No newline at end of file diff --git a/framework/libra-framework/sources/ol_sources/gas_coin.depr b/framework/libra-framework/sources/ol_sources/gas_coin.depr deleted file mode 100644 index a3e212540..000000000 --- a/framework/libra-framework/sources/ol_sources/gas_coin.depr +++ /dev/null @@ -1,265 +0,0 @@ - -module ol_framework::gas_coin { - use std::string; - use std::error; - use std::signer; - use std::vector; - use std::option::{Self, Option}; - - use diem_framework::coin::{Self, MintCapability, BurnCapability}; - use diem_framework::system_addresses; - - use ol_framework::globals; - - friend diem_framework::genesis; - friend ol_framework::genesis_migration; - - /// Account does not have mint capability - const ENO_CAPABILITIES: u64 = 1; - /// Mint capability has already been delegated to this specified address - const EALREADY_DELEGATED: u64 = 2; - /// Cannot find delegation of mint capability to this account - const EDELEGATION_NOT_FOUND: u64 = 3; - - struct GasCoin has key {} - - struct MintCapStore has key { - mint_cap: MintCapability, - } - - /// Delegation token created by delegator and can be claimed by the delegatee as MintCapability. - struct DelegatedMintCapability has store { - to: address - } - - /// The container stores the current pending delegations. - struct Delegations has key { - inner: vector, - } - - /// Can only called during genesis to initialize the Diem coin. - public(friend) fun initialize(diem_framework: &signer) { - system_addresses::assert_diem_framework(diem_framework); - - let (burn_cap, freeze_cap, mint_cap) = coin::initialize_with_parallelizable_supply( - diem_framework, - string::utf8(b"Gas Coin"), - string::utf8(b"GAS"), - globals::get_coin_decimal_places(), /* decimals MATCHES LEGACY 0L */ - true, /* monitor_supply */ - ); - - // Diem framework needs mint cap to mint coins to initial validators. This will be revoked once the validators - // have been initialized. - move_to(diem_framework, MintCapStore { mint_cap }); - - coin::destroy_freeze_cap(freeze_cap); - coin::destroy_burn_cap(burn_cap); - // (burn_cap, mint_cap) - } - - /// FOR TESTS ONLY - /// Can only called during genesis to initialize the Diem coin. - public(friend) fun initialize_for_core(diem_framework: &signer): (BurnCapability, MintCapability) { - system_addresses::assert_diem_framework(diem_framework); - - let (burn_cap, freeze_cap, mint_cap) = coin::initialize_with_parallelizable_supply( - diem_framework, - string::utf8(b"Gas Coin"), - string::utf8(b"GAS"), - globals::get_coin_decimal_places(), /* decimals MATCHES LEGACY 0L */ - true, /* monitor_supply */ - ); - - // Diem framework needs mint cap to mint coins to initial validators. This will be revoked once the validators - // have been initialized. - move_to(diem_framework, MintCapStore { mint_cap }); - - coin::destroy_freeze_cap(freeze_cap); - - (burn_cap, mint_cap) - } - - public fun has_mint_capability(account: &signer): bool { - exists(signer::address_of(account)) - } - - /// Only called during genesis to destroy the diem framework account's mint capability once all initial validators - /// and accounts have been initialized during genesis. - public(friend) fun destroy_mint_cap(diem_framework: &signer) acquires MintCapStore { - system_addresses::assert_diem_framework(diem_framework); - let MintCapStore { mint_cap } = move_from(@diem_framework); - coin::destroy_mint_cap(mint_cap); - } - - - #[view] - /// get the gas coin supply. Helper which wraps coin::supply and extracts option type - // NOTE: there is casting between u128 and u64, but 0L has final supply below the u64. - public fun supply(): u64 { - let supply_opt = coin::supply(); - if (option::is_some(&supply_opt)) { - return (*option::borrow(&supply_opt) as u64) - }; - 0 - } - - - #[test_only] - public fun restore_mint_cap(diem_framework: &signer, mint_cap: MintCapability) { - system_addresses::assert_diem_framework(diem_framework); - move_to(diem_framework, MintCapStore { mint_cap }); - } - - /// FOR TESTS ONLY - /// The `core addresses` sudo account is used to execute system transactions for testing - /// Can only be called during genesis for tests to grant mint capability to diem framework and core resources - /// accounts. - public(friend) fun configure_accounts_for_test( - diem_framework: &signer, - core_resources: &signer, - mint_cap: MintCapability, - ){ - system_addresses::assert_diem_framework(diem_framework); - - // Mint the core resource account GasCoin for gas so it can execute system transactions. - coin::register(core_resources); - - let coins = coin::mint( - 18446744073709551615, - &mint_cap, - ); - coin::deposit(signer::address_of(core_resources), coins); - - move_to(core_resources, MintCapStore { mint_cap }); - move_to(core_resources, Delegations { inner: vector::empty() }); - } - - // /// Only callable in tests and testnets where the core resources account exists. - // /// Create new coins and deposit them into dst_addr's account. - // mint_impl( - // root: &signer, - // amount: u64, - // ): Coin acquires MintCapStore { - // system_addresses::assert_ol(root); - - // let mint_cap = &borrow_global(signer::address_of(root)).mint_cap; - // coin::mint(amount, mint_cap) - // } - - // NOTE: needed for smoke tests - // TODO: guard some other way besides using the testing root account. - /// Root account can mint to an address. Only used for genesis and tests. - /// The "root" account in smoke tests has some privileges. - public entry fun mint_to_impl( - root: &signer, - dst_addr: address, - amount: u64, - ) acquires MintCapStore { - - let account_addr = signer::address_of(root); - - assert!( - exists(account_addr), - error::not_found(ENO_CAPABILITIES), - ); - - let mint_cap = &borrow_global(account_addr).mint_cap; - let coins_minted = coin::mint(amount, mint_cap); - coin::deposit(dst_addr, coins_minted); - } - - #[test_only] - public entry fun test_mint_to( - root: &signer, - dst_addr: address, - amount: u64, - ) acquires MintCapStore { - system_addresses::assert_ol(root); - mint_to_impl(root, dst_addr, amount); - } - - /// Only callable in tests and testnets where the core resources account exists. - /// Create delegated token for the address so the account could claim MintCapability later. - public entry fun delegate_mint_capability(account: signer, to: address) acquires Delegations { - system_addresses::assert_core_resource(&account); - let delegations = &mut borrow_global_mut(@core_resources).inner; - let i = 0; - while (i < vector::length(delegations)) { - let element = vector::borrow(delegations, i); - assert!(element.to != to, error::invalid_argument(EALREADY_DELEGATED)); - i = i + 1; - }; - vector::push_back(delegations, DelegatedMintCapability { to }); - } - - /// Only callable in tests and testnets where the core resources account exists. - /// Claim the delegated mint capability and destroy the delegated token. - public entry fun claim_mint_capability(account: &signer) acquires Delegations, MintCapStore { - let maybe_index = find_delegation(signer::address_of(account)); - assert!(option::is_some(&maybe_index), EDELEGATION_NOT_FOUND); - let idx = *option::borrow(&maybe_index); - let delegations = &mut borrow_global_mut(@core_resources).inner; - let DelegatedMintCapability { to: _ } = vector::swap_remove(delegations, idx); - - // Make a copy of mint cap and give it to the specified account. - let mint_cap = borrow_global(@core_resources).mint_cap; - move_to(account, MintCapStore { mint_cap }); - } - - fun find_delegation(addr: address): Option acquires Delegations { - let delegations = &borrow_global(@core_resources).inner; - let i = 0; - let len = vector::length(delegations); - let index = option::none(); - while (i < len) { - let element = vector::borrow(delegations, i); - if (element.to == addr) { - index = option::some(i); - break - }; - i = i + 1; - }; - index - } - - #[view] - /// helper to get balance in gas coin - public fun get_balance(account: address): u64 { - coin::balance(account) - } - - - #[test_only] - use diem_framework::aggregator_factory; - - #[test_only] - public fun initialize_for_test(diem_framework: &signer): (BurnCapability, MintCapability) { - aggregator_factory::initialize_aggregator_factory_for_test(diem_framework); - let (burn_cap, freeze_cap, mint_cap) = coin::initialize_with_parallelizable_supply( - diem_framework, - string::utf8(b"Gas Coin"), - string::utf8(b"GAS"), - 8, /* decimals */ - true, /* monitor_supply */ - ); - move_to(diem_framework, MintCapStore { mint_cap }); - - coin::destroy_freeze_cap(freeze_cap); - (burn_cap, mint_cap) - } - - // This is particularly useful if the aggregator_factory is already initialized via another call path. - #[test_only] - public fun initialize_for_test_without_aggregator_factory(diem_framework: &signer): (BurnCapability, MintCapability) { - let (burn_cap, freeze_cap, mint_cap) = coin::initialize_with_parallelizable_supply( - diem_framework, - string::utf8(b"Gas Coin"), - string::utf8(b"GAS"), - 8, /* decimals */ - true, /* monitor_supply */ - ); - coin::destroy_freeze_cap(freeze_cap); - (burn_cap, mint_cap) - } -} diff --git a/framework/libra-framework/sources/ol_sources/proof_of_fee.move b/framework/libra-framework/sources/ol_sources/proof_of_fee.move index 6ac284938..7dfa67a8f 100644 --- a/framework/libra-framework/sources/ol_sources/proof_of_fee.move +++ b/framework/libra-framework/sources/ol_sources/proof_of_fee.move @@ -86,6 +86,18 @@ module ol_framework::proof_of_fee { } } + // on a migration genesis for mainnet the genesis reward needs to be calculated + // from supply data. + public fun genesis_migrate_reward(vm: &signer, nominal_reward: u64) acquires + ConsensusReward { + if (signer::address_of(vm) != @ol_framework) return; + + let state = borrow_global_mut(@ol_framework); + state.nominal_reward = nominal_reward; + state.net_reward = nominal_reward; // just for info purposes. It gets calculated + // on first epoch change. + } + public fun init(account_sig: &signer) { let acc = signer::address_of(account_sig); diff --git a/framework/releases/head.mrb b/framework/releases/head.mrb index 90402a0a4..fda2e129c 100644 Binary files a/framework/releases/head.mrb and b/framework/releases/head.mrb differ diff --git a/tools/genesis/Makefile b/tools/genesis/Makefile index 05a9ac74b..4e6fd344c 100644 --- a/tools/genesis/Makefile +++ b/tools/genesis/Makefile @@ -14,6 +14,10 @@ RECOVERY_FILE = sample_end_user_single # RECOVERY_FILE = sample_rotated_auth endif +ifndef YEARS +YEARS = 7 +endif + ifndef CHAIN CHAIN = testing endif @@ -34,6 +38,7 @@ genesis: --json-legacy ./tests/fixtures/${RECOVERY_FILE}.json \ --target-supply ${TARGET_SUPPLY} \ --target-future-uses ${FUTURE_USES} \ + --years-escrow ${YEARS} \ --map-dd-to-slow 3A6C51A0B786D644590E8A21591FA8E2 \ --map-dd-to-slow 2B0E8325DEA5BE93D856CFDE2D0CBA12 @@ -45,6 +50,7 @@ wizard: --json-legacy ./tests/fixtures/${RECOVERY_FILE}.json \ --target-supply ${TARGET_SUPPLY} \ --target-future-uses ${FUTURE_USES} \ + --years-escrow ${YEARS} \ --map-dd-to-slow 3A6C51A0B786D644590E8A21591FA8E2 \ --map-dd-to-slow 2B0E8325DEA5BE93D856CFDE2D0CBA12 @@ -84,5 +90,6 @@ test-genesis: -i ${CAROL_IP} \ --target-supply ${TARGET_SUPPLY} \ --target-future-uses ${FUTURE_USES} \ + --years-escrow ${YEARS} \ --map-dd-to-slow 3A6C51A0B786D644590E8A21591FA8E2 \ --map-dd-to-slow 2B0E8325DEA5BE93D856CFDE2D0CBA12 diff --git a/tools/genesis/README.md b/tools/genesis/README.md new file mode 100644 index 000000000..377220c3c --- /dev/null +++ b/tools/genesis/README.md @@ -0,0 +1,60 @@ +## Genesis + + +### Migration Math + +#### `--target-supply ` Change in denomination (split) + +To adjust the count of coins, and how they get split, the genesis command offers +one input: `--target-supply`. + +If for example the supply will scale (pro-rata) from 2,000,000 to 100,000,000, +then the genesis supply calculator (`set_ratios_from_settings()`) will create a split of 50 redenominated coins +for 1 coin. Coins do not change name, no changes in ownership, and all policies +remain the same. + +#### `--years-escrow ` Provision an infrastructure escrow + +If the validators will set aside rewards for future validators this is done +with: `--years-escrow`. If for example this argument is used, the supply +calculator (`set_ratios_from_settings()`) will take the *remainder* of the +`--target-future-uses` which isn't already in a community-wallet. + +#### `--target-future-uses ` Community wallet and infra escrow target percentage of network supply +This parameter affects the expected funds available for infra-escrow pledges, +and the daily epoch reward budget. + +Note: this could have been implemented as a number targetting the infra-escrow percentage +(e.g. `target-infra-escrow`). However the user-experience of the validator at +genesis is more difficult in this case since the community wallet numbers are +not easily calculated in advance (it would require multiple steps with this tool). + +We calculate the infra-escrow budget by working backwards from one input the +community would already have: a target for "future users" (versus end-user +accounts). +If for example the community expected that the combined infra-escrow and +community wallets will represent 70% of the network as a target, we deduce that +infra-escrow will be the remainder of `((0.70 * total supply) - (sum of community +wallets))`. + +A lower amount of `--target-future-uses` means a lower amount available to +infrastructure escrow pledges to use over the time `--years-escrow`. i.e. if +target future uses is 55% (`--target-future-uses 0.55`) and the community +wallet balance is 45%, then there is +10% of supply to be spread over 7 years (`--years-escrow 7`). + +Note also we derive the baseline +daily validator rewards from those two parameters. In the example above the +daily reward baseline equals `(10% * Total +Supply) / 7 (years) * 100 validators (baseline) * 365 (days)` + +Troubleshooting. If the target percent % is below the proportion of the sum of community +accounts the program will exit with error. + + +#### `--map_dd_to_slow `. Adjusting the future-uses calculator + +Ususally in test-cases, there may be cases that the future-uses calculator gets +skewed because certain accounts are not in the expected state (a community +wallet wasn't expected to exist at that point). + diff --git a/tools/genesis/src/genesis_functions.rs b/tools/genesis/src/genesis_functions.rs index f09acb589..d5cb6e107 100644 --- a/tools/genesis/src/genesis_functions.rs +++ b/tools/genesis/src/genesis_functions.rs @@ -1,5 +1,5 @@ //! ol functions to run at genesis e.g. migration. -use crate::supply::{populate_supply_stats_from_legacy, SupplySettings}; +use crate::supply::{Supply, SupplySettings}; use anyhow::Context; use diem_types::account_config::CORE_CODE_ADDRESS; use diem_vm::move_vm_ext::SessionExt; @@ -15,13 +15,8 @@ use move_core_types::value::{serialize_values, MoveValue}; pub fn genesis_migrate_all_users( session: &mut SessionExt, user_recovery: &[LegacyRecovery], - supply_settings: &SupplySettings, + supply: &Supply, ) -> anyhow::Result<()> { - let mut supply = - populate_supply_stats_from_legacy(user_recovery, &supply_settings.map_dd_to_slow)?; - - supply.set_ratios_from_settings(supply_settings)?; - user_recovery .iter() .progress_with_style(OLProgress::bar()) @@ -179,7 +174,6 @@ pub fn genesis_migrate_infra_escrow( user_recovery: &LegacyRecovery, escrow_pct: f64, ) -> anyhow::Result<()> { - dbg!("infra"); if user_recovery.account.is_none() || user_recovery.auth_key.is_none() || user_recovery.balance.is_none() @@ -317,19 +311,17 @@ pub fn rounding_mint(session: &mut SessionExt, supply_settings: &SupplySettings) ); } -// pub fn mint_genesis_bootstrap_coin(session: &mut SessionExt, validators: &[Validator]) { -// validators.iter().for_each(|v| { -// let serialized_values = serialize_values(&vec![ -// MoveValue::Signer(AccountAddress::ZERO), // must be called by 0x0 -// MoveValue::Address(v.owner_address), -// ]); - -// exec_function( -// session, -// "infra_escrow", -// "genesis_coin_validator", -// vec![], -// serialized_values, -// ); -// }); -// } +pub fn set_validator_baseline_reward(session: &mut SessionExt, nominal_reward: u64) { + let serialized_values = serialize_values(&vec![ + MoveValue::Signer(AccountAddress::ZERO), // must be called by 0x0 + MoveValue::U64(nominal_reward), + ]); + + exec_function( + session, + "proof_of_fee", + "genesis_migrate_reward", + vec![], + serialized_values, + ); +} diff --git a/tools/genesis/src/supply.rs b/tools/genesis/src/supply.rs index aadd8571b..09dd24c5d 100644 --- a/tools/genesis/src/supply.rs +++ b/tools/genesis/src/supply.rs @@ -17,6 +17,9 @@ pub struct SupplySettings { /// for calculating escrow, what's the desired percent to future uses pub target_future_uses: f64, #[clap(long)] + /// for calculating base case validator reward + pub years_escrow: u64, + #[clap(long)] /// for future uses calc, are there any donor directed wallets which require mapping to slow wallets pub map_dd_to_slow: Vec, } @@ -26,6 +29,7 @@ impl Default for SupplySettings { Self { target_supply: 100_000_000_000.0, target_future_uses: 0.0, + years_escrow: 10, map_dd_to_slow: vec![], } } @@ -50,6 +54,7 @@ pub struct Supply { // which will compute later pub split_factor: f64, pub escrow_pct: f64, + pub epoch_reward_base_case: f64, } impl Supply { @@ -61,6 +66,9 @@ impl Supply { let target_future_uses = settings.target_future_uses * self.total; let remaining_to_fund = target_future_uses - self.donor_directed; self.escrow_pct = remaining_to_fund / self.slow_validator_locked; + self.epoch_reward_base_case = + remaining_to_fund / (365 * 100 * settings.years_escrow) as f64; // one hundred validators over 7 years every day. Note: discussed elsewhere: if this is an over estimate, the capital gets returned to community by the daily excess burn. + Ok(()) } } @@ -127,6 +135,7 @@ pub fn populate_supply_stats_from_legacy( donor_directed: 0.0, split_factor: 0.0, escrow_pct: 0.0, + epoch_reward_base_case: 0.0, }; let dd_wallets = rec @@ -162,6 +171,7 @@ fn test_genesis_math() { let settings = SupplySettings { target_supply: 10_000_000_000.0, target_future_uses: 0.70, + years_escrow: 10, map_dd_to_slow: vec![ // FTW "3A6C51A0B786D644590E8A21591FA8E2" diff --git a/tools/genesis/src/vm.rs b/tools/genesis/src/vm.rs index 313c57252..1f983fbc0 100644 --- a/tools/genesis/src/vm.rs +++ b/tools/genesis/src/vm.rs @@ -25,7 +25,10 @@ use diem_vm_genesis::{ }; use libra_types::{legacy_types::legacy_recovery::LegacyRecovery, ol_progress::OLProgress}; -use crate::{genesis_functions::rounding_mint, supply::SupplySettings}; +use crate::{ + genesis_functions::{rounding_mint, set_validator_baseline_reward}, + supply::{populate_supply_stats_from_legacy, SupplySettings}, +}; /// set the genesis parameters /// NOTE: many of the parameters are ignored in libra_framework @@ -131,8 +134,18 @@ pub fn encode_genesis_change_set( if let Some(r) = recovery { if !r.is_empty() { - crate::genesis_functions::genesis_migrate_all_users(&mut session, r, supply_settings) + let mut supply = populate_supply_stats_from_legacy(r, &supply_settings.map_dd_to_slow) + .expect("could not parse supply from legacy file"); + + supply + .set_ratios_from_settings(supply_settings) + .expect("could not set supply ratios from settings"); + + crate::genesis_functions::genesis_migrate_all_users(&mut session, r, &supply) .expect("could not migrate users"); + + // need to set the baseline reward based on supply settings + set_validator_baseline_reward(&mut session, supply.epoch_reward_base_case as u64); } } OLProgress::complete("user migration complete");