From 36ccb09700c77dbe5a197ab8bacd33b90a3b142b Mon Sep 17 00:00:00 2001 From: sirouk <8901571+sirouk@users.noreply.github.com> Date: Tue, 3 Oct 2023 14:42:28 -0400 Subject: [PATCH] [move] tower rewards (#60) Co-authored-by: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- .../src/libra_framework_sdk_builder.rs | 76 ++--- framework/libra-framework/sources/block.move | 1 + .../sources/modified_source/stake.move | 1 - .../sources/ol_sources/epoch_boundary.move | 145 ++++++---- .../{gas_coin.move => gas_coin.depr} | 0 .../sources/ol_sources/libra_coin.move | 268 +++++++++++++++++- .../sources/ol_sources/mock.move | 20 +- .../sources/ol_sources/musical_chairs.move | 31 +- .../sources/ol_sources/oracle.move | 90 ++++-- .../sources/ol_sources/proof_of_fee.move | 189 ++++++------ .../ol_sources/tests/boundary.test.move | 23 +- .../ol_sources/tests/musical_chairs.test.move | 8 +- .../ol_sources/tests/proof_of_fee.test.move | 68 ++--- .../sources/ol_sources/tests/stake.test.move | 3 - .../sources/ol_sources/tests/tower.test.move | 59 +++- ...n.test.move => validator_reward.test.move} | 11 +- .../sources/ol_sources/tests/vdf.test.move | 8 +- .../sources/ol_sources/tower_state.move | 10 +- framework/releases/head.mrb | Bin 582364 -> 588311 bytes framework/src/framework_cli.rs | 5 +- smoke-tests/Cargo.toml | 6 +- smoke-tests/src/configure_validator.rs | 1 - smoke-tests/src/tests/balance.rs | 1 - smoke-tests/src/tests/meta.rs | 38 ++- smoke-tests/src/tests/upgrade.rs | 6 +- smoke-tests/src/upgrade_fixtures.rs | 8 +- tools/config/src/config_cli.rs | 1 - tools/config/src/host.rs | 1 - tools/genesis/src/genesis_functions.rs | 2 - tools/genesis/src/genesis_reader.rs | 5 +- tools/genesis/src/parse_json.rs | 3 +- tools/genesis/src/supply.rs | 11 +- tools/genesis/tests/e2e.rs | 2 - .../tests/json_to_genesis_audit_single.rs | 3 +- tools/query/src/query_type.rs | 16 +- tools/tower/src/core/proof.rs | 2 - tools/tower/tests/tower_cli.rs | 2 +- types/src/legacy_types/app_cfg.rs | 2 +- types/src/legacy_types/fixtures.rs | 1 - types/src/legacy_types/network_playlist.rs | 8 - types/src/type_extensions/client_ext.rs | 11 - 42 files changed, 795 insertions(+), 353 deletions(-) rename framework/libra-framework/sources/ol_sources/{gas_coin.move => gas_coin.depr} (100%) rename framework/libra-framework/sources/ol_sources/tests/{reconfiguration.test.move => validator_reward.test.move} (91%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0b5c1c979..66de40cb0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,4 +9,4 @@ repos: - repo: https://github.com/doublify/pre-commit-rust rev: master hooks: - - id: fmt \ No newline at end of file + - id: fmt diff --git a/framework/cached-packages/src/libra_framework_sdk_builder.rs b/framework/cached-packages/src/libra_framework_sdk_builder.rs index 22857560f..1a0c45888 100644 --- a/framework/cached-packages/src/libra_framework_sdk_builder.rs +++ b/framework/cached-packages/src/libra_framework_sdk_builder.rs @@ -256,6 +256,12 @@ pub enum EntryFunctionCall { account_public_key_bytes: Vec, }, + /// Only a Voucher of the validator can flip the unjail bit. + /// This is a way to make sure the validator is ready to rejoin. + JailUnjailByVoucher { + addr: AccountAddress, + }, + /// Only callable in tests and testnets where the core resources account exists. /// Claim the delegated mint capability and destroy the delegated token. GasCoinClaimMintCapability {}, @@ -273,12 +279,6 @@ pub enum EntryFunctionCall { amount: u64, }, - /// Only a Voucher of the validator can flip the unjail bit. - /// This is a way to make sure the validator is ready to rejoin. - JailUnjailByVoucher { - addr: AccountAddress, - }, - /// Similar to add_owners, but only allow adding one owner. MultisigAccountAddOwner { new_owner: AccountAddress, @@ -734,10 +734,10 @@ impl EntryFunctionCall { DummyUseFnFromDiemStd { account_public_key_bytes, } => dummy_use_fn_from_diem_std(account_public_key_bytes), + JailUnjailByVoucher { addr } => jail_unjail_by_voucher(addr), GasCoinClaimMintCapability {} => gas_coin_claim_mint_capability(), GasCoinDelegateMintCapability { to } => gas_coin_delegate_mint_capability(to), GasCoinMintToImpl { dst_addr, amount } => gas_coin_mint_to_impl(dst_addr, amount), - JailUnjailByVoucher { addr } => jail_unjail_by_voucher(addr), MultisigAccountAddOwner { new_owner } => multisig_account_add_owner(new_owner), MultisigAccountAddOwners { new_owners } => multisig_account_add_owners(new_owners), MultisigAccountApproveTransaction { @@ -1559,6 +1559,23 @@ pub fn dummy_use_fn_from_diem_std(account_public_key_bytes: Vec) -> Transact )) } +/// Only a Voucher of the validator can flip the unjail bit. +/// This is a way to make sure the validator is ready to rejoin. +pub fn jail_unjail_by_voucher(addr: AccountAddress) -> 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!("jail").to_owned(), + ), + ident_str!("unjail_by_voucher").to_owned(), + vec![], + vec![bcs::to_bytes(&addr).unwrap()], + )) +} + /// Only callable in tests and testnets where the core resources account exists. /// Claim the delegated mint capability and destroy the delegated token. pub fn gas_coin_claim_mint_capability() -> TransactionPayload { @@ -1613,23 +1630,6 @@ pub fn gas_coin_mint_to_impl(dst_addr: AccountAddress, amount: u64) -> Transacti )) } -/// Only a Voucher of the validator can flip the unjail bit. -/// This is a way to make sure the validator is ready to rejoin. -pub fn jail_unjail_by_voucher(addr: AccountAddress) -> 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!("jail").to_owned(), - ), - ident_str!("unjail_by_voucher").to_owned(), - vec![], - vec![bcs::to_bytes(&addr).unwrap()], - )) -} - /// Similar to add_owners, but only allow adding one owner. pub fn multisig_account_add_owner(new_owner: AccountAddress) -> TransactionPayload { TransactionPayload::EntryFunction(EntryFunction::new( @@ -2840,6 +2840,16 @@ mod decoder { } } + pub fn jail_unjail_by_voucher(payload: &TransactionPayload) -> Option { + if let TransactionPayload::EntryFunction(script) = payload { + Some(EntryFunctionCall::JailUnjailByVoucher { + addr: bcs::from_bytes(script.args().get(0)?).ok()?, + }) + } else { + None + } + } + pub fn gas_coin_claim_mint_capability( payload: &TransactionPayload, ) -> Option { @@ -2873,16 +2883,6 @@ mod decoder { } } - pub fn jail_unjail_by_voucher(payload: &TransactionPayload) -> Option { - if let TransactionPayload::EntryFunction(script) = payload { - Some(EntryFunctionCall::JailUnjailByVoucher { - addr: bcs::from_bytes(script.args().get(0)?).ok()?, - }) - } else { - None - } - } - pub fn multisig_account_add_owner(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountAddOwner { @@ -3485,6 +3485,10 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::Lazy, + outgoing_vals_paid: vector
, + outgoing_total_reward: u64, + outgoing_nominal_reward_to_vals: u64, + outgoing_entry_fee: u64, + outgoing_clearing_percent: u64, + outgoing_vals_success: bool, // TODO + + // Oracle / Tower + tower_state_success: bool, // TODO oracle_budget: u64, + oracle_pay_count: u64, + oracle_pay_amount: u64, oracle_pay_success: bool, - epoch_burn_fees: u64, - epoch_burn_success: bool, - slow_wallet_drip: bool, + + epoch_burn_fees: u64, // TODO + epoch_burn_success: bool, // TODO + slow_wallet_drip: bool, // TODO // Process Incoming // musical chairs incoming_compliant: vector
, @@ -79,8 +85,8 @@ module diem_framework::epoch_boundary { incoming_final_set_size: u64, incoming_reconfig_success: bool, - infra_subsize_amount: u64, - infra_subsizize_success: bool, + infra_subsize_amount: u64, // TODO + infra_subsizize_success: bool, // TODO } public fun initialize(framework: &signer) { @@ -95,14 +101,21 @@ module diem_framework::epoch_boundary { dd_accounts_success: false, set_fee_makers_success: false, - tower_state_success: false, system_fees_collected: 0, - nominal_reward_to_vals: 0, - clearing_price: 0, + // Process Outgoing - process_outgoing_success: false, - outgoing_vals: vector::empty(), + outgoing_vals_paid: vector::empty(), + outgoing_total_reward: 0, + outgoing_vals_success: false, + outgoing_nominal_reward_to_vals: 0, + outgoing_entry_fee: 0, + outgoing_clearing_percent: 0, + + // Oracle / Tower + tower_state_success: false, oracle_budget: 0, + oracle_pay_count: 0, + oracle_pay_amount: 0, oracle_pay_success: false, epoch_burn_fees: 0, epoch_burn_success: false, @@ -134,7 +147,7 @@ module diem_framework::epoch_boundary { // Contains all of 0L's business logic for end of epoch. // This removed business logic from reconfiguration.move // and prevents dependency cycling. - public(friend) fun epoch_boundary(root: &signer, closing_epoch: u64, epoch_round: u64) acquires BoundaryStatus { + public(friend) fun epoch_boundary(root: &signer, closing_epoch: u64, _epoch_round: u64) acquires BoundaryStatus { system_addresses::assert_ol(root); let status = borrow_global_mut(@ol_framework); @@ -151,47 +164,77 @@ module diem_framework::epoch_boundary { // randomize the Tower/Oracle difficulty tower_state::reconfig(root); - settle_accounts(root, closing_epoch, epoch_round, status); + let (compliant_vals, n_seats) = musical_chairs::stop_the_music(root, closing_epoch); + status.incoming_compliant_count = vector::length(&compliant_vals); + status.incoming_compliant = compliant_vals; + status.incoming_seats_offered = n_seats; + + settle_accounts(root, compliant_vals, status); // drip coins slow_wallet::on_new_epoch(root); // ======= THIS IS APPROXIMATELY THE BOUNDARY ===== - process_incoming_validators(root, status); + process_incoming_validators(root, status, compliant_vals, n_seats); subsidize_from_infra_escrow(root); - // print(borrow_global(@ol_framework)) + print(borrow_global(@ol_framework)) } // TODO: instrument all of this /// withdraw coins and settle accounts for validators and oracles - fun settle_accounts(root: &signer, closing_epoch: u64, epoch_round: u64, _status: &mut BoundaryStatus) { + /// returns the list of compliant_vals + fun settle_accounts(root: &signer, compliant_vals: vector
, status: &mut BoundaryStatus): vector
{ assert!(transaction_fee::is_fees_collection_enabled(), error::invalid_state(ETX_FEES_NOT_INITIALIZED)); if (transaction_fee::system_fees_collected() > 0) { let all_fees = transaction_fee::root_withdraw_all(root); + status.system_fees_collected = coin::value(&all_fees); // Nominal fee set by the PoF thermostat - let (nominal_reward_to_vals, clearning_price_to_oracle, _ ) = proof_of_fee::get_consensus_reward(); + let (nominal_reward_to_vals, entry_fee, clearing_percent, _ ) = proof_of_fee::get_consensus_reward(); + status.outgoing_nominal_reward_to_vals = nominal_reward_to_vals; + status.outgoing_entry_fee = entry_fee; + status.outgoing_clearing_percent = clearing_percent; + + // validators get the gross amount of the reward, since they already paid to enter. This results in a net payment equivalent to: + // nominal_reward_to_vals - entry_fee. + let (compliant_vals, total_reward) = process_outgoing_validators(root, &mut all_fees, nominal_reward_to_vals, compliant_vals); - // validators get the gross amount of the reward, since they already paid to enter. This results in a net payment equivalidant to the - // clearing_price. - process_outgoing_validators(root, &mut all_fees, nominal_reward_to_vals, closing_epoch, epoch_round); + status.outgoing_vals_paid = compliant_vals; + status.outgoing_total_reward = total_reward; + + // check that the sustem fees collect were greater than reward + status.outgoing_vals_success = (status.system_fees_collected >= total_reward); + // check the the total actually deposited/paid is the expected amount + if (nominal_reward_to_vals > 0) { // check for zero + status.outgoing_vals_success = total_reward == (vector::length(&compliant_vals) * nominal_reward_to_vals) + }; // since we reserved some fees to go to the oracle miners - // we take the clearing_price, since it is the equivalent of what a - // validator would earn net of entry fee. - let oracle_budget = coin::extract(&mut all_fees, clearning_price_to_oracle); - oracle::epoch_boundary(root, &mut oracle_budget); - // in case there is any dust left - ol_account::merge_coins(&mut all_fees, oracle_budget); + // we take the NET REWARD of the validators, since it is the equivalent of what the validator would earn net of entry fee. + let net_val_reward = nominal_reward_to_vals - entry_fee; + + if (coin::value(&all_fees) > net_val_reward) { + let oracle_budget = coin::extract(&mut all_fees, net_val_reward); + status.oracle_budget = coin::value(&oracle_budget); + + let (count, amount) = oracle::epoch_boundary(root, &mut oracle_budget); + status.oracle_pay_count = count; + status.oracle_pay_amount = amount; + status.oracle_pay_success = status.oracle_budget == amount; + // in case there is any dust left + ol_account::merge_coins(&mut all_fees, oracle_budget); + }; // remainder gets burnt according to fee maker preferences burn::epoch_burn_fees(root, &mut all_fees); // there might be some dust, that should get burned coin::user_burn(all_fees); }; + + compliant_vals } @@ -199,28 +242,23 @@ module diem_framework::epoch_boundary { /// 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) - fun process_outgoing_validators(root: &signer, reward_budget: &mut Coin, reward_per: u64, closing_epoch: u64, epoch_round: u64): vector
{ + // 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 reward_deposited = 0; - let compliant_vals = vector::empty
(); let i = 0; while (i < vector::length(&vals)) { let addr = vector::borrow(&vals, i); - - let (performed, _, _, _) = grade::get_validator_grade(*addr); - // Failover. if we had too few blocks in an epoch, everyone should pass - // except for testing - if (!testnet::is_testnet() && (epoch_round < 1000)) performed = true; - - if (!performed && closing_epoch > 1) { // issues around genesis + let performed = vector::contains(&compliant_vals, addr); + if (!performed) { jail::jail(root, *addr); } else { - vector::push_back(&mut compliant_vals, *addr); + // vector::push_back(&mut compliant_vals, *addr); if (coin::value(reward_budget) > reward_per) { let user_coin = coin::extract(reward_budget, reward_per); + reward_deposited = reward_deposited + coin::value(&user_coin); rewards::process_single(root, *addr, user_coin, 1); } }; @@ -228,25 +266,22 @@ module diem_framework::epoch_boundary { i = i + 1; }; - return compliant_vals + return (compliant_vals, reward_deposited) } - fun process_incoming_validators(root: &signer, status: &mut BoundaryStatus) { + fun process_incoming_validators(root: &signer, status: &mut BoundaryStatus, compliant_vals: vector
, n_seats: u64) { system_addresses::assert_ol(root); - let (compliant, n_seats) = musical_chairs::stop_the_music(root); - status.incoming_compliant_count = vector::length(&compliant); - status.incoming_compliant = compliant; - status.incoming_seats_offered = n_seats; + // check amount of fees expected - let (auction_winners, all_bidders, only_qualified_bidders, entry_price) = proof_of_fee::end_epoch(root, &compliant, n_seats); + let (auction_winners, all_bidders, only_qualified_bidders, entry_fee) = proof_of_fee::end_epoch(root, &compliant_vals, n_seats); status.incoming_filled_seats = vector::length(&auction_winners); status.incoming_all_bidders = all_bidders; status.incoming_only_qualified_bidders = only_qualified_bidders; status.incoming_auction_winners = auction_winners; - let post_failover_check = stake::check_failover_rules(auction_winners, compliant); + let post_failover_check = stake::check_failover_rules(auction_winners, compliant_vals); status.incoming_post_failover_check = post_failover_check; // showtime! try to reconfigure @@ -255,7 +290,7 @@ module diem_framework::epoch_boundary { status.incoming_actual_vals = actual_set; status.incoming_reconfig_success = success; - let (_expected_fees, fees_paid, fee_success) = proof_of_fee::charge_epoch_fees(root, actual_set, entry_price); + let (_expected_fees, fees_paid, fee_success) = proof_of_fee::charge_epoch_fees(root, actual_set, entry_fee); status.incoming_fees = fees_paid; status.incoming_fees_success = fee_success; // make sure musical chairs doesn't keep incrementing if we are persistently @@ -268,7 +303,7 @@ module diem_framework::epoch_boundary { // set up rewards subsidy for coming epoch fun subsidize_from_infra_escrow(root: &signer) { system_addresses::assert_ol(root); - let (reward_per, _, _ ) = proof_of_fee::get_consensus_reward(); + let (reward_per, _, _, _ ) = proof_of_fee::get_consensus_reward(); let vals = stake::get_current_validators(); let count_vals = vector::length(&vals); count_vals = count_vals + ORACLE_PROVIDERS_SEATS; diff --git a/framework/libra-framework/sources/ol_sources/gas_coin.move b/framework/libra-framework/sources/ol_sources/gas_coin.depr similarity index 100% rename from framework/libra-framework/sources/ol_sources/gas_coin.move rename to framework/libra-framework/sources/ol_sources/gas_coin.depr diff --git a/framework/libra-framework/sources/ol_sources/libra_coin.move b/framework/libra-framework/sources/ol_sources/libra_coin.move index e100826af..86d9ed8c9 100644 --- a/framework/libra-framework/sources/ol_sources/libra_coin.move +++ b/framework/libra-framework/sources/ol_sources/libra_coin.move @@ -111,4 +111,270 @@ module 0x1::LibraCoin { assert!(value == 0, 11) } -} \ No newline at end of file +} + + +module ol_framework::gas_coin { + use std::string; + use std::error; + use std::signer; + use std::vector; + use std::option::{Self, Option}; + + use diem_framework::coin::{Self, MintCapability, BurnCapability}; + use diem_framework::system_addresses; + + use ol_framework::globals; + + friend diem_framework::genesis; + friend ol_framework::genesis_migration; + + /// Account does not have mint capability + const ENO_CAPABILITIES: u64 = 1; + /// Mint capability has already been delegated to this specified address + const EALREADY_DELEGATED: u64 = 2; + /// Cannot find delegation of mint capability to this account + const EDELEGATION_NOT_FOUND: u64 = 3; + + struct GasCoin has key {} + + struct MintCapStore has key { + mint_cap: MintCapability, + } + + /// Delegation token created by delegator and can be claimed by the delegatee as MintCapability. + struct DelegatedMintCapability has store { + to: address + } + + /// The container stores the current pending delegations. + struct Delegations has key { + inner: vector, + } + + /// Can only called during genesis to initialize the Diem coin. + public(friend) fun initialize(diem_framework: &signer) { + system_addresses::assert_diem_framework(diem_framework); + + let (burn_cap, freeze_cap, mint_cap) = coin::initialize_with_parallelizable_supply( + diem_framework, + string::utf8(b"Gas Coin"), + string::utf8(b"GAS"), + globals::get_coin_decimal_places(), /* decimals MATCHES LEGACY 0L */ + true, /* monitor_supply */ + ); + + // Diem framework needs mint cap to mint coins to initial validators. This will be revoked once the validators + // have been initialized. + move_to(diem_framework, MintCapStore { mint_cap }); + + coin::destroy_freeze_cap(freeze_cap); + coin::destroy_burn_cap(burn_cap); + // (burn_cap, mint_cap) + } + + /// FOR TESTS ONLY + /// Can only called during genesis to initialize the Diem coin. + public(friend) fun initialize_for_core(diem_framework: &signer): (BurnCapability, MintCapability) { + system_addresses::assert_diem_framework(diem_framework); + + let (burn_cap, freeze_cap, mint_cap) = coin::initialize_with_parallelizable_supply( + diem_framework, + string::utf8(b"Gas Coin"), + string::utf8(b"GAS"), + globals::get_coin_decimal_places(), /* decimals MATCHES LEGACY 0L */ + true, /* monitor_supply */ + ); + + // Diem framework needs mint cap to mint coins to initial validators. This will be revoked once the validators + // have been initialized. + move_to(diem_framework, MintCapStore { mint_cap }); + + coin::destroy_freeze_cap(freeze_cap); + + (burn_cap, mint_cap) + } + + public fun has_mint_capability(account: &signer): bool { + exists(signer::address_of(account)) + } + + /// Only called during genesis to destroy the diem framework account's mint capability once all initial validators + /// and accounts have been initialized during genesis. + public(friend) fun destroy_mint_cap(diem_framework: &signer) acquires MintCapStore { + system_addresses::assert_diem_framework(diem_framework); + let MintCapStore { mint_cap } = move_from(@diem_framework); + coin::destroy_mint_cap(mint_cap); + } + + + #[view] + /// get the gas coin supply. Helper which wraps coin::supply and extracts option type + // NOTE: there is casting between u128 and u64, but 0L has final supply below the u64. + public fun supply(): u64 { + let supply_opt = coin::supply(); + if (option::is_some(&supply_opt)) { + return (*option::borrow(&supply_opt) as u64) + }; + 0 + } + + + #[test_only] + public fun restore_mint_cap(diem_framework: &signer, mint_cap: MintCapability) { + system_addresses::assert_diem_framework(diem_framework); + move_to(diem_framework, MintCapStore { mint_cap }); + } + + /// FOR TESTS ONLY + /// The `core addresses` sudo account is used to execute system transactions for testing + /// Can only be called during genesis for tests to grant mint capability to diem framework and core resources + /// accounts. + public(friend) fun configure_accounts_for_test( + diem_framework: &signer, + core_resources: &signer, + mint_cap: MintCapability, + ){ + system_addresses::assert_diem_framework(diem_framework); + + // Mint the core resource account GasCoin for gas so it can execute system transactions. + coin::register(core_resources); + + let coins = coin::mint( + 18446744073709551615, + &mint_cap, + ); + coin::deposit(signer::address_of(core_resources), coins); + + move_to(core_resources, MintCapStore { mint_cap }); + move_to(core_resources, Delegations { inner: vector::empty() }); + } + + // /// Only callable in tests and testnets where the core resources account exists. + // /// Create new coins and deposit them into dst_addr's account. + // mint_impl( + // root: &signer, + // amount: u64, + // ): Coin acquires MintCapStore { + // system_addresses::assert_ol(root); + + // let mint_cap = &borrow_global(signer::address_of(root)).mint_cap; + // coin::mint(amount, mint_cap) + // } + + // NOTE: needed for smoke tests + // TODO: guard some other way besides using the testing root account. + /// Root account can mint to an address. Only used for genesis and tests. + /// The "root" account in smoke tests has some privileges. + public entry fun mint_to_impl( + root: &signer, + dst_addr: address, + amount: u64, + ) acquires MintCapStore { + + let account_addr = signer::address_of(root); + + assert!( + exists(account_addr), + error::not_found(ENO_CAPABILITIES), + ); + + let mint_cap = &borrow_global(account_addr).mint_cap; + let coins_minted = coin::mint(amount, mint_cap); + coin::deposit(dst_addr, coins_minted); + } + + #[test_only] + public entry fun test_mint_to( + root: &signer, + dst_addr: address, + amount: u64, + ) acquires MintCapStore { + system_addresses::assert_ol(root); + mint_to_impl(root, dst_addr, amount); + } + + /// Only callable in tests and testnets where the core resources account exists. + /// Create delegated token for the address so the account could claim MintCapability later. + public entry fun delegate_mint_capability(account: signer, to: address) acquires Delegations { + system_addresses::assert_core_resource(&account); + let delegations = &mut borrow_global_mut(@core_resources).inner; + let i = 0; + while (i < vector::length(delegations)) { + let element = vector::borrow(delegations, i); + assert!(element.to != to, error::invalid_argument(EALREADY_DELEGATED)); + i = i + 1; + }; + vector::push_back(delegations, DelegatedMintCapability { to }); + } + + /// Only callable in tests and testnets where the core resources account exists. + /// Claim the delegated mint capability and destroy the delegated token. + public entry fun claim_mint_capability(account: &signer) acquires Delegations, MintCapStore { + let maybe_index = find_delegation(signer::address_of(account)); + assert!(option::is_some(&maybe_index), EDELEGATION_NOT_FOUND); + let idx = *option::borrow(&maybe_index); + let delegations = &mut borrow_global_mut(@core_resources).inner; + let DelegatedMintCapability { to: _ } = vector::swap_remove(delegations, idx); + + // Make a copy of mint cap and give it to the specified account. + let mint_cap = borrow_global(@core_resources).mint_cap; + move_to(account, MintCapStore { mint_cap }); + } + + fun find_delegation(addr: address): Option acquires Delegations { + let delegations = &borrow_global(@core_resources).inner; + let i = 0; + let len = vector::length(delegations); + let index = option::none(); + while (i < len) { + let element = vector::borrow(delegations, i); + if (element.to == addr) { + index = option::some(i); + break + }; + i = i + 1; + }; + index + } + + #[view] + /// helper to get balance in gas coin + public fun get_balance(account: address): u64 { + coin::balance(account) + } + + + #[test_only] + use diem_framework::aggregator_factory; + + #[test_only] + public fun initialize_for_test(diem_framework: &signer): (BurnCapability, MintCapability) { + aggregator_factory::initialize_aggregator_factory_for_test(diem_framework); + let (burn_cap, freeze_cap, mint_cap) = coin::initialize_with_parallelizable_supply( + diem_framework, + string::utf8(b"Gas Coin"), + string::utf8(b"GAS"), + 8, /* decimals */ + true, /* monitor_supply */ + ); + move_to(diem_framework, MintCapStore { mint_cap }); + + coin::destroy_freeze_cap(freeze_cap); + (burn_cap, mint_cap) + } + + // This is particularly useful if the aggregator_factory is already initialized via another call path. + #[test_only] + public fun initialize_for_test_without_aggregator_factory(diem_framework: &signer): (BurnCapability, MintCapability) { + let (burn_cap, freeze_cap, mint_cap) = coin::initialize_with_parallelizable_supply( + diem_framework, + string::utf8(b"Gas Coin"), + string::utf8(b"GAS"), + 8, /* decimals */ + true, /* monitor_supply */ + ); + coin::destroy_freeze_cap(freeze_cap); + (burn_cap, mint_cap) + } +} diff --git a/framework/libra-framework/sources/ol_sources/mock.move b/framework/libra-framework/sources/ol_sources/mock.move index 83725ff09..39fb3a0d9 100644 --- a/framework/libra-framework/sources/ol_sources/mock.move +++ b/framework/libra-framework/sources/ol_sources/mock.move @@ -105,13 +105,10 @@ module ol_framework::mock { #[test_only] public fun pof_default(): (vector
, vector, vector){ - // system_addresses::assert_ol(vm); let vals = stake::get_current_validators(); let (bids, expiry) = mock_bids(&vals); - // DiemAccount::slow_wallet_epoch_drip(vm, 100000); // unlock some coins for the validators - // 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)); @@ -334,4 +331,21 @@ module ol_framework::mock { } + #[test(root = @ol_framework)] + fun test_setting_pof(root: &signer) { + // 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(); + + 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 == 1000000, 73570001); + assert!(clearing_percent == 1, 73570002); + assert!(entry_fee == 999, 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 08bb1a469..69faa7e25 100644 --- a/framework/libra-framework/sources/ol_sources/musical_chairs.move +++ b/framework/libra-framework/sources/ol_sources/musical_chairs.move @@ -3,6 +3,7 @@ module ol_framework::musical_chairs { use diem_framework::system_addresses; use diem_framework::stake; use ol_framework::grade; + // use ol_framework::testnet; use std::fixed_point32; use std::vector; // use diem_std::debug::print; @@ -67,23 +68,17 @@ module ol_framework::musical_chairs { /// (compliant_vals, seats_offered) public(friend) fun stop_the_music( // sorry, had to. vm: &signer, + epoch_round: u64, ): (vector
, u64) acquires Chairs { system_addresses::assert_ol(vm); let validators = stake::get_current_validators(); - let (compliant_vals, _non, ratio) = eval_compliance_impl(validators); + let (compliant_vals, _non, ratio) = eval_compliance_impl(validators, epoch_round); let chairs = borrow_global_mut(@ol_framework); let num_compliant_nodes = vector::length(&compliant_vals); - // catch failure mode - // mostly for testnets - if (chairs.seats_offered < 4) { - chairs.seats_offered = 4; - return (compliant_vals, chairs.seats_offered) - }; - // failover, there should not be more compliant nodes than seats that were offered. // return with no changes if (num_compliant_nodes > chairs.seats_offered) { @@ -109,6 +104,11 @@ module ol_framework::musical_chairs { chairs.seats_offered = chairs.seats_offered + 1; }; + // catch failure mode + // mostly for genesis, or testnets + if (chairs.seats_offered < 4) { + chairs.seats_offered = 4; + }; (compliant_vals, chairs.seats_offered) } @@ -125,15 +125,16 @@ module ol_framework::musical_chairs { } #[test_only] - public fun test_eval_compliance(root: &signer, validators: vector
): (vector
, vector
, fixed_point32::FixedPoint32) { + public fun test_eval_compliance(root: &signer, validators: vector
, epoch_round: u64): (vector
, vector
, fixed_point32::FixedPoint32) { system_addresses::assert_ol(root); - eval_compliance_impl(validators) + eval_compliance_impl(validators, epoch_round) } // use the Case statistic to determine what proportion of the network is compliant. // private function prevent list DoS. fun eval_compliance_impl( validators: vector
, + epoch: u64, ) : (vector
, vector
, fixed_point32::FixedPoint32) { let val_set_len = vector::length(&validators); @@ -141,6 +142,12 @@ module ol_framework::musical_chairs { let compliant_nodes = vector::empty
(); let non_compliant_nodes = vector::empty
(); + // if we are at genesis or otherwise at start of an epoch, we don't + // want to brick the validator set + // TODO: use status.move is_operating + if (epoch < 2) return (validators, non_compliant_nodes, fixed_point32::create_from_rational(1, 1)); + + let i = 0; while (i < val_set_len) { let addr = *vector::borrow(&validators, i); @@ -192,8 +199,8 @@ module ol_framework::musical_chairs { use diem_framework::chain_id; #[test_only] - public fun test_stop(vm: &signer): (vector
, u64) acquires Chairs { - stop_the_music(vm) + public fun test_stop(vm: &signer, epoch_round: u64): (vector
, u64) acquires Chairs { + stop_the_music(vm, epoch_round) } //////// TESTS //////// diff --git a/framework/libra-framework/sources/ol_sources/oracle.move b/framework/libra-framework/sources/ol_sources/oracle.move index 59858bf30..aa4823838 100644 --- a/framework/libra-framework/sources/ol_sources/oracle.move +++ b/framework/libra-framework/sources/ol_sources/oracle.move @@ -17,7 +17,10 @@ module ol_framework::oracle { use ol_framework::epoch_helper; use std::error; + use diem_std::debug::print; + friend ol_framework::epoch_boundary; + friend ol_framework::tower_state; /// You need a minimum of three Vouches on your account, and of unrelated /// buddies. Meaning: they don't come from the same ancestry of accounts. @@ -85,17 +88,18 @@ module ol_framework::oracle { // init a new provider account, if they are not migrating a tower. public entry fun init_provider(provider: &signer) { - move_to(provider, Tower { - last_commit_timestamp: 0, - previous_proof_hash: vector::empty(), - verified_tower_height: 0, - latest_epoch_mining: 0, - count_proofs_in_epoch: 0, - epochs_mining: 0, - contiguous_epochs_mining: 0, - distribute_rewards_events: account::new_event_handle(provider) - }); - + if (!exists(signer::address_of(provider))) { + move_to(provider, Tower { + last_commit_timestamp: 0, + previous_proof_hash: vector::empty(), + verified_tower_height: 0, + latest_epoch_mining: 0, + count_proofs_in_epoch: 0, + epochs_mining: 0, + contiguous_epochs_mining: 0, + distribute_rewards_events: account::new_event_handle(provider) + }); + } } /// At genesis this can be called once to migrate towers @@ -129,7 +133,7 @@ module ol_framework::oracle { ) acquires GlobalCounter, Tower, ProviderList { let provider_addr = signer::address_of(provider); - // Don't populate the oracle miner list wit accounts that don't have vouches. + // Don't populate the oracle miner list with accounts that don't have vouches. { // must have 3 accounts who are unrelated vouching for you. let frens = vouch::true_friends(provider_addr); @@ -171,8 +175,14 @@ module ol_framework::oracle { assert!(ed25519::signature_verify_strict(&sig, &pk, tower.previous_proof_hash), 77); // the proof is valid, update the tower state. + increment_stats(provider_addr, tower, time, signature_bytes); - // update the global state + } + + fun increment_stats(provider_addr: address, tower: &mut Tower, time: u64, signature_bytes: vector,) acquires GlobalCounter, ProviderList { + print(&333); + + // update the global state let global = borrow_global_mut(@ol_framework); global.lifetime_proofs = global.lifetime_proofs + 1; global.proofs_in_epoch = global.proofs_in_epoch + 1; @@ -186,15 +196,17 @@ module ol_framework::oracle { // also check if the tower is now above the threshold if (tower.count_proofs_in_epoch > threshold_of_signatures()) { + print(&333001); global.proofs_in_epoch_above_thresh = global.proofs_in_epoch_above_thresh + 1; // also add to the provider list which would be elegible for rewards let provider_list = borrow_global_mut(@ol_framework); vector::push_back(&mut provider_list.current_above_threshold, provider_addr); + print(provider_list); }; let current_epoch = epoch_helper::get_current_epoch(); - if (current_epoch == (tower.latest_epoch_mining - 1)) { + if (current_epoch > 0 && (current_epoch -1) == tower.latest_epoch_mining) { tower.contiguous_epochs_mining = tower.contiguous_epochs_mining + 1; }; @@ -203,6 +215,27 @@ module ol_framework::oracle { } + // while transitioning to oracle, allow vdf proofs from miners. + // can only be called by tower + public(friend) fun count_vdf_proof( + provider_addr: address, + signature_bytes: vector + ) acquires GlobalCounter, Tower, ProviderList { + // let provider_addr = signer::address_of(provider); + // the message needs to be exactly the hash of the previous proof. + // first check if enough time has passed. + let time = timestamp::now_microseconds(); + let tower = borrow_global_mut(provider_addr); + // can't send multiple in same tx + assert!(time > tower.last_commit_timestamp, ETIME_IS_IN_PAST_WHAAAT); // TODO: fill out error + // the sufficient time has passed + assert!(time > tower.last_commit_timestamp + proof_interval_seconds() , ETOO_SOON_SUBMITTED); + + increment_stats(provider_addr, tower, time, signature_bytes); + + + } + // how long should the delay be. // in testnet it should be 30 seconds. // in production its 1 hour. @@ -217,15 +250,16 @@ module ol_framework::oracle { // how many proofs needed in an epoch to be considered active fun threshold_of_signatures(): u64 { if (testnet::is_testnet()) { - 1 + 0 } else { 12 } } - public(friend) fun epoch_boundary(root: &signer, budget: &mut Coin) acquires GlobalCounter, ProviderList, Tower { + public(friend) fun epoch_boundary(root: &signer, budget: &mut Coin): (u64, u64) acquires GlobalCounter, ProviderList, Tower { + let (provider_count, paid_amount ) = epoch_reward(root, budget); reset_counters(root); - epoch_reward(root, budget) + (provider_count, paid_amount) } fun reset_counters(root: &signer) acquires ProviderList, GlobalCounter{ @@ -242,23 +276,31 @@ module ol_framework::oracle { /// from the total reward, available to the miners, divide equally among /// successful miners. - fun epoch_reward(root: &signer, budget: &mut Coin) acquires ProviderList, Tower { + /// returns: provider_list_len total_deposited + /// + fun epoch_reward(root: &signer, budget: &mut Coin): (u64, u64) acquires ProviderList, Tower { system_addresses::assert_ol(root); + // print(&666666); let coin_value = coin::value(budget); + // print(&coin_value); - let provider_list = &borrow_global_mut(@ol_framework).current_above_threshold; - let len = vector::length(provider_list); - - if (len == 0) return; + let provider_list = borrow_global_mut(@ol_framework).current_above_threshold; + print(&provider_list); + let len = vector::length(&provider_list); + if (len == 0) return (0, 0); + let total_deposited = 0; let per_user = coin_value / len; - vector::for_each_ref(provider_list, |addr| { + vector::for_each_ref(&provider_list, |addr| { emit_distribute_reward(root, addr, per_user); let split = coin::extract(budget, per_user); + let value = coin::value(&split); + total_deposited = total_deposited + value; ol_account::deposit_coins(*addr, split); - }); + + (len, total_deposited) } // since rewards are handled externally to stake.move we need an api to emit the event 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 d16cf47d2..6ac284938 100644 --- a/framework/libra-framework/sources/ol_sources/proof_of_fee.move +++ b/framework/libra-framework/sources/ol_sources/proof_of_fee.move @@ -61,8 +61,10 @@ module ol_framework::proof_of_fee { } struct ConsensusReward has key { - value: u64, - clearing_price: u64, + nominal_reward: u64, + net_reward: u64, + entry_fee: u64, + clearing_bid: u64, median_win_bid: u64, median_history: vector, } @@ -73,8 +75,10 @@ module ol_framework::proof_of_fee { move_to( vm, ConsensusReward { - value: GENESIS_BASELINE_REWARD, - clearing_price: 0, + nominal_reward: GENESIS_BASELINE_REWARD, + net_reward: GENESIS_BASELINE_REWARD, + entry_fee: 0, + clearing_bid: 0, median_win_bid: 0, median_history: vector::empty(), } @@ -104,7 +108,7 @@ module ol_framework::proof_of_fee { /// and finally charging the validators for their bid (everyone pays the lowest) /// for audit instrumentation returns: final set size, auction winners, all the bidders, (including not-qualified), and all qualified bidders. /// we also return the auction entry price (clearing price) - /// (final_set_size, auction_winners, all_bidders, only_qualified_bidders, actually_paid, entry_price) + /// (final_set_size, auction_winners, all_bidders, only_qualified_bidders, actually_paid, entry_fee) public(friend) fun end_epoch( vm: &signer, outgoing_compliant_set: &vector
, @@ -121,10 +125,10 @@ module ol_framework::proof_of_fee { // This is the core of the mechanism, the uniform price auction // the winners of the auction will be the validator set. // other lists are created for audit purposes of the BoundaryStatus - let (auction_winners, entry_price, _proven, _unproven) = fill_seats_and_get_price(vm, final_set_size, &only_qualified_bidders, outgoing_compliant_set); + let (auction_winners, entry_fee, _clearing_bid, _proven, _unproven) = fill_seats_and_get_price(vm, final_set_size, &only_qualified_bidders, outgoing_compliant_set); - (auction_winners, all_bidders, only_qualified_bidders, entry_price) + (auction_winners, all_bidders, only_qualified_bidders, entry_fee) } /// The fees are charged seperate from the auction and seating loop @@ -157,7 +161,7 @@ module ol_framework::proof_of_fee { #[view] public fun get_bidders(remove_unqualified: bool): vector
acquires ProofOfFeeAuction, ConsensusReward { let eligible_validators = validator_universe::get_eligible_validators(); - let (bidders, _) = sort_vals_impl(eligible_validators, remove_unqualified); + let (bidders, _) = sort_vals_impl(&eligible_validators, remove_unqualified); bidders } @@ -165,12 +169,12 @@ module ol_framework::proof_of_fee { // same as get bidders, but returns the bid public fun get_bidders_and_bids(remove_unqualified: bool): (vector
, vector) acquires ProofOfFeeAuction, ConsensusReward { let eligible_validators = validator_universe::get_eligible_validators(); - sort_vals_impl(eligible_validators, remove_unqualified) + sort_vals_impl(&eligible_validators, remove_unqualified) } - // returns two lists odrdered bidder addresss and the bid - fun sort_vals_impl(eligible_validators: vector
, remove_unqualified: bool): (vector
, vector) acquires ProofOfFeeAuction, ConsensusReward { + // 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 { // let eligible_validators = validator_universe::get_eligible_validators(); - let length = vector::length
(&eligible_validators); + let length = vector::length
(eligible_validators); // vector to store each address's node_weight let bids = vector::empty(); @@ -179,7 +183,7 @@ 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 cur_address = *vector::borrow
(eligible_validators, k); let (bid, _expire) = current_bid(cur_address); let (_, qualified) = audit_qualification(cur_address); @@ -269,17 +273,21 @@ module ol_framework::proof_of_fee { /// but we will check again here. /// we return: // a. the list of winning validators (the validator set) - // b. the clearing price paid - // c. the list of proven nodes added, for audit and instrumentation - // d. the list of unproven, for audit and instrumentation + // b. the entry fee paid + // c. the clearing bid (percentage paid) + // d. the list of proven nodes added, for audit and instrumentation + // e. the list of unproven, for audit and instrumentation public fun fill_seats_and_get_price( vm: &signer, final_set_size: u64, sorted_vals_by_bid: &vector
, proven_nodes: &vector
- ): (vector
, u64, vector
, vector
) acquires ProofOfFeeAuction, ConsensusReward { + ): (vector
, u64, u64, vector
, vector
) acquires ProofOfFeeAuction, ConsensusReward { system_addresses::assert_ol(vm); + // NOTE: this is duplicate work, but we are double checking we are getting a proper sort. + let (sorted_vals_by_bid, _) = sort_vals_impl(sorted_vals_by_bid, true); + // Now we can seat the validators based on the algo: // A. seat the highest bidding 2/3 proven nodes of previous epoch // B. seat the remainder 1/3 of highest bidding validators which may or MA NOT have participated in the previous epoch. Note: We assume jailed validators are not in qualified bidder list anyways, but we should check again @@ -298,9 +306,9 @@ module ol_framework::proof_of_fee { let i = 0u64; while ( (vector::length(&proposed_validators) < final_set_size) && // until seats full - (i < vector::length(sorted_vals_by_bid)) + (i < vector::length(&sorted_vals_by_bid)) ) { - let val = vector::borrow(sorted_vals_by_bid, i); + let val = vector::borrow(&sorted_vals_by_bid, i); // check if a proven node // NOTE: if the top bidders all all "proven" nodes, then there will // be no reason to add an unproven. Unproven nodes will only @@ -326,7 +334,7 @@ module ol_framework::proof_of_fee { // We failed to seat anyone. // let epoch_boundary.move deal with this. - if (vector::is_empty(&proposed_validators)) return (proposed_validators, 0, audit_add_proven_vals, audit_add_unproven_vals); + if (vector::is_empty(&proposed_validators)) return (proposed_validators, 0, 0, audit_add_proven_vals, audit_add_unproven_vals); // Find the clearing price which all validators will pay let lowest_bidder = vector::borrow(&proposed_validators, vector::length(&proposed_validators) - 1); @@ -335,9 +343,24 @@ module ol_framework::proof_of_fee { // update the clearing price let cr = borrow_global_mut(@ol_framework); - cr.clearing_price = lowest_bid_pct; + cr.clearing_bid = lowest_bid_pct; + + if (lowest_bid_pct > 0) { + cr.entry_fee = fixed_point32::multiply_u64(cr.nominal_reward, bid_as_fixedpoint(lowest_bid_pct)); + + if (cr.nominal_reward > cr.entry_fee) { + cr.net_reward = cr.nominal_reward - cr.entry_fee; + } else { + // shoudn't be reachable, but here for completion + cr.net_reward = cr.nominal_reward + }; + } else { + cr.entry_fee = 0; + cr.net_reward = cr.nominal_reward; + }; + - return (proposed_validators, lowest_bid_pct, audit_add_proven_vals, audit_add_unproven_vals) + return (proposed_validators, cr.entry_fee, cr.clearing_bid, audit_add_proven_vals, audit_add_unproven_vals) } #[view] @@ -369,13 +392,17 @@ module ol_framework::proof_of_fee { // skip the user if they don't have sufficient UNLOCKED funds // or if the bid expired. let unlocked_coins = slow_wallet::unlocked_amount(val); - let (baseline_reward, _, _) = get_consensus_reward(); - let coin_required = fixed_point32::multiply_u64(baseline_reward, fixed_point32::create_from_rational(bid_pct, 1000)); + let (_, entry_fee, _, _) = get_consensus_reward(); + // let coin_required = fixed_point32::multiply_u64(baseline_reward, bid_as_fixedpoint(bid_pct)); - if (unlocked_coins < coin_required) vector::push_back(&mut errors, ELOW_UNLOCKED_COIN_BALANCE); // 17 + if (unlocked_coins < entry_fee) vector::push_back(&mut errors, ELOW_UNLOCKED_COIN_BALANCE); // 17 (errors, vector::length(&errors) == 0) // friend of ours } + + fun bid_as_fixedpoint(bid_pct: u64): fixed_point32::FixedPoint32 { + fixed_point32::create_from_rational(bid_pct, 1000) + } // Adjust the reward at the end of the epoch // as described in the paper, the epoch reward needs to be adjustable // given that the implicit bond needs to be sufficient, eg 5-10x the reward. @@ -417,7 +444,7 @@ module ol_framework::proof_of_fee { }; - if (cr.value > 0) { + if (cr.nominal_reward > 0) { // TODO: this is an initial implementation, we need to @@ -442,12 +469,12 @@ module ol_framework::proof_of_fee { // decrease the reward by 10% - cr.value = cr.value - (cr.value / 10); + cr.nominal_reward = cr.nominal_reward - (cr.nominal_reward / 10); return // return early since we can't increase and decrease simultaneously } else if (epochs_above > short_window) { // decrease the reward by 5% - cr.value = cr.value - (cr.value / 20); + cr.nominal_reward = cr.nominal_reward - (cr.nominal_reward / 20); return // return early since we can't increase and decrease simultaneously @@ -469,12 +496,12 @@ module ol_framework::proof_of_fee { // increase the reward by 10% - cr.value = cr.value + (cr.value / 10); + cr.nominal_reward = cr.nominal_reward + (cr.nominal_reward / 10); } else if (epochs_below > short_window) { // increase the reward by 5% - cr.value = cr.value + (cr.value / 20); + cr.nominal_reward = cr.nominal_reward + (cr.nominal_reward / 20); }; // }; }; @@ -523,10 +550,10 @@ module ol_framework::proof_of_fee { /// get the baseline reward from ConsensusReward - /// returns (reward, clearing_price, median_win_bid) - public fun get_consensus_reward(): (u64, u64, u64) acquires ConsensusReward { + /// returns (reward, entry_fee, clearing_bid, median_win_bid) + public fun get_consensus_reward(): (u64, u64, u64, u64) acquires ConsensusReward { let b = borrow_global(@ol_framework); - return (b.value, b.clearing_price, b.median_win_bid) + return (b.nominal_reward, b.entry_fee, b.clearing_bid, b.median_win_bid) } // CONSENSUS CRITICAL @@ -674,16 +701,16 @@ module ol_framework::proof_of_fee { #[test_only] public fun test_mock_reward( vm: &signer, - value: u64, - clearing_price: u64, + nominal_reward: u64, + clearing_bid: u64, median_win_bid: u64, median_history: vector, ) acquires ConsensusReward { testnet::assert_testnet(vm); let cr = borrow_global_mut(@ol_framework ); - cr.value = value; - cr.clearing_price = clearing_price; + cr.nominal_reward = nominal_reward; + cr.clearing_bid = clearing_bid; cr.median_win_bid = median_win_bid; cr.median_history = median_history; @@ -705,10 +732,10 @@ module ol_framework::proof_of_fee { vector::singleton(33), ); - let (value, clearing, median) = get_consensus_reward(); - assert!(value == 100, 1000); - assert!(clearing == 50, 1001); - assert!(median == 33, 1002); + let(reward, _, clear_percent, median_bid) = get_consensus_reward(); + assert!(reward == 100, 1000); + assert!(clear_percent == 50, 1001); + assert!(median_bid == 33, 1002); } @@ -743,19 +770,19 @@ module ol_framework::proof_of_fee { ); // no changes until we run the thermostat. - let (value, clearing, median) = get_consensus_reward(); - assert!(value == 100, 1000); - assert!(clearing == 50, 1001); - assert!(median == 33, 1002); + let(reward, _, clear_percent, median_bid) = get_consensus_reward(); + assert!(reward == 100, 1000); + assert!(clear_percent == 50, 1001); + assert!(median_bid == 33, 1002); reward_thermostat(&vm); // This is the happy case. No changes since the rewards were within range // the whole time. - let (value, clearing, median) = get_consensus_reward(); - assert!(value == 100, 1000); - assert!(clearing == 50, 1001); - assert!(median == 33, 1002); + let(reward, _, clear_percent, median_bid) = get_consensus_reward(); + assert!(reward == 100, 1000); + assert!(clear_percent == 50, 1001); + assert!(median_bid == 33, 1002); } @@ -787,19 +814,19 @@ module ol_framework::proof_of_fee { ); // no changes until we run the thermostat. - let (value, clearing, median) = get_consensus_reward(); - assert!(value == 100, 1000); - assert!(clearing == 50, 1001); - assert!(median == 33, 1002); + let(reward, _, clear_percent, median_bid) = get_consensus_reward(); + assert!(reward == 100, 1000); + assert!(clear_percent == 50, 1001); + assert!(median_bid == 33, 1002); reward_thermostat(&vm); // In the decrease case during a short period, we decrease by 5% // No other parameters of consensus reward should change on calling this function. - let (value, clearing, median) = get_consensus_reward(); - assert!(value == 105, 1003); - assert!(clearing == 50, 1004); - assert!(median == 33, 1005); + let(reward, _, clear_percent, median_bid) = get_consensus_reward(); + assert!(reward == 105, 1003); + assert!(clear_percent == 50, 1004); + assert!(median_bid == 33, 1005); } @@ -835,19 +862,19 @@ module ol_framework::proof_of_fee { ); // no changes until we run the thermostat. - let (value, clearing, median) = get_consensus_reward(); - assert!(value == 100, 1000); - assert!(clearing == 50, 1001); - assert!(median == 33, 1002); + let(reward, _, clear_percent, median_bid) = get_consensus_reward(); + assert!(reward == 100, 1000); + assert!(clear_percent == 50, 1001); + assert!(median_bid == 33, 1002); reward_thermostat(&vm); // In the decrease case during a short period, we decrease by 5% // No other parameters of consensus reward should change on calling this function. - let (value, clearing, median) = get_consensus_reward(); - assert!(value == 110, 1003); - assert!(clearing == 50, 1004); - assert!(median == 33, 1005); + let(reward, _, clear_percent, median_bid) = get_consensus_reward(); + assert!(reward == 110, 1003); + assert!(clear_percent == 50, 1004); + assert!(median_bid == 33, 1005); } @@ -882,19 +909,19 @@ module ol_framework::proof_of_fee { ); // no changes until we run the thermostat. - let (value, clearing, median) = get_consensus_reward(); - assert!(value == 100, 1000); - assert!(clearing == 50, 1001); - assert!(median == 33, 1002); + let(reward, _, clear_percent, median_bid) = get_consensus_reward(); + assert!(reward == 100, 1000); + assert!(clear_percent == 50, 1001); + assert!(median_bid == 33, 1002); reward_thermostat(&vm); // In the decrease case during a short period, we decrease by 5% // No other parameters of consensus reward should change on calling this function. - let (value, clearing, median) = get_consensus_reward(); - assert!(value == 95, 1000); - assert!(clearing == 50, 1004); - assert!(median == 33, 1005); + let(reward, _, clear_percent, median_bid) = get_consensus_reward(); + assert!(reward == 95, 1000); + assert!(clear_percent == 50, 1004); + assert!(median_bid == 33, 1005); } @@ -929,19 +956,19 @@ module ol_framework::proof_of_fee { ); // no changes until we run the thermostat. - let (value, clearing, median) = get_consensus_reward(); - assert!(value == 100, 1000); - assert!(clearing == 50, 1001); - assert!(median == 33, 1002); + let(reward, _, clear_percent, median_bid) = get_consensus_reward(); + assert!(reward == 100, 1000); + assert!(clear_percent == 50, 1001); + assert!(median_bid == 33, 1002); reward_thermostat(&vm); // In the decrease case during a short period, we decrease by 5% // No other parameters of consensus reward should change on calling this function. - let (value, clearing, median) = get_consensus_reward(); - assert!(value == 90, 1003); - assert!(clearing == 50, 1004); - assert!(median == 33, 1005); + let(reward, _, clear_percent, median_bid) = get_consensus_reward(); + assert!(reward == 90, 1003); + assert!(clear_percent == 50, 1004); + assert!(median_bid == 33, 1005); } 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 208184671..ed7b1369d 100644 --- a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move @@ -4,10 +4,7 @@ module ol_framework::test_boundary { use std::vector; use diem_std::bls12381; use ol_framework::mock; - - // use ol_framework::account; use ol_framework::proof_of_fee; - // use ol_framework::reconfiguration; use ol_framework::jail; use ol_framework::slow_wallet; use ol_framework::vouch; @@ -16,6 +13,7 @@ module ol_framework::test_boundary { use diem_framework::stake; use ol_framework::epoch_boundary; use ol_framework::ol_account; + use diem_framework::reconfiguration; // use diem_std::debug::print; @@ -34,6 +32,10 @@ module ol_framework::test_boundary { mock::pof_default(); slow_wallet::slow_wallet_epoch_drip(root, 500000); + // 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(); + reconfiguration::test_helper_increment_epoch_dont_reconfigure(); + set } @@ -42,8 +44,10 @@ module ol_framework::test_boundary { fun e2e_boundary_happy(root: signer) { let _vals = common_test_setup(&root); + mock::trigger_epoch(&root); + let _vals_post = stake::get_current_validators(); assert!(epoch_boundary::get_reconfig_success(), 7357001); @@ -143,19 +147,8 @@ module ol_framework::test_boundary { let qualified_bidders = epoch_boundary::get_qualified_bidders(); assert!(vector::length(&qualified_bidders) == (vector::length(&vals) - 1), 7357003); - // all vals had winning bids, but it was less than the seats on offer + // 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) , 7357003); - - - // 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 - // assert!(epoch_boundary::get_seats_offered() == 11, 7357002); - - - // // all of the auction winners became the validators ulitmately - // assert!(vector::length(&epoch_boundary::get_actual_vals()) == 10, 7357004); } } \ No newline at end of file diff --git a/framework/libra-framework/sources/ol_sources/tests/musical_chairs.test.move b/framework/libra-framework/sources/ol_sources/tests/musical_chairs.test.move index 924ead3b1..40a1a713a 100644 --- a/framework/libra-framework/sources/ol_sources/tests/musical_chairs.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/musical_chairs.test.move @@ -20,13 +20,13 @@ module ol_framework::test_musical_chairs { // all vals compliant mock::mock_all_vals_good_performance(&root); - let (good, bad, ratio) = musical_chairs::test_eval_compliance(&root, vals); + let (good, bad, ratio) = musical_chairs::test_eval_compliance(&root, vals, 10); assert!(vector::length(&good) == 5, 7357002); assert!(vector::length(&bad) == 0, 7357003); assert!(fixed_point32::is_zero(ratio), 7357004); - let (outgoing_compliant_set, new_set_size) = musical_chairs::test_stop(&root); + let (outgoing_compliant_set, new_set_size) = musical_chairs::test_stop(&root, 10); // epoch_round = 10 assert!(vector::length(&outgoing_compliant_set) == 5, 7357005); @@ -43,14 +43,14 @@ module ol_framework::test_musical_chairs { // all vals compliant mock::mock_case_1(&root, *vector::borrow(&vals, 0)); - let (good, bad, bad_ratio) = musical_chairs::test_eval_compliance(&root, vals); + let (good, bad, bad_ratio) = musical_chairs::test_eval_compliance(&root, vals, 10); //epoch_round = 10 assert!(vector::length(&good) == 1, 7357002); assert!(vector::length(&bad) == 4, 7357003); assert!(!fixed_point32::is_zero(bad_ratio), 7357004); assert!(fixed_point32::create_from_rational(4, 5) == bad_ratio, 7357005); - let (_outgoing_compliant_set, _new_set_size) = musical_chairs::test_stop(&root); + let (_outgoing_compliant_set, _new_set_size) = musical_chairs::test_stop(&root, 10); // epoch_round = 10 } } \ No newline at end of file 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 bda624708..681909cb3 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 @@ -34,7 +34,7 @@ module ol_framework::test_pof { assert!(expires == 10000, 1002); let coin = slow_wallet::unlocked_amount(*alice); - let (r, _, _) = proof_of_fee::get_consensus_reward(); + let (r, _, _, _) = proof_of_fee::get_consensus_reward(); let bid_cost = (bid * r) / 1000; assert!(coin > bid_cost, 1005); } @@ -132,25 +132,29 @@ module ol_framework::test_pof { let set = mock::genesis_n_vals(&root, 4); let alice = *vector::borrow(&set, 0); - assert!(!jail::is_jailed(alice), 1001); + assert!(!jail::is_jailed(alice), 7357001); let a_sig = account::create_signer_for_test(alice); proof_of_fee::set_bid(&a_sig, 100, 10000); // 10 pct let (bid, expires) = proof_of_fee::current_bid(alice); - assert!(bid == 100, 1001); - assert!(expires == 10000, 1002); + assert!(bid == 100, 7357001); + assert!(expires == 10000, 7357002); // NOT ENOUGH FUNDS WERE UNLOCKED slow_wallet::slow_wallet_epoch_drip(&root, 500); let coin = slow_wallet::unlocked_amount(alice); - let (r, _, _) = proof_of_fee::get_consensus_reward(); + + // calculate consensus reward + proof_of_fee::fill_seats_and_get_price(&root, 4, &set, &set); + + let (r, _, _, _) = proof_of_fee::get_consensus_reward(); let bid_cost = (bid * r) / 1000; - assert!(coin < bid_cost, 1005); + assert!(coin < bid_cost, 7357005); // should NOT pass audit. let (err, pass) = proof_of_fee::audit_qualification(alice); - assert!(*vector::borrow(&err, 0) == 17, 1006); - assert!(!pass, 1007); + assert!(*vector::borrow(&err, 0) == 17, 7357006); + assert!(!pass, 7357007); } #[test(root = @ol_framework)] @@ -242,8 +246,11 @@ module ol_framework::test_pof { #[test(root= @ol_framework)] fun sorted_vals_none_qualify(root: signer) { - let _set = mock::genesis_n_vals(&root, 4); + let vals = mock::genesis_n_vals(&root, 4); let (val_universe, _their_bids, _their_expiry) = mock::pof_default(); + // calculate the auction + proof_of_fee::fill_seats_and_get_price(&root, 4, &vals, &vals); + let sorted = proof_of_fee::get_bidders(false); let len = vector::length(&sorted); @@ -296,7 +303,6 @@ module ol_framework::test_pof { let set = mock::genesis_n_vals(&root, 4); mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); - // mock::ol_initialize_coin(&root); let (val_universe, _their_bids, _their_expiry) = mock::pof_default(); let len = vector::length(&set); @@ -346,14 +352,14 @@ module ol_framework::test_pof { let sorted = proof_of_fee::get_bidders(true); assert!(vector::length(&sorted) == vector::length(&set), 1003); - let (seats, _p, _, _) = proof_of_fee::fill_seats_and_get_price(&root, len, &sorted, &sorted); + let (seats, _, _, _, _) = proof_of_fee::fill_seats_and_get_price(&root, len, &sorted, &sorted); assert!(vector::contains(&seats, vector::borrow(&set, 0)), 1004); // filling the seat updated the computation of the consensu reward. - let (reward, clear_price, median_bid) = proof_of_fee::get_consensus_reward(); + let (reward, _, clear_percent, median_bid) = proof_of_fee::get_consensus_reward(); assert!(reward == 1000000, 1005); - assert!(clear_price == 1, 1006); + assert!(clear_percent == 1, 1006); assert!(median_bid == 3, 1007); } @@ -372,22 +378,22 @@ module ol_framework::test_pof { assert!(vector::length(&sorted) == vector::length(&set), 1003); let len = vector::length(&set); - let (seats, _p, _, _) = proof_of_fee::fill_seats_and_get_price(&root, len, &sorted, &sorted); + let (seats, _, _, _, _) = proof_of_fee::fill_seats_and_get_price(&root, len, &sorted, &sorted); assert!(vector::contains(&seats, vector::borrow(&set, 0)), 1004); // filling the seat updated the computation of the consensu reward. - let (reward, clear_price, median_bid) = proof_of_fee::get_consensus_reward(); + let (reward, _, clear_percent, median_bid) = proof_of_fee::get_consensus_reward(); assert!(reward == 1000000, 1005); - assert!(clear_price == 1, 1006); + assert!(clear_percent == 1, 1006); assert!(median_bid == 3, 1007); // we expect no change in the reward_thermostat because there haven't been 5 epochs or more of historical data. proof_of_fee::reward_thermostat(&root); - let (reward, win_bid, median_bid) = proof_of_fee::get_consensus_reward(); + let (reward, _, clearing_percent, median_bid) = proof_of_fee::get_consensus_reward(); assert!(reward == 1000000, 1008); - assert!(win_bid == 1, 1009); + assert!(clearing_percent == 1, 1009); assert!(median_bid == 3, 1010); } @@ -420,7 +426,7 @@ module ol_framework::test_pof { assert!(vector::length(&set) == 5, 1004); assert!(vector::length(&sorted) == 4, 1005); - let (seats, _p, _, _) = proof_of_fee::fill_seats_and_get_price(&root, vector::length(&set), &sorted, &sorted); + let (seats, _, _, _, _) = proof_of_fee::fill_seats_and_get_price(&root, vector::length(&set), &sorted, &sorted); // EVE is not in the seats assert!(!vector::contains(&seats, vector::borrow(&set, 4)), 1004); @@ -428,9 +434,9 @@ module ol_framework::test_pof { assert!(vector::contains(&seats, vector::borrow(&set, 0)), 1005); // filling the seat updated the computation of the consensu reward. - let (reward, clear_price, median_bid) = proof_of_fee::get_consensus_reward(); + let (reward, _, clear_percent, median_bid) = proof_of_fee::get_consensus_reward(); assert!(reward == 1000000, 1006); - assert!(clear_price == 1, 1007); + assert!(clear_percent == 1, 1007); assert!(median_bid == 2, 1008); } @@ -457,7 +463,7 @@ module ol_framework::test_pof { let set_size = 3; - let (seats, _p, _, _) = proof_of_fee::fill_seats_and_get_price(&root, set_size, &sorted, &sorted); + let (seats, _, _, _, _) = proof_of_fee::fill_seats_and_get_price(&root, set_size, &sorted, &sorted); assert!(vector::length(&set) == 5, 1004); assert!(vector::length(&seats) == 3, 1005); @@ -473,9 +479,9 @@ module ol_framework::test_pof { // filling the seat updated the computation of the consensu reward. // Median bids and clearing prices will be different than the happy path test. - let (reward, clear_price, median_bid) = proof_of_fee::get_consensus_reward(); + let (reward, _, clear_percent, median_bid) = proof_of_fee::get_consensus_reward(); assert!(reward == 1000000, 1004); - assert!(clear_price == 3, 1005); + assert!(clear_percent == 3, 1005); assert!(median_bid == 5, 1006); } @@ -520,7 +526,7 @@ module ol_framework::test_pof { vector::push_back(&mut proven_vals, *vector::borrow(&set, 4)); vector::push_back(&mut proven_vals, *vector::borrow(&set, 5)); - let (seats, _p, _, _) = proof_of_fee::fill_seats_and_get_price(&root, set_size, &sorted, &proven_vals); + let (seats, _, _, _, _) = proof_of_fee::fill_seats_and_get_price(&root, set_size, &sorted, &proven_vals); assert!(vector::length(&set) == 6, 1004); assert!(vector::length(&seats) == 6, 1005); @@ -533,10 +539,10 @@ module ol_framework::test_pof { // filling the seat updated the computation of the consensu reward. // Median bids and clearing prices will be different than the happy path test. - let (reward, clear_price, median_bid) = proof_of_fee::get_consensus_reward(); + let (reward, _, clear_percent, median_bid) = proof_of_fee::get_consensus_reward(); assert!(reward == 1000000, 1004); // The clearing price is 1, Alice's lowest bid. Even though she was not "proven" - assert!(clear_price == 1, 1005); + assert!(clear_percent == 1, 1005); assert!(median_bid == 3, 1006); } @@ -562,8 +568,6 @@ module ol_framework::test_pof { #[test(root = @ol_framework)] fun fill_seats_unproven_sad(root: signer) { - - // 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); @@ -584,7 +588,7 @@ module ol_framework::test_pof { let (frank_bid, _) = proof_of_fee::current_bid(Frank); assert!(alice_bid < frank_bid, 1002); - let (seats, _p, _, _) = proof_of_fee::fill_seats_and_get_price(&root, set_size, &sorted, &proven_vals); + let (seats, _, _, _, _) = proof_of_fee::fill_seats_and_get_price(&root, set_size, &sorted, &proven_vals); assert!(vector::length(&set) == 6, 1003); assert!(vector::length(&seats) == 4, 1004); @@ -602,9 +606,9 @@ module ol_framework::test_pof { // filling the seat updated the computation of the consensu reward. // Median bids and clearing prices will be different than the happy path test. - let (reward, clear_price, median_bid) = proof_of_fee::get_consensus_reward(); + let (reward, _, clear_percent, median_bid) = proof_of_fee::get_consensus_reward(); assert!(reward == 1000000, 10012); - assert!(clear_price == 2, 10013); + assert!(clear_percent == 2, 10013); assert!(median_bid == 3, 10014); } 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 ca5fd5655..8a3aa8f65 100644 --- a/framework/libra-framework/sources/ol_sources/tests/stake.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/stake.test.move @@ -116,9 +116,6 @@ module ol_framework::test_stake { let (compliant, _, _, _) = grade::get_validator_grade(eve); assert!(!compliant, 735701); - // let v = grade::get_jailed_set(); - // assert!(vector::contains(&v, &eve), 735702); - } diff --git a/framework/libra-framework/sources/ol_sources/tests/tower.test.move b/framework/libra-framework/sources/ol_sources/tests/tower.test.move index 5c1d0289c..23c517f70 100644 --- a/framework/libra-framework/sources/ol_sources/tests/tower.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/tower.test.move @@ -1,11 +1,22 @@ #[test_only] /// tests for external apis, and where a dependency cycle with genesis is created. + +// NOTE: Cousin Alice: we have the Move Alice account 1000a, but in the VDF fixtures we +// have RUST alice 0x8751. The VDF fixture uses this one. module ol_framework::test_tower { use ol_framework::mock; use ol_framework::tower_state; use ol_framework::vdf_fixtures; + use std::signer; + use ol_framework::ol_account; + use ol_framework::proof_of_fee; + use ol_framework::stake; + use diem_framework::timestamp; + // use diem_framework::coin; + // use ol_framework::gas_coin::GasCoin; + use std::vector; - // use std::debug::print; + use std::debug::print; #[test(root = @ol_framework)] fun epoch_changes_difficulty(root: signer) { @@ -27,13 +38,15 @@ module ol_framework::test_tower { assert!(diff!=100, 735703); } - #[test(root = @ol_framework, alice = @0x87515d94a244235a1433d7117bc0cb154c613c2f4b1e67ca8d98a542ee3f59f5)] - fun init_tower_state(root: signer, alice: signer){ + #[test(root = @ol_framework, cousin_alice = @0x87515d94a244235a1433d7117bc0cb154c613c2f4b1e67ca8d98a542ee3f59f5)] + fun init_tower_state(root: signer, cousin_alice: signer){ mock::ol_test_genesis(&root); mock::ol_initialize_coin(&root); + timestamp::fast_forward_seconds(1); + ol_account::create_account(&root, signer::address_of(&cousin_alice)); tower_state::init_miner_state( - &alice, + &cousin_alice, &vdf_fixtures::alice_0_easy_chal(), &vdf_fixtures::alice_0_easy_sol(), vdf_fixtures::easy_difficulty(), @@ -71,4 +84,42 @@ module ol_framework::test_tower { } + + #[test(root = @ol_framework, cousin_alice = @0x87515d94a244235a1433d7117bc0cb154c613c2f4b1e67ca8d98a542ee3f59f5)] + fun miner_receives_reward(root: signer, cousin_alice: signer) { + let a_addr = signer::address_of(&cousin_alice); + let vals = mock::genesis_n_vals(&root, 5); + mock::ol_initialize_coin(&root); + mock::pof_default(); + ol_account::create_account(&root, a_addr); + proof_of_fee::fill_seats_and_get_price(&root, 5, &vals, &vals); + + assert!(vector::length(&vals) == 5, 7357001); + let vals = stake::get_current_validators(); + assert!(vector::length(&vals) == 5, 7357002); + + tower_state::minerstate_commit( + &cousin_alice, + vdf_fixtures::alice_0_easy_chal(), + vdf_fixtures::alice_0_easy_sol(), + vdf_fixtures::easy_difficulty(), + vdf_fixtures::security(), + ); + + let count_pre = tower_state::get_count_in_epoch(a_addr); + print(&count_pre); + + // all vals compliant + mock::mock_all_vals_good_performance(&root); + + let (_, alice_bal_pre) = ol_account::balance(@0x1000a); + assert!(alice_bal_pre == 0, 73570003); + + mock::trigger_epoch(&root); + + let (_, alice_bal) = ol_account::balance(@0x1000a); + + assert!(alice_bal_pre < alice_bal, 73570003); + } + } \ No newline at end of file diff --git a/framework/libra-framework/sources/ol_sources/tests/reconfiguration.test.move b/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move similarity index 91% rename from framework/libra-framework/sources/ol_sources/tests/reconfiguration.test.move rename to framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move index d0c0d75fe..5c629babb 100644 --- a/framework/libra-framework/sources/ol_sources/tests/reconfiguration.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move @@ -16,7 +16,7 @@ module ol_framework::test_reconfiguration { // Scenario: all genesis validators make it to next epoch #[test(root = @ol_framework)] - fun reconfig_happy_case(root: signer) { + fun reconfig_reward_happy_case(root: signer) { let vals = mock::genesis_n_vals(&root, 5); mock::ol_initialize_coin(&root); mock::pof_default(); @@ -28,7 +28,8 @@ module ol_framework::test_reconfiguration { assert!(coin::balance(@0x1000a) == 0, 7357003); - let (reward_one, _, _ ) = proof_of_fee::get_consensus_reward(); + + let (reward_one, _entry_fee, _, _ ) = proof_of_fee::get_consensus_reward(); // The epoch's reward BEFORE reconfiguration assert!(reward_one == 1000000, 7357004); // run ol reconfiguration @@ -38,7 +39,7 @@ module ol_framework::test_reconfiguration { assert!(vector::length(&vals) == 5, 7357005); let alice_bal = coin::balance(@0x1000a); - let (_, entry_fee, _ ) = proof_of_fee::get_consensus_reward(); + 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) @@ -67,7 +68,7 @@ module ol_framework::test_reconfiguration { // make alice non performant mock::mock_case_4(&root, *vector::borrow(&vals, 0)); - let (reward, _, _ ) = proof_of_fee::get_consensus_reward(); + let (reward, _, _, _ ) = proof_of_fee::get_consensus_reward(); // run ol reconfiguration mock::trigger_epoch(&root); @@ -79,7 +80,7 @@ module ol_framework::test_reconfiguration { assert!(vector::length(&vals) == 4, 7357003); assert!(!vector::contains(&vals, &@0x1000a), 7357004); - let (_, entry_fee, _ ) = proof_of_fee::get_consensus_reward(); + let (_, entry_fee, _, _ ) = proof_of_fee::get_consensus_reward(); // alice doesn't get paid diff --git a/framework/libra-framework/sources/ol_sources/tests/vdf.test.move b/framework/libra-framework/sources/ol_sources/tests/vdf.test.move index 18f2b3409..7699056bf 100644 --- a/framework/libra-framework/sources/ol_sources/tests/vdf.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/vdf.test.move @@ -34,18 +34,18 @@ module ol_framework::test_vdf{ #[test] fun verify_valid_weso_proof() { // this tests the happy case, that a proof is submitted with all three correct parameters. - let challenge = vdf_fixtures::weso_alice_0_easy_chal(); + let challenge = vdf_fixtures::alice_0_easy_chal(); // Generate solutions with: // cd ./verfiable_delay/vdf-cli && cargo run --release -- -l=512 aa 100 -tpietrzak // NOTE: the -l=512 is important because this is the security paramater of 0L miner. - let proof = vdf_fixtures::weso_alice_0_easy_sol(); - let weso = true; + let proof = vdf_fixtures::alice_0_easy_sol(); + let weso = false; assert!( ol_native_vdf::verify( &challenge, &proof, vdf_fixtures::easy_difficulty(), - vdf_fixtures::security_weso(), + vdf_fixtures::security(), weso ), 7357001); } diff --git a/framework/libra-framework/sources/ol_sources/tower_state.move b/framework/libra-framework/sources/ol_sources/tower_state.move index 8180e70fa..1b773b0dc 100644 --- a/framework/libra-framework/sources/ol_sources/tower_state.move +++ b/framework/libra-framework/sources/ol_sources/tower_state.move @@ -9,6 +9,7 @@ module ol_framework::tower_state { use ol_framework::epoch_helper; use diem_framework::testnet; use diem_framework::ol_native_vdf; + use ol_framework::oracle; // use diem_std::debug::print; @@ -229,6 +230,7 @@ module ol_framework::tower_state { ) acquires TowerProofHistory, TowerList, TowerCounter { // Get address, assumes the sender is the signer. let miner_addr = signer::address_of(miner_sign); + oracle::init_provider(miner_sign); // This may be the 0th proof of an end user that hasn't had tower state initialized if (!is_init(miner_addr)) { @@ -252,7 +254,7 @@ module ol_framework::tower_state { /// The entry point to commit miner state. public entry fun minerstate_commit( - sender: signer, + sender: &signer, challenge: vector, solution: vector, difficulty: u64, @@ -265,7 +267,7 @@ module ol_framework::tower_state { security, ); - commit_state(&sender, proof); + commit_state(sender, proof); } fun check_difficulty(miner_addr: address, proof: &Proof) acquires TowerProofHistory, VDFDifficulty { @@ -340,6 +342,7 @@ module ol_framework::tower_state { }; miner_history.latest_epoch_mining = epoch_helper::get_current_epoch(); + oracle::count_vdf_proof(miner_addr, miner_history.previous_proof_hash); increment_stats(miner_addr); } @@ -451,7 +454,7 @@ module ol_framework::tower_state { difficulty: u64, security: u64 ) acquires TowerProofHistory, TowerList, TowerCounter { - + oracle::init_provider(miner_sig); // NOTE Only signer can update own state. // Should only happen once. assert!(!exists(signer::address_of(miner_sig)), error::permission_denied(EALREADY_INITIALIZED)); @@ -750,6 +753,7 @@ module ol_framework::tower_state { security: u64, ) acquires TowerProofHistory, TowerList, TowerCounter { assert!(testnet::is_testnet(), 130102014010); + oracle::init_provider(miner_sig); move_to(miner_sig, TowerProofHistory { previous_proof_hash: vector::empty(), diff --git a/framework/releases/head.mrb b/framework/releases/head.mrb index 023be2ea20cd4bc6429828d0698447f411dba6fa..82ab003aec860f6b58745f4610cf2c9c306f6d27 100644 GIT binary patch delta 64221 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 F{eKEk{Ph3; diff --git a/framework/src/framework_cli.rs b/framework/src/framework_cli.rs index 96a566d99..d1cb0c463 100644 --- a/framework/src/framework_cli.rs +++ b/framework/src/framework_cli.rs @@ -120,7 +120,6 @@ fn make_template_files( framework_local_dir: &Path, script_name: &str, ) -> anyhow::Result<()> { - dbg!(&package_dir); std::fs::create_dir_all(package_dir) .context("could not create the output directory {new_path:?}")?; // TODO: rename this. init_move_package_with_local_framework @@ -143,13 +142,11 @@ script { } "#; - dbg!(&package_dir); - dbg!(&script_name); let filename = package_dir // .join(script_name) .join("sources") .join(format!("{}.move", script_name)); - dbg!(&filename); + std::fs::write(filename, t)?; println!("success: governance template created"); diff --git a/smoke-tests/Cargo.toml b/smoke-tests/Cargo.toml index 882c4bd43..626c32cbd 100644 --- a/smoke-tests/Cargo.toml +++ b/smoke-tests/Cargo.toml @@ -9,7 +9,7 @@ edition = { workspace = true } [dependencies] anyhow = { workspace = true } diem-crypto = { workspace = true } -diem-forge = { workspace = true } +diem-forge = { workspace = true} diem-sdk = { workspace = true } diem-temppath = { workspace = true } diem-types = { workspace = true } @@ -20,3 +20,7 @@ serde_json = { workspace = true } smoke-test = { workspace = true } tokio = { workspace = true } url = { workspace = true } + +[features] +#default = ["testing"] +testing = [] diff --git a/smoke-tests/src/configure_validator.rs b/smoke-tests/src/configure_validator.rs index 6861c1646..89cc49c78 100644 --- a/smoke-tests/src/configure_validator.rs +++ b/smoke-tests/src/configure_validator.rs @@ -36,7 +36,6 @@ pub async fn init_val_config_files( .get_profile_mut(None) .expect("could not get profile"); profile.set_private_key(&pri_key); - // dbg!(&profile); let local_account = LocalAccount::new(auth.derived_address(), pri_key, 0); diff --git a/smoke-tests/src/tests/balance.rs b/smoke-tests/src/tests/balance.rs index a7476568c..3123afe47 100644 --- a/smoke-tests/src/tests/balance.rs +++ b/smoke-tests/src/tests/balance.rs @@ -16,7 +16,6 @@ async fn sanity_balances() -> anyhow::Result<()> { let mut public_info: diem_forge::DiemPublicInfo = swarm.diem_public_info(); let bal_vec = get_libra_balance(public_info.client(), address).await?; - dbg!(&bal_vec); assert!(bal_vec.unlocked == 0, "expected zero balance at genesis"); assert!(bal_vec.total == 0, "expected zero balance at genesis"); diff --git a/smoke-tests/src/tests/meta.rs b/smoke-tests/src/tests/meta.rs index 311d9378e..a741242c2 100644 --- a/smoke-tests/src/tests/meta.rs +++ b/smoke-tests/src/tests/meta.rs @@ -5,11 +5,11 @@ use libra_cached_packages::libra_stdlib; use libra_framework::release::ReleaseTarget; use smoke_test::smoke_test_environment::new_local_swarm_with_release; -/// Testing that we can get a swarm up with the current head.mrb +/// testing that we can get a swarm up of 1 node with the current head.mrb #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn meta_can_start_swarm() { let release = ReleaseTarget::Head.load_bundle().unwrap(); - let mut swarm = new_local_swarm_with_release(4, release).await; + let mut swarm = new_local_swarm_with_release(1, release).await; let mut public_info = swarm.diem_public_info(); let payload = public_info @@ -29,8 +29,38 @@ async fn meta_can_start_swarm() { /// testing the LibraSmoke abstraction can load #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn meta_create_libra_smoke() { - let _s = LibraSmoke::new(Some(3)) +async fn meta_create_libra_smoke_single() { + let _s = LibraSmoke::new(Some(1)) .await .expect("cannot start libra swarm"); } + +#[tokio::test(flavor = "multi_thread", worker_threads = 5)] +async fn meta_create_libra_smoke_multi() -> anyhow::Result<()> { + let mut s = LibraSmoke::new(Some(5)) + .await + .expect("cannot start libra swarm"); + + let c = s.client(); + // let c = c.get_resource::<_>(address, resource_type).awa + let res = c + .get_account_resource( + "0x1".parse().unwrap(), + "0x1::epoch_boundary::BoundaryStatus", + ) + .await?; + + // check that the genesis epochs 0, 1, transitioned safely, and epoch 2 has + // all the validators + let t = res.inner().as_ref().unwrap(); + let o = t.data.as_object().expect("no epoch_boundary response"); + let num = o + .get("incoming_filled_seats") + .expect("no incoming filled seats value") + .as_str() + .unwrap() + .parse::()?; + assert!(num == 5); + + Ok(()) +} diff --git a/smoke-tests/src/tests/upgrade.rs b/smoke-tests/src/tests/upgrade.rs index ab01af42a..9275a34d9 100644 --- a/smoke-tests/src/tests/upgrade.rs +++ b/smoke-tests/src/tests/upgrade.rs @@ -69,7 +69,7 @@ async fn can_upgrade() { // note: this proposal hash was generated by the // framework::main::upgrade path and added here to test fixtures. let proposal_hash = std::fs::read_to_string(proposal_hash_path).unwrap(); - dbg!(&proposal_hash); + // dbg!(&proposal_hash); let payload = diem_governance_ol_create_proposal_v2( proposal_hash.as_bytes().to_vec(), @@ -82,7 +82,7 @@ async fn can_upgrade() { let txn = alice_account .sign_with_transaction_builder(public_info.transaction_factory().payload(payload)); - dbg!("proposal txn"); + // dbg!("proposal txn"); public_info.client().submit_and_wait(&txn).await.unwrap(); let proposal_id = 0; @@ -97,7 +97,7 @@ async fn can_upgrade() { // Alice submits. Note: the sequence number incremented. let txn = alice_account.sign_with_transaction_builder(built_tx); - dbg!("alice votes"); + // dbg!("alice votes"); // needs gas public_info diff --git a/smoke-tests/src/upgrade_fixtures.rs b/smoke-tests/src/upgrade_fixtures.rs index 4e6e14a95..604f40760 100644 --- a/smoke-tests/src/upgrade_fixtures.rs +++ b/smoke-tests/src/upgrade_fixtures.rs @@ -21,7 +21,7 @@ pub fn insert_test_file(core_module_name: &str, remove: bool) -> anyhow::Result< .join("framework") .join(core_module_name) .join("sources"); - dbg!(&core_module_sources); + let away_file_path = core_module_sources.join("all_your_base.move"); if remove { std::fs::remove_file(away_file_path)?; @@ -33,7 +33,6 @@ pub fn insert_test_file(core_module_name: &str, remove: bool) -> anyhow::Result< .join("tests") .join("fixtures") .join("all_your_base.move"); - dbg!(&file_path); std::fs::copy(file_path, away_file_path)?; @@ -46,13 +45,9 @@ pub fn generate_fixtures(output_path: PathBuf, modules: Vec) -> anyhow:: insert_test_file(&destination_module, false)?; let this_crate = PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap(); - dbg!(&this_crate); let libra_framework_sources = this_crate.parent().unwrap().join("framework"); make_framework_upgrade_artifacts(&output_path, &libra_framework_sources, &Some(modules))?; - - dbg!("ok"); - // ok, cleanup insert_test_file(&destination_module, true)?; @@ -67,7 +62,6 @@ fn make_the_upgrade_fixtures() -> anyhow::Result<()> { let p = fixture_path.join("upgrade_single_step"); std::fs::create_dir_all(&p)?; - dbg!(&p); let modules = vec!["move-stdlib".to_string()]; generate_fixtures(p, modules)?; diff --git a/tools/config/src/config_cli.rs b/tools/config/src/config_cli.rs index 3aad41b7d..938e3eceb 100644 --- a/tools/config/src/config_cli.rs +++ b/tools/config/src/config_cli.rs @@ -138,7 +138,6 @@ impl ConfigCli { np.nodes = vec![]; np.add_url(u.to_owned()); } - dbg!(&cfg); cfg.save_file()?; Ok(()) } diff --git a/tools/config/src/host.rs b/tools/config/src/host.rs index 5dc4dada8..224d8d2c8 100644 --- a/tools/config/src/host.rs +++ b/tools/config/src/host.rs @@ -18,7 +18,6 @@ pub fn initialize_validator( keep_legacy_address: bool, chain_name: Option, ) -> anyhow::Result<()> { - dbg!("init validator"); let (.., keys) = libra_wallet::keys::refresh_validator_files(mnem, home_path.clone(), keep_legacy_address)?; OLProgress::complete("Initialized validator key files"); diff --git a/tools/genesis/src/genesis_functions.rs b/tools/genesis/src/genesis_functions.rs index 900b40da5..e37151c19 100644 --- a/tools/genesis/src/genesis_functions.rs +++ b/tools/genesis/src/genesis_functions.rs @@ -108,8 +108,6 @@ pub fn genesis_migrate_one_user( .to_string(); let new_addr_type = AccountAddress::from_hex_literal(&format!("0x{}", acc_str))?; - // dbg!(&new_addr_type.to_hex_literal()); - // NOTE: Authkeys have the same format as in pre V7 let auth_key = user_recovery.auth_key.context("no auth key found")?; diff --git a/tools/genesis/src/genesis_reader.rs b/tools/genesis/src/genesis_reader.rs index 40f8dfa52..ff33fd183 100644 --- a/tools/genesis/src/genesis_reader.rs +++ b/tools/genesis/src/genesis_reader.rs @@ -130,7 +130,6 @@ pub fn total_supply(db_reader: &Arc) -> Option { .get_state_value_by_version(&state_key, version) .expect("aggregator value must exist in data store") .expect("supply value exists"); - // dbg!(&value); // todo!() bcs::from_bytes(value.bytes()).unwrap() } @@ -160,10 +159,8 @@ fn test_db_rw() { let ap = make_access_path(AccountAddress::ZERO, "slow_wallet", "SlowWalletList").unwrap(); let version = db_rw.reader.get_latest_version().unwrap(); - let bytes = db_rw + let _bytes = db_rw .reader .get_state_value_by_version(&StateKey::access_path(ap), version) .unwrap(); - - dbg!(&bytes); } diff --git a/tools/genesis/src/parse_json.rs b/tools/genesis/src/parse_json.rs index 18abcec20..1d81cbd29 100644 --- a/tools/genesis/src/parse_json.rs +++ b/tools/genesis/src/parse_json.rs @@ -17,9 +17,8 @@ fn parse_json_single() { assert!(&acc.to_string() == "6BBF853AA6521DB445E5CBDF3C85E8A0"); } - let has_root = r + let _has_root = r .iter() .find(|el| el.comm_wallet.is_some()) .expect("could not find 0x0 state in recovery file"); - dbg!(&has_root); } diff --git a/tools/genesis/src/supply.rs b/tools/genesis/src/supply.rs index b7e4d3d17..aadd8571b 100644 --- a/tools/genesis/src/supply.rs +++ b/tools/genesis/src/supply.rs @@ -181,13 +181,12 @@ fn test_genesis_math() { println!("before"); let pct_normal = supply.normal / supply.total; - dbg!(&pct_normal); + let pct_dd = supply.donor_directed / supply.total; - dbg!(&pct_dd); + let pct_slow = supply.slow_total / supply.total; - dbg!(&pct_slow); - let pct_val_locked = supply.slow_validator_locked / supply.total; - dbg!(&pct_val_locked); + + let _pct_val_locked = supply.slow_validator_locked / supply.total; let sum_all_pct = pct_normal + pct_slow + pct_dd; assert!(sum_all_pct == 1.0); @@ -195,7 +194,7 @@ fn test_genesis_math() { // genesis infra escrow math // future uses is intended to equal 70% in this scenario. - println!("after"); + dbg!("after"); supply.set_ratios_from_settings(&settings).unwrap(); // escrow comes out of validator locked only diff --git a/tools/genesis/tests/e2e.rs b/tools/genesis/tests/e2e.rs index 03c61c0f7..ab14aeae8 100644 --- a/tools/genesis/tests/e2e.rs +++ b/tools/genesis/tests/e2e.rs @@ -76,7 +76,6 @@ fn end_to_end_single() { .extract_raw_bytes() .unwrap(); let validator_set: ValidatorSet = bcs::from_bytes(&bytes).unwrap(); - // dbg!(&validator_set.active_validators().len()); assert!( validator_set.active_validators().len() == num_vals, "validator set count does not match" @@ -140,7 +139,6 @@ fn end_to_end_all() { .extract_raw_bytes() .unwrap(); let validator_set: ValidatorSet = bcs::from_bytes(&bytes).unwrap(); - // dbg!(&validator_set.active_validators().len()); assert!( validator_set.active_validators().len() == num_vals, "validator set count does not match" diff --git a/tools/genesis/tests/json_to_genesis_audit_single.rs b/tools/genesis/tests/json_to_genesis_audit_single.rs index e82442638..8dc691bee 100644 --- a/tools/genesis/tests/json_to_genesis_audit_single.rs +++ b/tools/genesis/tests/json_to_genesis_audit_single.rs @@ -131,7 +131,7 @@ fn test_check_ancestry() { .unwrap() .unwrap(); assert!(ancestry.tree.len() == 2); - dbg!(&ancestry.tree); + assert!(ancestry .tree .get(0) @@ -139,7 +139,6 @@ fn test_check_ancestry() { .to_string() .contains("bdb8ad37341c")); - // dbg!(&ancestry); // let vals_list: Vec = // genesis_vals.into_iter().map(|v| v.owner_address).collect(); diff --git a/tools/query/src/query_type.rs b/tools/query/src/query_type.rs index a48368d74..40e15b1c0 100644 --- a/tools/query/src/query_type.rs +++ b/tools/query/src/query_type.rs @@ -32,6 +32,9 @@ pub enum QueryType { #[clap(short, long)] /// account to query txs of account: AccountAddress, + #[clap(short, long)] + /// the path of the resource, such as 0x1::slow_wallet::SlowWallet + resource_path_string: String, }, /// Execute a View function on-chain View { @@ -169,9 +172,18 @@ impl QueryType { "address": addr })) }, - _ => { bail!("Not implemented for type: {:?}", self) } + QueryType::Resources { account , resource_path_string} => { + let res = client.get_account_resource(*account, resource_path_string).await?; + + if let Some(r) = res.inner() { + Ok(r.data.clone()) + } else { + bail!("no resource {resource_path_string}, found at address {account}"); + } + }, + + _ => { bail!("Not implemented for type: {:?}\n Ground control to major tom.", self) } // QueryType::BlockHeight => todo!(), - // QueryType::Resources { account } => todo!(), // QueryType::MoveValue { account, module_name, struct_name, key_name } => todo!(), } diff --git a/tools/tower/src/core/proof.rs b/tools/tower/src/core/proof.rs index 49084d53c..fdac06d93 100644 --- a/tools/tower/src/core/proof.rs +++ b/tools/tower/src/core/proof.rs @@ -374,8 +374,6 @@ async fn test_get_next() { let vdf = get_next_and_mine(&app_cfg, &dummy_client, true) .await .unwrap(); - // dbg!(&hex::encode(&vdf.preimage)); let next_preimage = &hex::encode(vdf.preimage); assert!(proof_hash_str == next_preimage); - // dbg!(&vdf); } diff --git a/tools/tower/tests/tower_cli.rs b/tools/tower/tests/tower_cli.rs index 9e8349e72..318c3f5f1 100644 --- a/tools/tower/tests/tower_cli.rs +++ b/tools/tower/tests/tower_cli.rs @@ -50,7 +50,7 @@ async fn tower_cli_e2e() { let p = next_proof::get_next_proof_params_from_local(&app_cfg) .expect("could not find a proof locally"); - dbg!(&p); + assert!(p.next_height == 1, "not the droid"); // // 2. Submit that proof to chain. diff --git a/types/src/legacy_types/app_cfg.rs b/types/src/legacy_types/app_cfg.rs index 5766ab388..324df20a5 100644 --- a/types/src/legacy_types/app_cfg.rs +++ b/types/src/legacy_types/app_cfg.rs @@ -702,7 +702,7 @@ tx_configs: "; let cfg: AppCfg = serde_yaml::from_str(raw_yaml).unwrap(); - // dbg!(&cfg); + assert!(cfg.workspace.default_chain_id == NamedChain::TESTING); let np = cfg.get_network_profile(None).unwrap(); diff --git a/types/src/legacy_types/fixtures.rs b/types/src/legacy_types/fixtures.rs index e4a4bea1c..715dab646 100644 --- a/types/src/legacy_types/fixtures.rs +++ b/types/src/legacy_types/fixtures.rs @@ -62,7 +62,6 @@ impl TestPersona { /// get mnemonic pub fn get_persona_mnem(&self) -> String { let path = env!("CARGO_MANIFEST_DIR"); - // dbg!(&self.to_string()); let buf = PathBuf::from_str(path) .unwrap() .parent() diff --git a/types/src/legacy_types/network_playlist.rs b/types/src/legacy_types/network_playlist.rs index e4950df35..6b0bfb11e 100644 --- a/types/src/legacy_types/network_playlist.rs +++ b/types/src/legacy_types/network_playlist.rs @@ -46,7 +46,6 @@ impl HostProfile { } async fn check_sync(mut self) -> anyhow::Result { - // dbg!("check_sync", &self.url); let client = Client::new(self.url.clone()); match client.get_index().await { @@ -272,10 +271,3 @@ impl NetworkPlaylist { Ok(self) } } - -// #[tokio::test] -// async fn test_parse_from_url() { -// let url = find_default_playlist(NamedChain::MAINNET).unwrap(); -// let pl = NetworkPlaylist::from_url(url, N).await.unwrap(); -// dbg!(&pl); -// } diff --git a/types/src/type_extensions/client_ext.rs b/types/src/type_extensions/client_ext.rs index 2ebb1d96d..ea0897db0 100644 --- a/types/src/type_extensions/client_ext.rs +++ b/types/src/type_extensions/client_ext.rs @@ -325,14 +325,3 @@ struct Person { x: f64, y: f64, } - -#[test] -fn serde_test() { - let s = r#"{"x": 1.0, "y": 2.0}"#; - let value: serde_json::Value = serde_json::from_str(s).unwrap(); - // value. - dbg!(&value); - - let p: Person = serde_json::from_value(value).unwrap(); - dbg!(&p); -}