diff --git a/framework/cached-packages/src/libra_framework_sdk_builder.rs b/framework/cached-packages/src/libra_framework_sdk_builder.rs index a3bae1d52..3c4909b23 100644 --- a/framework/cached-packages/src/libra_framework_sdk_builder.rs +++ b/framework/cached-packages/src/libra_framework_sdk_builder.rs @@ -474,6 +474,12 @@ pub enum EntryFunctionCall { epoch_expiry: u64, }, + /// update the bid using estimated net reward instead of the internal bid variables + ProofOfFeePofUpdateBidNetReward { + net_reward: u64, + epoch_expiry: u64, + }, + /// This function initiates governance for the multisig. It is called by the sponsor address, and is only callable once. /// init_gov fails gracefully if the governance is already initialized. /// init_type will throw errors if the type is already initialized. @@ -808,6 +814,10 @@ impl EntryFunctionCall { ProofOfFeePofUpdateBid { bid, epoch_expiry } => { proof_of_fee_pof_update_bid(bid, epoch_expiry) } + ProofOfFeePofUpdateBidNetReward { + net_reward, + epoch_expiry, + } => proof_of_fee_pof_update_bid_net_reward(net_reward, epoch_expiry), SafeInitPaymentMultisig { authorities } => safe_init_payment_multisig(authorities), SlowWalletSmokeTestVmUnlock { user_addr, @@ -2147,6 +2157,28 @@ pub fn proof_of_fee_pof_update_bid(bid: u64, epoch_expiry: u64) -> TransactionPa )) } +/// update the bid using estimated net reward instead of the internal bid variables +pub fn proof_of_fee_pof_update_bid_net_reward( + net_reward: u64, + epoch_expiry: u64, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("proof_of_fee").to_owned(), + ), + ident_str!("pof_update_bid_net_reward").to_owned(), + vec![], + vec![ + bcs::to_bytes(&net_reward).unwrap(), + bcs::to_bytes(&epoch_expiry).unwrap(), + ], + )) +} + /// This function initiates governance for the multisig. It is called by the sponsor address, and is only callable once. /// init_gov fails gracefully if the governance is already initialized. /// init_type will throw errors if the type is already initialized. @@ -3105,6 +3137,19 @@ mod decoder { } } + pub fn proof_of_fee_pof_update_bid_net_reward( + payload: &TransactionPayload, + ) -> Option { + if let TransactionPayload::EntryFunction(script) = payload { + Some(EntryFunctionCall::ProofOfFeePofUpdateBidNetReward { + net_reward: bcs::from_bytes(script.args().first()?).ok()?, + epoch_expiry: bcs::from_bytes(script.args().get(1)?).ok()?, + }) + } else { + None + } + } + pub fn safe_init_payment_multisig(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::SafeInitPaymentMultisig { @@ -3480,6 +3525,10 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::Lazy 0) { - print(&user_addr); - print(&locked); - }; + + let _locked = slow_wallet::hard_fork_sanitize(vm, user); + // remove a pledge account if there is one, so that coins there are // not dangling @@ -138,20 +127,11 @@ module ol_framework::last_goodbye { let good_capital = option::extract(&mut all_coins_opt); burn::burn_and_track(good_capital); }; - // print(&2001); option::destroy_none(all_coins_opt); - - // if (coin_val > 0) { - // print(&user_addr); - // print(&coin_val); - // }; - - let auth_key = b"Oh, is it too late now to say sorry?"; vector::trim(&mut auth_key, 32); - // print(&2008); // Oh, is it too late now to say sorry? // Yeah, I know that I let you down @@ -161,22 +141,17 @@ module ol_framework::last_goodbye { // another function can be called to drop the account::Account completely // and then the offline db tools can safely remove the key from db. account::rotate_authentication_key_internal(user, auth_key); - // print(&2009); } fun last_goodbye(vm: &signer, user: &signer) { - // print(&10000); let addr = signer::address_of(user); if (!account::exists_at(addr)) { - // print(&addr); return }; let auth_orig = account::get_authentication_key(addr); - // print(&10001); dont_think_twice_its_alright(vm, user); - // print(&10002); let new_auth = account::get_authentication_key(addr); // if the account is a validator they stay on ark a @@ -189,12 +164,8 @@ module ol_framework::last_goodbye { // Just hear this and then I'll go // You gave me more to live for // More than you'll ever know - // print(&10003); - account::hard_fork_drop(vm, user); - // print(&10004); - - // print(&@0xDEAD); + account::hard_fork_drop(vm, user); } #[test_only] diff --git a/framework/drop-user-tools/last_goodbye.test.move b/framework/drop-user-tools/last_goodbye.test.move index b79ed9380..731dcfde3 100644 --- a/framework/drop-user-tools/last_goodbye.test.move +++ b/framework/drop-user-tools/last_goodbye.test.move @@ -14,7 +14,7 @@ module ol_framework::test_last_goodbye { let _vals = mock::genesis_n_vals(framework, 1); // we are at epoch 0 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 0, 7357001); last_goodbye::danger_test_last_goodby(vm, bob); @@ -23,7 +23,7 @@ module ol_framework::test_last_goodbye { last_goodbye::danger_user_gc(vm, bob); mock::trigger_epoch(framework); // epoch 1 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 1, 7357002); let vals = stake::get_current_validators(); diff --git a/framework/drop-user-tools/pledge_accounts_with_gc.move b/framework/drop-user-tools/pledge_accounts_with_gc.move index 392b298c3..a2b8d17de 100644 --- a/framework/drop-user-tools/pledge_accounts_with_gc.move +++ b/framework/drop-user-tools/pledge_accounts_with_gc.move @@ -336,11 +336,7 @@ fun garbage_collection(pledge_account: &address): u64 acquires MyPledges, BeneficiaryPolicy { - // print(&5000); let pledge_list = get_user_pledges(pledge_account); - // print(&pledge_list); - // print(&5001); - // print(pledge_account); let coins = 0; let i = 0; while (i < vector::length(&pledge_list)) { @@ -349,14 +345,10 @@ let hundred_pct = fixed_point64::create_from_rational(1,1); let c = withdraw_pct_from_one_pledge_account(bene, pledge_account, &hundred_pct); - // print(&c); if (option::is_some(&c)) { - // print(&5002); - let coin = option::extract(&mut c); coins = coins + coin::value(&coin); - // print(&coin); burn::burn_and_track(coin); }; option::destroy_none(c); @@ -367,7 +359,6 @@ pledge_account); if (is_found) { - // print(&5006); vector::remove(&mut bene_state.pledgers, idx); }; i = i + 1; diff --git a/framework/libra-framework/sources/block.move b/framework/libra-framework/sources/block.move index 8df83ea52..89310c800 100644 --- a/framework/libra-framework/sources/block.move +++ b/framework/libra-framework/sources/block.move @@ -67,6 +67,8 @@ module diem_framework::block { system_addresses::assert_diem_framework(diem_framework); assert!(epoch_interval_microsecs > 0, error::invalid_argument(EZERO_EPOCH_INTERVAL)); + reconfiguration::set_epoch_interval(diem_framework, epoch_interval_microsecs); + move_to( diem_framework, BlockResource { @@ -81,28 +83,24 @@ module diem_framework::block { /// Update the epoch interval. /// Can only be called as part of the Diem governance proposal process established by the DiemGovernance module. public(friend) fun update_epoch_interval_microsecs( - _diem_framework: &signer, + diem_framework: &signer, new_epoch_interval: u64, ) acquires BlockResource { - //system_addresses::assert_vm(diem_framework); //TODO: remove after testing fork + system_addresses::assert_diem_framework(diem_framework); //TODO: remove after testing fork assert!(new_epoch_interval > 0, error::invalid_argument(EZERO_EPOCH_INTERVAL)); let block_resource = borrow_global_mut(@diem_framework); let old_epoch_interval = block_resource.epoch_interval; block_resource.epoch_interval = new_epoch_interval; + reconfiguration::set_epoch_interval(diem_framework, new_epoch_interval); + event::emit_event( &mut block_resource.update_epoch_interval_events, UpdateEpochIntervalEvent { old_epoch_interval, new_epoch_interval }, ); } - #[view] - /// Return epoch interval in seconds. - public fun get_epoch_interval_secs(): u64 acquires BlockResource { - borrow_global(@diem_framework).epoch_interval / 1000000 - } - /// Set the metadata for the current block. /// The runtime always runs this before executing the transactions in a block. fun block_prologue( @@ -244,16 +242,14 @@ module diem_framework::block { }; if (timestamp - reconfiguration::last_reconfiguration_time() >= block_metadata_ref.epoch_interval) { - // if (!features::epoch_trigger_enabled() || - // testnet::is_testnet()) { if (testnet::is_testnet()) { epoch_boundary::epoch_boundary( vm, - reconfiguration::get_current_epoch(), + reconfiguration::current_epoch(), round ); } else { - epoch_boundary::enable_epoch_trigger(vm, reconfiguration::get_current_epoch()); + epoch_boundary::enable_epoch_trigger(vm, reconfiguration::current_epoch()); } } } @@ -284,8 +280,7 @@ module diem_framework::block { } #[test(diem_framework = @diem_framework, account = @0x123)] - //#[expected_failure(abort_code = 0x50003, location = diem_framework::system_addresses)] //TODO: remove after testing fork - #[ignore] //TODO: remove after testing fork + #[expected_failure(abort_code = 0x50003, location = diem_framework::system_addresses)] public entry fun test_update_epoch_interval_unauthorized_should_fail( diem_framework: signer, account: signer, diff --git a/framework/libra-framework/sources/block.spec.move b/framework/libra-framework/sources/block.spec.move index 0b5cd0f58..c0a6b7b6d 100644 --- a/framework/libra-framework/sources/block.spec.move +++ b/framework/libra-framework/sources/block.spec.move @@ -115,10 +115,6 @@ spec diem_framework::block { ensures block_resource.epoch_interval == new_epoch_interval; } - spec get_epoch_interval_secs(): u64 { - aborts_if !exists(@diem_framework); - } - spec get_current_block_height(): u64 { aborts_if !exists(@diem_framework); } diff --git a/framework/libra-framework/sources/genesis.move b/framework/libra-framework/sources/genesis.move index f61594a56..ab419a90a 100644 --- a/framework/libra-framework/sources/genesis.move +++ b/framework/libra-framework/sources/genesis.move @@ -31,6 +31,7 @@ module diem_framework::genesis { use ol_framework::ol_account; use ol_framework::musical_chairs; use ol_framework::proof_of_fee; + use ol_framework::secret_bid; use ol_framework::slow_wallet; use ol_framework::libra_coin; use ol_framework::infra_escrow; @@ -256,7 +257,9 @@ module diem_framework::genesis { // default 90% to align with thermostatic rule in the PoF paper. // otherwise the thermostatic rule starts kicking-in immediately let sig = create_signer(validator.validator_config.owner_address); - proof_of_fee::pof_update_bid(&sig, 0900, 1000); // make the genesis + + // genesis validators need a bid struct + secret_bid::genesis_helper(diem_framework, &sig, 100); if (testnet::is_not_mainnet()) { // TODO: this is for testnet purposes only diff --git a/framework/libra-framework/sources/ol_sources/burn.move b/framework/libra-framework/sources/ol_sources/burn.move index 0fbfa81f7..9ad668975 100644 --- a/framework/libra-framework/sources/ol_sources/burn.move +++ b/framework/libra-framework/sources/ol_sources/burn.move @@ -29,6 +29,8 @@ module ol_framework::burn { friend ol_framework::pledge_accounts; friend ol_framework::make_whole; + #[test_only] + friend ol_framework::mock; #[test_only] friend ol_framework::test_burn; #[test_only] diff --git a/framework/libra-framework/sources/ol_sources/epoch_boundary.move b/framework/libra-framework/sources/ol_sources/epoch_boundary.move index f16de9b44..f793faa31 100644 --- a/framework/libra-framework/sources/ol_sources/epoch_boundary.move +++ b/framework/libra-framework/sources/ol_sources/epoch_boundary.move @@ -11,7 +11,7 @@ module diem_framework::epoch_boundary { use diem_framework::reconfiguration; use diem_framework::transaction_fee; use diem_framework::system_addresses; - use ol_framework::jail; + // use ol_framework::jail; use ol_framework::safe; use ol_framework::burn; use ol_framework::stake; @@ -45,6 +45,8 @@ module diem_framework::epoch_boundary { const ETRIGGER_NOT_READY: u64 = 2; /// Epoch number mismatch const ENOT_SAME_EPOCH: u64 = 3; + /// Supply should not change until burns + const ESUPPLY_SHOULD_NOT_CHANGE: u64 = 4; /////// Constants //////// /// How many PoF baseline rewards to we set aside for the miners. @@ -260,7 +262,7 @@ module diem_framework::epoch_boundary { assert!(state.ready, ETRIGGER_NOT_READY); // greater than, in case there is an epoch change due to an epoch bump in // testnet Twin tools, or a rescue operation. - assert!(state.closing_epoch <= reconfiguration::get_current_epoch(), + assert!(state.closing_epoch <= reconfiguration::current_epoch(), ENOT_SAME_EPOCH); true } @@ -277,7 +279,11 @@ module diem_framework::epoch_boundary { // and prevents dependency cycling. public(friend) fun epoch_boundary(root: &signer, closing_epoch: u64, epoch_round: u64) acquires BoundaryStatus { + print(&string::utf8(b"EPOCH BOUNDARY BEGINS")); + // assert the supply does not change until there are burns. + let supply_a = libra_coin::supply(); + // either 0x0 or 0x1 can call, but we will always use framework signer system_addresses::assert_ol(root); let root = &create_signer::create_signer(@ol_framework); @@ -307,11 +313,17 @@ module diem_framework::epoch_boundary { print(&string::utf8(b"musical_chairs::stop_the_music")); let (compliant_vals, n_seats) = musical_chairs::stop_the_music(root, closing_epoch, epoch_round); + print(&compliant_vals); status.incoming_compliant_count = vector::length(&compliant_vals); status.incoming_compliant = compliant_vals; status.incoming_seats_offered = n_seats; + // up to this point supply should remain unchanged. + let supply_b = libra_coin::supply(); + assert!(supply_b == supply_a, ESUPPLY_SHOULD_NOT_CHANGE); + print(&string::utf8(b"settle_accounts")); + settle_accounts(root, compliant_vals, status); print(&string::utf8(b"slow_wallet::on_new_epoch")); @@ -352,6 +364,7 @@ module diem_framework::epoch_boundary { /// withdraw coins and settle accounts for validators and oracles /// returns the list of compliant_vals fun settle_accounts(root: &signer, compliant_vals: vector
, status: &mut BoundaryStatus): vector
{ + let supply_a = libra_coin::supply(); assert!(transaction_fee::is_fees_collection_enabled(), error::invalid_state(ETX_FEES_NOT_INITIALIZED)); if (transaction_fee::system_fees_collected() > 0) { @@ -377,6 +390,10 @@ module diem_framework::epoch_boundary { status.outgoing_vals_success = total_reward == (vector::length(&compliant_vals) * nominal_reward_to_vals) }; + // up to this point supply should remain unchanged. + let supply_b = libra_coin::supply(); + assert!(supply_b == supply_a, ESUPPLY_SHOULD_NOT_CHANGE); + // Commit note: deprecated with tower mining. // remainder gets burnt according to fee maker preferences @@ -394,33 +411,27 @@ module diem_framework::epoch_boundary { /// process the payments for performant validators - /// jail the non performant /// NOTE: receives from reconfiguration.move a mutable borrow of a coin to pay reward /// NOTE: burn remaining fees from transaction fee account happens in reconfiguration.move (it's not a validator_universe concern) // Returns (compliant_vals, reward_deposited) fun process_outgoing_validators(root: &signer, reward_budget: &mut Coin, reward_per: u64, compliant_vals: vector
): (vector
, u64){ system_addresses::assert_ol(root); - let vals = stake::get_current_validators(); + // let vals = stake::get_current_validators(); let reward_deposited = 0; let i = 0; - while (i < vector::length(&vals)) { - let addr = vector::borrow(&vals, i); + while (i < vector::length(&compliant_vals)) { + let addr = vector::borrow(&compliant_vals, i); // belt and suspenders for dropped accounts in hard fork. if (!account::exists_at(*addr)) { i = i + 1; continue }; - let performed = vector::contains(&compliant_vals, addr); - if (!performed) { - jail::jail(root, *addr); - } else { - // vector::push_back(&mut compliant_vals, *addr); - if (libra_coin::value(reward_budget) >= reward_per) { - let user_coin = libra_coin::extract(reward_budget, reward_per); - reward_deposited = reward_deposited + libra_coin::value(&user_coin); - rewards::process_single(root, *addr, user_coin, 1); - }; + + if (libra_coin::value(reward_budget) >= reward_per) { + let user_coin = libra_coin::extract(reward_budget, reward_per); + reward_deposited = reward_deposited + libra_coin::value(&user_coin); + rewards::process_single(root, *addr, user_coin, 1); }; i = i + 1; diff --git a/framework/libra-framework/sources/ol_sources/epoch_helper.move b/framework/libra-framework/sources/ol_sources/epoch_helper.move index 4a6022b16..dd7e21242 100644 --- a/framework/libra-framework/sources/ol_sources/epoch_helper.move +++ b/framework/libra-framework/sources/ol_sources/epoch_helper.move @@ -1,13 +1,20 @@ module ol_framework::epoch_helper { + use diem_framework::system_addresses; + + friend diem_framework::genesis; friend diem_framework::reconfiguration; + // too many cyclic dependencies from reconfiguration being the only place to reach epoch. struct EpochHelper has key { epoch: u64 } - public fun initialize(root: &signer) { + public(friend) fun initialize(framework: &signer) { + // must be framework address + system_addresses::assert_diem_framework(framework); + if (!exists(@ol_framework)){ - move_to(root, EpochHelper { + move_to(framework, EpochHelper { epoch: 0 }) }; @@ -23,4 +30,12 @@ module ol_framework::epoch_helper { let state = borrow_global(@ol_framework); state.epoch } -} \ No newline at end of file + + #[test_only] + public fun test_set_epoch(framework: &signer, epoch: u64) acquires EpochHelper { + system_addresses::assert_diem_framework(framework); + initialize(framework); + set_epoch(epoch); + + } +} diff --git a/framework/libra-framework/sources/ol_sources/infra_escrow.move b/framework/libra-framework/sources/ol_sources/infra_escrow.move index b91fbd34d..260e6bbc3 100644 --- a/framework/libra-framework/sources/ol_sources/infra_escrow.move +++ b/framework/libra-framework/sources/ol_sources/infra_escrow.move @@ -18,15 +18,26 @@ module ol_framework::infra_escrow{ use ol_framework::ol_account; use ol_framework::libra_coin::LibraCoin; use ol_framework::pledge_accounts; - // use ol_framework::slow_wallet; - // use std::fixed_point32; - // use std::signer; - // use diem_std::debug::print; + use ol_framework::testnet; + use ol_framework::epoch_helper; + + // use diem_framework::debug::print; friend diem_framework::genesis; friend ol_framework::epoch_boundary; + #[test_only] + friend ol_framework::mock; + + + /// Not enough supply for genesis reward const EGENESIS_REWARD: u64 = 0; + + /// I'm sorry dave, this is only for testing + const EWITHDRAW_NOT_ON_MAINNET: u64 = 1; + + + /// for use on genesis, creates the infra escrow pledge policy struct public(friend) fun initialize(framework: &signer) { // NOTE: THIS MUST BE THE 0x0 address, because on epoch boundary it is that address @vm_reserved which will be calling the functions. @@ -95,13 +106,31 @@ module ol_framework::infra_escrow{ // and add them with mock.move when we need it. public(friend) fun genesis_coin_validator(framework: &signer, to: address) { system_addresses::assert_diem_framework(framework); - let bootstrap_amount = 1000000000; - if (infra_escrow_balance() > bootstrap_amount) { - let c_opt = infra_pledge_withdraw(framework, bootstrap_amount); + assert!(epoch_helper::get_current_epoch() == 0, EGENESIS_REWARD); + let bootstrap_amount = 1_000_000_000; // 1K scaled + framework_fund_account(framework, to, bootstrap_amount); + } + + // DANGER: keep this private, and only used in testnet + fun framework_fund_account(framework: &signer, to: address, amount: u64) { + system_addresses::assert_diem_framework(framework); + assert!(testnet::is_not_mainnet(), error::invalid_state(EWITHDRAW_NOT_ON_MAINNET)); + + if (infra_escrow_balance() > amount) { + let c_opt = infra_pledge_withdraw(framework, amount); assert!(option::is_some(&c_opt), error::invalid_state(EGENESIS_REWARD)); let coin = option::extract(&mut c_opt); ol_account::deposit_coins(to, coin); option::destroy_none(c_opt); } } + + #[test_only] + public(friend) fun test_fund_account_from_infra(framework: &signer, to: address, amount: u64) { + // belt and suspenders + system_addresses::assert_diem_framework(framework); + assert!(testnet::is_not_mainnet(), error::invalid_state(EWITHDRAW_NOT_ON_MAINNET)); + + framework_fund_account(framework, to, amount); + } } diff --git a/framework/libra-framework/sources/ol_sources/jail.move b/framework/libra-framework/sources/ol_sources/jail.move index cde4d4e77..75eafa8e9 100644 --- a/framework/libra-framework/sources/ol_sources/jail.move +++ b/framework/libra-framework/sources/ol_sources/jail.move @@ -49,7 +49,7 @@ module ol_framework::jail { use ol_framework::address_utils; friend ol_framework::validator_universe; - friend ol_framework::epoch_boundary; + friend ol_framework::musical_chairs; #[test_only] friend ol_framework::test_pof; @@ -62,7 +62,6 @@ module ol_framework::jail { const EVALIDATOR_CONFIG: u64 = 1; /// You are not a validator in the current set, you can't unjail anyone. const EVOUCHER_NOT_IN_SET: u64 = 2; - /// You not actually a valid voucher for this user. Did it expire? const EU_NO_VOUCHER: u64 = 3; diff --git a/framework/libra-framework/sources/ol_sources/mock.move b/framework/libra-framework/sources/ol_sources/mock.move index 8bf813019..3d7f3591c 100644 --- a/framework/libra-framework/sources/ol_sources/mock.move +++ b/framework/libra-framework/sources/ol_sources/mock.move @@ -2,7 +2,6 @@ #[test_only] module ol_framework::mock { use std::vector; - use std::signer; use diem_framework::coin; use diem_framework::block; use diem_framework::stake; @@ -23,6 +22,7 @@ module ol_framework::mock { use ol_framework::epoch_helper; use ol_framework::musical_chairs; use ol_framework::pledge_accounts; + use ol_framework::secret_bid; // use diem_std::debug::print; @@ -31,6 +31,32 @@ module ol_framework::mock { /// coin supply does not match expected const ESUPPLY_MISMATCH: u64 = 3; + //////// STATIC //////// + /// The starting nominal reward. Also how much each validator will have in their starting balance; as a genesis reward at start of network + const EPOCH_REWARD: u64 = 1_000_000; /// 1M + /// What is the fixed and final supply of the network at start + const FINAL_SUPPLY_AT_GENESIS: u64 = 100_000_000_000; // 100B + /// Place some coins in the system transaction fee account; + const TX_FEE_ACCOUNT_AT_GENESIS: u64 = 100_000_000; // 100M + /// Tbe starting entry fee for validators + const ENTRY_FEE: u64 = 1_000; // 1K + /// Initial funding of infra-escrow + const INFRA_ESCROW_START: u64 = 37_000_000_000; // 37B + + #[test_only] + public fun default_epoch_reward(): u64 { EPOCH_REWARD } + + #[test_only] + /// What is the fixed and final supply of the network at start + public fun default_final_supply_at_genesis(): u64 { FINAL_SUPPLY_AT_GENESIS } + + #[test_only] + public fun default_tx_fee_account_at_genesis(): u64 { TX_FEE_ACCOUNT_AT_GENESIS } + + #[test_only] + public fun default_entry_fee(): u64 { ENTRY_FEE } + + #[test_only] public fun reset_val_perf_one(vm: &signer, addr: address) { stake::mock_performance(vm, addr, 0, 0); @@ -94,43 +120,41 @@ module ol_framework::mock { //////// PROOF OF FEE //////// #[test_only] - public fun pof_default(): (vector
, vector, vector){ + public fun pof_default(framework: &signer): (vector
, vector){ let vals = stake::get_current_validators(); - let (bids, expiry) = mock_bids(&vals); + let bids = mock_bids(framework, &vals); // make all validators pay auction fee // the clearing price in the fibonacci sequence is is 1 - let (alice_bid, _) = proof_of_fee::current_bid(*vector::borrow(&vals, 0)); + let alice_bid = secret_bid::get_bid_unchecked(*vector::borrow(&vals, 0)); + assert!(alice_bid == 1, 03); - (vals, bids, expiry) + (vals, bids) } #[test_only] - public fun mock_bids(vals: &vector
): (vector, vector) { - // system_addresses::assert_ol(vm); + public fun mock_bids(framework: &signer, vals: &vector
): vector { let bids = vector::empty(); - let expiry = vector::empty(); let i = 0; let prev = 0; let fib = 1; while (i < vector::length(vals)) { - - vector::push_back(&mut expiry, 1000); let b = prev + fib; vector::push_back(&mut bids, b); let a = vector::borrow(vals, i); let sig = account::create_signer_for_test(*a); - // initialize and set. - proof_of_fee::pof_update_bid(&sig, b, 1000); + // initialize and set. Will likely by epoch 0, genesis. + let epoch = epoch_helper::get_current_epoch(); + secret_bid::mock_revealed_bid(framework, &sig, b, epoch); prev = fib; fib = b; i = i + 1; }; - (bids, expiry) + bids } use diem_framework::chain_status; @@ -177,22 +201,28 @@ module ol_framework::mock { #[test_only] public fun ol_initialize_coin_and_fund_vals(root: &signer, amount: u64, drip: bool) { - system_addresses::assert_ol(root); - - let mint_cap = if (coin::is_coin_initialized()) { - libra_coin::extract_mint_cap(root) - } else { - init_coin_impl(root) + if (!coin::is_coin_initialized()) { + let mint_cap = init_coin_impl(root); + libra_coin::restore_mint_cap(root, mint_cap); }; + fund_validators(root, amount, drip); + } + + fun fund_validators(root: &signer, amount: u64, + drip: bool) { + system_addresses::assert_ol(root); + let vals = stake::get_current_validators(); let i = 0; while (i < vector::length(&vals)) { let addr = vector::borrow(&vals, i); - let c = coin::test_mint(amount, &mint_cap); - ol_account::deposit_coins(*addr, c); + + let coin = pledge_accounts::test_single_withdrawal(root, @0xBA7, amount); + ol_account::vm_deposit_coins_locked(root, *addr, coin); let b = libra_coin::balance(*addr); + assert!(b == amount, 0001); i = i + 1; @@ -201,9 +231,14 @@ module ol_framework::mock { if (drip) { slow_wallet::slow_wallet_epoch_drip(root, amount); }; - libra_coin::restore_mint_cap(root, mint_cap); } + public fun mock_tx_fees_in_account(root: &signer, amount: u64) { + let tx_fees_start = pledge_accounts::test_single_withdrawal(root, @0xBA7, amount); + transaction_fee::vm_pay_fee(root, @ol_framework, tx_fees_start); + } + + #[test_only] fun init_coin_impl(root: &signer): coin::MintCapability { system_addresses::assert_ol(root); @@ -213,27 +248,25 @@ module ol_framework::mock { transaction_fee::initialize_fee_collection_and_distribution(root, 0); - let initial_fees = 5_000_000 * 100; // coin scaling * 100 coins - let tx_fees = coin::test_mint(initial_fees, &mint_cap); - transaction_fee::vm_pay_fee(root, @ol_framework, tx_fees); + let genesis_mint = coin::test_mint(FINAL_SUPPLY_AT_GENESIS, &mint_cap); + libra_coin::test_set_final_supply(root, FINAL_SUPPLY_AT_GENESIS); + assert!(libra_coin::supply() == FINAL_SUPPLY_AT_GENESIS, ESUPPLY_MISMATCH); - // Forge Bruce - let fortune = 100_000_000_000; + + // We need to simulate a long running network + // where the Infra Pledge account accumulated. + // Here we use Wayne Enterprises as the sole sponsor of the + // test environment Infra Pedge let bruce_address = @0xBA7; ol_account::create_account(root, bruce_address); + // Bruce inherits a fortune, the remainder of genesis mint + ol_account::deposit_coins(bruce_address, genesis_mint); - // Bruce mints a fortune + // Bruce pledges 37B to infra escrow let bruce = account::create_signer_for_test(bruce_address); - let fortune_mint = coin::test_mint(fortune, &mint_cap); - ol_account::deposit_coins(bruce_address, fortune_mint); - - // Bruce funds infra escrow - let framework = signer::address_of(root); - pledge_accounts::user_pledge(&bruce, framework, 37_000_000_000); + pledge_accounts::user_pledge(&bruce, @ol_framework, INFRA_ESCROW_START); - let supply_pre = libra_coin::supply(); - assert!(supply_pre == (initial_fees + fortune), ESUPPLY_MISMATCH); - libra_coin::test_set_final_supply(root, initial_fees); + assert!(libra_coin::supply() == FINAL_SUPPLY_AT_GENESIS, ESUPPLY_MISMATCH); mint_cap } @@ -290,6 +323,8 @@ module ol_framework::mock { i = i + 1; }; + + pof_default(root); } #[test_only] @@ -304,7 +339,7 @@ module ol_framework::mock { public fun trigger_epoch(root: &signer) { trigger_epoch_exactly_at( root, - reconfiguration::get_current_epoch(), + reconfiguration::current_epoch(), block::get_current_block_height() ); } @@ -314,11 +349,11 @@ module ol_framework::mock { epoch_boundary::ol_reconfigure_for_test(root, old_epoch, round); // always advance - assert!(reconfiguration::get_current_epoch() > old_epoch, + assert!(reconfiguration::current_epoch() > old_epoch, EDID_NOT_ADVANCE_EPOCH); // epoch helper should always be in sync - assert!(reconfiguration::get_current_epoch() == epoch_helper::get_current_epoch(), 666); + assert!(reconfiguration::current_epoch() == epoch_helper::get_current_epoch(), 666); } @@ -328,7 +363,7 @@ module ol_framework::mock { public fun meta_epoch(root: signer) { ol_test_genesis(&root); musical_chairs::initialize(&root, 10); - // ol_initialize_coin(&root); + let epoch = reconfiguration::current_epoch(); trigger_epoch(&root); let new_epoch = reconfiguration::current_epoch(); @@ -345,7 +380,7 @@ module ol_framework::mock { // will assert! case_1 mock_case_1(&root, *addr); - pof_default(); + pof_default(&root); // will assert! case_4 mock_case_4(&root, *addr); @@ -364,12 +399,10 @@ module ol_framework::mock { let n_vals = 5; let _vals = genesis_n_vals(root, n_vals); // need to include eve to init funds - let genesis_mint = 1_000_000; - ol_initialize_coin_and_fund_vals(root, genesis_mint, true); - let supply_pre = libra_coin::supply(); - let bruce_fortune = 100_000_000_000; - let mocked_tx_fees = 5_000_000 * 100; - assert!(supply_pre == bruce_fortune + mocked_tx_fees + (n_vals * genesis_mint), 73570001); + + ol_initialize_coin_and_fund_vals(root, EPOCH_REWARD, true); + + assert!(libra_coin::supply() == FINAL_SUPPLY_AT_GENESIS, 73570001); } @@ -378,15 +411,15 @@ module ol_framework::mock { // Scenario: unit testing that pof_default results in a usable auction let n_vals = 5; let vals = genesis_n_vals(root, n_vals); // need to include eve to init funds - pof_default(); + pof_default(root); proof_of_fee::fill_seats_and_get_price(root, n_vals, &vals, &vals); let (nominal_reward, entry_fee, clearing_percent, median_bid ) = proof_of_fee::get_consensus_reward(); - assert!(nominal_reward == 1_000_000, 73570001); + assert!(nominal_reward == EPOCH_REWARD, 73570001); assert!(clearing_percent == 1, 73570002); - assert!(entry_fee == 1_000, 73570003); + assert!(entry_fee == ENTRY_FEE, 73570003); assert!(median_bid == 3, 73570004); } } diff --git a/framework/libra-framework/sources/ol_sources/musical_chairs.move b/framework/libra-framework/sources/ol_sources/musical_chairs.move index 8e3283a8b..a9e38ca3f 100644 --- a/framework/libra-framework/sources/ol_sources/musical_chairs.move +++ b/framework/libra-framework/sources/ol_sources/musical_chairs.move @@ -1,12 +1,15 @@ module ol_framework::musical_chairs { + use std::error; use std::fixed_point32; use std::vector; use diem_framework::chain_status; use diem_framework::system_addresses; use diem_framework::stake; use ol_framework::grade; + use ol_framework::jail; use ol_framework::testnet; - //use diem_std::debug::print; + + // use diem_std::debug::print; friend diem_framework::genesis; friend diem_framework::diem_governance; @@ -14,6 +17,12 @@ module ol_framework::musical_chairs { #[test_only] friend ol_framework::mock; + //////// ERROR CODES //////// + /// non_compliant vals, should not appear in compliant + const EBAD_APPLE_IN_COMPLIANT: u64 = 0; + + + ///////// STATIC //////// /// We don't want to play the validator selection games /// before we're clear out of genesis const EPOCH_TO_START_EVAL: u64 = 2; @@ -21,6 +30,7 @@ module ol_framework::musical_chairs { /// when there are too few rounds committed const MINIMUM_ROUNDS_PER_EPOCH: u64 = 1000; + struct Chairs has key { // The number of chairs in the game seats_offered: u64, @@ -86,7 +96,18 @@ module ol_framework::musical_chairs { let chairs = borrow_global_mut(@ol_framework); let validators = stake::get_current_validators(); - let (compliant_vals, _non, fail_ratio) = eval_compliance_impl(validators, epoch, round); + let (compliant_vals, bad, fail_ratio) = eval_compliance_impl(validators, epoch, round); + + // jail the non-compliant + // commit note: we moved this from epoch_boundary process_outgoing, which + // is concerned mostly with payment. + // TODO:L Ideally stop_the_music would be a pure function. + // move the jailing to top level in epoch_boundary. + vector::for_each(bad, |addr| { + jail::jail(vm, addr); + }); + + let num_compliant_vals = vector::length(&compliant_vals); // Error handle. We should not have gone into an epoch where we had MORE validators than seats offered. @@ -176,6 +197,11 @@ module ol_framework::musical_chairs { let good_len = vector::length(&compliant_nodes) ; let bad_len = vector::length(&non_compliant_nodes); + // sanity check that the bad ones are not in the compliant list + vector::for_each_ref(&non_compliant_nodes, |el| { + assert!(!vector::contains(&compliant_nodes, el), error::invalid_state(EBAD_APPLE_IN_COMPLIANT)); + }); + // Note: sorry for repetition but necessary for writing tests and debugging. let null = fixed_point32::create_from_raw_value(0); if (good_len > val_set_len) { // safety @@ -190,11 +216,8 @@ module ol_framework::musical_chairs { return (vector::empty(), vector::empty(), null) }; - let ratio = if (bad_len > 0) { - fixed_point32::create_from_rational(bad_len, val_set_len) - } else { - null - }; + // commit note: if it's zero we want to know, and set it explicitly + let ratio = fixed_point32::create_from_rational(bad_len, val_set_len); (compliant_nodes, non_compliant_nodes, ratio) } diff --git a/framework/libra-framework/sources/ol_sources/ol_account.move b/framework/libra-framework/sources/ol_sources/ol_account.move index 7fe1da168..20adceb0f 100644 --- a/framework/libra-framework/sources/ol_sources/ol_account.move +++ b/framework/libra-framework/sources/ol_sources/ol_account.move @@ -38,6 +38,8 @@ module ol_framework::ol_account { friend ol_framework::test_multi_action; #[test_only] friend ol_framework::test_slow_wallet; + #[test_only] + friend ol_framework::mock; /// Account does not exist. const EACCOUNT_NOT_FOUND: u64 = 1; @@ -182,7 +184,6 @@ module ol_framework::ol_account { let sig_addr = signer::address_of(&new_signer); if (lookup_addr != sig_addr) { - // print(&lookup_addr); print(&sig_addr); }; diff --git a/framework/libra-framework/sources/ol_sources/pledge_accounts.move b/framework/libra-framework/sources/ol_sources/pledge_accounts.move index c25cf387c..e575182db 100644 --- a/framework/libra-framework/sources/ol_sources/pledge_accounts.move +++ b/framework/libra-framework/sources/ol_sources/pledge_accounts.move @@ -528,7 +528,6 @@ save_pledge(user_sig, beneficiary, coin); } - ////////// GETTERS ////////// fun pledge_at_idx(account: &address, address_of_beneficiary: &address): (bool, u64) acquires MyPledges { @@ -656,9 +655,16 @@ //////// TEST HELPERS /////// #[test_only] // Danger! withdraws from an account. - public fun test_single_withdrawal(vm: &signer, bene: &address, donor: &address, amount: u64): option::Option> acquires MyPledges, BeneficiaryPolicy{ - system_addresses::assert_ol(vm); - // testnet::assert_testnet(vm); - withdraw_from_one_pledge_account(bene, donor, amount) + public fun test_single_withdrawal(framework: &signer, donor: address, amount: u64): coin::Coin acquires MyPledges, BeneficiaryPolicy{ + use ol_framework::testnet; + system_addresses::assert_diem_framework(framework); + testnet::assert_testnet(framework); + + let opt = withdraw_from_one_pledge_account(&@ol_framework, &donor, amount); + assert!(option::is_some(&opt), 0); + + let c = option::extract(&mut opt); + option::destroy_none(opt); + return c } } 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 abf96b859..7e9336413 100644 --- a/framework/libra-framework/sources/ol_sources/proof_of_fee.move +++ b/framework/libra-framework/sources/ol_sources/proof_of_fee.move @@ -23,7 +23,9 @@ module ol_framework::proof_of_fee { use ol_framework::slow_wallet; use ol_framework::epoch_helper; use ol_framework::address_utils; - //use diem_std::debug::print; + use ol_framework::secret_bid; + + // use diem_std::debug::print; friend diem_framework::genesis; friend ol_framework::epoch_boundary; @@ -53,6 +55,9 @@ module ol_framework::proof_of_fee { const LONG_WINDOW: u64 = 10; // 10 epochs /// Margin for vouches const VOUCH_MARGIN: u64 = 2; + /// Maximum days before a bid expires. + const MAXIMUM_BID_EXPIRATION_EPOCHS: u64 = 30; + //////// ERRORS ///////// /// Not an active validator @@ -75,6 +80,10 @@ module ol_framework::proof_of_fee { const EBID_EXPIRED: u64 = 16; /// not enough coin balance const ELOW_UNLOCKED_COIN_BALANCE: u64 = 17; + /// reward should never reach zero, very bad + const EWTF_WHY_IS_REWARD_ZERO: u64 = 18; + /// don't try to set a net reward greater than the max epoch reward + const ENET_REWARD_GREATER_THAN_REWARD: u64 = 19; // A struct on the validators account which indicates their // latest bid (and epoch) @@ -157,13 +166,14 @@ module ol_framework::proof_of_fee { vm: &signer, outgoing_compliant_set: &vector
, mc_set_size: u64 // musical chairs set size suggestion - ): (vector
, vector
, vector
, u64) acquires ProofOfFeeAuction, ConsensusReward { + ): (vector
, vector
, vector
, u64) acquires ConsensusReward { system_addresses::assert_ol(vm); let all_bidders = get_bidders(false); + let only_qualified_bidders = get_bidders(true); - // Calculate the final set size considering the number of compliant validators, + // Calculate the final set size considering the number of compliantget_bidders validators, // number of qualified bidders, and musical chairs set size suggestion let final_set_size = calculate_final_set_size( vector::length(outgoing_compliant_set), @@ -249,20 +259,21 @@ module ol_framework::proof_of_fee { #[view] - public fun get_bidders(remove_unqualified: bool): vector
acquires ProofOfFeeAuction, ConsensusReward { + public fun get_bidders(remove_unqualified: bool): vector
acquires ConsensusReward { let eligible_validators = validator_universe::get_eligible_validators(); + let (bidders, _) = sort_vals_impl(&eligible_validators, remove_unqualified); bidders } #[view] // same as get bidders, but returns the bid - public fun get_bidders_and_bids(remove_unqualified: bool): (vector
, vector) acquires ProofOfFeeAuction, ConsensusReward { + public fun get_bidders_and_bids(remove_unqualified: bool): (vector
, vector) acquires ConsensusReward { let eligible_validators = validator_universe::get_eligible_validators(); sort_vals_impl(&eligible_validators, remove_unqualified) } // returns two lists: ordered bidder addresss and the list of bids bid - fun sort_vals_impl(eligible_validators: &vector
, remove_unqualified: bool): (vector
, vector) acquires ProofOfFeeAuction, ConsensusReward { + fun sort_vals_impl(eligible_validators: &vector
, remove_unqualified: bool): (vector
, vector) acquires ConsensusReward { // let eligible_validators = validator_universe::get_eligible_validators(); let length = vector::length
(eligible_validators); @@ -273,13 +284,14 @@ module ol_framework::proof_of_fee { while (k < length) { // TODO: Ensure that this address is an active validator let cur_address = *vector::borrow
(eligible_validators, k); - let (bid, _expire) = current_bid(cur_address); - let (_, qualified) = audit_qualification(cur_address); + let entry_fee = secret_bid::current_revealed_bid(cur_address); + let (_err, qualified) = audit_qualification(cur_address); + if (remove_unqualified && !qualified) { k = k + 1; continue }; - vector::push_back(&mut bids, bid); + vector::push_back(&mut bids, entry_fee); vector::push_back
(&mut filtered_vals, cur_address); k = k + 1; }; @@ -391,7 +403,7 @@ module ol_framework::proof_of_fee { final_set_size: u64, sorted_vals_by_bid: &vector
, proven_nodes: &vector
- ): (vector
, u64, u64, vector
, vector
) acquires ProofOfFeeAuction, ConsensusReward { + ): (vector
, u64, u64, vector
, vector
) acquires ConsensusReward { system_addresses::assert_ol(vm); // NOTE: this is duplicate work, but we are double checking we are getting a proper sort. @@ -413,10 +425,12 @@ module ol_framework::proof_of_fee { let num_unproven_added = 0; let i = 0u64; + while ( (vector::length(&proposed_validators) < final_set_size) && // until seats full (i < vector::length(&sorted_vals_by_bid)) ) { + let val = vector::borrow(&sorted_vals_by_bid, i); if (!account::exists_at(*val)) { i = i + 1; @@ -452,7 +466,7 @@ module ol_framework::proof_of_fee { // Find the clearing price which all validators will pay let lowest_bidder = vector::borrow(&proposed_validators, vector::length(&proposed_validators) - 1); - let (lowest_bid_pct, _) = current_bid(*lowest_bidder); + let lowest_bid_pct = secret_bid::current_revealed_bid(*lowest_bidder); // update the clearing price let cr = borrow_global_mut(@ol_framework); @@ -472,13 +486,12 @@ module ol_framework::proof_of_fee { cr.net_reward = cr.nominal_reward; }; - return (proposed_validators, cr.entry_fee, cr.clearing_bid, audit_add_proven_vals, audit_add_unproven_vals) } #[view] /// consolidate all the checks for a validator to be seated - public fun audit_qualification(val: address): (vector, bool) acquires ProofOfFeeAuction, ConsensusReward { + public fun audit_qualification(val: address): (vector, bool) acquires ConsensusReward { let errors = vector::empty(); // Safety check: node has valid configs @@ -495,12 +508,14 @@ module ol_framework::proof_of_fee { if (!is_above_thresh) vector::push_back(&mut errors, ETOO_FEW_VOUCHES); // 14 // check if current BIDS are valid - let (bid_pct, expire) = current_bid(val); - if (bid_pct == 0) vector::push_back(&mut errors, EBID_IS_ZERO); // 15 + let entry_fee_bid = secret_bid::current_revealed_bid(val); + if (entry_fee_bid == 0) vector::push_back(&mut errors, EBID_IS_ZERO); // 15 // Skip if the bid expired. belt and suspenders, this should have been checked in the sorting above. // TODO: make this it's own function so it can be publicly callable, it's useful generally, and for debugging. - if (epoch_helper::get_current_epoch() > expire) vector::push_back(&mut errors, EBID_EXPIRED); // 16 + let valid = secret_bid::has_valid_bid(val); + + if (!valid) vector::push_back(&mut errors, EBID_EXPIRED); // 16 // skip the user if they don't have sufficient UNLOCKED funds // or if the bid expired. let unlocked_coins = slow_wallet::unlocked_amount(val); @@ -600,7 +615,7 @@ module ol_framework::proof_of_fee { /// find the median bid to push to history // this is needed for reward_thermostat - fun set_history(vm: &signer, proposed_validators: &vector
) acquires ProofOfFeeAuction, ConsensusReward { + fun set_history(vm: &signer, proposed_validators: &vector
) acquires ConsensusReward { system_addresses::assert_ol(vm); let median_bid = get_median(proposed_validators); @@ -617,7 +632,7 @@ module ol_framework::proof_of_fee { }; } - fun get_median(proposed_validators: &vector
):u64 acquires ProofOfFeeAuction { + fun get_median(proposed_validators: &vector
):u64 { // TODO: the list is sorted above, so // we assume the median is the middle element let len = vector::length(proposed_validators); @@ -629,8 +644,8 @@ module ol_framework::proof_of_fee { } else { vector::borrow(proposed_validators, 0) }; - let (median_bid, _) = current_bid(*median_bidder); - return median_bid + + secret_bid::current_revealed_bid(*median_bidder) } //////////////// GETTERS //////////////// @@ -645,24 +660,42 @@ module ol_framework::proof_of_fee { // get the current bid for a validator // CONSENSUS CRITICAL - // ALL EYES ON THIS // Proof of Fee returns the current bid of the validator during the auction for upcoming epoch seats. // returns (current bid, expiration epoch) + // #[view] + // public fun current_bid(node_addr: address): (u64, u64) acquires ProofOfFeeAuction { + // if (exists(node_addr)) { + // let pof = borrow_global(node_addr); + // let e = epoch_helper::get_current_epoch(); + // // check the expiration of the bid + // // the bid is zero if it expires. + // // The expiration epoch number is inclusive of the epoch. + // // i.e. the bid expires on e + 1. + // if (pof.epoch_expiration >= e || pof.epoch_expiration == 0) { + // return (pof.bid, pof.epoch_expiration) + // }; + // return (0, pof.epoch_expiration) + // }; + // return (0, 0) + // } + + #[view] - public fun current_bid(node_addr: address): (u64, u64) acquires ProofOfFeeAuction { - if (exists(node_addr)) { - let pof = borrow_global(node_addr); - let e = epoch_helper::get_current_epoch(); - // check the expiration of the bid - // the bid is zero if it expires. - // The expiration epoch number is inclusive of the epoch. - // i.e. the bid expires on e + 1. - if (pof.epoch_expiration >= e || pof.epoch_expiration == 0) { - return (pof.bid, pof.epoch_expiration) - }; - return (0, pof.epoch_expiration) - }; - return (0, 0) + /// Convenience function to calculate the implied net reward + /// that the validator is seeking on a per-epoch basis. + /// @returns the unscaled coin value (not human readable) of the net reward + /// the user expects + public fun user_net_reward(node_addr: address): u64 acquires + ConsensusReward { + // get the user percentage rate + + let user_entry_fee = secret_bid::current_revealed_bid(node_addr); + if (user_entry_fee == 0) return 0; + // get the current nominal reward + let (nominal_reward, _, _ , _) = get_consensus_reward(); + if (user_entry_fee >= nominal_reward) return 0; + + return nominal_reward - user_entry_fee } #[view] @@ -696,7 +729,7 @@ module ol_framework::proof_of_fee { // Get the top N validators by bid, this is FILTERED by default - public(friend) fun top_n_accounts(account: &signer, n: u64, unfiltered: bool): vector
acquires ProofOfFeeAuction, ConsensusReward { + public(friend) fun top_n_accounts(account: &signer, n: u64, unfiltered: bool): vector
acquires ConsensusReward { system_addresses::assert_vm(account); let eligible_validators = get_bidders(unfiltered); @@ -735,6 +768,47 @@ module ol_framework::proof_of_fee { pof.bid = bid; } + /// converts a current desired net_reward to the internal bid percentage + // Note: this uses the current epoch reward, which may not reflect the + // incoming epochs ajusted reward. + fun convert_net_reward_to_bid(net_reward: u64): u64 acquires ConsensusReward { + // if user wants zero, return 100% scaled + if (net_reward == 0) { + return 1000 + }; + + let (nominal_reward, _, _ , _) = get_consensus_reward(); + assert!(nominal_reward > 0, EWTF_WHY_IS_REWARD_ZERO); + assert!(net_reward < nominal_reward, ENET_REWARD_GREATER_THAN_REWARD); + + let pct_with_decimal = (net_reward * 10) / nominal_reward; + + return pct_with_decimal + } + + /// Instead of setting a bid with the internal variables of pct bid, we + /// allow the user to set their expected net_reward in an epoch + fun set_net_reward(account_sig: &signer, net_reward: u64, expiry_epoch: u64) acquires + ConsensusReward, ProofOfFeeAuction { + // double check the epoch expiry + let epoch_checked = check_epoch_expiry(expiry_epoch); + // convert to bid + let scaled_pct = convert_net_reward_to_bid(net_reward); + set_bid(account_sig, scaled_pct, epoch_checked); + } + + /// check if the expiry is too far in the future + /// and if so, return what the maximum allowed would be. + /// if within range returns the provided epoch without change + /// @returns checked epoch for bid expiration + fun check_epoch_expiry(expiry_epoch: u64): u64 { + let this_epoch = epoch_helper::get_current_epoch(); + if (expiry_epoch > MAXIMUM_BID_EXPIRATION_EPOCHS) { + return this_epoch + MAXIMUM_BID_EXPIRATION_EPOCHS + }; + expiry_epoch + } + /// Note that the validator will not be bidding on any future /// epochs if they retract their bid. The must set a new bid. fun retract_bid(account_sig: &signer) acquires ProofOfFeeAuction { @@ -765,9 +839,18 @@ module ol_framework::proof_of_fee { } /// update the bid for the sender - public entry fun pof_update_bid(sender: &signer, bid: u64, epoch_expiry: u64) acquires ProofOfFeeAuction { + // Deprecated + // public entry fun pof_update_bid(sender: &signer, bid: u64, epoch_expiry: u64) acquires ProofOfFeeAuction { + // // update the bid, initializes if not already. + // set_bid(sender, bid, epoch_expiry); + // } + + /// update the bid using estimated net reward instead of the internal bid variables + public entry fun pof_update_bid_net_reward(sender: &signer, net_reward: u64, + epoch_expiry: u64) acquires ProofOfFeeAuction, ConsensusReward { + let checked_epoch = check_epoch_expiry(epoch_expiry); // update the bid, initializes if not already. - set_bid(sender, bid, epoch_expiry); + set_net_reward(sender, net_reward, checked_epoch); } /// retract bid @@ -1068,23 +1151,7 @@ module ol_framework::proof_of_fee { assert!(median_bid == 33, 1005); } - // #[test(vm = @ol_framework)] - // fun pof_set_retract(vm: signer) { - // use diem_framework::account; - - // validator_universe::initialize(&vm); - - // let sig = account::create_signer_for_test(@0x123); - // let (_sk, pk, pop) = stake::generate_identity(); - // stake::initialize_test_validator(&pk, &pop, &sig, 100, true, true); - - // validator_universe::is_in_universe(@0x123); - - // } - - // Calculate Final Set Size tests - #[test] fun test_calculate_final_set_size_boot_up_happy_day() { // Happy Day: test complete boot up with plenty qualified bidders over multiple epochs diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move new file mode 100644 index 000000000..105797288 --- /dev/null +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -0,0 +1,495 @@ +///////////////////////////////////////////////////////////////////////// +// 0L Module +// Secret Bid +// Implements a commit-reveal scheme for more private bidding in PoF auction +/////////////////////////////////////////////////////////////////////////// + +module ol_framework::secret_bid { + use std::bcs; + use std::error; + use std::hash; + use std::signer; + use std::vector; + + use diem_std::ed25519; + use diem_std::comparator; + use diem_framework::epoch_helper; + use diem_framework::reconfiguration; + use diem_framework::system_addresses; + + use ol_framework::testnet; + use ol_framework::address_utils; + + friend ol_framework::genesis; + friend ol_framework::proof_of_fee; + + #[test_only] + use diem_framework::account; + + #[test_only] + friend ol_framework::test_boundary; + #[test_only] + friend ol_framework::mock; + #[test_only] + friend ol_framework::test_pof; + + /// User bidding not initialized + const ECOMMIT_BID_NOT_INITIALIZED: u64 = 1; + /// Invalid signature on bid message + const EINVALID_SIGNATURE: u64 = 2; + /// Must reveal bid in same epoch as committed + const EMISMATCH_EPOCH: u64 = 3; + /// Must submit bid before reveal window opens, and reveal bid only after. + const ENOT_IN_REVEAL_WINDOW: u64 = 4; + /// Bad Alice, the reveal does not match the commit + const ECOMMIT_DIGEST_NOT_EQUAL: u64 = 5; + /// Bid is for different epoch, expired + const EBID_EXPIRED: u64 = 6; + + struct CommittedBid has key { + reveal_entry_fee: u64, + entry_fee_history: vector, // keep previous 7 days bids + commit_digest: vector, + commit_epoch: u64, + } + + struct Bid has drop, copy { + entry_fee: u64, + epoch: u64, + } + + public(friend) fun genesis_helper(framework: &signer, validator: &signer, entry_fee: u64) acquires CommittedBid { + system_addresses::assert_diem_framework(framework); + commit_entry_fee_impl(validator, b"genesis"); + + let state = borrow_global_mut(signer::address_of(validator)); + state.reveal_entry_fee = entry_fee; + } + + /// Transaction entry function for committing bid + public entry fun commit(user: &signer, digest: vector) acquires CommittedBid { + // don't allow commiting within reveal window + assert!(!in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); + commit_entry_fee_impl(user, digest); + } + + /// Transaction entry function for revealing bid + public entry fun reveal(user: &signer, pk: vector, entry_fee: u64, signed_msg: vector) acquires CommittedBid { + // don't allow commiting within reveal window + assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); + reveal_entry_fee_impl(user, pk, entry_fee, signed_msg); + } + + /// user transaction for setting a signed message with the bid + fun commit_entry_fee_impl(user: &signer, digest: vector) acquires CommittedBid { + + if (!is_init(signer::address_of(user))) { + move_to(user, CommittedBid { + reveal_entry_fee: 0, + entry_fee_history: vector::empty(), + commit_digest: vector::empty(), + commit_epoch: 0, + }); + }; + + let state = borrow_global_mut(signer::address_of(user)); + // if first commit in an epoch reset the counters + maybe_reset_bids(state); + + state.commit_digest = digest; + } + + /// if this is the first commit in the epoch then we can reset bids + fun maybe_reset_bids(state: &mut CommittedBid) { + if (epoch_helper::get_current_epoch() > state.commit_epoch) { + // restart bidding + state.commit_epoch = epoch_helper::get_current_epoch(); + + vector::push_back(&mut state.entry_fee_history, state.reveal_entry_fee); + + if (vector::length(&state.entry_fee_history) > 7) { + vector::trim(&mut state.entry_fee_history, 7); + }; + + state.reveal_entry_fee = 0; + } + } + + /// The hashing protocol which the client will be submitting commitments + /// instead of a sequence number, we are using the signed message for hashing nonce, which would be private to the validator before the reveal. + // TODO: should we also use a salt or overkill for these purposes? + fun make_hash(entry_fee: u64, epoch: u64, signed_message: vector): vector{ + let bid = Bid { + entry_fee, + epoch, + }; + + let bid_bytes = bcs::to_bytes(&bid); + vector::append(&mut bid_bytes, copy signed_message); + let commitment = hash::sha3_256(bid_bytes); + commitment + } + + /// user sends transaction which takes the committed signed message + /// submits the public key used to sign message, which we compare to the authentication key. + /// we use the epoch as the sequence number, so that messages are different on each submission. + public fun reveal_entry_fee_impl(user: &signer, pk: vector, entry_fee: u64, signed_msg: vector) acquires CommittedBid { + assert!(is_init(signer::address_of(user)), error::invalid_state(ECOMMIT_BID_NOT_INITIALIZED)); + + let state = borrow_global_mut(signer::address_of(user)); + // must reveal within the current epoch of the bid + let epoch = epoch_helper::get_current_epoch(); + assert!(epoch == state.commit_epoch, error::invalid_state(EMISMATCH_EPOCH)); + + let commitment = make_hash(entry_fee, epoch, signed_msg); + + let bid = Bid { + entry_fee, + epoch, + }; + + check_signature(pk, signed_msg, bid); + + assert!(comparator::is_equal(&comparator::compare(&commitment, &state.commit_digest)), error::invalid_argument(ECOMMIT_DIGEST_NOT_EQUAL)); + + state.reveal_entry_fee = entry_fee; + } + + fun check_signature(account_public_key_bytes: vector, signed_message_bytes: vector, bid_message: Bid) { + let pubkey = ed25519::new_unvalidated_public_key_from_bytes(account_public_key_bytes); + + // confirm that the signature bytes belong to the message (the Bid struct) + let sig = ed25519::new_signature_from_bytes(signed_message_bytes); + assert!(ed25519::signature_verify_strict_t(&sig, &pubkey, bid_message), error::invalid_argument(EINVALID_SIGNATURE)); + + ///////// + // NOTE: previously we would check if the public key for the signed message + // belonged to this account. However that is not necessary for prevention of the replay attack. Any ed25519 key pair would be acceptable for producing the nonce, and the user may prefer to use one that is different than their main account's key. + // let expected_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&pubkey); + // assert!(account::get_authentication_key(signer::address_of(user)) == expected_auth_key, error::invalid_argument(EWRONG_CURRENT_PUBLIC_KEY)); + //////// + } + + /// populate the list of bids, but don't sort + fun get_bids_for_account_list(list: vector
): (vector
, vector) acquires CommittedBid { + let bids_vec = vector[]; + vector::for_each_ref(&list, |el| { + let b = get_bid_unchecked(*el); + vector::push_back(&mut bids_vec, b); + }); + + (list, bids_vec) + } + + /// get a sorted list of the addresses and their entry fee + public(friend) fun get_bids_by_account_sort_low_high(list: vector
): (vector
, vector) acquires CommittedBid { + let (addr, bids) = get_bids_for_account_list(list); + address_utils::sort_by_values(&mut addr, &mut bids); + (addr, bids) + } + + ///////// GETTERS //////// + + public(friend) fun get_bid_unchecked(user: address): u64 acquires CommittedBid { + let state = borrow_global(user); + + state.reveal_entry_fee + } + + #[view] + /// check if we are within the reveal window + /// do not allow bids within the reveal window + /// allow reveal transaction to be submitted + public fun in_reveal_window(): bool { + // get the timestamp + let remaining_secs = reconfiguration::get_remaining_epoch_secs(); + let window = if (testnet::is_testnet()) { + // ten secs + 10 + } else { + // five mins + 60*5 + }; + + if (remaining_secs > window) { + return false + }; + true + } + + #[view] + public fun is_init(account: address): bool { + exists(account) + } + + #[view] + /// get the current bid, and exclude bids that are stale + public fun current_revealed_bid(user: address): u64 acquires CommittedBid { + // // if we are not in reveal window this information will be confusing. + // assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); + + // let state = borrow_global(user); + // assert!(state.commit_epoch == epoch_helper::get_current_epoch(), error::invalid_state(EBID_EXPIRED)); + + // state.reveal_entry_fee + get_bid_unchecked(user) + } + + /// Find the most recent epoch the validator has placed a bid on. + public(friend) fun latest_epoch_bid(user: address): u64 acquires CommittedBid { + let state = borrow_global(user); + state.commit_epoch + } + + /// does the user have a current bid + public(friend) fun has_valid_bid(user: address): bool acquires CommittedBid { + let state = borrow_global(user); + state.commit_epoch == epoch_helper::get_current_epoch() + } + + /// will abort if bid is not valid + fun assert_valid_bid(user: address) acquires CommittedBid { + // if we are not in reveal window this information will be confusing. + assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); + + assert!(has_valid_bid(user), error::invalid_state(EBID_EXPIRED)); + } + + #[view] + /// get the current bid, and exclude bids that are stale + public fun historical_bids(user: address): vector acquires CommittedBid { + let state = borrow_global(user); + state.entry_fee_history + } + + //////// TESTS //////// + + #[test_only] + public(friend) fun mock_revealed_bid(framework: &signer, user: &signer, reveal_entry_fee: u64, commit_epoch: u64) acquires CommittedBid { + system_addresses::assert_diem_framework(framework); + testnet::assert_testnet(framework); + let user_addr = signer::address_of(user); + if (!exists(user_addr)) { + move_to(user, CommittedBid { + reveal_entry_fee, + entry_fee_history: vector[0], + commit_digest: vector[0], + commit_epoch, + }); + } else { + let state = borrow_global_mut(user_addr); + state.reveal_entry_fee = reveal_entry_fee; + state.commit_epoch = commit_epoch; + } + + } + + #[test] + fun test_sign_message() { + use diem_std::from_bcs; + + let (new_sk, new_pk) = ed25519::generate_keys(); + let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); + let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); + let new_addr = from_bcs::to_address(new_auth_key); + let _alice = account::create_account_for_test(new_addr); + + let message = Bid { + entry_fee: 0, + epoch: 0, + }; + + let to_sig = ed25519::sign_struct(&new_sk, copy message); + let sig_bytes = ed25519::signature_to_bytes(&to_sig); + // end set-up + + // yes repetitive, but following the same workflow + let sig_again = ed25519::new_signature_from_bytes(sig_bytes); + + assert!(ed25519::signature_verify_strict_t(&sig_again, &new_pk_unvalidated, message), error::invalid_argument(EINVALID_SIGNATURE)); + + + } + + #[test] + fun test_check_signature() { + use diem_std::from_bcs; + + let (new_sk, new_pk) = ed25519::generate_keys(); + let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); + let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); + let new_addr = from_bcs::to_address(new_auth_key); + let _alice = account::create_account_for_test(new_addr); + + let message = Bid { + entry_fee: 0, + epoch: 0, + }; + + let to_sig = ed25519::sign_struct(&new_sk, copy message); + let sig_bytes = ed25519::signature_to_bytes(&to_sig); + // end set-up + + // yes repetitive, but following the same workflow + let sig_again = ed25519::new_signature_from_bytes(sig_bytes); + + assert!(ed25519::signature_verify_strict_t(&sig_again, &new_pk_unvalidated, copy message), error::invalid_argument(EINVALID_SIGNATURE)); + + let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); + + check_signature(pk_bytes, sig_bytes, message); + + } + + #[test] + #[expected_failure(abort_code = 65538, location = Self)] + fun test_check_signature_sad() { + use diem_std::from_bcs; + + let (new_sk, new_pk) = ed25519::generate_keys(); + let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); + let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); + let new_addr = from_bcs::to_address(new_auth_key); + let _alice = account::create_account_for_test(new_addr); + + let message = Bid { + entry_fee: 0, + epoch: 0, + }; + + let to_sig = ed25519::sign_struct(&new_sk, copy message); + let sig_bytes = ed25519::signature_to_bytes(&to_sig); + // end set-up + + // yes repetitive, but following the same workflow + let sig_again = ed25519::new_signature_from_bytes(sig_bytes); + + assert!(ed25519::signature_verify_strict_t(&sig_again, &new_pk_unvalidated, copy message), error::invalid_argument(EINVALID_SIGNATURE)); + + let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); + + let message = Bid { + entry_fee: 2, // incorrect + epoch: 0, + }; + + check_signature(pk_bytes, sig_bytes, message); + + } + + + #[test(framework = @0x1)] + fun test_commit_message(framework: &signer) acquires CommittedBid { + use diem_std::from_bcs; + + let this_epoch = 1; + epoch_helper::test_set_epoch(framework, this_epoch); + + let (new_sk, new_pk) = ed25519::generate_keys(); + let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); + let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); + let new_addr = from_bcs::to_address(new_auth_key); + let alice = account::create_account_for_test(new_addr); + let entry_fee = 5; + let epoch = 0; + + let message = Bid { + entry_fee, + epoch, + }; + + let to_sig = ed25519::sign_struct(&new_sk, copy message); + let sig_bytes = ed25519::signature_to_bytes(&to_sig); + let digest = make_hash(entry_fee, epoch, sig_bytes); + // end set-up + + commit_entry_fee_impl(&alice, digest); + } + + #[test(framework = @0x1)] + fun test_reveal(framework: &signer) acquires CommittedBid { + use diem_std::from_bcs; + + let epoch = 1; + epoch_helper::test_set_epoch(framework, epoch); + + let (new_sk, new_pk) = ed25519::generate_keys(); + let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); + let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); + let new_addr = from_bcs::to_address(new_auth_key); + let alice = account::create_account_for_test(new_addr); + let entry_fee = 5; + let message = Bid { + entry_fee, + epoch, + }; + + let to_sig = ed25519::sign_struct(&new_sk, copy message); + let sig_bytes = ed25519::signature_to_bytes(&to_sig); + let digest = make_hash(entry_fee, epoch, sig_bytes); + // end set-up + + commit_entry_fee_impl(&alice, digest); + + let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); + + check_signature(pk_bytes, sig_bytes, message); + + reveal_entry_fee_impl(&alice, pk_bytes, 5, sig_bytes); + } + + #[test(framework = @0x1)] + #[expected_failure(abort_code = 65538, location = Self)] + fun test_reveal_sad_wrong_epoch(framework: &signer) acquires CommittedBid { + use diem_std::from_bcs; + let epoch = 1; + epoch_helper::test_set_epoch(framework, epoch); + + let (new_sk, new_pk) = ed25519::generate_keys(); + let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); + let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); + let new_addr = from_bcs::to_address(new_auth_key); + let alice = account::create_account_for_test(new_addr); + let entry_fee = 5; + let wrong_epoch = 100; + + let message = Bid { + entry_fee, + epoch: wrong_epoch, // wrong epoch, we are at 1 + }; + + let to_sig = ed25519::sign_struct(&new_sk, copy message); + let sig_bytes = ed25519::signature_to_bytes(&to_sig); + let digest = make_hash(entry_fee, epoch, sig_bytes); + // end set-up + + commit_entry_fee_impl(&alice, digest); + + let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); + + check_signature(pk_bytes, sig_bytes, message); + + reveal_entry_fee_impl(&alice, pk_bytes, 5, sig_bytes); + } + + #[test(framework = @0x1, alice = @0x10001, bob = @0x10002, carol = @0x10003)] + fun test_sorting_secret_bids(framework: &signer, alice: &signer, bob: &signer, carol: &signer) acquires CommittedBid { + testnet::initialize(framework); + + let this_epoch = 1; + epoch_helper::test_set_epoch(framework, this_epoch); + + mock_revealed_bid(framework, alice, 234, this_epoch); + mock_revealed_bid(framework, bob, 1, this_epoch); + mock_revealed_bid(framework, carol, 30, this_epoch); + + let (accounts, bids) = get_bids_for_account_list(vector[@0x10001, @0x10002, @0x10003]); + + assert!(*vector::borrow(&accounts, 0) == @0x10001, 713570001); + assert!(*vector::borrow(&bids, 0) == 234, 713570002); + + let (sorted_accounts, sorted_bids) = get_bids_by_account_sort_low_high(vector[@0x10001, @0x10002, @0x10003]); + assert!(*vector::borrow(&sorted_accounts, 0) == @0x10002, 713570003); + assert!(*vector::borrow(&sorted_bids, 0) == 1, 713570004); + } +} diff --git a/framework/libra-framework/sources/ol_sources/testnet.move b/framework/libra-framework/sources/ol_sources/testnet.move index 1f0aca795..88ea53e1e 100644 --- a/framework/libra-framework/sources/ol_sources/testnet.move +++ b/framework/libra-framework/sources/ol_sources/testnet.move @@ -33,6 +33,13 @@ module ol_framework::testnet { chain_id::get() == 2 // TESTNET named chain } + #[test_only] + public fun initialize(vm: &signer) { + use diem_framework::system_addresses; + system_addresses::assert_ol(vm); + chain_id::initialize_for_test(vm, 4); + } + #[test_only] public fun unset(vm: &signer) { use diem_framework::system_addresses; diff --git a/framework/libra-framework/sources/ol_sources/tests/_meta_epoch.test.move b/framework/libra-framework/sources/ol_sources/tests/_meta_epoch.test.move index a09f0493d..d148b1460 100644 --- a/framework/libra-framework/sources/ol_sources/tests/_meta_epoch.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/_meta_epoch.test.move @@ -11,17 +11,17 @@ module ol_framework::test_meta { let _vals = mock::genesis_n_vals(root, 2); // we are at epoch 0 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 0, 7357001); mock::trigger_epoch(root); // epoch 1 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 1, 7357002); mock::trigger_epoch(root); // epoch 2 mock::trigger_epoch(root); // epoch 3 mock::trigger_epoch(root); // epoch 4 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 4, 7357003); } @@ -33,11 +33,11 @@ module ol_framework::test_meta { // NOTE: genesis_n_vals, DOES trigger a genesis END event. mock::genesis_n_vals(&root, 4); - let a = reconfiguration::get_current_epoch(); + let a = reconfiguration::current_epoch(); // create a new epoch reconfiguration::reconfigure_for_test(); - let b = reconfiguration::get_current_epoch(); + let b = reconfiguration::current_epoch(); assert!(a == 0, 10001); assert!(b == 1, 10002); diff --git a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move index 659f207d1..497f2d533 100644 --- a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move @@ -11,10 +11,12 @@ module ol_framework::test_boundary { use diem_framework::transaction_fee; use diem_framework::account; use ol_framework::burn; + use ol_framework::epoch_helper; use ol_framework::mock; + use ol_framework::grade; use ol_framework::proof_of_fee; + use ol_framework::secret_bid; use ol_framework::jail; - use ol_framework::slow_wallet; use ol_framework::vouch; use ol_framework::testnet; use ol_framework::validator_universe; @@ -22,7 +24,7 @@ module ol_framework::test_boundary { use ol_framework::block; use ol_framework::ol_account; - use diem_std::debug::print; + // use diem_std::debug::print; const Alice: address = @0x1000a; const Bob: address = @0x1000b; @@ -36,16 +38,31 @@ module ol_framework::test_boundary { let set = mock::genesis_n_vals(root, 10); mock::ol_initialize_coin_and_fund_vals(root, 500000, true); mock::mock_all_vals_good_performance(root); - mock::pof_default(); - slow_wallet::slow_wallet_epoch_drip(root, 500000); + // NOTE: if there are no transaction fees mocked, the whole process_outgoing loop will be skipped. + mock::mock_tx_fees_in_account(root, 100_000_000); // NOTE: for e2e epoch tests, we need to go into an operating epoch (not 0 or 1). Advance to epoch #2 reconfiguration::test_helper_increment_epoch_dont_reconfigure(1); reconfiguration::test_helper_increment_epoch_dont_reconfigure(1); + // need to mock the bids again since we jumped to epoch 2 and they would have expired. + mock::pof_default(root); + set } + // test we can get qualified bidders in the auction + #[test(root = @ol_framework)] + fun bidders_qualify(root: signer) { + let _vals = common_test_setup(&root); + + mock::trigger_epoch(&root); + let qualified = epoch_boundary::get_qualified_bidders(); + let win = epoch_boundary::get_qualified_bidders(); + assert!(vector::length(&qualified) == 10, 7357001); + assert!(vector::length(&win) == 10, 7357002); + } + // We need to test e2e of the epoch boundary #[test(root = @ol_framework)] fun e2e_boundary_happy(root: signer) { @@ -59,8 +76,6 @@ module ol_framework::test_boundary { mock::trigger_epoch(&root); - let _vals_post = stake::get_current_validators(); - assert!(epoch_boundary::get_reconfig_success(), 7357001); // all validators were compliant, should be +1 of the 10 vals @@ -79,17 +94,20 @@ module ol_framework::test_boundary { // check subsidy for new rewards and fees collected // fees collected = 10 * 1_000_000 + 10 * 1_000 = 10_010_000 - print(&transaction_fee::system_fees_collected()); assert!(transaction_fee::system_fees_collected() == 10_010_000, 7357006); } #[test(root = @ol_framework, alice = @0x1000a, marlon_rando = @0x12345)] fun e2e_add_validator_happy(root: signer, alice: signer, marlon_rando: signer) { + let epoch_reward = 1_000_000; + let entry_fee = 1_000; + let marlon_starting_balance = 200_000; + let initial_vals = common_test_setup(&root); // generate credentials for validator registration - ol_account::transfer(&alice, @0x12345, 200000); + ol_account::transfer(&alice, @0x12345, marlon_starting_balance); let (_sk, pk, pop) = stake::generate_identity(); let pk_bytes = bls12381::public_key_to_bytes(&pk); let pop_bytes = bls12381::proof_of_possession_to_bytes(&pop); @@ -99,7 +117,7 @@ module ol_framework::test_boundary { let vals = validator_universe::get_eligible_validators(); assert!(vector::length(&vals) == 11, 7357001); - mock::mock_bids(&vals); + mock::mock_bids(&root, &vals); // MARLON HAS MANY FRIENDS vouch::test_set_buddies(@0x12345, vals); @@ -131,20 +149,23 @@ module ol_framework::test_boundary { while (i < vector::length(&initial_vals)) { let (_unlocked, current) = ol_account::balance(*vector::borrow(&initial_vals, i)); let previous = *vector::borrow(&balances, i); - assert!(current == (previous + 1_000_000 - 1_000), 7357007); + assert!(current == (previous + epoch_reward - entry_fee), 7357007); i = i + 1; }; // check Marlon's balance: 200_000 - 1_000 = 199_000 let (_unlocked, marlon_balance) = ol_account::balance(@0x12345); - assert!(marlon_balance == 199_000, 7357008); + assert!(marlon_balance == (marlon_starting_balance - entry_fee), 7357008); // check subsidy for new rewards and fees collected - // fees collected = 11 * 1_000_000 + 11 * 1_000 = 11_011_000 + // fees collected = (11 * 1_000_000) + (11 * 1_000) = 11_011_000 + assert!((11* epoch_reward) + (11 * entry_fee) == 11_011_000, 7357009); assert!(transaction_fee::system_fees_collected() == 11_011_000, 7357009); // another epoch and everyone is compliant as well mock::mock_all_vals_good_performance(&root); + mock::mock_bids(&root, &vals); + mock::trigger_epoch(&root); assert!(epoch_boundary::get_seats_offered() == 12, 7357010); @@ -156,13 +177,13 @@ module ol_framework::test_boundary { while (i < vector::length(&initial_vals)) { let (_unlocked, current) = ol_account::balance(*vector::borrow(&initial_vals, i)); let previous = *vector::borrow(&balances, i); - assert!(current == (previous + 2_000_000 - 2_000), 7357012); + assert!(current == (previous + (2*epoch_reward) - (2*entry_fee)), 7357012); i = i + 1; }; - // check Marlon's balance: 200_000 + 1_000_000 - 2_000 = 1_198_000 + // check Marlon's balance = 1_000_000 + 200_000 - 2_000 = 1_198_000 let (_unlocked, marlon_balance) = ol_account::balance(@0x12345); - assert!(marlon_balance == 1_198_000, 7357013); + assert!(marlon_balance == (epoch_reward + 198_000), 7357013); // CHECK BURNT FEES @@ -181,8 +202,7 @@ module ol_framework::test_boundary { #[test(root = @ol_framework, alice = @0x1000a, marlon_rando = @0x12345)] fun e2e_add_validator_sad_vouches(root: signer, alice: signer, marlon_rando: signer) { let _vals = common_test_setup(&root); - // this test requires prod settings, since we don't check vouches on testing - testnet::unset(&root); + // generate credentials for validator registration ol_account::transfer(&alice, @0x12345, 200000); let (_sk, pk, pop) = stake::generate_identity(); @@ -192,14 +212,17 @@ module ol_framework::test_boundary { let vals = validator_universe::get_eligible_validators(); assert!(vector::length(&vals) == 11, 7357000); - mock::mock_bids(&vals); + + mock::mock_bids(&root, &vals); + // this test requires prod settings, since we don't check vouches on testing + testnet::unset(&root); mock::trigger_epoch(&root); - assert!(epoch_boundary::get_reconfig_success(), 7357002); + assert!(epoch_boundary::get_reconfig_success(), 7357001); // all validators were compliant, should be +1 of the 10 vals - assert!(epoch_boundary::get_seats_offered() == 11, 7357003); + assert!(epoch_boundary::get_seats_offered() == 11, 7357002); // NOTE: now MARLON is INCLUDED in this, and we filled all the seats on offer. // all vals had winning bids, but it was less than the seats on offer @@ -209,7 +232,7 @@ module ol_framework::test_boundary { } #[test(root = @ol_framework)] - fun e2e_boundary_excludes_jail(root: signer) { + fun jail_bad_grades(root: signer) { let vals = common_test_setup(&root); let alice_addr = *vector::borrow(&vals, 0); @@ -223,47 +246,44 @@ module ol_framework::test_boundary { // make Alice val not compliant to end up in jail stake::mock_performance(&root, alice_addr, 10, 10); + let (a, _, _) = grade::get_validator_grade(@0x1000a); + + // Alice has a bad grade + assert!(a == false, 73570001); // get Alice balance before epoch boundary let (_unlocked, alice_before) = ol_account::balance(alice_addr); - // new epoch mock::trigger_epoch(&root); - // check that Alice is jailed - assert!(jail::is_jailed(alice_addr), 7357001); + // // check that Alice is jailed + assert!(jail::is_jailed(alice_addr), 7357002); - // ensure Alice did not receive rewards - let (_unlocked, alice_after) = ol_account::balance(alice_addr); - assert!(alice_before == alice_after, 7357002); + // should be offering less seats because of jail. + assert!(epoch_boundary::get_seats_offered() == (vector::length(&vals) - 1), 7357003); - // check that validator set reduced by 1 - let qualified_bidders = epoch_boundary::get_qualified_bidders(); - assert!(vector::length(&qualified_bidders) == 9, 7357003); - // check subsidy for new rewards and fees collected - // fees collected = 9 * 1_000_000 + 9 * 2_000 = 9_018_000 - assert!(transaction_fee::system_fees_collected() == 9_018_000, 7357004); - // all vals had winning bids, but it was less than the seats on offer - assert!(vector::length(&epoch_boundary::get_auction_winners()) == vector::length(&qualified_bidders) , 7357005); - assert!(epoch_boundary::get_reconfig_success(), 7357006); + // ensure Alice did not receive rewards + let (_unlocked, alice_after) = ol_account::balance(alice_addr); + assert!(alice_before == alice_after, 7357004); } + #[test(root = @ol_framework, marlon = @0x12345)] fun epoch_any_address_trigger(root: &signer, marlon: &signer) { common_test_setup(root); // testing mainnet, so change the chainid testnet::unset(root); // test setup advances to epoch #2 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 2, 7357001); epoch_boundary::test_set_boundary_ready(root, epoch); // test the APIs as root timestamp::fast_forward_seconds(1); // needed for reconfig epoch_boundary::test_trigger(root); - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 3, 7357002); // scenario: marlon has no privileges @@ -272,27 +292,28 @@ module ol_framework::test_boundary { epoch_boundary::test_set_boundary_ready(root, epoch); timestamp::fast_forward_seconds(1); // needed for reconfig diem_governance::trigger_epoch(marlon); - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 4, 7357003); } #[test(root = @ol_framework)] fun epoch_increase_thermostat(root: &signer) { let vals = common_test_setup(root); - + let this_epoch = epoch_helper::get_current_epoch(); + let entry_fee_bid = 0100; // mock bids vector::for_each(vals, |a| { let sig = account::create_signer_for_test(a); - proof_of_fee::pof_update_bid(&sig, 0100, 30); // 10% for 30 epochs + secret_bid::mock_revealed_bid(root, &sig, entry_fee_bid, this_epoch); // 10% for 30 epochs }); // mock bid history to increase thermostat by 5% proof_of_fee::test_mock_reward( root, - 1000, // nominal reward - 0100, // clearing bid - 0100, // median win bid - vector[ 0100, 0100, 0100, 0100, 0100, 0100 ] // median history bellow 50% + 100_000, // nominal reward + entry_fee_bid, // clearing bid + entry_fee_bid, // median win bid + vector[ entry_fee_bid, entry_fee_bid, entry_fee_bid, entry_fee_bid, entry_fee_bid, entry_fee_bid, ] // median history bellow 50% ); // trigger epoch @@ -300,29 +321,31 @@ module ol_framework::test_boundary { // check subsidy increased by 5% // fees collected = entry fee + reward * 105% - // entry fee = 100 * 10 = 1_000 - // reward = 1050 * 10 = 10_500 + // entry fee = 100_000 * 0.1 * 10 = 100_000 + // reward = 100_000 * 1.05 * 10 = 1_050_000 // fees collected = 11_500 - assert!(transaction_fee::system_fees_collected() == 11_500, 7357001); + assert!(transaction_fee::system_fees_collected() == 1150000, 7357001); } #[test(root = @ol_framework)] fun epoch_decrease_thermostat(root: &signer) { let vals = common_test_setup(root); + let entry_fee_bid = 0970; // mock bids + let this_epoch = epoch_helper::get_current_epoch(); vector::for_each(vals, |a| { let sig = account::create_signer_for_test(a); - proof_of_fee::pof_update_bid(&sig, 0970, 30); // 97% for 30 epochs + secret_bid::mock_revealed_bid(root, &sig, entry_fee_bid, this_epoch); }); // mock bid history to decrease thermostat by 5% proof_of_fee::test_mock_reward( root, 100_000, // nominal reward - 0970, // clearing bid - 0970, // median win bid - vector[ 0970, 0970, 0970, 0970, 0970, 0970 ] // median history above 95% + entry_fee_bid, // clearing bid + entry_fee_bid, // median win bid + vector[ entry_fee_bid, entry_fee_bid, entry_fee_bid, entry_fee_bid, 0970, entry_fee_bid ] // median history above 95% ); // trigger epoch @@ -343,14 +366,14 @@ module ol_framework::test_boundary { // testing mainnet, so change the chainid testnet::unset(root); // test setup advances to epoch #2 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 2, 7357001); epoch_boundary::test_set_boundary_ready(root, epoch); // test the APIs as root timestamp::fast_forward_seconds(1); // needed for reconfig epoch_boundary::test_trigger(root); - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 3, 7357002); // scenario: marlon has no privileges @@ -378,7 +401,7 @@ module ol_framework::test_boundary { // assert!(!features::epoch_trigger_enabled(), 101); // // test setup advances to epoch #2 - // let epoch = reconfiguration::get_current_epoch(); + // let epoch = reconfiguration::current_epoch(); // assert!(epoch == 2, 7357001); // epoch_boundary::test_set_boundary_ready(root, epoch); @@ -388,7 +411,7 @@ module ol_framework::test_boundary { // block::test_maybe_advance_epoch(root, 602000001, 602000000); // // test epoch advances - // let epoch = reconfiguration::get_current_epoch(); + // let epoch = reconfiguration::current_epoch(); // assert!(epoch == 3, 7357002); // } @@ -399,7 +422,7 @@ module ol_framework::test_boundary { // testing mainnet, so change the chainid testnet::unset(root); // test setup advances to epoch #2 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 2, 7357001); epoch_boundary::test_set_boundary_ready(root, epoch); @@ -409,12 +432,12 @@ module ol_framework::test_boundary { block::test_maybe_advance_epoch(root, 603000001, 602000000); // test epoch did not advance and needs to be triggered - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 2, 7357002); // test epoch can be triggered and advances epoch_boundary::test_trigger(root); - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 3, 7357002); } diff --git a/framework/libra-framework/sources/ol_sources/tests/burn.test.move b/framework/libra-framework/sources/ol_sources/tests/burn.test.move index 51c66a9f6..583aaf623 100644 --- a/framework/libra-framework/sources/ol_sources/tests/burn.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/burn.test.move @@ -1,7 +1,7 @@ #[test_only] module ol_framework::test_burn { - use ol_framework::mock; + use ol_framework::mock::{Self, default_epoch_reward, default_final_supply_at_genesis, default_entry_fee, default_tx_fee_account_at_genesis}; use ol_framework::libra_coin; use ol_framework::ol_account; use ol_framework::match_index; @@ -73,8 +73,9 @@ module ol_framework::test_burn { // EVE donates to both. But mostly to Alice. // The Match Index, should reflect that. - let vals = mock::genesis_n_vals(root, 5); // need to include eve to init funds - mock::ol_initialize_coin_and_fund_vals(root, 1000000, true); + let vals = mock::genesis_n_vals(root, 5); // need to include eve to init funds) + + mock::ol_initialize_coin_and_fund_vals(root, default_epoch_reward(), true); // start at epoch 1, since turnout tally needs epoch info, and 0 may cause issues mock::trigger_epoch(root); @@ -137,7 +138,7 @@ module ol_framework::test_burn { // The Match Index, should reflect that. let vals = mock::genesis_n_vals(root, 5); // need to include eve to init funds - mock::ol_initialize_coin_and_fund_vals(root, 1000000, true); + mock::ol_initialize_coin_and_fund_vals(root, default_epoch_reward(), true); // start at epoch 1, since turnout tally needs epoch info, and 0 may cause issues mock::trigger_epoch(root); @@ -191,52 +192,72 @@ module ol_framework::test_burn { assert!(balance == eve_donation_to_A, 7357006); } + #[test(root = @ol_framework)] fun epoch_fees_burn(root: &signer) { // Scenario: - // the network starts. - // we mock some tx fees going into the collection - // the epoch turns and the fee account should be empty + // the network starts + // we mock some tx fees going into the fee account + // validators have ZERO balance, so they cannot pay into tx fee account + // the validators perform correctly, and the epoch changes (they + // are allowed to continue because of failover rules) + // Expected Outcome: + // The fees from the previous epoch were used to pay validators. + // however there was an excess amount in Tx account, and this amount + // would be burned. + // and the supply should be lower let n_vals = 5; - let _vals = mock::genesis_n_vals(root, n_vals); // need to include eve to init funds - let genesis_mint = 1_000_000; // 1 coin per - let epoch_reward = genesis_mint; // just to be explicit + let genesis_balance_per_val = 0; // simplify calcs, no validators funded at start - mock::ol_initialize_coin_and_fund_vals(root, genesis_mint, true); + let exact_amount_for_reward = n_vals * default_epoch_reward(); + let excess_in_tx_fee_account = 30_000_000; + + let starting_tx_fees = excess_in_tx_fee_account + exact_amount_for_reward; + + let _vals = mock::genesis_n_vals(root, n_vals); + mock::ol_initialize_coin_and_fund_vals(root, genesis_balance_per_val, true); let supply_pre = libra_coin::supply(); - let bruce_fortune = 100_000_000_000; - let mocked_tx_fees = 5_000_000 * 100; // 100 coins in tx fee account - assert!(supply_pre == mocked_tx_fees + bruce_fortune + (n_vals * genesis_mint), 73570001); + mock::mock_tx_fees_in_account(root, starting_tx_fees); - let fees = transaction_fee::system_fees_collected(); - assert!(fees == mocked_tx_fees, 73570002); + // supply at genesis always be equal to final supply + assert!(supply_pre == default_final_supply_at_genesis(), 73570001); + + // The only fees in the tX fee account at genesis should be what was mocked by default + assert!(transaction_fee::system_fees_collected() == starting_tx_fees, 73570002); + + // no change in supply, since these coins came from infra pledge + assert!(libra_coin::supply() == default_final_supply_at_genesis(), 73570003); // start at epoch 1. NOTE Validators WILL GET PAID FOR EPOCH 1 - mock::trigger_epoch(root); // under 1000 rounds we don't + // but they have no balance to contribute to next epochs funds (for simplicity of calcs) + let (lifetime_burn_pre, _lifetime_match) = burn::get_lifetime_tracker(); + mock::trigger_epoch(root); + // NOTE: under 1000 rounds we don't evaluate performance of validators + let (lifetime_burn_post, _lifetime_match) = burn::get_lifetime_tracker(); + assert!(lifetime_burn_post > lifetime_burn_pre, 73570004); + // supply should be lower + let supply_post = libra_coin::supply(); - // evaluate performance of validators - let fees = transaction_fee::system_fees_collected(); - // There should be only entry fee left in tx fee wallet - assert!(fees == 5_000_000, 73570003); + let fees_account_balance = transaction_fee::system_fees_collected(); + let infra_pledge_subsidy = n_vals * default_epoch_reward(); + let entry_fees_total = n_vals * default_entry_fee(); - let validator_rewards = epoch_reward * n_vals; + assert!(supply_post < default_final_supply_at_genesis(), 73570005); - // of the initial supply, - // we expect to burn everything which was in the TX FEEs AFTER PAYING VALIDATOR REWARDS - let amount_burned_excess_tx_account = mocked_tx_fees - validator_rewards; - // So the current supply should be lower, - // The the old supply was reduced by what was burned (the excess in tx bucket) + // Now for the follwing epoch the tx fee account should have: + // a) the entry fees of validators (they can afford it now) + // b) the infra pledge subsidy * vals - let supply_post = libra_coin::supply(); - assert!(supply_post == supply_pre - amount_burned_excess_tx_account, 73570003); + assert!(fees_account_balance == (infra_pledge_subsidy + entry_fees_total), 73570006); - // this ALSO means that the only supply left in this example - // is the rewards to validators from genesis, and from epoch 1 - // and the bruce fortune used to pledge infra escrow - assert!(supply_post == validator_rewards + (n_vals * genesis_mint) + bruce_fortune, 73570003); + // In this scenario + // we expect to burn everything which was in the txs after paying validator rewards. + // we funded the tx fee account with an excess, beyond what was needed for the validator reward. + + assert!(supply_post == supply_pre - excess_in_tx_fee_account, 73570007); } @@ -319,9 +340,13 @@ module ol_framework::test_burn { #[test(root=@ol_framework, alice=@0x1000a)] fun track_fees(root: &signer, alice: address) { - // use ol_framework::libra_coin; - let _vals = mock::genesis_n_vals(root, 1); // need to include eve to init funds - mock::ol_initialize_coin_and_fund_vals(root, 10000, true); + + let _vals = mock::genesis_n_vals(root, 1); + mock::ol_initialize_coin_and_fund_vals(root, default_epoch_reward(), true); + mock::mock_tx_fees_in_account(root, default_tx_fee_account_at_genesis()); + + let fees = transaction_fee::system_fees_collected(); + assert!(fees == default_tx_fee_account_at_genesis(), 7357001); let marlon_rando = @0x12345; ol_account::create_account(root, marlon_rando); @@ -337,25 +362,37 @@ module ol_framework::test_burn { option::destroy_none(coin_option); let fees = transaction_fee::system_fees_collected(); - assert!(fees == 500_000_005, 7357001); - // Fees will include the initialization by MOCK. ol_initialize_coin_and_fund_vals + assert!(fees == (default_tx_fee_account_at_genesis() + rando_money), 7357001); // marlon is the only fee maker (since genesis) let fee_makers = fee_maker::get_fee_makers(); - // includes 0x1 which makes a deposit on assert!(vector::length(&fee_makers)==1, 7357002); + let (_found, idx) = vector::index_of(&fee_makers, &marlon_rando); + assert!( idx == 0, 7357003); let marlon_fees_made = fee_maker::get_user_fees_made(marlon_rando); - assert!(marlon_fees_made == 5, 7357003); + assert!(marlon_fees_made == 5, 7357004); + let (lifetime_burn_pre, _lifetime_match) = burn::get_lifetime_tracker(); mock::trigger_epoch(root); + // NOTE: under 1000 rounds we don't evaluate performance of validators + let (lifetime_burn_post, _lifetime_match) = burn::get_lifetime_tracker(); + assert!(lifetime_burn_post > lifetime_burn_pre, 0); let marlon_fees_made = fee_maker::get_user_fees_made(marlon_rando); - assert!(marlon_fees_made == 0, 7357004); + assert!(marlon_fees_made == 0, 7357005); + + // However, Alice (the only validator) did pay an entry fee during Proof of Fee, + // and that fee should be tracked let fee_makers = fee_maker::get_fee_makers(); - assert!(vector::length(&fee_makers) == 0, 7357005); + + assert!(vector::length(&fee_makers) == 1, 7357006); + let (_found, idx) = vector::index_of(&fee_makers, &alice); + assert!( idx == 0, 7357007); + + let fees = transaction_fee::system_fees_collected(); - assert!(fees == 1_000_000, 7357006); // val set entry fee + assert!(fees == default_entry_fee() + default_epoch_reward(), 7357008); // val set entry fee } #[test(root = @ol_framework, alice_val = @0x1000a)] @@ -400,6 +437,7 @@ module ol_framework::test_burn { assert!(cumu_burn == 0, 7357004); // simulate epoch boundary burn + mock::mock_tx_fees_in_account(root, 60_000_000); let all_fees = transaction_fee::test_root_withdraw_all(root); burn::epoch_burn_fees(root, &mut all_fees); coin::destroy_zero(all_fees); // destroy the hot potato diff --git a/framework/libra-framework/sources/ol_sources/tests/donor_voice.test.move b/framework/libra-framework/sources/ol_sources/tests/donor_voice.test.move index 2b39cd964..69c492ba2 100644 --- a/framework/libra-framework/sources/ol_sources/tests/donor_voice.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/donor_voice.test.move @@ -12,17 +12,16 @@ module ol_framework::test_donor_voice { use ol_framework::donor_voice_governance; use ol_framework::community_wallet_init; use ol_framework::community_wallet; - use ol_framework::burn; use ol_framework::slow_wallet; use diem_framework::multisig_account; use std::guid; use std::vector; use std::signer; - use diem_std::debug::print; + // use diem_std::debug::print; #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b)] - fun dd_init(root: &signer, alice: &signer, bob: &signer) { + fun dv_init(root: &signer, alice: &signer, bob: &signer) { let vals = mock::genesis_n_vals(root, 2); let (resource_sig, _cap) = ol_account::test_ol_create_resource_account(alice, b"0x1"); @@ -48,7 +47,7 @@ module ol_framework::test_donor_voice { } #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b)] - fun dd_propose_payment(root: &signer, alice: &signer, bob: &signer) { + fun dv_propose_payment(root: &signer, alice: &signer, bob: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -78,7 +77,7 @@ module ol_framework::test_donor_voice { } #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d)] - fun dd_schedule_happy(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { + fun dv_schedule_happy(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -124,7 +123,7 @@ module ol_framework::test_donor_voice { } #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d, eve = @0x1000e)] - fun dd_propose_and_veto(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer, eve: &signer) { + fun dv_propose_and_veto(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer, eve: &signer) { // Scenario: Eve wants to veto a transaction on a donor directed account. // Alice creates a donor directed account where Alice, Bob and Carol, are admins. // Dave and Eve make a donation and so are able to have some voting on that account. The veto can only happen after Alice Bob and Carol are able to schedule a tx. @@ -213,7 +212,7 @@ module ol_framework::test_donor_voice { // should not be able sign a tx twice #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d)] #[expected_failure(abort_code = 65550, location = 0x1::multi_action)] - fun dd_reject_duplicate_proposal(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { + fun dv_reject_duplicate_proposal(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -252,7 +251,7 @@ module ol_framework::test_donor_voice { #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d)] - fun dd_process_unit(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { + fun dv_process_unit(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -328,7 +327,7 @@ module ol_framework::test_donor_voice { } #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, marlon_rando = @0x123456)] - fun dd_process_epoch_boundary(root: &signer, alice: &signer, bob: &signer, carol: &signer, marlon_rando: &signer) { + fun dv_process_epoch_boundary(root: &signer, alice: &signer, bob: &signer, carol: &signer, marlon_rando: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -397,7 +396,7 @@ module ol_framework::test_donor_voice { } #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d, marlon_rando = @0x123456)] - fun dd_process_multi_same_epoch(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer, marlon_rando: &signer) { + fun dv_process_multi_same_epoch(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer, marlon_rando: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -489,7 +488,6 @@ module ol_framework::test_donor_voice { // the default timed payment is 3 epochs, we are in epoch 1 let list = donor_voice_txs::find_by_deadline(donor_voice_address, 3); assert!(vector::contains(&list, &first_uid_bob), 73570027); - print(&list); assert!(vector::contains(&list, &second_uid_bob), 73570028); // process epoch 3 accounts @@ -503,7 +501,6 @@ module ol_framework::test_donor_voice { // MARLON'S FIRST PAYMENT GOES THROUGH let (_, marlon_rando_balance_post) = ol_account::balance(signer::address_of(marlon_rando)); - print(&marlon_rando_balance_post); // the first proposal should be processed let (found, idx, status_enum, completed) = @@ -523,28 +520,14 @@ module ol_framework::test_donor_voice { assert!(completed, 73570036); // now vote is completed - - assert!(marlon_rando_balance_post == (marlon_rando_balance_pre + marlon_pay_one + marlon_pay_two), 73570027); - - // // MARLON'S SECOND PAYMENT SHOULD SUCCEED - - // mock::trigger_epoch(root); // epoch 5 should include the next payment - // let (_, marlon_rando_balance_post) = - // ol_account::balance(signer::address_of(marlon_rando)); - - // print(&marlon_rando_balance_pre); - // print(&marlon_rando_balance_post); - - // assert!(marlon_rando_balance_post == (marlon_rando_balance_pre + - // marlon_pay_one + marlon_pay_two), 73570028); } #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, marlon_rando = @0x123456)] - fun dd_process_multi_different_epochs(root: &signer, alice: &signer, bob: &signer, carol: &signer, marlon_rando: &signer) { + fun dv_process_multi_different_epochs(root: &signer, alice: &signer, bob: &signer, carol: &signer, marlon_rando: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -660,16 +643,13 @@ module ol_framework::test_donor_voice { let (_, marlon_rando_balance_post) = ol_account::balance(signer::address_of(marlon_rando)); - print(&marlon_rando_balance_pre); - print(&marlon_rando_balance_post); - assert!(marlon_rando_balance_post == (marlon_rando_balance_pre + marlon_pay_one + marlon_pay_two), 73570028); } #[test(root = @ol_framework, _alice = @0x1000a, dave = @0x1000d, eve = @0x1000e, donor_voice = @0x1000f)] - fun dd_liquidate_to_donor(root: &signer, _alice: &signer, dave: &signer, eve: &signer, donor_voice: &signer) { + fun dv_liquidate_to_donor(root: &signer, _alice: &signer, dave: &signer, eve: &signer, donor_voice: &signer) { // Scenario: // Alice creates a donor directed account where Alice, Bob and Carol, are admins. // Dave and Eve make a donation and so are able to have some voting on that account. @@ -680,8 +660,6 @@ module ol_framework::test_donor_voice { // start at epoch 1, since turnout tally needs epoch info, and 0 may cause // issues mock::trigger_epoch(root); - // a burn happend in the epoch above, so let's compare it to end of epoch - let (lifetime_burn_pre, _) = burn::get_lifetime_tracker(); let donor_voice_address = signer::address_of(donor_voice); // the account needs basic donor directed structs @@ -745,13 +723,10 @@ module ol_framework::test_donor_voice { // eve shoul have received funds back assert!(eve_balance > eve_balance_pre, 7357010); - // nothing should have been burned, it was a refund - let (lifetime_burn_now, _) = burn::get_lifetime_tracker(); - assert!(lifetime_burn_now == lifetime_burn_pre, 7357011); } #[test(root = @ol_framework, alice = @0x1000a, dave = @0x1000d, eve = @0x1000e)] - fun dd_liquidate_to_match_index(root: &signer, alice: &signer, dave: &signer, eve: &signer) { + fun dv_liquidate_to_match_index(root: &signer, alice: &signer, dave: &signer, eve: &signer) { // Scenario: // Alice creates a donor directed account where Alice, Bob and Carol, are admins. // Dave and Eve make a donation and so are able to have some voting on that account. @@ -761,8 +736,7 @@ module ol_framework::test_donor_voice { mock::ol_initialize_coin_and_fund_vals(root, 100000, true); // start at epoch 1, since turnout tally needs epoch info, and 0 may cause issues mock::trigger_epoch(root); - // a burn happend in the epoch above, so let's compare it to end of epoch - let (lifetime_burn_pre, _) = burn::get_lifetime_tracker(); + let (resource_sig, _cap) = ol_account::test_ol_create_resource_account(alice, b"0x1"); let donor_voice_address = signer::address_of(&resource_sig); @@ -832,10 +806,6 @@ module ol_framework::test_donor_voice { // program says it goes to the matching index. assert!(eve_balance == eve_balance_pre, 7357010); - let (lifetime_burn_now, _lifetime_match) = burn::get_lifetime_tracker(); - - // nothing should have been burned, it was a refund - assert!(lifetime_burn_now == lifetime_burn_pre, 7357011); } #[test(root = @ol_framework, community = @0x10011, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c)] diff --git a/framework/libra-framework/sources/ol_sources/tests/proof_of_fee.test.move b/framework/libra-framework/sources/ol_sources/tests/proof_of_fee.test.move index f1aa0fe21..3e4aec706 100644 --- a/framework/libra-framework/sources/ol_sources/tests/proof_of_fee.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/proof_of_fee.test.move @@ -2,6 +2,7 @@ module ol_framework::test_pof { use ol_framework::mock; use ol_framework::account; + use ol_framework::epoch_helper; use ol_framework::proof_of_fee; use ol_framework::reconfiguration; use ol_framework::jail; @@ -9,11 +10,12 @@ module ol_framework::test_pof { use ol_framework::vouch; use ol_framework::testnet; use ol_framework::globals; + use ol_framework::secret_bid; use diem_framework::stake; use diem_framework::chain_id; use std::vector; - //use diem_std::debug::print; + // use diem_std::debug::print; const Alice: address = @0x1000a; const Bob: address = @0x1000b; @@ -23,72 +25,67 @@ module ol_framework::test_pof { const Frank: address = @0x1000f; #[test_only] - fun mock_good_bid(_root: &signer, alice: &address) { + fun mock_good_bid(root: &signer, alice: &address) { let a_sig = account::create_signer_for_test(*alice); - // mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); - proof_of_fee::pof_update_bid(&a_sig, 1, 10000); - let (bid, expires) = proof_of_fee::current_bid(*alice); + let epoch = 0; // we are at genesis in tests + secret_bid::mock_revealed_bid(root, &a_sig, 1, epoch); + let bid = secret_bid::get_bid_unchecked(*alice); assert!(bid == 1, 1001); - assert!(expires == 10000, 1002); let coin = slow_wallet::unlocked_amount(*alice); let (r, _, _, _) = proof_of_fee::get_consensus_reward(); let bid_cost = (bid * r) / 1000; - assert!(coin > bid_cost, 1005); + assert!(coin > bid_cost, 1002); } + #[test(root = @ol_framework)] - fun pof_set_retract (root: signer) { - // genesis(); + fun audit_happy (root: signer) { let set = mock::genesis_n_vals(&root, 4); mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); - let alice = vector::borrow(&set, 0); - stake::is_valid(*alice); - - let a_sig = account::create_signer_for_test(*alice); - proof_of_fee::init_bidding(&a_sig); - - let (bid, expires) = proof_of_fee::current_bid(*alice); - assert!(bid == 0, 1001); - assert!(expires == 0, 1002); - - proof_of_fee::pof_update_bid(&a_sig, 100, 0); - let (bid, expires) = proof_of_fee::current_bid(*alice); - assert!(bid == 100, 1003); - assert!(expires == 0, 1004); + let alice = *vector::borrow(&set, 0); - // now retract - proof_of_fee::pof_retract_bid(a_sig); - let (bid, expires) = proof_of_fee::current_bid(*alice); - let (is_rectracted, epoch) = proof_of_fee::is_already_retracted(*alice); - assert!(is_rectracted, 1007); + assert!(stake::is_valid(alice), 1000); + assert!(!jail::is_jailed(alice), 1001); - let this_epoch = reconfiguration::get_current_epoch(); + mock_good_bid(&root, &alice); - assert!(epoch == this_epoch, 1008); - assert!(bid == 0, 1009); - assert!(expires == 0, 10010); + let (_err, pass) = proof_of_fee::audit_qualification(alice); + assert!(pass, 1006); } #[test(root = @ol_framework)] - fun audit_happy (root: signer) { + fun meta_get_bidders (root: signer) { let set = mock::genesis_n_vals(&root, 4); mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); let alice = *vector::borrow(&set, 0); - assert!(stake::is_valid(alice), 1000); assert!(!jail::is_jailed(alice), 1001); - mock_good_bid(&root, &alice); + let exclude_bad = true; - let (_, pass) = proof_of_fee::audit_qualification(alice); - assert!(pass, 1006); + // all bidders will be 4 + let bidders = proof_of_fee::get_bidders(!exclude_bad); + assert!(vector::length(&bidders) == 4, 1002); + + // filtering for qualified should have no impact, since they all qualify + let bidders_filtered = proof_of_fee::get_bidders(exclude_bad); + assert!(vector::length(&bidders_filtered) == 4, 1002); + + // now jail one + jail::jail(&root, alice); + assert!(jail::is_jailed(alice), 1002); + + let bidders_filtered = proof_of_fee::get_bidders(exclude_bad); + assert!(vector::length(&bidders_filtered) == 3, 1003); } + + #[test(root = @ol_framework)] fun audit_vouch (root: signer) { let set = mock::genesis_n_vals(&root, 4); @@ -133,10 +130,10 @@ module ol_framework::test_pof { assert!(!jail::is_jailed(alice), 7357001); let a_sig = account::create_signer_for_test(alice); - proof_of_fee::pof_update_bid(&a_sig, 100, 10000); // 10 pct - let (bid, expires) = proof_of_fee::current_bid(alice); + let epoch = epoch_helper::get_current_epoch(); + secret_bid::mock_revealed_bid(&root, &a_sig, 100, epoch); + let bid = secret_bid::get_bid_unchecked(alice); assert!(bid == 100, 7357001); - assert!(expires == 10000, 7357002); // NOT ENOUGH FUNDS WERE UNLOCKED slow_wallet::slow_wallet_epoch_drip(&root, 500); @@ -157,6 +154,7 @@ module ol_framework::test_pof { #[test(root = @ol_framework)] fun audit_expired(root: signer) { + use ol_framework::epoch_helper; let set = mock::genesis_n_vals(&root, 7); mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); let alice = *vector::borrow(&set, 0); @@ -177,19 +175,22 @@ module ol_framework::test_pof { let a_sig = account::create_signer_for_test(alice); // set a bid in the past - proof_of_fee::pof_update_bid(&a_sig, 1, 0); - let (bid, expires) = proof_of_fee::current_bid(alice); - assert!(bid == 1, 1006); - assert!(expires == 0, 1007); + secret_bid::mock_revealed_bid(&root, &a_sig, 100, 0); + + let bid = secret_bid::get_bid_unchecked(alice); + assert!(bid == 100, 1003); + // should NOT pass audit. let (_err, pass) = proof_of_fee::audit_qualification(alice); - assert!(!pass, 1008); + assert!(!pass, 1004); // fix it - proof_of_fee::pof_update_bid(&a_sig, 1, 1000); + let epoch = epoch_helper::get_current_epoch(); + secret_bid::mock_revealed_bid(&root, &a_sig, 100, epoch); + let (_err, pass) = proof_of_fee::audit_qualification(alice); - assert!(pass, 1009); + assert!(pass, 1005); } @@ -227,7 +228,7 @@ module ol_framework::test_pof { i = i + 1; }; // mock_good_bid(&root, alice); - let (val_universe, _their_bids, _their_expiry) = mock::pof_default(); + let (val_universe, _their_bids) = mock::pof_default(&root); let sorted = proof_of_fee::get_bidders(false); let len = vector::length(&sorted); @@ -242,7 +243,7 @@ module ol_framework::test_pof { #[test(root= @ol_framework)] fun sorted_vals_none_qualify(root: signer) { let vals = mock::genesis_n_vals(&root, 4); - let (val_universe, _their_bids, _their_expiry) = mock::pof_default(); + let (val_universe, _their_bids) = mock::pof_default(&root); // calculate the auction proof_of_fee::fill_seats_and_get_price(&root, 4, &vals, &vals); @@ -274,10 +275,10 @@ module ol_framework::test_pof { let alice = vector::borrow(&set, 0); jail::jail(&root, *alice); - let (val_universe, _their_bids, _their_expiry) = mock::pof_default(); + let (val_universe, _their_bids) = mock::pof_default(&root); - // let (val_universe, _their_bids, _their_expiry) = mock::pof_default(); + // let (val_universe, _their_bids, _their_expiry) = mock::pof_default(&root); let sorted = proof_of_fee::get_bidders(false); let len = vector::length(&sorted); assert!(len == vector::length(&val_universe), 1000); @@ -298,37 +299,21 @@ module ol_framework::test_pof { let set = mock::genesis_n_vals(&root, 4); mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); - let (val_universe, _their_bids, _their_expiry) = mock::pof_default(); + let alice_addr = *vector::borrow(&set, 0); + let alice_sig = account::create_signer_for_test(alice_addr); + secret_bid::mock_revealed_bid(&root, &alice_sig, 55, 10); + assert!(secret_bid::has_valid_bid(alice_addr) == false, 7357001); - let len = vector::length(&set); - let i = 0; - while (i < len) { - let addr = vector::borrow(&set, i); - mock_good_bid(&root, addr); - i = i + 1; - }; - - // set an expired bid for alice - let alice = vector::borrow(&set, 0); - let alice_sig = account::create_signer_for_test(*alice); - proof_of_fee::pof_update_bid(&alice_sig, 55, 1); - - // advance the epoch 2x, so the previous bid is expired. - mock::mock_all_vals_good_performance(&root); - mock::trigger_epoch(&root); - mock::mock_all_vals_good_performance(&root); - mock::trigger_epoch(&root); - - // Get all vals but don't filter the ones that have passing bids + // Get all bidders, but don't run audit let sorted = proof_of_fee::get_bidders(false); - let len = vector::length(&sorted); - assert!(len == vector::length(&val_universe), 1000); - assert!(vector::length(&sorted) == vector::length(&val_universe), 1002); + let bids_len = vector::length(&sorted); + assert!(bids_len == vector::length(&set), 1000); + // now filter for expired bids, should have one missing + let sorted_audit = proof_of_fee::get_bidders(true); - let sorted_two = proof_of_fee::get_bidders(true); - assert!(vector::length(&sorted_two) != vector::length(&val_universe), 1004); - assert!(vector::length(&sorted_two) == vector::length(&val_universe) - 1, 1005); + assert!(vector::length(&sorted_audit) != vector::length(&set), 1004); + assert!(vector::length(&sorted_audit) == vector::length(&set) - 1, 1005); } // We can send the fill seats function a list of validators, and the list of performing validators, and it will return the winning bidders and the bid. @@ -337,7 +322,7 @@ module ol_framework::test_pof { let set = mock::genesis_n_vals(&root, 5); let len = vector::length(&set); mock::ol_initialize_coin_and_fund_vals(&root, 500000, true); - mock::pof_default(); + mock::pof_default(&root); slow_wallet::slow_wallet_epoch_drip(&root, 500000); @@ -360,7 +345,7 @@ module ol_framework::test_pof { fun fill_seats_happy_and_noop_thermostat(root: signer) { let set = mock::genesis_n_vals(&root, 5); mock::ol_initialize_coin_and_fund_vals(&root, 500000, true); - mock::pof_default(); + mock::pof_default(&root); slow_wallet::slow_wallet_epoch_drip(&root, 500000); @@ -400,11 +385,11 @@ module ol_framework::test_pof { fun fill_seats_few_bidders(root: signer) { let set = mock::genesis_n_vals(&root, 5); mock::ol_initialize_coin_and_fund_vals(&root, 500000, true); - mock::pof_default(); + mock::pof_default(&root); // Ok now EVE changes her mind. Will force the bid to expire. let a_sig = account::create_signer_for_test(*vector::borrow(&set, 4)); - proof_of_fee::pof_update_bid(&a_sig, 0, 0); + secret_bid::mock_revealed_bid(&root, &a_sig, 0, 0); slow_wallet::slow_wallet_epoch_drip(&root, 500000); let sorted = proof_of_fee::get_bidders(true); @@ -441,7 +426,7 @@ module ol_framework::test_pof { #[test(root = @ol_framework)] fun fill_seats_many_bidders(root: signer) { let set = mock::genesis_n_vals(&root, 5); - mock::pof_default(); + mock::pof_default(&root); mock::ol_initialize_coin_and_fund_vals(&root, 500000, true); let sorted = proof_of_fee::get_bidders(true); @@ -496,7 +481,7 @@ module ol_framework::test_pof { // we need 6 seats so that we can have 4 proven, and 2 unproven slots let set = mock::genesis_n_vals(&root, 6); mock::ol_initialize_coin_and_fund_vals(&root, 500000, true); - mock::pof_default(); + mock::pof_default(&root); let sorted = proof_of_fee::get_bidders(true); @@ -554,7 +539,7 @@ module ol_framework::test_pof { // we need 6 seats so that we can have 4 proven, and 2 unproven slots let set = mock::genesis_n_vals(&root, 6); mock::ol_initialize_coin_and_fund_vals(&root, 500000, true); - mock::pof_default(); + mock::pof_default(&root); let sorted = proof_of_fee::get_bidders(true); @@ -566,9 +551,9 @@ module ol_framework::test_pof { vector::push_back(&mut proven_vals, Carol); vector::push_back(&mut proven_vals, Dave); - let (alice_bid, _) = proof_of_fee::current_bid(Alice); + let alice_bid = secret_bid::get_bid_unchecked(Alice); assert!(alice_bid == 1, 1001); - let (frank_bid, _) = proof_of_fee::current_bid(Frank); + let frank_bid = secret_bid::get_bid_unchecked(Frank); assert!(alice_bid < frank_bid, 1002); let (seats, _, _, _, _) = proof_of_fee::fill_seats_and_get_price(&root, set_size, &sorted, &proven_vals); diff --git a/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move b/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move index e4b9103c6..3f697f2e2 100644 --- a/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move @@ -5,7 +5,7 @@ module ol_framework::test_slow_wallet { use diem_framework::stake; use diem_framework::account; use ol_framework::slow_wallet; - use ol_framework::mock; + use ol_framework::mock::{Self, default_tx_fee_account_at_genesis}; use ol_framework::ol_account; use ol_framework::libra_coin; use ol_framework::epoch_boundary; @@ -18,7 +18,7 @@ module ol_framework::test_slow_wallet { use std::vector; use std::signer; - use diem_std::debug::print; + // use diem_std::debug::print; #[test(root = @ol_framework)] // we are testing that genesis creates the needed struct @@ -111,24 +111,28 @@ module ol_framework::test_slow_wallet { #[test(root = @ol_framework)] fun test_epoch_drip(root: signer) { let set = mock::genesis_n_vals(&root, 4); - mock::ol_initialize_coin_and_fund_vals(&root, 100, false); + let should_drip = false; + mock::ol_initialize_coin_and_fund_vals(&root, 100, should_drip); + mock::mock_tx_fees_in_account(&root, default_tx_fee_account_at_genesis()); let a = *vector::borrow(&set, 0); assert!(slow_wallet::is_slow(a), 7357000); - assert!(slow_wallet::unlocked_amount(a) == 100, 735701); + // mock:: does not drip by default + assert!(slow_wallet::unlocked_amount(a) == 0, 735701); let coin = transaction_fee::test_root_withdraw_all(&root); rewards::test_helper_pay_reward(&root, a, coin, 0); - let (u, b) = ol_account::balance(a); - print(&b); - assert!(b==500_000_100, 735702); - assert!(u==100, 735703); + let (unlocked, total_balance) = ol_account::balance(a); + + assert!(total_balance == (default_tx_fee_account_at_genesis() + 100), 735702); + assert!(unlocked == 0, 735703); slow_wallet::slow_wallet_epoch_drip(&root, 233); - let (u, b) = ol_account::balance(a); - assert!(b==500_000_100, 735704); - assert!(u==333, 735705); + let (unlocked, total_balance) = ol_account::balance(a); + // no change total balances + assert!(total_balance==(default_tx_fee_account_at_genesis() + 100), 735704); + assert!(unlocked==233, 735705); } #[test(root = @ol_framework, alice = @0x123, bob = @0x456)] @@ -187,9 +191,6 @@ module ol_framework::test_slow_wallet { // slow transfer let b_balance = libra_coin::balance(@0x456); assert!(b_balance == transfer_amount, 735704); - // print(&alice_init_balance); - // print(&transfer_amount); - // print(&slow_wallet::unlocked_amount(@0x123)); assert!(slow_wallet::unlocked_amount(@0x123) == (alice_init_balance - transfer_amount), 735705); assert!(slow_wallet::unlocked_amount(@0x456) == transfer_amount, 735706); @@ -238,7 +239,7 @@ module ol_framework::test_slow_wallet { // mock::ol_initialize_coin(&root); let a = vector::borrow(&set, 0); assert!(slow_wallet::unlocked_amount(*a) == 0, 735701); - epoch_boundary::ol_reconfigure_for_test(&root, reconfiguration::get_current_epoch(), block::get_current_block_height()) + epoch_boundary::ol_reconfigure_for_test(&root, reconfiguration::current_epoch(), block::get_current_block_height()) } diff --git a/framework/libra-framework/sources/ol_sources/tests/stake.test.move b/framework/libra-framework/sources/ol_sources/tests/stake.test.move index 047ec5ae8..7d46d0163 100644 --- a/framework/libra-framework/sources/ol_sources/tests/stake.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/stake.test.move @@ -161,7 +161,7 @@ module ol_framework::test_stake { mock::genesis_n_vals(&root, 6); mock::mock_all_vals_good_performance(&root); // all validators bid - mock::pof_default(); + mock::pof_default(&root); // now make Eve not compliant let eve = @0x1000e; diff --git a/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move b/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move index bcccaba78..3ffdff610 100644 --- a/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move @@ -4,93 +4,65 @@ module ol_framework::test_reconfiguration { use std::vector; use diem_framework::stake; - use diem_framework::transaction_fee; - // use diem_framework::coin; - use ol_framework::mock; + // use diem_framework::transaction_fee; + use ol_framework::mock::{Self, default_tx_fee_account_at_genesis}; use ol_framework::testnet; use ol_framework::libra_coin; use ol_framework::proof_of_fee; use diem_framework::reconfiguration; use ol_framework::epoch_helper; use ol_framework::ol_account; - use ol_framework::infra_escrow; - use diem_std::debug::print; + // use diem_std::debug::print; // Scenario: all genesis validators make it to next epoch #[test(root = @ol_framework)] fun reconfig_reward_happy_case(root: signer) { - let vals = mock::genesis_n_vals(&root, 5); - - mock::pof_default(); - assert!(vector::length(&vals) == 5, 7357001); - let vals = stake::get_current_validators(); - assert!(vector::length(&vals) == 5, 7357002); - // all vals compliant - mock::mock_all_vals_good_performance(&root); + let _vals = mock::genesis_n_vals(&root, 5); - let (unlocked, alice_bal) = ol_account::balance(@0x1000a); - assert!(unlocked==0, 7367001); - assert!(alice_bal==0, 7357002); + let starting_balance = 500_000; + mock::ol_initialize_coin_and_fund_vals(&root, starting_balance, false); + mock::mock_tx_fees_in_account(&root, default_tx_fee_account_at_genesis()); - let (reward_one, _entry_fee, _, _ ) = proof_of_fee::get_consensus_reward(); + let (reward_one, genesis_entry_fee, _, _ ) = proof_of_fee::get_consensus_reward(); + // entry fee at genesis is zero + assert!(genesis_entry_fee == 0, 7357004); // The epoch's reward BEFORE reconfiguration - assert!(reward_one == 1000000, 7357004); - - let infra = infra_escrow::infra_escrow_balance(); - print(&555); - print(&infra); - - let subsidy = transaction_fee::system_fees_collected(); - print(&666); - print(&subsidy); + assert!(reward_one == 1_000_000, 7357004); // run ol reconfiguration mock::trigger_epoch(&root); - let infra = infra_escrow::infra_escrow_balance(); - print(&5552); - print(&infra); - - let subsidy = transaction_fee::system_fees_collected(); - print(&6662); - print(&subsidy); - + // magic: in test mode and under block 100 all validators perform let vals = stake::get_current_validators(); assert!(vector::length(&vals) == 5, 7357005); - // let alice_bal = libra_coin::balance(@0x1000a); + let (_unlocked, alice_bal) = ol_account::balance(@0x1000a); let (_, entry_fee, _, _ ) = proof_of_fee::get_consensus_reward(); - // need to check that the user paid an PoF entry fee for next epoch. - // which means the balance will be the nominal reward, net of the PoF clearing price bid - assert!(alice_bal == (reward_one - entry_fee), 7357006); - // test new subsidy - let (reward_two, _entry_fee, _, _ ) = proof_of_fee::get_consensus_reward(); - print(&777); - print(&reward_two); - let new_budget = reward_two * 5; - print(&new_budget); - - let subsidy = transaction_fee::system_fees_collected(); - print(&888); - print(&subsidy); + // need to check that the user paid an PoF entry fee for next epoch. + // which means the balance will be: + // the nominal reward minus, entry fee (clearing price), plus any existing balance. + assert!(alice_bal == (reward_one - entry_fee + starting_balance), 7357006); } #[test(root = @ol_framework)] fun drop_non_performing(root: signer) { - let _vals = mock::genesis_n_vals(&root, 5); - // mock::ol_initialize_coin(&root); - mock::pof_default(); - assert!(libra_coin::balance(@0x1000a) == 0, 7357000); + let vals = mock::genesis_n_vals(&root, 5); + let alice = *vector::borrow(&vals, 0); + + assert!(libra_coin::balance(alice) == 0, 7357000); // NOTE: epoch 0 and 1 are a special case, we don't run performance grades on that one. Need to move two epochs ahead reconfiguration::test_helper_increment_epoch_dont_reconfigure(1); reconfiguration::test_helper_increment_epoch_dont_reconfigure(1); + // epoch changed, need new valid bids + mock::pof_default(&root); + assert!(epoch_helper::get_current_epoch() == 2, 7357001); let vals = stake::get_current_validators(); @@ -98,27 +70,18 @@ module ol_framework::test_reconfiguration { // all vals compliant mock::mock_all_vals_good_performance(&root); - // make alice non performant - mock::mock_case_4(&root, *vector::borrow(&vals, 0)); - - let (reward, _, _, _ ) = proof_of_fee::get_consensus_reward(); + mock::mock_case_4(&root, alice); // run ol reconfiguration mock::trigger_epoch(&root); - let vals = stake::get_current_validators(); // one validator missing. + let vals = stake::get_current_validators(); assert!(vector::length(&vals) == 4, 7357003); - assert!(!vector::contains(&vals, &@0x1000a), 7357004); - - let (_, entry_fee, _, _ ) = proof_of_fee::get_consensus_reward(); + assert!(!vector::contains(&vals, &alice), 7357004); - // alice doesn't get paid - assert!(libra_coin::balance(@0x1000a) == 0, 7357005); - // bob does - assert!(libra_coin::balance(@0x1000b) == (reward - entry_fee), 7357006); } @@ -127,9 +90,7 @@ module ol_framework::test_reconfiguration { #[test(root = @ol_framework)] fun reconfig_failover(root: signer) { let vals = mock::genesis_n_vals(&root, 5); - // mock::ol_initialize_coin(&root); - - mock::pof_default(); + mock::pof_default(&root); // validators did not perform. let i = 0; diff --git a/framework/libra-framework/sources/ol_sources/tests/vote_lib/multi_action.test.move b/framework/libra-framework/sources/ol_sources/tests/vote_lib/multi_action.test.move index 66e5dd5b6..7e5f651c0 100644 --- a/framework/libra-framework/sources/ol_sources/tests/vote_lib/multi_action.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/vote_lib/multi_action.test.move @@ -16,7 +16,7 @@ module ol_framework::test_multi_action { // print //use std::debug::print; - struct DummyType has drop, store {} + struct DummyType has drop, store {} #[test(root = @ol_framework, carol = @0x1000c)] fun init_multi_action(root: &signer, carol: &signer) { @@ -36,7 +36,7 @@ module ol_framework::test_multi_action { fun propose_offer(root: &signer, carol: &signer) { mock::genesis_n_vals(root, 4); let carol_address = @0x1000c; - + // check the offer does not exist assert!(!multi_action::exists_offer(carol_address), 7357001); assert!(!multi_action::is_multi_action(carol_address), 7357002); @@ -140,7 +140,7 @@ module ol_framework::test_multi_action { // check the account is multi_action assert!(multi_action::is_multi_action(carol_address), 7357002); - + // check authorities let authorities = multi_action::get_authorities(carol_address); assert!(authorities == vector[@0x1000a, @0x1000b], 7357003); @@ -173,7 +173,7 @@ module ol_framework::test_multi_action { // check the account is multi_action assert!(multi_action::is_multi_action(carol_address), 7357002); - + // check authorities let authorities = multi_action::get_authorities(carol_address); assert!(authorities == vector[@0x1000a, @0x1000b], 7357003); @@ -388,7 +388,7 @@ module ol_framework::test_multi_action { mock::genesis_n_vals(root, 3); let carol_address = @0x1000c; multi_action::init_gov(carol); - + // invite bob multi_action::propose_offer(carol, vector[@0x1000b], option::none()); @@ -402,13 +402,13 @@ module ol_framework::test_multi_action { fun claim_expired_offer(root: &signer, carol: &signer, alice: &signer, bob: &signer) { mock::genesis_n_vals(root, 3); multi_action::init_gov(carol); - + // offer to alice and bob multi_action::propose_offer(carol, vector[@0x1000a, @0x1000b], option::some(2)); // alice claim the offer multi_action::claim_offer(alice, @0x1000c); - + mock::trigger_epoch(root); // epoch 1 valid mock::trigger_epoch(root); // epoch 2 valid mock::trigger_epoch(root); // epoch 3 expired @@ -418,7 +418,7 @@ module ol_framework::test_multi_action { } // Try to claim offer of an account without proposal - #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b)] + #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b)] #[expected_failure(abort_code = 0x60011, location = ol_framework::multi_action)] fun claim_offer_without_proposal(root: &signer, alice: &signer) { mock::genesis_n_vals(root, 2); @@ -462,7 +462,7 @@ module ol_framework::test_multi_action { fun finalize_without_enough_claimed(root: &signer, alice: &signer, bob: &signer) { mock::genesis_n_vals(root, 3); multi_action::init_gov(alice); - + // offer to bob and carol authority on the alice account multi_action::propose_offer(alice, vector[@0x1000b, @0x1000c], option::none()); @@ -480,7 +480,7 @@ module ol_framework::test_multi_action { mock::genesis_n_vals(root, 3); let alice_address = @0x1000a; multi_action::init_gov(alice); - + // offer bob and carol authority on the alice account multi_action::propose_offer(alice, vector[@0x1000b, @0x1000c], option::none()); @@ -494,7 +494,7 @@ module ol_framework::test_multi_action { } // Governance Tests - + // Happy Day: propose a new action and check zero votes #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c)] fun propose_action(root: &signer, alice: &signer, bob: &signer, carol: &signer) { @@ -505,11 +505,11 @@ module ol_framework::test_multi_action { multi_action::init_gov(alice); multi_action::init_type(alice, true); multi_action::propose_offer(alice, vector[@0x1000b, @0x1000c], option::none()); - + // bob and alice claim the offer multi_action::claim_offer(bob, alice_address); - multi_action::claim_offer(carol, alice_address); - + multi_action::claim_offer(carol, alice_address); + // alice finalize multi action workflow to release control of the account multi_action::finalize_and_cage(alice, 2); @@ -546,8 +546,8 @@ module ol_framework::test_multi_action { // bob and alice claim the offer multi_action::claim_offer(bob, alice_address); - multi_action::claim_offer(carol, alice_address); - + multi_action::claim_offer(carol, alice_address); + // alice finalize multi action workflow to release control of the account multi_action::finalize_and_cage(alice, 2); @@ -590,10 +590,10 @@ module ol_framework::test_multi_action { let alice_address = @0x1000a; multi_action::init_gov(alice); // Ths is a simple multi_action: there is no capability being stored - multi_action::init_type(alice, false); + multi_action::init_type(alice, false); multi_action::propose_offer(alice, vector[@0x1000b, @0x1000c], option::none()); multi_action::claim_offer(bob, alice_address); - multi_action::claim_offer(carol, alice_address); + multi_action::claim_offer(carol, alice_address); multi_action::finalize_and_cage(alice, 2); // bob create a proposal @@ -628,7 +628,7 @@ module ol_framework::test_multi_action { multi_action::init_type(alice, true); multi_action::propose_offer(alice, vector[@0x1000b, @0x1000c], option::none()); multi_action::claim_offer(bob, alice_address); - multi_action::claim_offer(carol, alice_address); + multi_action::claim_offer(carol, alice_address); multi_action::finalize_and_cage(alice, 2); // bob create a proposal and vote @@ -650,7 +650,7 @@ module ol_framework::test_multi_action { assert!(option::is_some(&cap_opt), 7357003); let cap = option::extract(&mut cap_opt); let c = ol_account::withdraw_with_capability(&cap, 42,); - // deposit to erik account + // deposit to erik account ol_account::create_account(root, @0x1000e); ol_account::deposit_coins(@0x1000e, c); option::fill(&mut cap_opt, cap); @@ -671,7 +671,7 @@ module ol_framework::test_multi_action { mock::ol_initialize_coin_and_fund_vals(root, 10000000, true); // we are at epoch 0 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 0, 7357001); // dave creates erik resource account. He is not one of the validators, and is not an authority in the multisig. @@ -681,15 +681,15 @@ module ol_framework::test_multi_action { // fund the account ol_account::transfer(alice, erik_address, 100); - + // offer alice and bob authority on the safe safe::init_payment_multisig(&erik, vector[@0x1000a, @0x1000b]); // both need to sign multi_action::claim_offer(alice, erik_address); - multi_action::claim_offer(bob, erik_address); + multi_action::claim_offer(bob, erik_address); multi_action::finalize_and_cage(&erik, 2); // make a proposal for governance, expires in 2 epoch from now - let id = multi_action::propose_governance(alice, erik_address, vector::empty(), true, + let id = multi_action::propose_governance(alice, erik_address, vector::empty(), true, option::some(1), option::some(2)); mock::trigger_epoch(root); // epoch 1 @@ -697,12 +697,12 @@ module ol_framework::test_multi_action { mock::trigger_epoch(root); // epoch 3 -- voting ended here mock::trigger_epoch(root); // epoch 4 -- now expired - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 4, 7357003); // trying to vote on a closed ballot will error let _passed = multi_action::vote_governance(bob, erik_address, &id); - } + } // Happy day: change the authorities of a multisig #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d)] @@ -715,7 +715,7 @@ module ol_framework::test_multi_action { mock::ol_initialize_coin_and_fund_vals(root, 10000000, true); let carol_address = @0x1000c; let dave_address = @0x1000d; - + // fund the account ol_account::transfer(alice, carol_address, 100); // offer alice and bob authority on the safe @@ -724,7 +724,7 @@ module ol_framework::test_multi_action { let authorities = vector[@0x1000a, @0x1000b]; multi_action::propose_offer(carol, authorities, option::none()); multi_action::claim_offer(alice, carol_address); - multi_action::claim_offer(bob, carol_address); + multi_action::claim_offer(bob, carol_address); multi_action::finalize_and_cage(carol, 2); // alice is going to propose to change the authorities to add dave and increase the threshold to 3 @@ -786,7 +786,7 @@ module ol_framework::test_multi_action { // Happy day: change the threshold of a multisig #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d)] fun governance_change_threshold(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { - // Scenario: The multisig gets initiated with the 2 bob and carol as the only authorities. + // Scenario: The multisig gets initiated with the 2 bob and carol as the only authorities. // It takes 2-of-2 to sign. // They decide next only 1-of-2 will be needed. // Then they decide to invite dave and make it 3-of-3. @@ -807,12 +807,12 @@ module ol_framework::test_multi_action { multi_action::init_type(&resource_sig, false); multi_action::propose_offer(&resource_sig, vector[@0x1000b, @0x1000c], option::none()); multi_action::claim_offer(carol, new_resource_address); - multi_action::claim_offer(bob, new_resource_address); + multi_action::claim_offer(bob, new_resource_address); multi_action::finalize_and_cage(&resource_sig, 2); // carol is going to propose to change the threshold to 1 let id = multi_action::propose_governance(carol, new_resource_address, vector::empty(), true, option::some(1), option::none()); - + // check authorities and threshold let authorities = multi_action::get_authorities(new_resource_address); assert!(authorities == vector[@0x1000c, @0x1000b], 7357002); // no change @@ -844,7 +844,7 @@ module ol_framework::test_multi_action { // now bob decide to invite dave and make it 3-of-3. multi_action::propose_governance(bob, new_resource_address, vector[signer::address_of(dave)], true, option::some(3), option::none()); - + // check authorities and threshold did not change let authorities = multi_action::get_authorities(new_resource_address); assert!(vector::length(&authorities) == 2, 7357011); @@ -892,10 +892,10 @@ module ol_framework::test_multi_action { // carol is going to propose to change the authorities to add dave let id = multi_action::propose_governance(carol, alice_address, vector[@0x1000d], true, option::none(), option::none()); - + // bob votes and dave does not claims the offer multi_action::vote_governance(bob, alice_address, &id); - + // check authorities and threshold assert!(multi_action::get_authorities(alice_address) == vector[@0x1000b, @0x1000c], 7357001); let (n, _m) = multi_action::get_threshold(alice_address); @@ -930,7 +930,7 @@ module ol_framework::test_multi_action { #[expected_failure(abort_code = 0x3000C, location = ol_framework::multi_action)] fun vote_action_twice(root: &signer, alice: &signer, bob: &signer, carol: &signer) { // Scenario: Testing that a vote cannot be done twice on the same ballot. - + mock::genesis_n_vals(root, 4); mock::ol_initialize_coin_and_fund_vals(root, 10000000, true); let carol_address = @0x1000c; @@ -942,11 +942,11 @@ module ol_framework::test_multi_action { multi_action::init_type(carol, true); multi_action::propose_offer(carol, vector[@0x1000a, @0x1000b], option::none()); multi_action::claim_offer(alice, carol_address); - multi_action::claim_offer(bob, carol_address); + multi_action::claim_offer(bob, carol_address); multi_action::finalize_and_cage(carol, 2); // alice is going to propose to change the authorities to add dave - let id = multi_action::propose_governance(alice, carol_address, + let id = multi_action::propose_governance(alice, carol_address, vector[dave_address], true, option::none(), option::none()); // bob votes @@ -973,7 +973,7 @@ module ol_framework::test_multi_action { // carol is going to propose to change the authorities to add dave let id = multi_action::propose_governance(carol, alice_address, vector[@0xCAFE], true, option::none(), option::none()); - + // bob votes and dave does not claims the offer multi_action::vote_governance(bob, alice_address, &id); } @@ -993,7 +993,7 @@ module ol_framework::test_multi_action { multi_action::finalize_and_cage(alice, 2); // carol is going to propose to change the authorities to add dave twice - let _id = multi_action::propose_governance(carol, alice_address, + let _id = multi_action::propose_governance(carol, alice_address, vector[@0x1000d, @0x1000d], true, option::none(), option::none()); } @@ -1048,13 +1048,13 @@ module ol_framework::test_multi_action { multi_action::finalize_and_cage(alice, 2); // carol is going to propose to remove dave - let _id = multi_action::propose_governance(carol, alice_address, + let _id = multi_action::propose_governance(carol, alice_address, vector[@0x1000d], false, option::none(), option::none()); } // Two governance proposals at the same time #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d, eve = @0x1000e)] - fun two_simultaneous_governance_vote(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer, eve: &signer) { + fun two_simultaneous_governance_vote(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer, eve: &signer) { mock::genesis_n_vals(root, 5); let alice_address = @0x1000a; @@ -1067,7 +1067,7 @@ module ol_framework::test_multi_action { // carol is going to propose to change the authorities to add dave let id_set_n_3 = multi_action::propose_governance(carol, alice_address, vector[@0x1000d], true, option::some(3), option::none()); - + // bob is going to propose to change the authorities to add erik let id_set_n_1 = multi_action::propose_governance(bob, alice_address, vector[@0x1000e], true, option::some(1), option::none()); diff --git a/framework/libra-framework/sources/ol_sources/tests/vote_lib/safe.test.move b/framework/libra-framework/sources/ol_sources/tests/vote_lib/safe.test.move index e6c4aaedc..edbb400e6 100644 --- a/framework/libra-framework/sources/ol_sources/tests/vote_lib/safe.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/vote_lib/safe.test.move @@ -93,14 +93,15 @@ module ol_framework::test_safe { fun safe_root_security_fee(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { let vals = mock::genesis_n_vals(root, 3); - mock::ol_initialize_coin_and_fund_vals(root, 1000000000000, true); + let balance = 100_000_000; + mock::ol_initialize_coin_and_fund_vals(root, balance, true); // Dave creates the resource account. HE is not one of the validators, and is not an authority in the multisig. let (resource_sig, _cap) = ol_account::test_ol_create_resource_account(dave, b"0x1"); let new_resource_address = signer::address_of(&resource_sig); assert!(resource_account::is_resource_account(new_resource_address), 0); - + safe::init_payment_multisig(&resource_sig, vals); // requires 3 - + // vals claim the offer multi_action::claim_offer(alice, new_resource_address); multi_action::claim_offer(bob, new_resource_address); @@ -110,9 +111,12 @@ module ol_framework::test_safe { multi_action::finalize_and_cage(&resource_sig, 2); // fund the account - ol_account::transfer(alice, new_resource_address, 1000000); + ol_account::transfer(alice, new_resource_address, 100_000); let (_, bal) = ol_account::balance(new_resource_address); + mock::trigger_epoch(root); + + // Use of Safe should debit the account at epoch boundary let (_, new_bal) = ol_account::balance(new_resource_address); assert!(new_bal < bal, 735701); } diff --git a/framework/libra-framework/sources/ol_sources/tests/vouch.test.move b/framework/libra-framework/sources/ol_sources/tests/vouch.test.move index 17a1acde8..5dbcc78dd 100644 --- a/framework/libra-framework/sources/ol_sources/tests/vouch.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/vouch.test.move @@ -41,6 +41,8 @@ module ol_framework::test_vouch { // fast forward to epoch 1 mock::trigger_epoch(root); + vouch::set_vouch_price(root, 0); + // carol vouches for bob vouch::vouch_for(carol, @0x1000b); @@ -146,6 +148,8 @@ module ol_framework::test_vouch { // fast forward to epoch 1 mock::trigger_epoch(root); + vouch::set_vouch_price(root, 0); + // alice vouches for bob again vouch::vouch_for(alice, @0x1000b); diff --git a/framework/libra-framework/sources/ol_sources/vote_lib/donor_voice_txs.move b/framework/libra-framework/sources/ol_sources/vote_lib/donor_voice_txs.move index 011290e05..3c99de165 100644 --- a/framework/libra-framework/sources/ol_sources/vote_lib/donor_voice_txs.move +++ b/framework/libra-framework/sources/ol_sources/vote_lib/donor_voice_txs.move @@ -55,7 +55,7 @@ module ol_framework::donor_voice_txs { use ol_framework::donor_voice; use ol_framework::slow_wallet; - use diem_std::debug::print; + // use diem_std::debug::print; friend ol_framework::community_wallet_init; friend ol_framework::epoch_boundary; @@ -343,14 +343,9 @@ module ol_framework::donor_voice_txs { let list = &mut state.scheduled; let split_point = vector::stable_partition(list, |e| { let e: &TimedTransfer = e; - // &tt.deadline > epoch - // print(&tt.uid); - print(&e.deadline); - print(&epoch); e.deadline > epoch }); - print(&split_point); - // vector::empty() + vector::trim(&mut state.scheduled, split_point) } @@ -364,7 +359,6 @@ module ol_framework::donor_voice_txs { let i = 0; let due_list = filter_scheduled_due(state, epoch); - print(&due_list); // find all Txs scheduled prior to this epoch. let len = vector::length(&due_list); @@ -386,13 +380,8 @@ module ol_framework::donor_voice_txs { amount_transferred = coin::value(&c); ol_account::vm_deposit_coins_locked(vm, t.tx.payee, c); // update the records (don't copy or drop) - print(&state.scheduled); - print(&state.paid); - vector::push_back(&mut state.paid, t); - print(&state.scheduled); - print(&state.paid); } else { // if it could not be paid because of low balance, // place it back on the scheduled list diff --git a/framework/libra-framework/sources/ol_sources/vote_lib/tally/binary_tally.move b/framework/libra-framework/sources/ol_sources/vote_lib/tally/binary_tally.move index 852e99102..d1005b870 100644 --- a/framework/libra-framework/sources/ol_sources/vote_lib/tally/binary_tally.move +++ b/framework/libra-framework/sources/ol_sources/vote_lib/tally/binary_tally.move @@ -199,7 +199,7 @@ fun maybe_tally(t: &mut BinaryTally): Option { - if (reconfiguration::get_current_epoch() > t.deadline_epoch) { + if (reconfiguration::current_epoch() > t.deadline_epoch) { if (t.votes_for > t.votes_against) { t.tally_result = option::some(true); diff --git a/framework/libra-framework/sources/randomness.move b/framework/libra-framework/sources/randomness.move index 682d14076..f9d998fcd 100644 --- a/framework/libra-framework/sources/randomness.move +++ b/framework/libra-framework/sources/randomness.move @@ -29,8 +29,8 @@ module diem_framework::randomness { use std::vector; use diem_framework::system_addresses; use diem_framework::transaction_context; - #[test_only] - use diem_std::debug; + // #[test_only] + // use diem_std::debug; #[test_only] use diem_std::table_with_length; @@ -450,13 +450,15 @@ module diem_framework::randomness { } #[test(fx = @diem_framework)] - fun randomness_smoke_test(fx: signer) acquires PerBlockRandomness { + fun randomness_integer(fx: signer) acquires PerBlockRandomness { initialize(&fx); set_seed(x"0000000000000000000000000000000000000000000000000000000000000000"); // Test cases should always have no bias for any randomness call. // assert!(is_unbiasable(), 0); let num = u64_integer(); - debug::print(&num); + assert!(num > 0, 0); + let num2 = u64_integer(); + assert!(num != num2, 1); } // #[test_only] diff --git a/framework/libra-framework/sources/reconfiguration.move b/framework/libra-framework/sources/reconfiguration.move index 908bc2099..282dff273 100644 --- a/framework/libra-framework/sources/reconfiguration.move +++ b/framework/libra-framework/sources/reconfiguration.move @@ -24,6 +24,7 @@ module diem_framework::reconfiguration { friend diem_framework::gas_schedule; friend diem_framework::genesis; friend diem_framework::version; + friend diem_framework::block; /// Event that signals consensus to start a new epoch, /// with new configuration information. This is also called a @@ -42,6 +43,10 @@ module diem_framework::reconfiguration { events: event::EventHandle, } + struct EpochInterval has key { + epoch_interval: u64 + } + /// Reconfiguration will be disabled if this resource is published under the /// diem_framework system address struct DisableReconfiguration has key {} @@ -58,14 +63,6 @@ module diem_framework::reconfiguration { const EINVALID_GUID_FOR_EVENT: u64 = 5; - //////// 0L //////// - #[view] - /// Returns the current epoch number - public fun get_current_epoch(): u64 acquires Configuration { - let config_ref = borrow_global(@diem_framework); - config_ref.epoch - } - /// Only called during genesis. /// Publishes `Configuration` resource. Can only be invoked by diem framework account, and only a single time in Genesis. public(friend) fun initialize(diem_framework: &signer) { @@ -194,6 +191,50 @@ module diem_framework::reconfiguration { ); } + // convenience to hold the epoch interval outside of the block:: resource + public(friend) fun set_epoch_interval(framework: &signer, epoch_interval: u64) acquires EpochInterval { + if (!exists(@ol_framework)) { + move_to(framework, EpochInterval { + epoch_interval + }) + } else { + let state = borrow_global_mut(@ol_framework); + state.epoch_interval = epoch_interval + } + } + + #[view] + /// Return epoch interval in seconds. + public fun get_epoch_interval_secs(): u64 acquires EpochInterval { + let state = borrow_global_mut(@ol_framework); + state.epoch_interval / 1000000 + } + + #[view] + /// Return rough remaining seconds in epoch + public fun get_remaining_epoch_secs(): u64 acquires Configuration, EpochInterval { + let now = timestamp::now_seconds(); + let last_epoch_secs = last_reconfiguration_time() / 1000000; + let interval = get_epoch_interval_secs(); + if (now < last_epoch_secs) { // impossible underflow, some thign bad, or tests + return 0 + }; + + let deadline = last_epoch_secs + interval; + + if (now > deadline) { // we've run over the deadline + return 0 + }; + + // belt and suspenders + if (deadline > now) { + return deadline - now + }; + + return 0 + + } + // For tests, skips the guid validation. #[test_only] public fun initialize_for_test(account: &signer) { diff --git a/framework/releases/head.mrb b/framework/releases/head.mrb index d0e20fa11..6bfd477db 100644 Binary files a/framework/releases/head.mrb and b/framework/releases/head.mrb differ diff --git a/tools/txs/src/txs_cli_vals.rs b/tools/txs/src/txs_cli_vals.rs index 6dbd3eef5..98d1ea749 100644 --- a/tools/txs/src/txs_cli_vals.rs +++ b/tools/txs/src/txs_cli_vals.rs @@ -6,8 +6,8 @@ use diem_genesis::config::OperatorConfiguration; use diem_types::account_address::AccountAddress; use libra_cached_packages::libra_stdlib::EntryFunctionCall::{ self, JailUnjailByVoucher, ProofOfFeePofRetractBid, ProofOfFeePofUpdateBid, - StakeUpdateNetworkAndFullnodeAddresses, ValidatorUniverseRegisterValidator, VouchRevoke, - VouchVouchFor, + ProofOfFeePofUpdateBidNetReward, StakeUpdateNetworkAndFullnodeAddresses, + ValidatorUniverseRegisterValidator, VouchRevoke, VouchVouchFor, }; use libra_config::validator_registration; use libra_types::global_config_dir; @@ -18,14 +18,17 @@ use std::{fs, path::PathBuf}; pub enum ValidatorTxs { /// Proof-of-Fee auction bidding Pof { + #[clap(short('r'), long)] + /// Estimated net reward you would like to receive each epoch + net_reward: u64, #[clap(short, long)] /// Percentage of the nominal reward you will bid to join the /// validator set, with three decimal places: 1.234 is 123.4% - bid_pct: f64, + bid_pct: Option, #[clap(short, long)] - /// Epoch until the bid is valid (will expire in `expiry` + 1) + /// Epoch until the bid is valid (will expire in `expiry` + 1). Max 30 epochs expiry: u64, - #[clap(short, long)] + #[clap(long)] /// Eliminates the bid. There are only a limited amount of retractions that can happen in an epoch retract: bool, }, @@ -69,15 +72,16 @@ impl ValidatorTxs { pub fn make_payload(&self) -> anyhow::Result { let p = match self { ValidatorTxs::Pof { + net_reward, bid_pct, - expiry: epoch_expiry, + expiry, retract, } => { if *retract { ProofOfFeePofRetractBid {} - } else { + } else if let Some(b) = bid_pct { // TODO: the u64 will truncate, but without rounding it will drop the last digit. - let scaled_bid = (bid_pct * 1000.0).round() as u64; // scale to 10ˆ3. + let scaled_bid = (b * 1000.0).round() as u64; // scale to 10ˆ3. if scaled_bid > 1100 { bail!( "a bid amount at 110.0% or above the epoch's reward, will be rejected" @@ -85,7 +89,13 @@ impl ValidatorTxs { } ProofOfFeePofUpdateBid { bid: scaled_bid, - epoch_expiry: *epoch_expiry, + epoch_expiry: *expiry, + } + } else { + // Default path is to update based on the expected net reward + ProofOfFeePofUpdateBidNetReward { + net_reward: *net_reward, + epoch_expiry: *expiry, } } } diff --git a/tools/txs/tests/trigger_epoch.rs b/tools/txs/tests/trigger_epoch.rs index ba1450884..72454c0e1 100644 --- a/tools/txs/tests/trigger_epoch.rs +++ b/tools/txs/tests/trigger_epoch.rs @@ -5,7 +5,7 @@ use libra_smoke_tests::libra_smoke::LibraSmoke; use libra_txs::{submit_transaction::Sender, txs_cli_governance::GovernanceTxs}; /// Test triggering a new epoch #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -#[ignore] // TODO +// #[ignore] // TODO async fn trigger_epoch() -> anyhow::Result<()> { // create libra swarm and get app config for the validator let mut ls = LibraSmoke::new(Some(1), None)