From d6e050e50f98b4c9903890ae2497a88dfeb24a16 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:17:58 +0000 Subject: [PATCH 01/34] APIs for reading/setting bids with estimated net_reward. --- .../sources/ol_sources/proof_of_fee.move | 82 ++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/framework/libra-framework/sources/ol_sources/proof_of_fee.move b/framework/libra-framework/sources/ol_sources/proof_of_fee.move index abf96b859..a8e90e62d 100644 --- a/framework/libra-framework/sources/ol_sources/proof_of_fee.move +++ b/framework/libra-framework/sources/ol_sources/proof_of_fee.move @@ -53,6 +53,9 @@ module ol_framework::proof_of_fee { const LONG_WINDOW: u64 = 10; // 10 epochs /// Margin for vouches const VOUCH_MARGIN: u64 = 2; + /// Maximum days before a bid expires. + const MAXIMUM_BID_EXPIRATION_EPOCHS: u64 = 30; + //////// ERRORS ///////// /// Not an active validator @@ -75,6 +78,10 @@ module ol_framework::proof_of_fee { const EBID_EXPIRED: u64 = 16; /// not enough coin balance const ELOW_UNLOCKED_COIN_BALANCE: u64 = 17; + /// reward should never reach zero, very bad + const EWTF_WHY_IS_REWARD_ZERO: u64 = 18; + /// don't try to set a net reward greater than the max epoch reward + const ENET_REWARD_GREATER_THAN_REWARD: u64 = 19; // A struct on the validators account which indicates their // latest bid (and epoch) @@ -645,7 +652,6 @@ module ol_framework::proof_of_fee { // get the current bid for a validator // CONSENSUS CRITICAL - // ALL EYES ON THIS // Proof of Fee returns the current bid of the validator during the auction for upcoming epoch seats. // returns (current bid, expiration epoch) #[view] @@ -665,6 +671,31 @@ module ol_framework::proof_of_fee { return (0, 0) } + #[view] + /// Convenience function to calculate the implied net reward + /// that the validator is seeking on a per-epoch basis. + /// @returns the unscaled coin value (not human readable) of the net reward + /// the user expects + public fun user_net_reward(node_addr: address): u64 acquires + ConsensusReward, ProofOfFeeAuction { + // get the user percentage rate + + let (bid_pct, _) = current_bid(node_addr); + if (bid_pct == 0) return 0; + // get the current nominal reward + let (nominal_reward, _, _ , _) = get_consensus_reward(); + + let user_entry_fee = bid_pct * nominal_reward; + if (user_entry_fee == 0) return 0; + user_entry_fee = user_entry_fee / 10; + + if (user_entry_fee < nominal_reward) { + return nominal_reward - user_entry_fee + }; + + return 0 + } + #[view] // which epoch did they last retract a bid? public fun is_already_retracted(node_addr: address): (bool, u64) acquires ProofOfFeeAuction { @@ -735,6 +766,47 @@ module ol_framework::proof_of_fee { pof.bid = bid; } + /// converts a current desired net_reward to the internal bid percentage + // Note: this uses the current epoch reward, which may not reflect the + // incoming epochs ajusted reward. + fun convert_net_reward_to_bid(net_reward: u64): u64 acquires ConsensusReward { + // if user wants zero, return 100% scaled + if (net_reward == 0) { + return 1000 + }; + + let (nominal_reward, _, _ , _) = get_consensus_reward(); + assert!(nominal_reward > 0, EWTF_WHY_IS_REWARD_ZERO); + assert!(net_reward < nominal_reward, ENET_REWARD_GREATER_THAN_REWARD); + + let pct_with_decimal = (net_reward * 10) / nominal_reward; + + return pct_with_decimal + } + + /// Instead of setting a bid with the internal variables of pct bid, we + /// allow the user to set their expected net_reward in an epoch + fun set_net_reward(account_sig: &signer, net_reward: u64, expiry_epoch: u64) acquires + ConsensusReward, ProofOfFeeAuction { + // double check the epoch expiry + let epoch_checked = check_epoch_expiry(expiry_epoch); + // convert to bid + let scaled_pct = convert_net_reward_to_bid(net_reward); + set_bid(account_sig, scaled_pct, epoch_checked); + } + + /// check if the expiry is too far in the future + /// and if so, return what the maximum allowed would be. + /// if within range returns the provided epoch without change + /// @returns checked epoch for bid expiration + fun check_epoch_expiry(expiry_epoch: u64): u64 { + let this_epoch = epoch_helper::get_current_epoch(); + if (expiry_epoch > MAXIMUM_BID_EXPIRATION_EPOCHS) { + return this_epoch + MAXIMUM_BID_EXPIRATION_EPOCHS + }; + expiry_epoch + } + /// Note that the validator will not be bidding on any future /// epochs if they retract their bid. The must set a new bid. fun retract_bid(account_sig: &signer) acquires ProofOfFeeAuction { @@ -770,6 +842,14 @@ module ol_framework::proof_of_fee { set_bid(sender, bid, epoch_expiry); } + /// update the bid using estimated net reward instead of the internal bid variables + public entry fun pof_update_bid_net_reward(sender: &signer, net_reward: u64, + epoch_expiry: u64) acquires ProofOfFeeAuction, ConsensusReward { + let checked_epoch = check_epoch_expiry(epoch_expiry); + // update the bid, initializes if not already. + set_net_reward(sender, net_reward, checked_epoch); + } + /// retract bid public entry fun pof_retract_bid(sender: signer) acquires ProofOfFeeAuction { // retract a bid From ac3f2a2673f1f9205f8af9a2ac60e31193e18917 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:30:28 +0000 Subject: [PATCH 02/34] make bidding by net reward the default --- .../src/libra_framework_sdk_builder.rs | 171 +++++++++++------- framework/releases/head.mrb | Bin 890723 -> 893147 bytes tools/txs/src/txs_cli_vals.rs | 40 ++-- 3 files changed, 136 insertions(+), 75 deletions(-) diff --git a/framework/cached-packages/src/libra_framework_sdk_builder.rs b/framework/cached-packages/src/libra_framework_sdk_builder.rs index a3bae1d52..a2fdf941e 100644 --- a/framework/cached-packages/src/libra_framework_sdk_builder.rs +++ b/framework/cached-packages/src/libra_framework_sdk_builder.rs @@ -474,6 +474,12 @@ pub enum EntryFunctionCall { epoch_expiry: u64, }, + /// update the bid using estimated net reward instead of the internal bid variables + ProofOfFeePofUpdateBidNetReward { + net_reward: u64, + epoch_expiry: u64, + }, + /// This function initiates governance for the multisig. It is called by the sponsor address, and is only callable once. /// init_gov fails gracefully if the governance is already initialized. /// init_type will throw errors if the type is already initialized. @@ -808,6 +814,10 @@ impl EntryFunctionCall { ProofOfFeePofUpdateBid { bid, epoch_expiry } => { proof_of_fee_pof_update_bid(bid, epoch_expiry) } + ProofOfFeePofUpdateBidNetReward { + net_reward, + epoch_expiry, + } => proof_of_fee_pof_update_bid_net_reward(net_reward, epoch_expiry), SafeInitPaymentMultisig { authorities } => safe_init_payment_multisig(authorities), SlowWalletSmokeTestVmUnlock { user_addr, @@ -2147,6 +2157,28 @@ pub fn proof_of_fee_pof_update_bid(bid: u64, epoch_expiry: u64) -> TransactionPa )) } +/// update the bid using estimated net reward instead of the internal bid variables +pub fn proof_of_fee_pof_update_bid_net_reward( + net_reward: u64, + epoch_expiry: u64, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("proof_of_fee").to_owned(), + ), + ident_str!("pof_update_bid_net_reward").to_owned(), + vec![], + vec![ + bcs::to_bytes(&net_reward).unwrap(), + bcs::to_bytes(&epoch_expiry).unwrap(), + ], + )) +} + /// This function initiates governance for the multisig. It is called by the sponsor address, and is only callable once. /// init_gov fails gracefully if the governance is already initialized. /// init_type will throw errors if the type is already initialized. @@ -2381,7 +2413,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::AccountOfferRotationCapability { - rotation_capability_sig_bytes: bcs::from_bytes(script.args().first()?).ok()?, + rotation_capability_sig_bytes: bcs::from_bytes(script.args().get(0)?).ok()?, account_scheme: bcs::from_bytes(script.args().get(1)?).ok()?, account_public_key_bytes: bcs::from_bytes(script.args().get(2)?).ok()?, recipient_address: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2396,7 +2428,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::AccountOfferSignerCapability { - signer_capability_sig_bytes: bcs::from_bytes(script.args().first()?).ok()?, + signer_capability_sig_bytes: bcs::from_bytes(script.args().get(0)?).ok()?, account_scheme: bcs::from_bytes(script.args().get(1)?).ok()?, account_public_key_bytes: bcs::from_bytes(script.args().get(2)?).ok()?, recipient_address: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2431,7 +2463,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::AccountRevokeRotationCapability { - to_be_revoked_address: bcs::from_bytes(script.args().first()?).ok()?, + to_be_revoked_address: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2443,7 +2475,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::AccountRevokeSignerCapability { - to_be_revoked_address: bcs::from_bytes(script.args().first()?).ok()?, + to_be_revoked_address: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2455,7 +2487,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::AccountRotateAuthenticationKey { - from_scheme: bcs::from_bytes(script.args().first()?).ok()?, + from_scheme: bcs::from_bytes(script.args().get(0)?).ok()?, from_public_key_bytes: bcs::from_bytes(script.args().get(1)?).ok()?, to_scheme: bcs::from_bytes(script.args().get(2)?).ok()?, to_public_key_bytes: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2473,7 +2505,7 @@ mod decoder { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::AccountRotateAuthenticationKeyWithRotationCapability { - rotation_cap_offerer_address: bcs::from_bytes(script.args().first()?).ok()?, + rotation_cap_offerer_address: bcs::from_bytes(script.args().get(0)?).ok()?, new_scheme: bcs::from_bytes(script.args().get(1)?).ok()?, new_public_key_bytes: bcs::from_bytes(script.args().get(2)?).ok()?, cap_update_table: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2487,7 +2519,7 @@ mod decoder { pub fn burn_set_send_community(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::BurnSetSendCommunity { - community: bcs::from_bytes(script.args().first()?).ok()?, + community: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2497,7 +2529,7 @@ mod decoder { pub fn code_publish_package_txn(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::CodePublishPackageTxn { - metadata_serialized: bcs::from_bytes(script.args().first()?).ok()?, + metadata_serialized: bcs::from_bytes(script.args().get(0)?).ok()?, code: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2508,8 +2540,8 @@ mod decoder { pub fn coin_transfer(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::CoinTransfer { - coin_type: script.ty_args().first()?.clone(), - to: bcs::from_bytes(script.args().first()?).ok()?, + coin_type: script.ty_args().get(0)?.clone(), + to: bcs::from_bytes(script.args().get(0)?).ok()?, amount: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2523,7 +2555,7 @@ mod decoder { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::CommunityWalletInitChangeSignerCommunityMultisig { - multisig_address: bcs::from_bytes(script.args().first()?).ok()?, + multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, new_signer: bcs::from_bytes(script.args().get(1)?).ok()?, is_add_operation: bcs::from_bytes(script.args().get(2)?).ok()?, n_of_m: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2540,7 +2572,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::CommunityWalletInitFinalizeAndCage { - num_signers: bcs::from_bytes(script.args().first()?).ok()?, + num_signers: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2552,7 +2584,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::CommunityWalletInitInitCommunity { - initial_authorities: bcs::from_bytes(script.args().first()?).ok()?, + initial_authorities: bcs::from_bytes(script.args().get(0)?).ok()?, check_threshold: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2565,7 +2597,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::CommunityWalletInitProposeOffer { - new_signers: bcs::from_bytes(script.args().first()?).ok()?, + new_signers: bcs::from_bytes(script.args().get(0)?).ok()?, num_signers: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2579,7 +2611,7 @@ mod decoder { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::DiemGovernanceAddApprovedScriptHashScript { - proposal_id: bcs::from_bytes(script.args().first()?).ok()?, + proposal_id: bcs::from_bytes(script.args().get(0)?).ok()?, }, ) } else { @@ -2592,7 +2624,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DiemGovernanceAssertCanResolve { - proposal_id: bcs::from_bytes(script.args().first()?).ok()?, + proposal_id: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2604,7 +2636,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DiemGovernanceCreateProposalV2 { - execution_hash: bcs::from_bytes(script.args().first()?).ok()?, + execution_hash: bcs::from_bytes(script.args().get(0)?).ok()?, metadata_location: bcs::from_bytes(script.args().get(1)?).ok()?, metadata_hash: bcs::from_bytes(script.args().get(2)?).ok()?, is_multi_step_proposal: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2619,7 +2651,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DiemGovernanceOlCreateProposalV2 { - execution_hash: bcs::from_bytes(script.args().first()?).ok()?, + execution_hash: bcs::from_bytes(script.args().get(0)?).ok()?, metadata_location: bcs::from_bytes(script.args().get(1)?).ok()?, metadata_hash: bcs::from_bytes(script.args().get(2)?).ok()?, is_multi_step_proposal: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2632,7 +2664,7 @@ mod decoder { pub fn diem_governance_ol_vote(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DiemGovernanceOlVote { - proposal_id: bcs::from_bytes(script.args().first()?).ok()?, + proposal_id: bcs::from_bytes(script.args().get(0)?).ok()?, should_pass: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2663,7 +2695,7 @@ mod decoder { pub fn diem_governance_vote(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DiemGovernanceVote { - proposal_id: bcs::from_bytes(script.args().first()?).ok()?, + proposal_id: bcs::from_bytes(script.args().get(0)?).ok()?, should_pass: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2676,7 +2708,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DonorVoiceTxsProposeLiquidateTx { - multisig_address: bcs::from_bytes(script.args().first()?).ok()?, + multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2688,7 +2720,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DonorVoiceTxsProposePaymentTx { - multisig_address: bcs::from_bytes(script.args().first()?).ok()?, + multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, payee: bcs::from_bytes(script.args().get(1)?).ok()?, value: bcs::from_bytes(script.args().get(2)?).ok()?, description: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2703,7 +2735,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DonorVoiceTxsProposeVetoTx { - multisig_address: bcs::from_bytes(script.args().first()?).ok()?, + multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, id: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2716,7 +2748,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DonorVoiceTxsVoteLiquidationTx { - multisig_address: bcs::from_bytes(script.args().first()?).ok()?, + multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2726,7 +2758,7 @@ mod decoder { pub fn donor_voice_txs_vote_veto_tx(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DonorVoiceTxsVoteVetoTx { - multisig_address: bcs::from_bytes(script.args().first()?).ok()?, + multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, id: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2747,7 +2779,7 @@ 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().first()?).ok()?, + addr: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2769,7 +2801,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::LibraCoinDelegateMintCapability { - to: bcs::from_bytes(script.args().first()?).ok()?, + to: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2779,7 +2811,7 @@ mod decoder { pub fn libra_coin_mint_to_impl(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::LibraCoinMintToImpl { - dst_addr: bcs::from_bytes(script.args().first()?).ok()?, + dst_addr: bcs::from_bytes(script.args().get(0)?).ok()?, amount: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2790,7 +2822,7 @@ mod decoder { pub fn multi_action_claim_offer(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultiActionClaimOffer { - multisig_address: bcs::from_bytes(script.args().first()?).ok()?, + multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2812,7 +2844,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultiActionMigrationMigrateOffer { - multisig_address: bcs::from_bytes(script.args().first()?).ok()?, + multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2822,7 +2854,7 @@ mod decoder { pub fn multisig_account_add_owner(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountAddOwner { - new_owner: bcs::from_bytes(script.args().first()?).ok()?, + new_owner: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2832,7 +2864,7 @@ mod decoder { pub fn multisig_account_add_owners(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountAddOwners { - new_owners: bcs::from_bytes(script.args().first()?).ok()?, + new_owners: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2844,7 +2876,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountApproveTransaction { - multisig_account: bcs::from_bytes(script.args().first()?).ok()?, + multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?, sequence_number: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2855,7 +2887,7 @@ mod decoder { pub fn multisig_account_create(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountCreate { - num_signatures_required: bcs::from_bytes(script.args().first()?).ok()?, + num_signatures_required: bcs::from_bytes(script.args().get(0)?).ok()?, metadata_keys: bcs::from_bytes(script.args().get(1)?).ok()?, metadata_values: bcs::from_bytes(script.args().get(2)?).ok()?, }) @@ -2869,7 +2901,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountCreateTransaction { - multisig_account: bcs::from_bytes(script.args().first()?).ok()?, + multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?, payload: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2883,7 +2915,7 @@ mod decoder { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::MultisigAccountCreateTransactionWithHash { - multisig_account: bcs::from_bytes(script.args().first()?).ok()?, + multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?, payload_hash: bcs::from_bytes(script.args().get(1)?).ok()?, }, ) @@ -2898,7 +2930,7 @@ mod decoder { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::MultisigAccountCreateWithExistingAccount { - multisig_address: bcs::from_bytes(script.args().first()?).ok()?, + multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, owners: bcs::from_bytes(script.args().get(1)?).ok()?, num_signatures_required: bcs::from_bytes(script.args().get(2)?).ok()?, account_scheme: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2919,7 +2951,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountCreateWithOwners { - additional_owners: bcs::from_bytes(script.args().first()?).ok()?, + additional_owners: bcs::from_bytes(script.args().get(0)?).ok()?, num_signatures_required: bcs::from_bytes(script.args().get(1)?).ok()?, metadata_keys: bcs::from_bytes(script.args().get(2)?).ok()?, metadata_values: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2935,7 +2967,7 @@ mod decoder { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::MultisigAccountExecuteRejectedTransaction { - multisig_account: bcs::from_bytes(script.args().first()?).ok()?, + multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?, }, ) } else { @@ -2948,7 +2980,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountMigrateWithOwners { - additional_owners: bcs::from_bytes(script.args().first()?).ok()?, + additional_owners: bcs::from_bytes(script.args().get(0)?).ok()?, num_signatures_required: bcs::from_bytes(script.args().get(1)?).ok()?, metadata_keys: bcs::from_bytes(script.args().get(2)?).ok()?, metadata_values: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2963,7 +2995,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountRejectTransaction { - multisig_account: bcs::from_bytes(script.args().first()?).ok()?, + multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?, sequence_number: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2976,7 +3008,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountRemoveOwner { - owner_to_remove: bcs::from_bytes(script.args().first()?).ok()?, + owner_to_remove: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -2988,7 +3020,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountRemoveOwners { - owners_to_remove: bcs::from_bytes(script.args().first()?).ok()?, + owners_to_remove: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -3000,7 +3032,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountUpdateMetadata { - keys: bcs::from_bytes(script.args().first()?).ok()?, + keys: bcs::from_bytes(script.args().get(0)?).ok()?, values: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -3013,7 +3045,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountUpdateSignaturesRequired { - new_num_signatures_required: bcs::from_bytes(script.args().first()?).ok()?, + new_num_signatures_required: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -3025,7 +3057,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountVoteTransanction { - multisig_account: bcs::from_bytes(script.args().first()?).ok()?, + multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?, sequence_number: bcs::from_bytes(script.args().get(1)?).ok()?, approved: bcs::from_bytes(script.args().get(2)?).ok()?, }) @@ -3037,7 +3069,7 @@ mod decoder { pub fn object_transfer_call(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::ObjectTransferCall { - object: bcs::from_bytes(script.args().first()?).ok()?, + object: bcs::from_bytes(script.args().get(0)?).ok()?, to: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -3048,7 +3080,7 @@ mod decoder { pub fn ol_account_create_account(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::OlAccountCreateAccount { - auth_key: bcs::from_bytes(script.args().first()?).ok()?, + auth_key: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -3060,7 +3092,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::OlAccountSetAllowDirectCoinTransfers { - allow: bcs::from_bytes(script.args().first()?).ok()?, + allow: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -3070,7 +3102,7 @@ mod decoder { pub fn ol_account_transfer(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::OlAccountTransfer { - to: bcs::from_bytes(script.args().first()?).ok()?, + to: bcs::from_bytes(script.args().get(0)?).ok()?, amount: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -3097,7 +3129,20 @@ mod decoder { pub fn proof_of_fee_pof_update_bid(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::ProofOfFeePofUpdateBid { - bid: bcs::from_bytes(script.args().first()?).ok()?, + bid: bcs::from_bytes(script.args().get(0)?).ok()?, + epoch_expiry: bcs::from_bytes(script.args().get(1)?).ok()?, + }) + } else { + None + } + } + + pub fn proof_of_fee_pof_update_bid_net_reward( + payload: &TransactionPayload, + ) -> Option { + if let TransactionPayload::EntryFunction(script) = payload { + Some(EntryFunctionCall::ProofOfFeePofUpdateBidNetReward { + net_reward: bcs::from_bytes(script.args().get(0)?).ok()?, epoch_expiry: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -3108,7 +3153,7 @@ mod decoder { pub fn safe_init_payment_multisig(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::SafeInitPaymentMultisig { - authorities: bcs::from_bytes(script.args().first()?).ok()?, + authorities: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -3120,7 +3165,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::SlowWalletSmokeTestVmUnlock { - user_addr: bcs::from_bytes(script.args().first()?).ok()?, + user_addr: bcs::from_bytes(script.args().get(0)?).ok()?, unlocked: bcs::from_bytes(script.args().get(1)?).ok()?, transferred: bcs::from_bytes(script.args().get(2)?).ok()?, }) @@ -3140,7 +3185,7 @@ mod decoder { pub fn stake_initialize_validator(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::StakeInitializeValidator { - consensus_pubkey: bcs::from_bytes(script.args().first()?).ok()?, + consensus_pubkey: bcs::from_bytes(script.args().get(0)?).ok()?, proof_of_possession: bcs::from_bytes(script.args().get(1)?).ok()?, network_addresses: bcs::from_bytes(script.args().get(2)?).ok()?, fullnode_addresses: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -3153,7 +3198,7 @@ mod decoder { pub fn stake_rotate_consensus_key(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::StakeRotateConsensusKey { - validator_address: bcs::from_bytes(script.args().first()?).ok()?, + validator_address: bcs::from_bytes(script.args().get(0)?).ok()?, new_consensus_pubkey: bcs::from_bytes(script.args().get(1)?).ok()?, proof_of_possession: bcs::from_bytes(script.args().get(2)?).ok()?, }) @@ -3167,7 +3212,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::StakeUpdateNetworkAndFullnodeAddresses { - validator_address: bcs::from_bytes(script.args().first()?).ok()?, + validator_address: bcs::from_bytes(script.args().get(0)?).ok()?, new_network_addresses: bcs::from_bytes(script.args().get(1)?).ok()?, new_fullnode_addresses: bcs::from_bytes(script.args().get(2)?).ok()?, }) @@ -3181,7 +3226,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::ValidatorUniverseRegisterValidator { - consensus_pubkey: bcs::from_bytes(script.args().first()?).ok()?, + consensus_pubkey: bcs::from_bytes(script.args().get(0)?).ok()?, proof_of_possession: bcs::from_bytes(script.args().get(1)?).ok()?, network_addresses: bcs::from_bytes(script.args().get(2)?).ok()?, fullnode_addresses: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -3194,7 +3239,7 @@ mod decoder { pub fn version_set_version(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::VersionSetVersion { - major: bcs::from_bytes(script.args().first()?).ok()?, + major: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -3204,7 +3249,7 @@ mod decoder { pub fn vouch_insist_vouch_for(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::VouchInsistVouchFor { - friend_account: bcs::from_bytes(script.args().first()?).ok()?, + friend_account: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -3214,7 +3259,7 @@ mod decoder { pub fn vouch_revoke(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::VouchRevoke { - friend_account: bcs::from_bytes(script.args().first()?).ok()?, + friend_account: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -3224,7 +3269,7 @@ mod decoder { pub fn vouch_vouch_for(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::VouchVouchFor { - friend_account: bcs::from_bytes(script.args().first()?).ok()?, + friend_account: bcs::from_bytes(script.args().get(0)?).ok()?, }) } else { None @@ -3480,6 +3525,10 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::LazyylK|{fs!Ow<-#hFFK#d%mI*x9&QIoUZlB)GUFSjE{nxh2?HMMb&AS$UX6 zxkWg|xH#EF*x5K(*|@kwS=czlMcLUTIXSqwB4~!ee@=)tnqkQGCeWpHP%o%Kz;yUT z=AVsj9Yrczqnb2$Ec3yK^`KTWo+`8`#ab3z?R1F-n0+Cs)vl{Pb{S|Qc$R4qRThnd zC&nWw0*i^p;_(Am@B83s#@*kF(i4pbX{ep$Ix?YD%~R5*S&pzmMSsi`G?F=Lez96c7n&6tu#h??@cq}VC@dELdQ(1beExKbe5z&H z^f(pm^nP|^%mY-ZbA-!Yf8mL820XUNldChOS zw0`}%I=x#u9$OPq&=cMz1Sd(ZIA2*g#;pHm;d5_j>%TTu&?~Ry_gtyS!U>TTz3eJ#Je@b6y6-H7cer~C;lvUtx z@Iaw-0fBLcTv34byz=z?1o&8b`qkZ@6LyMy$`%*2MIin$e3-hS8P%V|?Wku9cZ z$gK5ADC0cOS=WdTiM5bHK(|+s$K~b|Rt{;#V*r6+uKqNmKF{|a4PfQ@>f-P+vr2gp zRYPLbq!Y3Oe9%UN-5S9X6m4>V$aJ}tt^oI*TA^hh&$=+Hd$&?ecj3?9`s<8g^MYGyCq6S^BDH!}Kn3|aBri!jr{CH(u!FxWp=Rvs_bHMEos^S3CLg1!~s${E(6%4T}zv#UplonE%rJ` zJd`MfR}FIOCy$Ex3##qP6l+e2s*46mn28Os>unAv)@YDmtAI%Qtejiq{#OkY4AH+F zq!DEAcr9oldblD%HBr`H*q zWsOyEU#y}=Qah~yi_p3?8A6j*6f&pcJ)zz$(fZ7p7%_1&L*4foo7?gEO4kO?xcFf5 z8{K;lB~9%c)&L0nbhnb!A>HSa8*>>QllznPcDW^jRRR2I?C4KcRwL@ahpz?COsGmq zO}Bwkz_LCroc%K*VHPv!bKuxrRp=T0HAq|Yx#&$`F4Xs`7*~EhpSEcToJ{$wpt1D` zJB$_F;6M-ABN$qdLc4r4kHS!O1#~&LHR;{96xtG96kw6bA*C+C9xTzb9>cSNO4}Jl zR*gy=&Ov_9uLwVKKrU^t2T@9Z>5qDHk2ytYK&-!1NQ@lVpn(=ScJtpqOTJiV`1w7J z<3#9rMv7E2vjV2lQ|8GH-AErQhvkn(d)eG&<#2P2AevM2-GJIRz|OrYus}uE@MV$W6iGG0M{OQjU>N z#YYmVs1y*N!4W3ILg6{faLGED21&b;X~vqc2C#PxtR>Bq1!SZVtsysiJCc)WMprA(Jpdr;38KQ)p&lj}qBks6@|? z8Us*(D0nIiZw(V2W~k)U{rmENL;eaOH5YtE61%sDb6<|H1=+Mma6}kysdAfgc0O{iozi2cSW^m*++pzL&UT{k^{i&g#{3U ztqdxy7MyCL%7H3w8cn5O-l0rr&_nC>L3@$T&p#v@2DhWDOaj6@k9VPG+n(P(O#$?g zux;FzV8Zor(h;?;-=;@YyB4MuV`hY7)-FF?Ak<_`Ch$RK+!_;vb1Cl3xCTqp`&ulC%kFKw;(=&U*gBsp}ZN?Df zSobRYGsQk5*wW1h&KJT}OrmeA`q*lxn2S^$AH2V&h=7NZ&HuPag-5#vN?h3z!`-2% zQ`ixh0@9;QzhnXl*|2LU0YL38NZ*?X*JwJ#RO7JcYb@}%+%E@Hu|MN|+H6H)ZMYm7 zE{Yr1>l&-!4Mq7-E$S+EPICT5FWDP<1&?7J-%#Zx#jC-!(ph6c;>XvL?_Ps*_?2W% zgzH%H6klT?tR8CwDKRS<35wUjVPPxCk$~1( zepm$7{TfP*Hno8Or&7KuuB&nvw-@qZ?LN!!G$cNvtW>y4a#yBbicd?|bv5 zA4^lFGnm_$$~}Ru-Vv&Iaed80>c6hnP)DoqY{qspylH%y(e&4)8-*ZTTMW_7I2dWD z!+)V&WObh|#xvzF5&$`BD;1qc((!}Dm{xDmip~UUD|Nj~sk(>Ab!C9i-QUHxi~-Ix z`+k0><52YZcbP*^5p_P1^t&JQY#lTB#h%CIfu94_#z28<10L`e>8kH0=NEM;QHH3V zE7k7>??W91;WiKEWxl^_nucv=I@iIrfBueNPN1u z+d~#T(kl&6bI4&W9a?QZCRUfds_uo$b*L3DH))o)iu`%(qrsTo_3aVexeWerK#Sr*&1`3ag^H(eaMRT)H zGGL!Dk-!6;q;8hcGmoY4^o@}~4hx$+MXccq7nCm6N@#Lw#LYSx%yy53IO%o8+Mdp@T35T5m+SG5&< zK>@Jk#;ai%5#vX4U}_7M55b60b12}Yd@LWgg^TUoQ(jSmlp*gTjUr`CMI??h0WCqO zb*g;p8Od@iHK3ub>cvnmLlD(9xMhEKk{929pRXN#8!Y$?r@1L8brANpd!O?$o?2Uw z^Twq67f$E!-}|A@zL&B1)qT(Npj&45odqDjo!e>RKce2*1K94|fq%Vo*~iztwF7WT&Axq=0!})U$Hq_@a)e?XMphm>=<4@~ftB3X6 zGxh#juH9_#?P6PET)c^uf0R0dXwbN9=WQYte+hKk4_6a>FBM=fr3ASk~Cy>+2Og$a)3j%|6h=DPZxIi5JTG=O0M-fso!UGZD#o^*$6x zYz6Vwb%lI`uYw?aNsEAhJkSzH!+`wn0Q=6(wf0KUNEIsF(398YHog4qlo$1c(-ih49ia z(g*+9V}0zC=@DT2ESMA8o^uss|GMe9^}X#l&GDV}r`tGH3?SSf^xNBYtrPnGSbUBe z0S~|K>Hccz<9BQFnmm4FAw9)=v{`Zyjk45}3X}k*9 z4R%CiI)H$nJV%pQ_Q`&Y4iK`q!UuML9yhr5BdE;LCM^kbsNVL#EaRD60l=Gx4;Yf7 zS@?26Z89kyxPYyqP~$`Kuxrh+(GNNW7>?ghmW&<;jBKeeP#9uA|oy?Ot0#lA4MJ4k_ zT9%M5mJWcpkdjW3{z909Z^B?matwNc!WEn2Hx|EvI8iP4G`xOrYqkl08iD?c9H0YX zJGB<%$6pxV`r%bv>W?y^eXOnQjG`9ZHHtW7IED8`?p2zM3drHlZ2;jO>>suOcj8T^-(G+pg}AO?u|A_e?MnsziM5!1(|N+hp2=|aKY?8W9*YP( zq#VCg)YjN1I-$>hq4XtOghQn}K$mF77miRx7L?YMjBGDo6jr|(&L70s9J8GC>`2-? zQ|Q{wnNE9`6V@TI^eZecwfcd*=Yhgr@1Fx`%ZG(mP_D5yAlnx{L%d_)=ZPf^PKSVT zCoGJ6kjj8*)8B!DD~<5;n8F^Qr^~)uxY)p|h%2zcdBgTxXV6aBaYhnY^~RqfBryCZ zm;h%J`Xe}%X+1_Ehl4kh=5YRblVES*4)sDTtMZwy+lqWn&f<9Z1IEMsk;?%?9;5Zd zpwFGTELU5?ge`30{#6kvh`uNm0zCOFSD-+!N8F(R`0@`wOetA2xN>#roU7$KPC4Px zXfw?6Oq6Kp)sQUYhCfI2$eAQbLm~`;6bp`K=o9Y107Mzoi7{b;+7oq(wUEt(I)ona zTCf&8R%MEnaePv1mr~^AFF!ym%M-W@2Lp>%M}VW3@WNh@yh^jAnFANd-;61*dI=!s zF)L9u`0{7RbhLZW#q0_W_CJ;2QvWO<8Kni%7yQv_0ua}}rG>CI{N4MY;N{m{K1t9H z?L^0=&4WYCg|4$h-`o4r(h?`Xuk+)y;TQ8|5WJ-=)Zg*$!(snE3@`wFaFr8RJAU$Y zhexEC4f|55H3KKro;RDyQk;n2<$`0uvLnKKOxP<|dv-szj33oHfwXe8uYsNzUDlC| z0@9wpVc3&xI*~svk#C33lgCpw`76@juE8ZMxx_9?-|suf+`lMFpe0L;eq2j^9k5f+xh`qE~iO-v&CvOhov zGE%b^WcHFC&0q1Bv&hI>rGeTwp`m9Xg1Y;nx$<%a<7EwpS~w`r{Dd??FKQ{M*wv0= zey&w_F^mxwTO!voCZbKTKm#S!g3RW4WYh0ny>d}D!0L^7^ievn_m_G>rg$FcN}qI? z1)6nGV)LfD)o9k9S7JBjvQakN48 z4Az7Skf^DGM3D?%gkwV;g*wqyc&4R-B+k9h8t-x&`sY=Zf}14sl1X_}6tXTBL9$_z zzgsv5aC*ulPnx>hnIi2VdxI%gl+Kf`1!+Mu!Jdc^VF)@>E5_>)X1w;QR!HoQW)b6z z6uWw~fAS~!*(4&1|JGK^ARmm5NR5o%=EA7cxaU~&mfk`s_Fuw7UL`08-A@jiaEm32Z{?=gq^}J|`I~I|vBW!y-fi^N4qtFk<4M^O zN!GYAyAXo9I#SV_h4P;?j$IHNfZ~rR65h@f)AxNyXgu)@+RPNo9TusXGiLubSC%4y z=p>wa6a_X0j8eV$eZEK78u;ffKS(t5;z(!BB!ld#4Gm9h-ch#W2gPvafVs0JEI|4D z>EIWJ+$D&_aK47HLa=P^Ay$${E`)CYPoP*htKgSgNk%PdKw5Ys8*~6uKVzX)M^0D) z#=>ruC7~=BC;K@5i!2y7I~CqWo7uEU(e#KfB-2igYaCoezSSJ`T@Pp4OwUGm1)6Om z4Ht;PV5wIcgoiU|rq>$y7jqh8P!Qr-f>{mbabSF(RGzdN|5HaaLv&p>!8RmQGz0UK zGL$)t*+1>*A+2yxlpSC(g*j@oN#oA2Gqvk_t>AVIoFXY;)4SMKc)ISSwNfpE3bs#l z3B-1;UkpJplUpdamHp$XuH%>ZLY( z9hV{}zaY*SQ-^Gv*OupkdZT^5z|Qx}7ViAc>ULY3H&sS$a(jP$ zSfniK3zC>#ED4#UGf87aNFfyEKBc1xeA`@JgnS{*dlZBJ`M<)MQI_i!@(T$ON)%yw z$Cn*)3K2A$dIvy>1M?j6noRZRz%Sn76Y}jjK)>NIm#B-RC~-jD(w1a{oqs8k5KZ!V znQfDWf0=#p)MYX8kH2iWoft2Rv=quy-(4Z1iaIPX9EpV^P=_zGaLw@%t~m6BhXPhe zLxKu(>E(P#pV_QI1Y{!rX-m)~R9qkU}{BauKmL>3A z54hnkx?^Dh)7l%M7^8qo9}?e8W8kD3PEjdcsoz04^h(-b4mVYZu3t2-v-IW@^;t!w zM7H^w1!;!qS~%`tW2o%zmUUw z8H)xJ8wn6;GPnue>`O8JQvT~%+S+w>861!Zcq7rK=;YMA<*c_a!W3Ur+ zxvAo!F*U;i;#u`4?N279@^*zhgC_FGk?s{85YN%rrXfG*<5s;CYYX}FP1qs1MG*j* zP7*Qh?W7z++BW66Cae&KfH~nSq7Z_BKCvazK{E1SPmRp;yS6$o3kjEWbFQGiafp;$ zq7h=5MmkD5PWh8omm(MF5E7H-SWW}SDoJrEvJFj(IZ*+$5W7iWbI+K1OyHAP3>`{F zWYsiKP&NnG0MaMsn;yyhNn_(Ci3f;HN;D#V+J~xJT_rkoORsv5%XYj&UcgGH+6@Tz zK>SQ9`XE;jGiiLamAgpAmGM)c(sN5yP5gP5sEZ`9hSPcSPRV)L*+PU|lqAP$n*qlO zZx+0&LFaj5CqhKUBU}GjwSy00?&*`*l13_+X$()%E1Jdyu?Y+0j1qu9%K(z%gY-nM zrGGqI5rMoPZ!J)@*$?3g!d)2x3M)kSf4C5CDP@x&)4x3;TZjgFl>L+?dXr{T0-gj!KQuwhUC-sur4rK@GlS!L zdya!=^+VXN^F#phB4y~yX(H3<3Vq=2b||hW*-#fpBqt;%ARKN#DmnyIVb%!AJ3uh(X37KrH-AZZ;8M;6Ua)330yl*PG6o31vqcytjpBm-_()^M z9(B)o(ujSomx=BuRpMZ+or)UJWDSe>{r99NW3?A&3J?U_B(Ae&66;Rv#Co*U%(1AM ze&KPQKm5I7+eN_UXCmSN6D*d&eF=ZBD0hJK!VAB=$o*^TB9m^T#{r~;GS0bfOZYm@ z^sg;;d5v?7uRq2zwexgXPMb(>wCO{GuZTav7`KVP+W~d-^42h0+$)~+x7R2t0Fsp} z@BJ7~8L)95Glc|^iaytF=kK;*E(BFcm*>1u156uXW7OM~d+3Qf1S$@*&3@V|TOQz^ z24F3hc#U>^gDxL?oft$oxE?X@b?TDCA4W0&8|h=n|AD948xu4Lc3wqN2WD9r6F5Vb z^ZrDC9!tv~AAH&FM`#7<{v_SQDfcx1nB!ipmGypV=0R6Z4VvkI@0*j=$frHCJ$Y{b zNOvJ}OC2erqeRVT`vHgzJm7x@C5E}3r|@**X5BGLf-8Bp^-f*q&!n~D1i zzfm#cE;OAjF^L@WNa#UN8v4&M^3Af4N>5kF*%<_}=~d!yKW6J$L}$X@t)TQ>NK&QP zgKVM|3i@R%sTz`>=tJQ%51_nCziA22Uk{*AG5ZlL(~-&>y`q-G|G*1j84$`Gw4eR+ z)r1b060v7x1_bTT*+3%5@0f#>YlEYULIwWeVkSNE#Q7{kp$csV3RMNcl12-2;AiyL z&<}>(-?7{yD785XDD2Nl~{QLk`*CaN&+{Erv!?-Wr!aV9;gJM zF2q5bWB|w)xnLH_1`wE<<93TU${}zZl4@Gm`@)rsus$kq^@wL4?n;Rr0UgkLQ*i&( z^rtF7H-ArJvOL8dL7M|3RX)hhI%1MzV0TID?Cq_?N#QLkd=Rh06nB)qGV-IFU zNkb(br3Q>;ALzq4w-NGQZOG@C5ND~*Ka;J#Oy&Ma7Rh~}7a2bev~bU{(cgstWhTCI zGKlgLMJgp{+#?O>`ph$H&7*(7EIuv)S7_dfWqfr%74 z^^@k=2{W1Sph_23og^7xDQ_nIgg7#-rA(vn_MurL#h=c#pZJq!I-s!zQN1g9BFQbV zbN9zO3DMpAiMxulaA(p-;dvJ%(`r?Nxv&3(G(&tQf8Y`1Gc#cZkZBF%J^IOfcSSp) z!i-oUa5sAXXXA*$MhS8aTRBPct+{o5fw0rIYl9E(SLlOvVw-pk;z_|w)Q7(O2pcmd z1ar{>F?vW0^{feYc>faU!xt1~kLmuqr`PTs++@$qC_|xQQ^U4ylyiM z>?+1M?PvKo!!iv>;)lFwW0_8(2WjC5*Flr8k6yr;>i~~v@3|m3ol$%eoQFpA&pYFq zKhk&Ct<5~H&Ahg;wQ=TBZ<}0m`3mR6Chh&p^z>gCsVe;CC9xu|eK0oL^EDInQ3P2X z^uDSRrG-KlINI`mRkIv3jV;=6ejE^)f*rpDp*yHk7N++Bz#GJp0M7Zx;6@A1U|p0+ zs}pb_uNbcB0;r8KjcbBahz~~!>~TJ%JM#~;f_x}{@Vs!WyfXzeIUf4JwP0AD2zy)? z?1qCIods4oKb`@BUQR}?Mzk!IK6C@8yc8q9E&!D>H6FS>Bk>$i}gWY%{Y_ zj-3i$v;QDn_?qnp?!bT9nMp-N@Z*$Y-w@9gnQy-j`OBv)O1ZsZmND7Xa@3c(eniFG z2h5uz6-mw%QqiB}yuCVcL5mPwv{J#rgmJ?lUE-etO5H`E534-DKa_M70mvCq5zjol zD+%)KkMoE+iVW-?%8Y0B6M+n4BR1}?I$_IsgAr39y+CG$$_U5SN^2j*RPs=u&u2D~ zm!W)!p}hExV<6PhJ;u}LZ780`|6YDH)HgrYs8u;epOk#JK1o_QjGn5T9DDgNwbrpt zO~=w??KKmJ&H9kK6?(TqrPMeqYyTxT8h1fB$kAKub+u00t7|5sbo<^9a9mW1X3YGp z!rR+k+1x#?!P?qh*l1Q`-AetBXVV$W>nn}~kZZ#*0GVjiEgZa6zNAyRtuQZC-WKyGx3s_G7OJlJ>2JMvCq z!n0(?NL_j&%RIG^B=+hXIdA_;kFF-jS^IY5oK-w@HuyFboMkMM111%h6DLeJ4tTre z#aIHc(7#}K^wqIw>s-EjdB}}TGjh@_Bi}GU;lL#y*)x&ol)2?fW%3p=SPLEK%5WE7 zKj0!hw+y2xmvK#*@`yUYr89L$(il4$n4b?{B_#1{~a)|MNIKlQ2xx z!@r2Skjzzt{_bOlCQM0QGK*V;DxF`{B~;`!TiOyP;6mWj~9+YMqjoTK^01 zLFbK(UY2a83xZ$*VunCN7@Nf77h;Qam)<04cf&p=H5M#7dm$`eSQJxIms_da4;OOZ)g%r$u>z5A^!7=6Fd-?mz@3%-Z@`q=Ip{PmEVvXC*bws`DTJBfD`B1Zs^zD+Dqf#Y}_1eRED9CexcKi z4B9hxLf-xj zU)~ISAZg$u+NmsCo=@Ihb+afa2D2L#vZ|#J;A&jTT;8c%M?h2W8ODf&nC~i_uO7}n|+>dzT#vQ-l$fG zMQ6r8Ft_YkedgZHx7!{qM+1Na%}jiKu5HU+)@H%2`~bSs;bR+=^6nDSXl-YU}m9^ z&x}fL&Au)W`0@W<46ZZJS79CN*n~Y32L&g8Zm(-OxrPXY1r1^;eN0X+9*H;%LBe0A zmw*89D{CW@z+kpvxaWM6PfKd=uBN^|Ur{!KTRt;$#Y=n0)W$Bzgv;-SrncMa4u>`M z33j@?TY68eH99D99Bz`x7kDlqRFWdiXQ z;^LbXXbrwwAUbT?jNu2hg2Jcg>-)g!^WW6>UV%3CyQOcrB@w`4i5M;cQdmE85?%Zl z<0}Pn&InbVam1T{1!ArdD7p#VMeS3PAMfF*C=8bO0Va=WbtGkiR!&&Ye0mHNb`N3W z8*N#SF-Zx*kb|y|CWt7h{r3dplv+lvIf zyp_Dlk2JoJM=mc9faMqH6SduVb6{39VTPY0`!-58%NSjPVHf#C1}13kd#tx-JONhpJvg#l>gOGx$+5q(LXNq^ z_Z12uOBNGt{L;H4dXD13g$3+CMp}Ml`HK4DJ-&4 z`BG+3&Hx0XmS|1l4gI|`Q%#(kbg!hQim2M(2Z^2x@d5gDIxe@FSmi5g*fv- z>@TMCblN|=s$$$B#?B4=XQX5V@~CgB^6m zHx>S89MlZs8V=s!^atMasE-_(p|fV9KlbLR64yWu;clhVe1ot;?ck|b8XHAR?kM3v z%1c?Z+O@9xM%t$TiCfV9^Cjs;D5%TLO`c~vFJU8MUJR>oENd%LrO>|P!l z?KjpDNx!i4g;FdkkQX&zH1YWrh6n)mOX2Bh{5_m7QPaM$0QUwZ9bD{BTM0ce z6plLzwSN+@RrXOPT2OQvXUKOjcu>~|38@q{{d}yZ7$i>UhQ`D=< zQY}Q%_a}svKCiiO#Z?GhBmm){avHH2A2Xb5dP_CYR%uWwvPYjncn<~P6u=28KvPPd zG=^%#D>_;92&U4ePGfMxm_!0!PetPRSCQV^tNEgF(7Ui?qbV zO%fBxdjy(y&}_z+cARHHmIH9im}Z89(lg&5_~!Bd67#aUz+OJTWc3y6RGoU^?B-Dl1ehOZYTfY7Z)ig9w;og)!^Sj9OD{ zu<+{62?7<8$H4sqo*=R`xJ@}yxuDd4ewxr0$n2IXM34?jCXKG1bEe*^A}3o7P6cf+ zk=bzo4}&^VX39b{Uzkho*X`{DWeA2QN&*uXjHs$X0P=G1-{4my`9Qv~H*$421(mO% z$3Z5$bdZpTL@hTM_7VWh1#F;05Q^VO>Z=@Y`#*ux=zR6ID3X=?7k(m2&Ifwm%|v2! zVd79M>Dm$b^pv5%66%zs;a9=XUCZ`k5Ilw+297^Wtg4VZ_uj3p8R3Pv_U<|7F@wT; zRV*jQ>L+0gf&K|Il(Lijc-HnNDKO||5hhS{Di*9qyK|E`Xb1p_BpkhDY}h6i1fnm* zYi;O}v8>g9lI>iRJBm)e8nY|~jNt#$B&Z_qWOSFUO!5e=K2=VA83t`nIIAUbOhrYG z2ad*zr6cTYch7z7buot&W6H>kw_oR^aE!S>@^zcM!Ga0dR7O^MD#o5+S>0c54lMI3 zi9W49d029J^brBxqi(vU;v|k5eUKJnShg#-MH6x9E%-JSk;vyD$^!n4cfU0#=n3qR zrHw%s=Ov(K$=>UL?Nw~sU4oXS%&tot9#mmxH7i{$YF`+P)x>4WES-E&QemPlMonYfFIahNSVp+VD`;ae?`7lmV{Yu1Mmv{AGj2*<;vvSO-v zLlWgCw8U6u8k5eB@j)9UpV7VKKvM@5P_BL1F!_$;jinHaFXP5(2TS8 z>akdFu)LIpkCB7Y%M;K)4e$0^RENLVk8pXGy43(o$_hdq(aj%86>O)?S}%FBD9ZNyknTT_&lKu4Qck>K<=yh+HxiI+2Qnf+ zyoZ086q1A8L)SkH<=<9aL7xm|P1l6QQV&ENhJTMg!^`4WU~_{F)hQsVKVl-!{SCbzhWcxIlD&_! z-w}SBBTlO8*E#E_^b<&8EN^P(3cbE;x(FU;#8WSpZaTX@*m6DBX=$0ZzJR~D&G3%z?toH$-Cda*0 z!i|vy`%aMmTv>ze*7%bC*e&TcnXBq>X)C7`6Fbc*FC4tw&Jy3D-$sj*Ep0AkQz6ta zLb@}sKB%H$6e+WFpZ-t?qoq1G200P1x+r-Om;~E`T$HX{qWJ{d>w40}LkV%l=cL!U zbwXEY4BA~2q9r<9)M*1I>0x2sy~j0)~WYu>3VMk&h9ff?M>24O5`s$IU>WnM;sqqO(Sqkk{{K2S7Lqfh!%BM($SAx2} zK#RKa`ceQWDm+30OR^T*zQ+xxi?7`|>!LRmT)B?L!yKG%t>8^SF#!|uM1yW6r#)jJ zniK9vju(|Rw@XB#M)X3}Hd5m7WfmOm?5`Jk+^Frh3gb^Ckjq6JlDrR4JERViYA}!= z@5mmizYwlAV4Eo24%DD~{-*S7?8{gk%w{%>aWkCmYTV zDx&ZA7c{vw;zCB~G82wT&>7-3r3)Q62!#`frU%*x#|=#tWr03`bz&TreS$s@aSHvY zK!Bp7>XC}rK0d4QG$?a~oG;{}BP?I9MMe8h(qhfUD^Z6}7HTr5g&m330;Kr%< zLb2&!JX;u-yB6D8^Tg#g_%S2BVm>baeo#&wEz{G)P?&6p&cw@=uhM)89p!WkP8114 zwVI(cC%u19%hm~CI(dO%8t*J3F@ybqLKxpKlSYZJsl||c4=z5xL(#^iWTn?kt#iPg z+zc-1Lqo+aICX;Zowd%&#oS_uc9q$jxH&l71>T^-%H{eT99BHpX&O>i(YB2La^^fa$H z{8t@|{|CHhuH=Ose^sqW!_WM&gcZ!iuG@hB?$gy-I7YRY3K@^x1y}f!c zg&q0$2r~zuWd#SgIQH;D9`*Zg^gO!e3)MYeC^vT9lmM)3I&2+yxR~SuPb2YYMs(gO z!8I~nE(~wvKBx06P#i_VOLZQD0Qik~IZ0fVH!Xc%pSW!M=(iULk^n-7l&JDMx)vUh z882*y3zn#RVF+`r1EbFL)eC1cgr+LYd#AtGgn$e%pfbTHX7oe|uI1oK^l+^>yNMd6 z#!$%2u-(w?qT))Mk%OIr*eB1O9HV!_Q-0)!1WkvsRD4FO;;!h-;SzX5Vt;zda&mTS zB&&f|326Z~yEqK56RaBw(E;w8;m)lj6_yw(OxZ|0BFS((&O;bYi<|)wKO*7P>z#C> ze%B9(J_~&f8&_8FN2?_)bQ`?_5k@{&P(@YKCJ-=#O3v0U6BXS*l0-s-bTP0kHISGm zbGL?sV$!iTPW+}6ES}rD!ltz6?;ZB(bS@OjZiq`EIXnl0A{90Yfp8^r6OvGa7b!Gv z9rSx2f;^rO0Z}at@<3db;%(iW{(WbybD5>`867HF%C|}q}rdU1P`(pIQWHJ#K=NvnL>)~y_Hla6O54cq7Vd4NMyHP;PD9v zq}i1p!?g>rx-kJsfJ!M4y|;C_%8`ctqMKM$q}`<$O^?xaM878m>gZ4lBaks1-;GXY z6bY>uG+`exCKhkr)W}TB=0t;r&+2LCTD$nX9#d3zF2{)7FzalTzK6&%VDaMY5Xsws zqTy2RV69L>-fK@2CieP+J9LT zCx{Tdt&|f8cU_fz(6413tT@ZFS>MJ2*h`o?iAcx%bXY9#*m1oKqvT z0CYXS~^G0LN%>1`dC6w&jg#n7lmw~SQjZt*;6tcT}JwV~WZMd3_b4LhVbf$iRCV&(U%xbmj<-y4m^w`F?Y1 zH8q?I$%z&pjB^?~8@(QWF&m%~3MLHLt_y9%`Xb|$n6N;$)I&w>gG2=*7n@N)q7-HG zmqd-doZQkbh<;CistXql&bqLLlvby)uHS784wlfN)nnHifv=;Swft-j0Hr)e7BJr>FStSlca2kMItZO7P1)?^G z>B+Gl$Qjl(=R*N3?Nk)K5GFwvAeyQP=iNlXG(ob&9x4feWSBdZ{;zoBe9>}d$S7!i3?`a0B($&Wwc!Ygf~u@|D9IS2 z>Eup9I0w0oib@Y8$_`-Cco?@_LaneL<#bTVZo?V!&IU`$S9bDS{bvV?%Ruyuub~V? z)yYi+lpobrazBF8VX~*srp#WUd>}xH->_Ksv&?xkJ#hYj{@t zAFHI2OG*oFBUvgKyy}(_hYx|ACL(Ji7bH}@@>LYNF*!GdOfH~nv`j}O@FmP8EdaaJ zCEban$~hKd{brcPGQz5B2-ssUkCO3Q%#yVdHR zRCFR2Zz^aP(G5VWPt6`K#zEgLH7AFoi1a(TsN?lE&4*?XFnN|wKhqfsOy2xA3DaH6 zx9kZ=z;8z~a^KDFSQj>&bAl?f>=U%c9@Vca?{?M)Y1RSqFLW*}9=ekHqLryLo3ZUOjr>+S2TIIhoPGiyafJnaZz z^JI4HdT}-)1C-5bXnsRWpS#f)4YHpYAQ0IR8T{+Po}M!ABODTG7k;iN+Zk3khJhlu zGC->p{78Peb00)hm?fR0SA;~EWfARf1>W~o@bcY@&n_~&b`w?;1Wu<2Gv%(pK{3r3 z$W`5t)c{a7xGcIL&((de*M@m0lKUvaF3h+uFEsXem{e|B0STR3+1_~$iHxoJUA+g= zp*J+NwXgjCj4{JG;Dj=|i|y~9d=o26h6@P)a2jA&j8=MuXYL$yqwzQIKEEB-9Aa3p z0)+6pV7E?Ui`fDUoWVud2z1&k*UXV$peLZ(7XVgj>k8?Q3i$+c{Y&~f@@&M}ePHym zs+h#?u`D(Np*qI7Dmmv4F~TeMr+cp?gc;a|_v57@$b_w3y{&`BpW@Lo2fhswZH(ZY z$7#uX9MaA8*)BD$y%xh9sxS=$^iy+KPq=7q4%I6-C%mo?cDExeI@`@)AK&xwZeQD9^k4fP!&%QmZt6M%>{z8;}gY^?JHJn%9{ra zHiwM%tXGM^v(44)k=Km7SXI_Ya4=7m##|uEEa12iDc;GArKqSyo#F&dL;~J@JEi12 z6J=m9K6k5AZlJY(DHY^Ch*oGZJ~WL4u#54h3@uFkz22;Z;Z@Qn(}Y=U7nG8Ke=%~{zrbx*eGnb z{I8)CpS`j2x`s07?M`0NIDHzMp>mbVbkuMQHrW{Au7~*VK_`eNOG{s1dsxvDlfy@J znGb>sl&Q{pa-V<@E2g29Cpmp;?S@-)*%Occ-2e6U9#BmM-`;2%HKF&8^xk_BBE1Kt zNtIrtN$(L5kuD&FP5^}f0*cb5C`vCP0i=r3q$(gq5d03_`~B~IZ>{&vTAAPMGJ9t4 z*>g^km6`mM*?F?5O%VqT=U`K_6$wO#&hfN$zMiTh@7HLMZ5X22T(e^t z^UlC7W&0fiZV~R=ud9}7B0s&75xz(f^2NR~(q5(R*?jkI9-8qNvC&2z6X875iyj{d zi)19U=^#v<2fpUVt~ez649x5`Sa9q<{`f1%^xEy^vhgw|a7jBM`$y@xY39ibb zRg-2$;tKR*9yNoS?wbS&S+0tPC$SgMGm2WR1^zl$*zsWg+@I?C@E<$!D0c=LQ^%sQ ziwoTI^U97y`Ih*bf%C+LPxqU1m^nX<~9S2UJez64=W8(bSN*4sfjrL%9(VN1@ka*@1!8uao@b8IV6Elyo-J zZMK`y6%-s0B9VuCx>vo<#Zeo$cuH}dJLGvO9jWjg5C5pvo_Scy%bm~Y4tBjg%43-1 z#j(-fvKM`sGPko(zj<>4p>%(E*w8B~qw~QGoWq{ivGYv7&cN+UOC ze44t{mR{Ly=ZcUC6wH|wZC6KaP(^tiGG>I@Hd?IpnKycSvYc74Xz51Q)$=%M1xnC; zojkXmQG`zILupUoEmoGQxrbT>Fsj{T?ij7nmlyLQfA?ruDmPkNmqNzvIfu8SmP~|4 zUcw(Q5%%5wv7}z#vVC?x(a9s>$rOzrgXmiH^Bha0x4@Izf6bj<`l6$|-n~}&aF=ZT zU^BQa`EThpX0EAjZqB<9hL)ruymI*$!xyeiVF_QV5yEB^Z?-Ol7j;wOX)DQQt2Zqy zIJo2U%RQeKW__O_ZuQIip0eh9mKOa^`!>9no`I&5j=}WE=;1=MeF!E-x$JHP$zq)! z-}|hLh2ee^Jl-R#U}6kSzMm6h^`6k=;%TLEPDp&r+t>QvJ;y*$W2LaKZcj>zF3$uF zzu{G^JQEV1A?VztT60AibTxZlPudst0)OwHcUXip25XxIA8 zS>>F+DfF@_wVY!HYu2_*5n(S37{L3aXu`!M?_q?Cd1!lQ zaKtl<$cxC8Cw5QUVrCs$=6^4}mtFO74LWUE$TD$w@P3|ukavdpy8;KZX>h}?$LtLM zVAH;C&kNh_rC-4umFn(OEzu9eSv)Ojq9;ho;8|59D+Tx8UxYu>>b{FAp>_zE1`C(E zM0Zn=QaZHg|4{Na`+{Db4r*)bTdes0HE!{ce*1!j;#~+Q5gl)VM_d;)NM-%A%TnHp z$Ae|lOvY>6^3^@Qbc3w2t$(F@W!nN0W-@fwbHl$y9{ECWYYPG2mPomGo^sY{v>rCC zO!tnV+ITejy*U|*<@Zi$!g`CJhyA6qiZyxJ8puLP7EX?l40($FHuE&82>Lvx;j?_B zo>P^?Yq2|t=Ird(7UwAZH=h?2_=-jZ+nhdK{IT+4psZ=f$D6&HJehdS@Tuzv4F%(g zz-3hTxBS*hSA_(FOk?QLf-R#=G{1_}oBJ>5l%7V>_;H$+7!(_>8l3fUKbe`@4|bjG zoSs!&GHj{icx@QSik^;`pzjLURU!%1$e8Zb-E_HWv_V$I$@57z*d#1$mH1)p!w%lU zK}z=WCthB~=6pee77g#*+bvzpp|6VutuM6;yI4Ox%itpp*3uUk?s;9ZT_knlGxfdr zQ!#d-aq}5@yhrCVWr6SW=RSAGhig6b6e2TqMH-D0+ocotT~5)W=8kwxXmS76$s23W z=Lu6sjMYEr63U;T9aIbY_o|DTVDFp6wd-_6-6uF2(D)<9MP9q-<;FO;@x&U%=1E1v z+nb@g!W+TI^}jBjD?K)0BG(!)>pxIcRsIB0E^J(Vciyuw zbi{7@TQ#qyQgEgP_t%DZd$=%xv6DPN}WvI1+r0Eet zbe`UbJ}gP_8$n;b7k_8ZT;`BQ<7;kM=`A~}|Kp_2ULizHtj;i7{N24Xx4~?XS&$M@ zzP}UW3P0B+-@|hrK_`mpN8`zy^+Xb+dVd#3FRpiadN4mzLfqT68T?^w_`Uxux;CHd zg)OnC!i#5ezdlDOJurI|U8_Jyv{qWU?y$Z6wNQ8h1!5jRSEd~WhnqapU?$@a()^yx zL%m@>QBAj^Cl{xcY0zGoec{&u9{@2cw1! zDoUpcH$L*|YCYp3SK}@o5Ps8EVXhY%DR*rmJ#u#EQ|)qGa?^`%;|G=((VM&ljr03- zYXX(i=eUGDQ*_^#%ct_+eNS}4??mdwdRl$eGjXUmx=u$k<;qq}PCjYKrPwC(`>lQW znvCv`=5ANFTe3};p`F^kd-Fa?bpu52$(;xlnm-7c?gzRjg+-x6OYFI3i?S~Tzr-*c_+*UNrbH^d<5j601fOrZ{i(E&-utER z6XR#2t54*Ct}%I6f3DcgV|54nA#O8MH~)e6R#3mcQg;makstNrY0C$vz{xvbf;8y5 zZK@TKATxvpP1VCMB1iqnCnr|2}CdHJFYG zaXyaFd(^4(^pdMepS&jTr^~Wh_-C$ge%YY;yCGJK^UZZuudFa%dB#6UN7=mEYY{j4 z69%^+zkGIx?wWVDQXH2n|6^w``t$GX^UW>+`aC&z~X!-izLAiw3XR z743@|&X#+Jq_p+QJ@gV&m6Da1Dg{pd5Ddf&$#A~sTiUl`>&;Bx;@VJh}1mD+uI5fi=vTav*_;w0czgSa06JUvZAtespZpOzSjS{h?Id42)kZ(OM zYP9)FT!=oC`tCk|-P;HH%hq`%CL`kYN;!P2sd~K0RB`6)TcFQ$@!f5MqPsNXO`hXT zP0O}6XSRhofj%PFXDk-m*rSpHeZICe@3gUtDa>d@*VhfT$pKs7905^uQlRSlK-Je9 zS$j)U@xdKuE)+ui^VU&~zAbwmi%S>NwV%PVfdHeYSydn#%Vh`0D=({_`;~b)#LmG4{-*)O0 z{~&>NYW44Ku3YybYzmgYwL1nMD!#FtVExIA{bR~Fta3eidA-Zn*y6L=K<2in=~!pN z&66cP3;sQYkp?r?z& z`VmPZUJ7*;eNggCo(p7gqkhRks3P*M$C99|(MOHx?axa0rNAlmZJWODN~!zC;2+Ul ze0hqE{pz!=R8QnF8sT>d1P-^MJvWouZK7_CkSsbLxLS|gigG@&vey{+ogumze2+}% zvOS?}nB~uYiyz#M!uLUa;_$?g2VG?tMdrc2xoBf^7f<7*E^a5pH*Ux`R8Xj+lMbag zxITDeY&$qcZQu`-Gwec}c*!b#d6W)KiFm~QX6Ww95YyA?)7^SoO}cwsb6V#&=pWji z-+bPimuvmAU;bf39n074w>P4u-h^wZ{rb5g@rZy(miWQvZTEM}90W}nzHxK*?!ovF zS+B^<v6?ht_Ag@6#h~FXMEV)Zex(XZXS6JC~#o?eQ~yZwouqGBlRc!VjIPPSHAV zch-$0I)YiIqh2ICYW}d`S-3ggs`tl6&g3lvkKm5euS_igy2Q-;7Md`##Pgm%V|J`b zx~C^5!YO|}9{l{NedBCGG>kP~$}GooubBQeRfgz|4U#Y!ye(5CgJTkN*YN`-V#S>N z7Jk0+M-fgnlm_aCbWZ25->pzo?$30fwPjL8(4s|4VbgKVa!sR zSJV|Q)noV1Woin2hulE7FZHhx&aci^~_?o8>+;mBds zM%wi*G$Y!)Wv|nGqX=E2)Y86pRP$-1nB#cv@N3|QAM!pF#?i`uGN@)Qr(}QZOWrCh zRZ{j1eq8Bu|L2dt=O!p!yUrOK&kx->?Mp_JTMv@?6TPgjKRtRfP>5{1vBq536GXY- zb7zmQm8eI>*nj+fc+TBpeZ93{rlna*nJW$mKB#@ z2~kFo!VaBh%i4toD8ie}}WI@pqk=+2fU{;w5q8PR9zUq^$- zB3>SEaTy(|JCd`p>MKlGze~T$)RSboWjYwKGz+R7wNLj7ynL2;8`nXU=lzvM%s}uPF zd7;_JAfT1_$VCFjC1Az_?AAjhB)Kr-ZV1@&BofuLU(2FOD4-|PN`6XMqejb{wSi}= zj?fC=!QHE||1B6>0JG!Ef|82cMc?;MMY26~(me1)>0o2I(7zRM#UD8{{~+-9JYML= zK)e`7@M~PNY-S2U3A);G$?*>#t)9fzItbbjqS!v7GM^JQfs&`JXxR9(?yA=%JWN_u z#qPyFOj#Ah9&2!G9_XRQk^7C~up_0Q9PbUNWw&+xE33)tvD(S2Raj&73eAHb*mUH~ zz*`EO$7$~bI=}8>3|85o>l1DtFu(a zzp>}(zh8jk$&!x~O7Nu|6t1#sKTB&QfUd<}@?`CzvAOEmnvLZ6n;wn2OQc(J0_HrJ z*meSann59yaHXdq>YVX~YOzNHX;rVa`-uVXaokdsKIa>{+6M27kPYhW&WkUhv&e@ ziMA#gGee=jLlNnP`+~n#X!2x$>C?LW760 zaf(=L=X|{<6UN~IQq=fOE4P3~DtxJP{@|c58r9G*m7RToC4VoKO=DhKzBV|>f?Y~= z<>MzpvLyqcj8vr?HZ)!W2Xp1-7cG!%r2y>%Z0ZU&yvL0g!**$odNn+HwITXo4+607@Z8t^mk?y%1sSgI!1?MOgvh1>Yn; z12(D38#)*De*ums7m!syTr!B_96ijTs~B}(?6X<_yIq~Tfeu|lllBD%S; zjj8VRJO)T34uJ9n9nFRHZE%2&E&=E!_X0NSZe)OuHUa35*Jm9+{o|nSm^&1dS-Dxz z9MPVP?_FqzO13o3Z5~E$+dPdsDMNp>5ExkrR#uf`xG~LV)2T30qLX=na^QRDD>nK1 z2qkXgoI|?9dujRt{qV*wrH0Nq_a<{n>W;|MfwoZ$Cf=FyXn%~SJuO$dpJbcqE;aDJXNxl8TZvS^{wnug10|Ew(IV2 z-h_+F_1We1cp(92ME!)CYy39M{8k^MZN>Qzp(WYdW~sjAF`_0c(DYEf2_s23BLQk; zvV&|*OZBDJBk+QS6Z7hI0%+$-V{PR%!9j1=a*>fItDu1jR{;F5={b_OvKFz|MV%{o zIv`I~cqLQoRd^N2w!U1+`DGtoX%jtG%auImtx~URlsM;oiKP}9iP4k7Nz%J@^H7;( zCFCn<#W6cMRvK>&3DUc5$q<<30=%`j0#AT9iwzJNLE}BM);?ups*H`W^wxRRlhh~! z(Gk2S748Gjz>I9j574BDoAUx-US0JHD1g0)?+lBl@6X+FJT}1RaJ&Ng9*=AHqkjc& zBw9tnU%?Ofa4jMuVguJuepY#QloiDXVgvk~Cb#qK9;4&VX5a0^MUiX?Un!!m349T8 zV|dEog#FG0v;d**NdsD!Sh2#lJ8`ghX36YXf_%t9B_Pz5#Ccu`Z=kfK&8R6^M2+bN ztx@&^Jm*fRG*tV}7o=#Ml;$ULFUco@8BQo0&R4qEAzlnmc(ZJfstDI%1DDv767C-v zaVp@oA$EGaC8|Uh9RuFtdD8ImRthBZ4Y3eoXi&E9F=&cGh<1bc7NvZxmX$w#jwol?^99yE!8<^OVhd)5zLueYQd18Je;=zvqNsUr z(Gxkdc5w$)fz{761m(cYlU8N1+8R^L9bDg@s50OXIBlL$Y(SYc&_F2BmP?W930c6f z%{6T94R%FmLj2HeuT4!`H=fjNyiS1x`z8V{% z{+?@i4fyCLy+B(qV>J|;=I^CqiJw+pE4gdQ<2KFWor53bxLiTG;$8R zj4E9Gawo9O`gyQixa%Qhaujib6NyR7d2pisANs;(81LSLGjQ`6^G=A!xSnf$a{qhC zJGmF9Q7zcLMeaP;wn%kV)wtWak>vQ4E7DyZt*Q*r(k1Z=&SUZI3P2tHPC7Z&J$J1f zNp=O-`*MgrNDl{SqaS0|`YO8cVQ#m_U^NmYw{s1kva%(!M3NENZ|=)O&4N{4-M56+ z2$tOLxeIO*0N~Yq>*o=jf4*tVznH^M|E@OPV4xPA9!rDXGbPcY#$vl-cF;`zuAh1~ za0W-&{Z7|g+vVIun45;2?~&tpeMYQo$-VBfNYi8Dtf%`@KWL-)=kqKN5ID?S-pf`B z{63)Eh1kjgV2>CtnDL@cG$Z1Q44WO13IhHBDFRo;gN$W77I(FVdbZ%6MKUR(`*-}*+p_2+k12i7RK zHKTzqUeNvJBG}6gp8cVj3Qr?yNIxHeI%$!(?LCa_TzrT2hWQX3S-@XWx!?u;VA0Sm z480rocM;s7=!o`5CRBH`1Pc-wxV*6eAVZFML9aKA43tAmrQz$>RJq$=(cF~Lzx}aJ zF0sOXAr1Q1UN-;S#ys90M36KYD_2!H8dyVN)|vAyfU;GP^0<9<$IM^QxKBSZcI$`B z^B@NlTGfk5*p8raY|skW=&uEcSVF~ZHpx+iMX5P*C0z@yc$T_q$LFuPZ0<77&y#z_ zkU2+g#1&ZfezvdJ7z_!Z!ab8sEjC+?mVI&rx3z1I0p69>I?9ctDQDG{vf(F~`_3+y z@bv%)9?UZLRjN{W`H?wK(*Cv}d*#|mhQ`m!j^rNcp{l&{M7JB%qwRXVZwnvXAbUl1 zA&5(n9nlVw+0prH?ZzDe!sP+b&Vh@ilvEgZAcRWYx1}TnGZK=W{`hed&(H02CZC+n z^07(Wu<@-)eEUi75aqexFr8$WJ|&u~XAW7=%tTtdQ^fP4jzxtM{9XM>UBwFBymi1Y zzv;1JNF1+stiR0V#Q66}mEF<>vC>^nAS?NB-(lu{`!xXiUSTdQMFso-!E@{upysM# z%JmiAQ0L1jt=WhiWgi%k%8i{2^JU8lMr*sCNDe&Fmh}q{af?s(CvSwaU5mCPHE*^CT|HhjJw|G&aH!fR_7nijVT! z7(=v=7qjol&&D%-DTWVS&)Z*A5*WYZww?tWf>RY=mJJp(yEp3B@u>_9gHDY08jDp^ zx%$flN?YtjkL2fUuiw>zXM>}eKZvPs(Uxg1I-4J|Oh}+9bhQ*fN6ua5-X{G~lCm3xmw9xn7m0Y562AgdhG?1;tmv*CM|SX<&Ur?)4o9lGD#)Ija~I%5MoO$7I8+;+_&&xqt+d5dXc9~Mck59@WO5QQwqDqwaPn&OC{Ot&( zba+xaRCSBm5o64-;<1LrC=fHG;1To8~E(F{MQL_Imwt9Cfpid-?%uq3I9G% zWo2zRCWZxvF-$F$S9-a#@s|j%NVBp*HqUkb+X~T%-u}d-=J%OpcHD%>Ja_E$_ZPx$ zl@kK%RWI zO1;w}MVp*qHe7=bTg_jedbSXP%>mh>g zhfs}QV*qnQXoN@<% zELLeP$cIA!O&Itl&8vz(UT>#_!s|9+_U8+;%E}AgY`vWr5`32--^)lEoTYUB`#m!3 zv5REV;$Kj>93boh2@QJTts=-Zib8c(M&lP|#aa*OMJdULV z;n!32-gOZRDHZCyldBiwcN`}Qwv@|$uuBje3Vo&W|I?p*X{_VPH&-?uISz%s6#H&O4MJ=n{YJo`bk zFm65|k7vgW)v_U>_c>CXZNxF_Md*E{CxEIN;nqGYAIZE`bXN9@#C0z1G1sgY<7HhR zZLgTNcOSpfN1HlO;RPBJ?-zh%OXjv{K;;sM@5I&_*Q|Dm-;Uz-m{{Xhz@ar+?`6YWDgyqVPP z?^~F<2yEGk_Ag<}vC4u6q~CqRzc2!y$TNxplS5*liw2qQ`0R4ssWI7uE=kC&TmzytfBbYb-(T=F|2wC|7mF0`qs|Y;W8g+=qIQU)?8B?$&DHM-AHQ z`YtWBeJm`Pc1}OjK|SNqYAx12vDtJr@?!-cyv%zu1Qz1rc=;$QN(6WLk%l&$_qCmU zDUmE<4w*Ul(Kyo(oI|e4+dv=Jc%wt}Ku6`-K~&l-^I&Sv6aE{;ucoRRm)55X-yAhL zaS@+xJrd)1xu%gxE`UoM_d87K7|qpP5PN{WAf9c)&Sh}r?Y8@1uczN>-lq27-R92U zZD&C~(~+7Fra>-J4tmd|!EzY%*rFIX*LK@KU@fjBbl9lOk&xp3+|}K7HmrTB%h@V3 ziiInGS4rzS70{%P#MW-9UYT@DljoIP3k5z_*R8`iQwLCJkB+S!o)1nwyMa!l_${gG zFYCQMCG=8d>l1ivnsD6DGr);4HBS5na-x0s_S$p*O*>suVzz3}m)y$TB3bCqFx$7f zZj>xdL-DOlcpvV3|P%x@99L+Que5Y=)ObP!(ueBgO zs|h8)K3O{3UKve9&=HhzYM`+kv1xnb7pFgo(q!m)oIXCk>#nWCUI!W!+&T7ZQ7yLM zskC+^7mg)Z$duHqdhAz#G_EkH62kx+=n5(hP?-YN+;;EN5XQABNwZt4k4`YX^8BR| zuCPG1MOBdNB6>mPy=Pz4ZQ*8k32DGvG6rauDCeU~8yi^`-<)JczG)lNKi) z%vbHnfolM$L652;ybPuB&l-KcYC#x&D#!5VEpb$*aQ@RuO%yurkyzs$#B^n!z&!KT zug_DOP04520M#My`1Zulon-9YQ=5+0Yi3J;uL9N4R5?fO{Z*v0_WIB1O%`vIdEhd- z+wD)xBki1s-3jc-j<_0r^7*b0F#57b(=M1eGv4*Z3)vhddE9j)bsl}CPFE>09VLJ( zr|m0nAly0tfW_}d^ZH%jaItqI6~L29FyZBS6IQNDBIHZh-^N8W4gMu<-(ENI)+7Kd zXg0g%bBDMyNkGe8J(y(}u~n*mnf31T#a>O<%hW}+XIm__k~vF^&pfM5hbn))nibC{ zDt)D|G6UlcFc)+alVDi>B;W>9LW-t1X)oJhr?n-ye;NZ1FP-IGY}CXEu?peAFrYxL z9xVw_MWC%Y4>y=GkvaWnY7e>sot6%ed*I*tw9=5YD$Sr?ZnlEs+lgB?s?qKJWhv=f z3d`ASs~OeBM2v6lZ(4NU-|4zfI*B~8!b(!zYLGBuVXyQ}KUMOC_0-ZL;yM535%pwA ztoL1lysR06-%#S7IgW%?gUs3bQW;nTw1!t)>2dP!s=wSJ{2jz7ZEuXO?I3aQ8(zcq zJ(OPgfDjTcViG4uMJPzyE2)bTyQ@qSqLh1?GL9@4&X@fAoa+eN_)_<+`jojUL5Hdm z)g|?8TleHo$=HLlEN?aKy**CuUgATa&~0hqeylwl&A?s6B{Ioz#4GJNJ{&bgb##ur z$cG6{4kHSMk!i4h?Tp_@0<5C~$?F%bEc(H%KeGw2VBYl1WVmjQEz1wGR$k9^_ zlUxqA88{~KkQ8?T@K|BN1ArcbdS_V#?vLWfF)b@KdptqtsnqgCif-sAW{>g&@q0x9_W(>Cnr+2>N~y-WD$=5O8y^P=Dn`^{V-= zWY3D3#6AVe0rmF1AWBW-PHMj+N=@ueYGQfJO?h7Nn(uH~pQU$VZ!$xAQp&`N;#x>g z=MwCqBnwUNoZo0lc5Im5ZL;F5H1<$Q<8lXn?WV-L6se#+x|WhR6HYE`|MHQNqPTzb z?_eb~%3+jiJ-A(6R?I01DUJJ(!svj4cnxy(F$j27H;Ny$&wrmS$d&5|tax`H$?237w69`hBim3L_*WU_bfJO{R$(DcDs=!wMY4x}x7JoJ_rt-fn#YJ@ z43t9))=6YmI>HTgA}R3Q>ym7{E%Vt7kdH&iT|*t^s``bopx3WB=|n?cmm(KGj(vV$ zitTHWwhMsaNOlb|X85LE!zho}x6;qHkg~6(O&!E=h4cBi@$dHo@GTI0_U_0*$pl(s zEvHrY`AyAWiEW|ov6j)q0pMZyi`ljxhNcbZk75#AfWPUKDX)J{u; z@SC={HPcFrt!j5bUuy<|Y;}?3-dC*CFw=^!isEc*17WbGqVvKJP*WQq$cT+72OxRj z)>xi51r9TuFW5Qp)@8-_7wv2-OSGoko?jfWsgLmlikePyi{0`bLGnyti{#d=CS*{zd`4cDCyO*n_9s^o%qbSdlyHBaxW*CRNQ>8J z2}BGDp-$%WoKbRdb53aIbmVP_m#}6~CR+9myct>dB*>LLh>*8kJ^*g@b-A2*N=c6) zrq79z^u2tIi-J?RIq{39nS~y_@{Ss?8zva}eN%U5?iH)5crPy()@?tZoLhoOA8()v zK@BrA_0NIzCmNl7php3KQcHY9SLVIv8F3M1nfbDUUMAe=mAJH!q4Xmg)E9ZF^do*$ zw;TXpurXH_^ibRdjG;_mFDsfAvGKC8uSTgIGWBBP2_jfGS;_QZ2<{hO-%Dm^3;SXth;o_ar$KpSpJvl3?ss~79Iqg8kJ zQ{F@Lakcsh4&{(ebyzzL>NES(70;Vkw(x*7m@T~VNVSy%+vU6X^>uh&gJ1i*yB`f~UdM`BO77@ny5WE? z^aD5XOrzo;bW_ebL1)hLbn{JPB7D0=-zK9`Zt>U#*0@E=*j)|RV8c}9ywJH5G$!OF zwh|N(pIk7kP<)B~wtiEvto7_gTk0g*6#ui0SsId3KOr+OE985o`sI!V)&=@Yx$D9< z-Dw-UG;-pE*2Ta6r0M(a(mJk^?S6Yao!@f8M8PowuC%kPOPVSDba%$WtP4h37u5bI zB6`{XUNzpmb!Z0lI1G!V-AaEL(H>-y;~+!hW>!FPRK{^ZV73G|xmCzRal(woFUS=z z?ImE>)7&0*OgM$Nb|bdpCr13`?VqYFbVH|()1#R_41LO~$lPm@ z&ZU`Nyeq4d3 z+vpy919ihGP^#H^8bjhjrN!@AZ`Ri@@FyugRbjv%@BlWf zQ9gQrR2bW(>y*x}MpUCC1(2Rc=~?*|{l!@8Yyjrf zMfKd!>Ff=JO)~k45h~p3Lpm8>pk9Gjs`RM&D^<(52$|c?hP(9Ftg=G6BdGCx6l^A7Mip7B|L{BiQ49IdINmebawc)rXod<3WP+XA@zVYF{rSLj06(-}Ke^ zM?asHVVxEMBkjWbIg$d2g)fiHvCIppbu&owb|xFYREpW@mU!#0Zr&GcSc?uOn{H~n zko>ENNm8%Q!hO0U169;EfA#~f02eRDiS`m5Xl@5!TttXcG zeeng$ju!dU(5E4{9kSa73Ua*0B#m~fkA?@whhO9t99FJl9^SoHDRXF6>lqQ-_EDw@ z9l5NAv@uNht@`?L`N*Gl-$1O@35LVs`B|Q$MxrAU8w7X1CXWt^%hFj-9vB={dSNba z-bCH`!>gMjLASACGimf}daSXsyzBIqrV9bWLmG;3geTWev+>}xch6`$V7OECRyU=SCC z3jr0Yguw6+Qi3H&K7{B5HbzSPfr688jba&0`GKm9i1d(vm6;GG}sEIHFh3HiwMgfdX{7Nc857Z>Dcv4J2A$?VoVFrxKvQVKxAUO~O z1Xl-9f=H1-L39Hspc)hq5UD00AkZR#f{3+AARs~=fJdkc6l8j&5D-EiFby^Ulqn3a zQG!7fMqEINFb25fCKOa4I0`5zP5A{tBsb|nAlNOyM+!4hF)#>XEhyUZ#(GSa=58wiM*-lV6Ao2MBu+h z&Kc<87wqlA>FWx(#2Mh~=J(hots)nGp(Br zX1Yk@|K_Q=`1$e&aRvdcg8VpT0$qbR9RagJfYGZEak>M>gMdDQJRE(2!j;po1GM2fMxhhV4te>;r{yocyU%R?2Qiclq}GE{{W3Wh*cq1T~mP!KSAATXV*I5lweL6J3x{CqlgjEt5x*xx(4G1 z_yn2|DjqnL2PQ@aK@h<-A#e~CF9JqIj_C|Q$RhZzmJ5hbiWEddjkyd!$Z$|o@k2-< zMAUpRPEs%c2p9nu2?7R(!6X2h0%ID8pp)i@z#$MQ82HC5YAOK)_^LGzj1mqaB>nHs znF#^mzl)wqkO;~^#gEAkL^OgaFor=0CV46zm>C>QNc(@a5v0TV&X2y_)(4pPwniV7j&cj$lh3!x$hgTX*6@PEXB7X;KqWc;KcQZP4+fq;pKnE!vd z#Rvy8(?f{CATXF+fD|MMhVjBd!e9iHiU&g&jJQP$tZ-m1Iaq-y6DFZTAfUul2#ilK zf{BCls;jH<62f5+(tm24O+e(Rgy9fkDt64XU_=mD1j8JH@CCDCl0p#Dgv4M32@wtE zT?pb48V>Xc;-&#+PaGI-hD7PV`0&7pAVf4mARuADq!0=y9UKgYP!P~T5dU(Diik=A z0VW3nt=Pd3;NA+1jf(Jpco6a{t2~rMG{PVRh!jK#r3DJuRRN)egD44UA;A3&4Cdv) z42B{sl!?I*;Kl^vqoI-nTmg~8sHDIM2ssQ1qJkozU zK_EpDIc0F#+;}*N2sKy~Oh66?fg#jj7>EK2jExFH1I!2o%~ddnXaqq(uERk@AGbIfU|G%*n1|PDwAm;y=;qGE#3|Em+g zL04g+Vu3J#VL${R!~j8v0D1=4P%1VE@Hm9qcO*1#+p z!u~4{d0|upv_w!U8O&ukf}WNY$Yu~&SL?G1J+5SmL;Qu{-Fcs0& zG+eC$z&Un|X9PkEOpIxWK%jW#$OsjnWH1F786g==j7s5O+Hw3#JLxOSihvCSh9?q% zM05Vj3mA~+R6xf4@6~V(M*2U2{jbFWfdh%gc;(K2B-da}|EWXZ(Eqh^uhuDq8(1R% zo8jLw{pT$xK-2HO0+=XGNg6cd)Z#pt!T2i>r7*a3Dqk bL&l2vmPk>`IAl0fGxYHwfRj^-kCR_UQh<+-mrqt&kdI4HQc8wHLQtAlkXwM0 zPfCDWf>)YPfS-d?nv+LXLPC<8k4Hj6`ZLNQDIg^g#V`c@dBe0Z48g88K`v!M`oIl< zln2f;-M^djxn!n|6ZzB}#LDEdjP3@1{8@qy4f{XOVoGZMbK9Wmj1U8I?ry zz|dCC5a~vKCXCW+WMRDVku>h2Xqx2D!q#Z4ZMO<%v)92YM!Mii)uLf)^qR28Lx-ra z!_u4!tshS_(9uy0+WOERl!%fkbOxP)whD3Qr^iKq-SVE9yIqOqcmDYOEk?EeLGpw8 z2Rr+Bn~Tv0%IB)pL_No%<2>h`$QHc|QNWS6jrJ`#K6SW@+!|<>d=o+nr>I z@aXxMy&z(3nSJN}ZX*fG!iQJX|M4;luTJ)36e&8yDILfg2oe?j$J33(&StaoJp8O&{GJ#2(9-whKR8i`m7SQ< z1uJ=@cGE9ry9G6aOlu1NxW)VDDwRdTnz1i2X^dT_6sBJKQr*M7Rb7iPH^k=Y^6mYZ zW&c<|Vh-$)D_D%|iT2{`h9|$Clts?IS-J6(uN&3{;joK*^otFClhT(NagczbmuTn9 zliwjzx~H^uN<+4@#AR^3KCTMUpk8ped;KUu%Y~w??3;q7Q@dPlCEZoqX}V4`6~Ch} zots|mL7l98E9#L7iu2E03^$AJ^tOQ$0tI>ZYmdsMpKBOqodeMI?HhmY%+a$~%tXl!(i-3aU|`-Aaqjri;ntZP7A{z;iTllb*F zzG9w17&X<@x4Hhk$~4sp9C4UZFXH zaA6}bd{oAY5qf3ohPHKNfu;_-h%h?4%9OSBZAn_vQmL?rt4C$kHCc7@`Ed1~OA?2M z6i;key7MBc!|H8`0FjVRIMR&m7JJI{TIUJFpf2t?@w6vaf48c>F`8QBuf@X~tDN0v zvb}2(Pw91MP8>uIaiDJNZ9QLpA;NtX{)wQGDQ&bbkyd9c_j>nx4P`lCIVtAyT`K2C z34CrwUeYr-Q7F;(?tTQhM~GNTVMTb+NE2FV8$0;+Zz}li@aH^FZDc>6!#Dbgh_&P; zy&e#in9C&qniy!8xH3KIWD_wz8Dza*Uk=U@xrg!$oliMQsta4p_;N5&r`}xSspI(VT7cfj{&@JnIS73b zXLru0rbPWi15H3EaPO>J(BirHS9nB`e`ggm5Fr^?94>ca05RgEUSZO+)UXAKcdR=|hIahV_ zc}77py&&oH=vbt8z?YM3WsbRMrJw5V#K_XQ*bT8bX^3TvR_)~mZ%a;R96n1B3fuTl zrYkiJx=odXm5h{|-=#iiI4t4dexzC^XD*Nh8D_@zO$4bBbOu4V30C4j_0cv=ba6bG zY9{H2^MTO9AR^|X?Z=Z;W@zIG1R>FEcFFcXe3NCSsV9|I_G$z%9}bSYuR73wx%skF za;IMBkhK{Wq9!VY;?*d#k6Sztl6Key$ayDo(naBkm*el%*HPT;360=jB`Dbz+=*c& zBSp+(WMJ!u(E#94OH(LOREWzl*}g%X!$(~Yrh^tzFdrx;3n)eTcmAq%XNkMI!M)7U z?vXBIV1IQi9zEWZQ15bPVpy3!9+9hOcKPz>5z1Bpu;Rb;X7>J=n`Z@;xN(2hxh2-6 zGlxG}O0`PR-MU?d9O(XMkj61w&YKTX$4vjX6blqHMv^@i+OwJ!uFwHGZ=9&*rHAY5 zU?(WatScA^kc(A|lU5x=L1e&V$21vndVQ+Wzo(|&yT3z-$E0=rD-;ScIOG>_OMmcv z$chefnOQ##mECWAE^HD@G3>(HF54bnkucOvJ-A;|H0iS5J!gN;Ke-{sxiy#&iO47j zQYQC(F%JF!(^+h91QKJM`&B0>#LJ4IR0>20=go*%s|U%7*r2mKA^IKn&!kC`F*{R6}lyN%inqsCX8OZp?b+U|D>YKsB79B#C|)JpM6 zt51m-ObksPi&q_;wc225mxGyrvoF4XvdKSl&P0Xjpim>Sxt2Ctm2C-&OCm z4#NbGSuD3>KFr@hj`vU{_Aty0?tGHUb7OU!2Deoq`R&{oz+Xxe2)|-wSdu`k)J=Qi z(8=G28-aub0zH+RGWrP>^haTg@i_U3S7Ch?0BkkN)gZ{#?YXT#!Q z&-@h?75m+u+H`K&QGtu&D$e|~|7M7AFrELxwrfX2&%=d-NV4^buj5q_C$~I6SXYlz zzoW>n1A930NxL6HbYAy_*9zFi@m)&weg9|_iJy8*8_Vst@d>Xtp{lm?6;0pAxUFD> z3Wsj*y+~xPd{eG78_I13A(44`bCli`O@FjHdIYe0=y;g9R5Y#KRp%jVrkQPh_~q44 zyLj*D(cbyd=ET#jo;lNs@tBqvwOl4gs+yq(ou@r_$$k;f>AP^A&9=k5lDb9n&z2@A z8(2g2UY(yfCgVGksZQke31X<7?*6%qEoJbSQH}jAC_%paYth6o$bYuGjcUK(Xz5h) zoys#s;KpMw4SsyqRyIy>s<-oyHLG6!hDEs5Rqp9CkOf+rGL;sq zfk&Ma4kR&FcXe2L7i354Q_E2k?UMB=(1Ix&i0z}K#z{vEWIL}jM=?#RoGIdU?_nznbaQPn|A@3Mg(> zj!&*Q$P8Al5t`FitiJ-d?#rQOhZy`U#7 z7D2y|4SEudCu(#X_zm5DuaK`Gk0Mi>CtcOymrw3TfcRgBhC`G#AFsXe)V42BS|7dK zL!d_w7xg(BcL8Upd>c(W*!lCB7-TKR_Ue646)BV-@htE7Egb{#u z-U@_|pe~m7TYwQzUu~w^cv!5O#uR`d^MjzCBL{S%zo^3dNnW?63+c+mki)D2BiX-;|ix5%~ z^jCM_HUph?9c{^XgY=D@OcCczK>`BenvpyX0ph<4>R&;Dolm-U=BP=d_JblVCvk>W zrXmNWzg3kzAw@V;NpgL-b~A#ERY)>Get?RVYK*Fpp&8s7371}si_(eKd`&SOU9t$; zYoD{Fjt&D)m&{RBPQAHkxXj*|&4@3~dWi9STy|giUv(Vi`_BY2vp*+x2-q0~ZDjC+ zKz|d`wm>@GXA`HbDWr0Wcm&mP=*GOGwzx@&o#4C z<|avRLL3k?Jlevg5mpweQ_aHIu1A0%tj~qPZzwmbu%<6k0n!Vw)iHioGT>iW7XPTT z-|Z8n-8rpow88pxF>kDutjYfSJIp0KPFT{ns8tSCcJ@xrZp-Z(8a43qC3xoN3;44< z%jF$)RoeFa#7<;kPyW`tooBRuxMN=Gr}IBuzxbxJY4KPP0M!+6?);PjtAjH`IZ5QQuThtu{q0HWyp@3{=KrlAZRyw;D z$@mro9^aGL^E`-U*^;cLd?_nWbw~1(pm}*SIgc2@e;VQiSF$AejuOCJyM!JeqVh!o zBl#xkiWsBWxAH)Bw|GJEFtO6T&qbJP9cMh|Ow33uW>~?Ey96GE`xlGSvt5oeZKb_o zpTRAt(}K_DUS!be-Y0)R-z$7(T|&5qxDVg_dEG@UW-KIf_6R+K&;sd3xDO4Bdx-z=u}$RuW7f~tuIn1< z`u$tJu23%SA|hUpfBpJ2nWH@hZap(eYRg&P2VYGMB*6=pi!}c-#sUR0z6s#P`YV;u% zVcxE3tmGIfK|C!|8U>n)uw3|tXv?rHWxR2B;4Nj<9kHIwjU8|nupo284=#+bjNbPH z5-**{?b`z#C>~?Wh~JHNG5d1ca*QJf-q6CP7a-mY?5ET8_$%QuCI-pwEU z4+`-{MU7eah^sGCa&k6FQXq(5SIPbeNVxLAk5=gDo z#yYpI53WHe!M-l{m$?;G;a3fw3!0%`$oEY7cRKLwCWcgL1!6$e&{6Po6il1XVKZ-Q zi9+$xKE&oDN7{Cw$x)KyIgO=l8mufqZk{4i~$C3 z#L6JXcys(Pjpo`_STNl;4nLy{TF}9qBYouz4jqp_))F$KvTgZ~Op@FJSTDf{#OWSb zFV;_N!h4B+;trKE%33_MTHZ>si3Iqrkuz5-UI|$>bs_?_)Tc$qQv=J(oPjtk8$wht z5pEspS;1Aj#&C7)Zre(_RQT!tMmW9D!p&89eL{G+>dmN}i*FJB3!-{Pt#nb+vZYG_ zLE;)!OLE63ho&^^YTJ(FZcC@U;a0xZGV!Fz6ITYEBJ%tK)5q;205g(tQb=H{*Q-MX zVrrpyu~O+$r2tO#)fB2!Hlg;7|HqJACv6FmvmkEXU!9M*;~YRbd9)3i@9jxwX6U3L z`!Ru=(nadW+#nO0qCS82Fr4UpMWC7_l8n1xB`!vuJR97-Kv^`^^6{{{-Kc|;;(<{- zVWZ@^dC^sIuOh?A^7gR7cGCjLqn?Pt|2yZf7v+7UesS? z%G4V)n(!?I!&+lj|TYQzzo3WM-Md-ZscW4h*?T9N$|C4Z+`ah-hUD^Yf zRd=8Z18PRrVos?Yw#RA#7vjE@;GJqr&DwrI0!Jxe zev(e!+9x*#jnK^+zQ05{s7e(q-S9RG;YjCBqeBMOMsiNBU$eFgOT_c#!`*9Ql%l>o zHS*EG?8W0v*-BAVUYp7izX4bgtecB5wUjt4n)t%jiKbR-alyY}xcM9e8Dv0{d=7|} zZj|xULWnn>p9!dW6GsG#CM1cB2oAn86j?H|0ZRiR2{IJxM%LT;tpN_ z!4a6bf0J3#D9LL*nehPn)m(h4-<8zwf1%=e0j{CgX<Vl?57=`mt$G700D;!} z)r(iT-`0>wH7v+^;=`qR{7&uv+!`4%!tgZ5RY6XzN1gb+x7O<_+7?QsUWYPqRb}_c zeYte!Pz1?0+pqokrN8pFwW)6ZhK<*ky=hd#io@Iqngye^Z5qI{H7j`18{6;?1+RMUP1V>Y&AM;#fPTh&IuFKIAauzE(M*9|j+z4Zwl{_<2N=#1#s!g(!EAS${3T^K0B$tQryP{uKYah#PY>ly8r&d=AdjY-H1<;WJ095 z#T$&IY;oH8F_H6)^L|6vXGGmjd6^GY%bG~L1vVpNM9ay`pSnmOo!BsM#<0=={q))M zP;x^EI9vH%)u5`|VvZhWe>%8i-aB*L^NW4OJP`0K^)jv>B?b)u zeT3!1e|eX9nf$r}P>Dpr7>|SJZ`43p#dL+I&;^2FWMq(?Qm{)tCT zeQiuenz|w5`u8ZI-_@NIDNJDq8H6N{4$}}*8|#xZoHNBahkE)~`H-MeEj0iI^asjb zj?M$c5dL)SQ)|adFFCN-W&+}K=Wy-Y0d2T*EKCs&MY-Yx;Wh!Fw$E~#7iiI#O}Ynj z55Uu;(MPeD`9wARa^^5jjAwURob->7^@7hij;}x*iHze5SAIBZf#0}1tXm? zPcWL1OZ0O@h0@NOCK>XyUujSa_^CV>%m`>^yS3~?!mOO!mo1P>8r&IH_cIyi#{9vj9Nd>)%#|C~Bi+t$ zEyGjoD-{4E4sLw+O=z#pseOyb(3gcN0rNo5UHUixAr_%rfM&M})n4>Jq(+XEmjfi= zb4>gs2n|jzGdt4LV56A$>@|9J#WKW(1QMa6r$Qs7r3-J+uc%P%|g_%K*Grh^H<@ zvzNu%izWpVr4h(=5iR9@W`983|NaE17p9sz#%-t$%tz0u_si5LM*um^>YeEQJ$uMf z&r6)zMi1V?vO*@1m^vsN%|1V3d{h;JcY0vo0$IYBMOjSeag;A5AMq3yIZb#2!agrz zynqenzVQ>MrIQ_t7U0cRj*dm~;Fqz1eN}PD`>Idp63T0Rz*5>J^d&C>G#+gXeULY0 zDg7=1OT$e#?{U1CTzivWQU{G&*<+JMY9qsd<#CFYrv`LVnpU3ubC^kJZVmOOB@dWZ zrb{nC^i-{Uxi-^L3Nx7Myh9#rme{5~FoBe4i;~RgyVe6nNqxctnDrlMDnW==P=j9q zjQVIONnz_aBQTMmuM$K9*_dB`YQsA}?ltL1LFR z)KUHiq3Z(1i?DbW zmih>V6B#i|UqUkAW=UvXs!a6kB=Uy6>E7od?YW04T6839f)he17&2UP!kX3zRnTE@ zok2D{8xV@~jpeVQM3y!@-AP&L_x$KuAmP$pXo-w9!uMsltot^&^wAlC9D87k_sID1 z8RHRSW~jc4&7YL-K1295;MO(5uVporj-8L8>ozug&xk+erzo&u+mDt8Q!YsH2! zez}9vzA|m3u>y|>WwVUK0Mkg7GcCKa(wk^}!DQ|ndRQ`|=b!#d=cM@BFEsn_VdJSR z5ckoan1bfr>bF$T2TZ$t#&~Ze;WfYOqMlZMTD6e(C7(Ely3hOs$TL`I_rEnfunx=7 z(Q05%`%cdm#WbhVAy^c3wBerH*5NACyk>)HoM5j1p6X@)kZFbflQQM=rVS1%`h*mC zQ_))FLmhZU$4v%8uY63+S=CXmSUQ_57iWNU$eT+u*Tp#w4=2MPT$mrq1OzR&;%ryL zSS#p+`B8@Gfl~1$-q}L*QlG)CG#pALm34?jdxBrLG1|V6?gy;JY{I_)b(82{U~&V> zR3|U}lKdlU>Z*npbr+dTCZD04zDvG!m>lypqpOC8(;z6?G%{TRu07()ZNyC*y zQ913_p_8{SO}xDMCx4kUUh`sDzU;e0lI<6}%5qOifJ8}D-yOk^uvXKKP`u*#^HU~a z`nWR$aj;;#NN)HTeIN+IM)EmJf)wc!%V4x9?&2=7pCl8nL=ATF$_)RoU$}{KfFE%3 zQ7H|TgkZ&lcmran z540luljO-6s%7KTAqH%4d^+&p4@XO=tyzE(-)ChE+#<-iF~wFsctfC*E<~}HfWf9p zg6E|SMNILsV2v6x0C>rAP~P|fJE^lVe90^KFqOyM&?~z!l{wx8DvPMIOSWKv{8XQS z2RqAh$cP7eb{u~}blKTEJE6(H(ju2!i!hKAZU}@vt@BYoWbHhtf$* zAX^sy1ANQuCyL-&wmxyM3t$ucfL;3XUsoLXdtUbLJ|=K0CwsT7q=%n#SRwFm@}M^- z*p*E@l}XhvOuQms3a*7lBQTsYMJ-sLyh>~URpBU&rX#@pq9IZue7TucwajHXe zi#g&13{szxBY*@;mSiV!h-uss4hMu6S909L6F^hLWhA)$lucK0O{gI<5StQ&^Odc5 z=^bR2)za#W)QR`ddD$)c`B78TSiR0$X76~g4Kck>BJ{Ji@u$7#inxxJj@GMf%FO(X zJALk(?27n{9p}vHJhqWV{$P|R`VP?r3$a*0NiW{n`Kb~4)0K*!-QO8RYG&Ci?Jru| zKl2ACWfR@dyanZSGSG=F=032O(DR+5Wb)wJWb)wWl|ae5|G!0g5$N!BQ9k$j@h#T= z!E@dE&xrRfL`Xe$pQp`M@-NqCys&Y(rV^tDi&AM+hd@!*CMUAWN>+wplI;7 zYK>)&UhU;D_^)@pE%4}CHYVMwvd=UFHMrkQ!bzIE-)5^o zGCW1$gACg6;|zZI!!}2H{<((ItX{rk=(EdqJj&(QewYb2YYh2TObIqpxz++Xm2s|E zNdqi;Wv3!t(OP#&BKeg{L~_I2yvo*gJW99CfnSkBzSWMOrzvn4hc6y;>2+;w7E=F# zdU7F=IW=)mJg_TUaBS`-WyKrcjd`U3CCpl<{5z&1vX0PM>{~vhxV5WIY5`{Nv2jklVAq>BEE=n5uF=p-;C*abO2g zXymI0a8^NFoVU+TU&Vcbe0;FF0{ z8U^2rGr~9bAO|94gXrd=ikW0+L2-S082-_Jwoz*GfHLFPgF`?Ra9n@`Io9zzRefG8$sbj=00c>xF*0u;t3 z7wE(N0Z^)v%}>fM!SdG+rvI*o{}Fe=AY73Ke_z`Y_2J~O5XHIC z=``hLy)lb~!l|0%#2-e&;>LdBB7>^dbi`|NSH}+MbKC-u0!X43)Zu7_pIuROZk%Vh z2*13F{iNDcGn32Jhzn|nrPmE#UG?Qfl<>HHjVQM`0`XQ?3}!)?Vt7-CfH@KH2}l|q z-KJ-bBKW+MbnRZ7FcZi)Tm%PaOKRUR2u$4~9*S-q1!5#QJZGRlW{b8sQB4q*2KUH7 zPKduXPQ!4Qnm+$(zg39Ms?q|(;ZXw>Lnt|fXhFSup`QVMC5aTa8wakooXyWNK zOZPj5fqL;wkyXIHX|@toKD9*R(&VOWCyP+vLf((lbp;hsj#>y9on+brv>W59IJ?QW zPK1A;9Db|gm23spwz#k?++1OTv-a7*THhbB*)@K8ePr3yi@x46@{T-CuSqvQ4%2ww zZ%a8SiP-Ilia6Jp`&mw^?FeQ~bB6L>I&|}GK?qR%bhcQigX0`>&X%u8@xLvr7XlJT z(Ca-``NE}P%Iu7>|D)Iv`HnG11Z~s8|;tA24Qjy z4{Ac7*|UR6Jhtj{*5sIOva0P{ExWuJp#O^0cbiMJz{)>(|4|r*LY#)psS4qR%pm!7;@N3f)o=ApHhLXS#ASk@oaKdTH4a}}K5|Kx!0=}h?k?C#KQ!m7-fod(iN9UBd zW380JkfMC!$L;enj?)!$DJpTa9!tD*X4&LCw47Ak{Acgt-eope{@#!TdoJtSBOVQk3N0+|^U)ALWyr-vX+ z(ncUkA=&*E;Yfs^{6aw(ec{O<1+5oafcjuu{Es2$J-zC&X+KpQ6Fc%pG{#}FWk@e4 zRfz(=D`Aw0>o!U%wjbVM@!NtkQ$zGlb&v zrq-bXIEV`Ax9@uk;y^2^^P0znv;D#3GO;Mblw)AM!3a>grof<$Mjwk;;LOBa3BT7w;aXb>!0fAxGZ35P|`L20Mpi5pJ z*Jtu*e#VR-*1CDwt;AwqD&)sVc9c3HemTU0L}PL?)vYbKB}Sb}IiD<+uvR)}K6yMD_*P4aEtaVC%_;#w78v ze&vKFnt+nU6hTtn}x=~2yYiVK-V|sl|t}UZc6!^9+fIw)- zXz;z+`WFj`$jKq$C$~Fd(VYKXM4#^Qd z93zglql<|$=034t~@nX*rI1`x(FUHK-o~P9MoSi|w8R63ncn_UA=#$g$K7ALs-U~+ax2J-P?>Afs>G7B#4I6a%3D+}%@Q|%ma_04 zVmD%I>@LrANX$$rA@RcSDcvC@fUF@l`#2g90I4Ik%Kc=!!#dM2*3-oL?od&uXN|d`~~_2 zm}>g-b7x4JXyhGvPE@9PDAO(&7AN68c9T`xxEO`O!Y;#90(KS91^yTPz4)2VFOn^U zgINnY^UbO(?To^ZxOkve`PKpYt#!_5&!@R2l?^-5#t}bTaeM;RgU~Gmj156;iR@98 zttbPd9;h&9QTY6Qyg*x9;cJeF&L%SU>u4`Tb!LO@Dik7f>a zz^?RP-EAyM#k%SJe6p(s)hJM?Cp>rQU$S$5S72}!*7VzZt( zb={}^KvC|QEFohbHF#%D{NOBeNjLn;>#N_(;YD{w`p9Ux@UAgi+ z-dHk6f1duSpK$gt9RqyT?{p%@nbao+lab}%|Gbwb3G^(iI7*Hr@39H^F7+#UZeu_^ z@4gOot}-g>HM3e@$;>%1&NH7yIb3nUwyIo1lj5jK_gmM(^o0`^OvRU{XOh~{YQdZg z!CF_G22$uQ0FmxFVXEx*C=hfGwJ9MMJy=hKf_(YqFR7lh)!@Kr((LVDg6N?1Fbl?I z#u%Z+5G1x&=rr*_i1K%+f9f6XWVH3AwXJ@f<3thOOJ$0Wq7c-gL_KOP4K6Ck)sXI! ziFj2pw;UeHi@zW&_)~zCL$+Q*r#~<{Q7Tp~qGR^C2ba#>rK8m~i6B7JEjPBn4!vz@ zU4eh6(IVOQdYFr^05zUyn`0QhyOkLGwtEsFCD=R}CizU((3J$X4wYrZx}(lYLeDXd zgkaF&rQ8!zBoicTQiHMb;2C0Z?B`pLHffIUI8(NG2G#^ox-oQxC<&9LyV+Gc zK%Z_*(wO0cns5asl@zM;@=Q#RHKY}v=GNQ6vcrmpF)fS+1jLOeA>}7GB za>`|*%htXLo}YhC_O6r_NV2shAYa>FY_!E1b#8ahVb1MN{t1eqD;vi+W3sD&`O6hD zPdWtMK}g4|LgL!aNm_w0ph|3^sj|etinJj`f?p5d(h|`*)}td~+k|VKx#{4pg%3cX zv=fos9;GUshiTc){F94`auppJc)eYk(i~~4EP46`r2%jk={?$>0SSSn3&AWLvrF_f zeK~cGEyr>nCKqIhmlSz`xNw3|7-EK^h+3Ls7ApaDE}j&1<)nBlb!Z{esOz7DrkiGe zF1o54&(Jb9bFP;Pz&Ry5s=4GR583m>r1xbKMifZ$B}Be>B4q!AN0Z`9{{TzF z^^+=+|20sbPZZKEsFq?MKsmfFIpK?l64V68UtX)q`2ZrY)`AS^FR=B6QEg;dtN&?# z5>HMMdPvvNEhD{HA~;T;h^-)OS{7vDDVc z0`V?iT|US`-;yXjkJH1hM&h+L0UHU~PrM9^hzK<C zxlJS|E{qiDAon{d?tMZTthsJ zeu`QJnuZA^V1N1FC%(~wFaFsY8R7PkIl)Z=Hr5F@V0wVbEqnNKITLKc{%B$S2 zS4kn#?O(0F1zZF*nj+d4NWAs%?U?LT(^^)H9d5FXTWwyW8?6_?Bcp;= zT;A`bq7xoAI2OPZGkBxe!OX@&hOv-rlPjtrJ=LiOM<;^WDk3lg3}|>+2qMil4+bBR zkdCiD@Q_@GlLP)5$xvTI1Y-Pd}u>%6Pbgbl_byy!^m7qv#U( z%jV;10+`TDYN^iQXiz%L_u3!FTfq`eNMDN{KYx>Y7KAT_0OoD<~%*OIc%XH3;-9oe++Zi|rKMHRfM5mRr9(W7-p&B+2 z;`qYCuPokjZc~WpD#`{^hY_rYck_Y;$c>p%68^-%k$k*N%0x@=wv9iQJp*1DD@{RMI{#)GSN}V=mz|^*+ofU3%nyoD;H2ABnUuwm$9SGzHqpKr zd4OjXwZ4y^mz?L=E^F=Pg1RCh6jDGW5fQXs4)w70*{{MjkFxEu`$dCL(p(0j*EUO3T}jWC<)AuwDmXE*p2 zlmAWg+PCxi0BTGb@oquA&h&cWJfrzEs7zb>Apx}Zovy&X?LAi@%JL?w3|N3-quq^k zcZ+p!`4?T|t3UVbBJ091^z!!~0$9buiH*$0vwZln<})FgG)HaiFYoZ<%Zn!cB!>j` zh|8wS#--Lie0492CO-c_Z4UIZgvkG{@r4onT?rLz-nw^TkmyIpzWP6Op6`6ntU>S- zIU#{M?L2w}wMMwF1@5|{r^fmDK(M;$WlY{)37kUX$V~%6r2;EQ$WYpC1rZi<`W&Eu zVcNVsXWNF2gce-9GkSeTysbFapI(FUzQzWBV;~N8GPm38>1tAN=f6E#2i>I6UrtS4W+ftoCNRUR10Q;k=yJFh<93!s-Y z+$n&>(H5!Wsv$U!@){j1u&{roxMV!+iuqDg6cq^in~Yds%1C`kye25nBM{NC)glmB zFQ+Z!iew1LU?J|t?Zf;4nFRR)LJF24_bB;)0KW3{9|b4icDPLcbg;?kp#ORv7#YO7 z`UUb^ej*L{Pve;qX8QhvRM+$qKN@SrGXafjd>I&BVYj`Llv+=Va*#5^5b73^tS?;S zSDO4GYnGQ!cxfCVY27y{&MX8b{mL^bz1%`X^`~o1o3=}h`eia?T0ns9M3QG#3GLWy zp5$Mx5{yYf76oVI9`!9sk8nFclpAP4FF)7F6E*3p^3lTs^V@SIC9_Xib(kw(;UuMz1I-ATAyC#Au{{>oh717CT zG|C!-v5D=DCC7zcRjlnW&-^VPU?{MV;n`ZKs_)gh%s@8oJ#NdX!ttX9?-`_AbUc3m z*N~39b7~N)n-Le~qa64&*1G0zwEjNUgv^3LoN=UEhmY1+En=Vqbvw)}7-7U^4E$23 zG7|sKiC#I9vkyJUHSP>bcUY?j(hXUug5UWqF3Jzd9nM1UB}-gXoD0|3_G_swlM&|$ z9{P#LPqH1ozvpuNen8#i$3@WJ!C|hiQGxm*_YLN{sHj&J!nFnUeYyYG1RpbwIKAF9 zgj_CXyIlM}zj`!}FyzG>2QohFh%8OF)5_UY!jQKA2*f<_aXdmkv$H%q_=fQ~ISxv; za8N%^P3AHtG{l!Vt9EPuud25Ii{cCWhPM~krMnU7Zlqa3Lc*n`q>=8BTtYyR6l4KG z7768UTMNmpf0Rch5clf{G`#jgT*ERR=&NKHpbI;7ioU`lCbUpLNB}Lle z$K_bF4M3GBls~oVdE=GwP~6XH`VOvG_r6bWUr053zy9sI&BhV(cl!lp#8!9nsR#aU zMdbsTAxLJ3`AnXk+LD&S`>(v}q3_LZzM2j)F08tO%1Dwe^pcshj>eR_o^VwBd3_E( zOPS&IyW!0m9}`q@su8ZjU39`kntx|*&Qclt?~2G~ywbv3c3Z11=IpyTgt*6xEL5KK zXRi0|EHYsM*Jaf|J>{Jf-dK)Y5JkN9a2Ic+vI8IGP%{!O#p~!xpMujfA z-)znGBxP+oiuTd_`?)UHc7_6Fzz(Q8Ii5^!oZc-T4o{r(PS;Z=*D8!oymN|HaDXU> zi~hZt;{WdIic7dsV;tiR0o=!JpF4Q^?i0IJNc<1`s9Ua|=hy9N`9$&B(O(i7mlJ|?@j=u0{eOD2kk&AKTC(n-{+w@8ccf@+s*ww5i zDTZB>YR*ctp3E)8V(hm_kGZmn1g}?tpRTE`e%LN_-+lFL%(P21ZR;{lZ${8t;hZmt zkt(wV%p;b!=zdxWHsny-l?cgI#MqVhX&ys&IJ=HqJg{hc$zL!b4lbmcM< zl*L@X3N4)WEFx_?-rVz6X{`F3s_1Xsrat$!Gic@EvBu%Z(u(U6m%pFio>20e!&fvh z-(R$yflwhRL+dMQWxYmSxWN?en&X0I>o0dKGt%z}T71{Y^RoV!X`3rrJ;dOPc*10k z5QhZEAtBC(pyoCZ9sY^{A8DqVCQXj`S>OQbhgWrWjwUV+9vw^y}iJ{Hi~z@6(_ zbpLXz%Na`S*XFnAD#V38v086G;*L4my182&aDKNa<0_VpdL`8uel+s?-$JG7s6l|m zOErboWV`CLvyO5r)pP9|P^jUiBF{qD40!OGUOi3q;-34I>6eM?xeP_$p1Pm>Q?eAc zM0$-`XWpFf4O_5hes&u0H&e~V6dsrod<#oq!8c)d$4yZaC)59w>SIpt?a;bvP$c5h)_^wCaXc;Dl^L zdpH5Cqnuj)BpI`pmWk)Surxk$RwU*iw{< ze&p;PukIRz-fv*U+3!vg61fkb2S#$!m4l0l(|wlnISV_7vf>7!SGG!4?udmF%ZnHd z8|wAgTlzUl>=P`iTxaW6vdH99w?1-johyKl2Zx)zy+cdEfeW+p;e9V;RwC{0< zmV^UXzrU^Cq=@Q3ZYH*@F_1KLe+1myX=|gf$?B5JSlfa-&}~d=?7yv@WYJ&amam@` z<6&;^J}VbEaKr9AG&qQPh4o@iP>oJ6geoKaN1Ao>ANTra9v|=UIZNDr zFt|4uz01g&OHaBRvRqxn;Va3(7OYmtlHDL|vM9^{_oLqPQ({O9!<1Z)cVxqB;msLZ zO7(Bx$CfdaI1qh+;J3v$$Ev0Nr7_K2Ppd5oO&(NmP18d8g1)!6-B)^am6a3t-9q2- z?a8YiNAcRux2Ut#wtfA2f48BPXkNKeNO<2Ng&f%rbjA%*ed3v7U5sfrU2}AoX!nzA zc6|08Tdf?wR98hb)C;FZ^c_8YXD%gtXutx~0!``x$IAzDI(2^~Tm?k52F9~p z-2dgYoCvQ$52Lw@tlMcuR%NN3HPnAO-SQi(7E`!3nENT{U4yS@T=8hWNx*Fww^wM1 z&@c70FExw4PMy2uADXy-W|Q~8?!U?HXRv+5T7PX$+`P2$Q~8(Fw(&dG5Xw%bn*&AF zEz)yeIPVPLtRNDh^H$4uZOdnJ$Es^XrmZ73ll}yIW*BoZeEPxGQ4Aq^gi^^ky_@XM zH2iD7)#sU;#AQlb!jG||T&fc7+#5Z#jtiDoet8tzANqiN!8htt)l(1y|0PQHSELF* zTcZ>j_Ls+^Pg{8YZ9T^M`Y`PN?Cp?^Lp-#0anbVI!UgKRedY<#pMZU%i#*W|HEOQf z9P~K-P&yQtU9tG)4?5hHDmGnxW+Pk3MP;-M3!QmK?JJ*T!b*ZZ!(^KACrXO~t!an)Ph{&R)Et6Dt-zQJVhdt79iicaT@d)rAlh>43| zZ13+>ZavO`p2g#(qwxa_(PfgfLmx8!`V>O5OP{4+dH4642+eLsX{ttzc@ZDY*{K9W zOeu@A9K2efMs)=w71Y0co>}uYJ*_iE)4clrpx;DC>k`{#(=T7jIoqB#ok{rgpgv8I z?fI>HQ`jmmhP!!s??>dj=#0<3^MMBjU;bJ>^_vp+_*v#3u^4Xu7!`pZf=XNAoAo&S zi$(se^~B%H=jovHZQDj2LTuyg)^hEH;Ef*J#znX7aO*#9uG_a2-RtppTTx~klBUYx zX(9rutiL7wECqL;xo!>>`dWtW5*WRiHs2SmSV!y3O$`r~1r?Fs=pF9u2~!V=LW@@V zU)Ed1wU9Xem5e^#d1NiyZqC{#!X)fwMcGm*7t|*n*w&SLn^V^=<&tggn4fg`2l4%} za)XMpqF??h5j$?F)ng`NcZ!^@^on-QNZSj-5A=PH#)Tw`+!u;B!M9WyR)US^;RFqK zBU|1BQGSkF$yv$+Ccj>W(b`)*+gD<4ep6;0hO-D%RE>}h*_GOGn1x4v5AJ54Q~OgO z=^@4Zm|dM4n{6}qJvsI9RM)cZuvOgd2iv#$FO`^!cZp+yt;)})zOQlhT79dp&b?mn@8P!AKUHi>{>Q! z>N3XKlP9h1{AkClI1SOw4sxYX4HcZy zf}asn_PrKwH02nL!+j&t*okXdu!?#wxsK!Ra+}5PfIQeYyC`(9_|fJSuf`|)9XPV# z9_Ss`hc;Ao>Eph=&3nzSn*-;T-Ur@ed_Cg;sf!hFJ#{ds^E=XDZv7dnXX05hj^FuH z{|5h&h3A3pNbAnBvdeCue#2ty4+mQu-`Dpk9EDx?@6lQGulG4LPAv!mt99jGWBFZv z2a$79pWVoMEphXB=;jAUHcZKOe@tt%aF6qt~ZjJr(TY$J7YN_jMRrYGGi`?tSYdol)qC-B|ua4)TqDjE)-#uTa7jw4Bn3lbM z@jHzzC!S=T(*Cg`kbh$N+l#P9+ZLy>lxh~8kP-A(ra%V6w~IN~(A_-xPB`YWO!2GF zucX16czFrRpHtSB>x_7)&1w%VsK{>+D{7@S(tu%X4R0!Ea5%1*{EHnv zrNFiDX63`3(Klb49c`rU-PH2gd@xfC*?&xDRNV9P=O*1)i!u9S#nfTnF#pmk3Q?j{ z$<=({Q{6vGvhP|8rF$8zoYg<;<^!?$PRlMDL^k2 zKWK=x>Kl}|>CL7894*xhV}=D&_SV80la~|{=ef$b%e5tb)RH)}Qkgbk&E+khkHVxR zX+C>9s>hAvr);n1wpVKRoC33e%Cr|z`>{mt8<`p$b~u8{eREfrBvaecohOb`G-&|?(l(YlyAw*fJQ=~un$h&djPFLAGWj9h zP`G9c6nDrpq(zY9y!_BVj!ntn`OWMtaFry3;KMUa`PCTh{SLj&J>Ccwl8F$8k2>L0 z+o4)-XPV?wetLgJIBP-k2wy@+gn7j2aXn~K$13y_UTG$3?<26brP|q@2$HGC3q5u4 zhQ7x=J$1x}tp`yLm?l#;|1rd1Wq{BfFF`Ye!}Hcs=ZHfMJ(Y6#9<~ug2(Ir6zklVO z2UXFsU3wTw*eGSlE<$;6=88O=i*F}q6aHg}^)5xZsc?J78EV0B#`oV_k{wfW}aZK#bHhH!DpXnT=|x|kSpdx1~y2pmcq(xw;7=owSx)2nHeQb#j1Pw<(P%Ps;B*KINmMG)4s z=6S6ty(Hwc_ypr+L>OhCcd>n_GH$}9Os^{x+B-c5rgT|t(&c|io#6)mj^GczG=Pb* z9~!N50EGgJD5fz{nhnbvI(RK=dbcvY)Dq{tSFxs3@)lU7kQ&a6As4UB&?%Wq`3}t- z+|Dj5S`}(WY%HwETz&F_c3+%9t9WdQJLzeeZbce?$xr|KqpOaB_n=Il`G@jxxyh;- z0fS`7G(x5Dq`)mK)o?E6r*>-F6O8_-Hy6aqj_zQG4nt||oFH`IIfxKP@uv5 zWd|x?VurFt0lD$RoQYXsq#$$L_3Q2l^uXTr{~>wS(YFQ89uX%Vj~brF5b^|NbIxtB zp+lq3aQwu_IR*OvD6oorvN^HSA|pBHNQ_zz;8y}a#M#Km{#YdUvI3yx+nM=AD=rud zDgerOpQ-)`Pw--^V_Dz(d8hm+ZH}IBaMS8c+_sS8(V(TYwHam`OsX9}t0{VlDduo1d8YW%{BhHjq)@GRm>%3U@ejGg<|mPU+!7b;(4NsH zU@ZI+`)95@JnQrMQ?Baj^Gj4n`8WM6c8$1UL^uU;#5b*1zN6kJ;E+bFSsKxTHcv{I zZf(>YdnJvSlLIxdHpdshxD^< z`;5lwGi+=+-##*k#l!#&8gnPAI=NgGTnK6^CND2M=|8!>C&rhALjw>vk9>BmT61d@CUuvS9{9?<0u2)qpFQU|IzL3WrIr za}UFqV$y47^)R2!5Yuk^a%8)`&D zzD4ArlDWjgD9sqK#pLBW(Yc2Mu ze$zZ-3>vyjMUu_3<>S8B8J^k6jox3(vf`VFdsA)R%zGj`IE^iKw$jE$H>_VBNi2=b z=AaNE_sY`cAd+Lt^!gKq6AD*qY1BUQ^b|=$tK92uDbwQTF>M5QVEXJZW%HmKtGNn8 z?s^lUF8=ENJ!mrv#mJ}UggnhY-jO|8vK5{0YRMA5M$UH;QX+6svJ6YB56nN=x7B+} z2Knw`3QDmpYlI2@It5$?ya0Y}K-uoArLM~|q-_3Dy^o)Eb_(I<4c*28#-rBIZ!LE` z6>@)Sc7fSTE%B(4VABHb!*}H(U6f+#i(xNBP4nH_l(CA>q(*`ii+Hx2=QXP2OE?3A~fk#K57(I|E-M4T|ocT;NAzzvQBE$m7BDVG@uO4KZ` z0N@NmY3O!@8U4a1xs(o;S9)aqx`QKE9sp5DsGCS*93EVC{W$XEo&WQ9UbeMg(zkw4 zx2h$--KI*7c(4>c`>;T(T)jLGWAapbkJNi|b!rS;-99w>WI+Y=Z&N# zuE&h>p4wp9qepq$1$OtO4VE8cd@rEzZ&@bJ`e!#MiHf^%sf(Kg1|Wup#IEk%YnxN@ zJKt>!kBQlE$$R;aa^p64V_FP?B&qJs`iC8R51kM0>FF``KNhUa*@gb4Vf(#kU90@n z+>lFa$`3j&u5k$YSyo!tC0*}I@6@9mU&3=9^`>9D+@|!3|9ZDZRHy^me1oKUhSU6; z|LaNc8$aIEjHLVX99*w|*W)!~RO9v>)=RfP#Hal`Wj%aH>G~IcvgP(qp7GF) z+V$_;9jq)uW??@1XO|TjaUQ~C-yIo2? z(F%0$94{TI5YGQCsND0n!8Pn{HLCO+X4?51c=K#!rP?PS?enZfC%$d1FUxAzxPEjH zZe-=P5Yj*40QWirAhY+(XvF<*w1SXR zY#RnGQnBZAy^=V6xL4JMnmp&M6KY-!ux^BpVreA~Ag?4q*d{0gg5p`)Ru?zukdSnL zDm>w%x0Ca)6wG#`)DQ1Znrg02Pa_U8R_-VxNtUAYH~xVdthnfJ48*05xqSQK@{w^( z+S3VF@f<(>roXhCJi^fC!v`|q$|Oo)U66riNtEDnPbG059--S@IZVKcAsDWltw&H1 z#Z3L9;Y^aSV3f7y>Z&yNz~*Mr6TtmZeAT$i`!{*3qM=dhIZR?Mm%}o|kVYGmF z4J%1RG>7R(gXefn*3${8d8SsPkNU)YX%*M>;*aLpWkC8vVO|dr5Eo-Ij|S&3G0|UI zZF+G$6?qj9DxP=4fwn}6i~xK{C+vtgzJKEfYHoLS~q z7tMLo%uI>};82-ZW+{*~;$v_8RYbJ!{N|;cH;*W*US;WjEu`?W0;Cy;mnQ(t95_aLej+=nfT0d}<7YegEMNiYbsMm*KKR^G^hO@C$xZ(tpP?=q8=MNjh~}8MvuP_D zD}T+9Vonu%aAAuja9l_tSoRASeu4s%w6L93Vtd6s{jV}sB`XYd^_XH|AnYjqlyISA zp0imk%60C;Nn;;(0+ZN~B;)L}QmRx^tt8D5@IhA-<;D*2LNw_;iApS_vghBOh>MJ^ zFtr-FpUAV0nZvqX4k^|{XxdYX=S6V}X*6jYBVo-9?YGp$Xh_Y4c{k$077+0=J@%#| z-owzktng*b0(e0)`*A7zsyQE)T2s+vWqDG(p%{k@)o8k|E=6sw7P(N{;0B5SwWsrH z#Kj6ubYVEBCUSXu?(XCsr(g>6 zSZbPx$p(3L8n8Z~ns7Z+0voj&|GHY~A^GW&e?*6y9=90d(Ntx~uU`mGqbEn2FEEBM zutwX268EGNCOb%|%2sHL>_NvE>t3rp8SW2lgEyu*^Ow`BO{VzBChA7}aD=&`YNP!e z(%cyUW|WA;lleN8q}iP1=PtqDCLZpG*)s}-1Ez3kAp_NE+-M7*|C~5D)b=6>RHX!X zPp>rZjfUB4JqwNK@B`NV0)z!G?B;U8wz835`t4Vnmby0hmQA_CCL9;{ei`+xdlA2q zlwWr4&M$MPTmGx}4EiR=0{2gRdK$qN)2x2GY@OpFL|wBthdFFbb9mrsBD-`dYoB}V zK97WU%z>_SVk^QtfI;?NWgJDiGh$C0i_ZW6f}O+keZNEb0a+HiJo1+Yek0>FT{3vq zLrfrj=db$p&g+t`I29dP$>|!4*aGT=$S}DBneaqqsltc?Z%kL+uD*golv@7VC}rGr z{$b$?(a4wjo7**3HFuO-3vR7hQzGSOM^r@{V@|Bc;PMjyYoriau(O3^{3)ZZpM&a5 ztF(q~h)BBZ$hEy6>3siKb79NvZ;HTN6l*;e?3EaM5LAxSm)J7t2uV2=C%5txQexbA z6MdSqY`Jj*8&WnPd2S3AQCs;&_f!KJ-v92`!m;MrH|Nv3`R>o*QQtD0KN~KlBOfif zr-g|T)%`?FC`i}*D)7|sN8j8dPwtV;Kd3W1VmTpDER5dCC$H)o3t8aBzVmZecRNyz zSRp0Ye0N{NnOViR&yI~&)Z`@_P~&D@;AbNGg?Vh-t-I~3qpvkHfh-ARd?J7J8=FREpXKh54O zSuBUn*lZ+T*BBN{Xzy=@)SkcidLYX^BR1YgffHibrHuT0$)33F&A*4ax1Y4RG}=3W9WV?8<} zmt|SK&xHqgiQgIJ@ljjn!wT+I;m8;CZiwQ0mbsv zmmF$;8>w99`(bJ*hJ{Q%d7buJm{3KZPx~D{lm9wRlUm4VhcSuje5PLEM!`<{Mb!r$pJUH-`71#W(VINhq=QFZ`}X>aekW&@+ow2gKPiy z0@swuBT$`(ATG6>%Q0AGx>l87^Rd9M{JCR?v(ECRUw?Oh-MFtq>mV3I{Zp56`T3A; z{D6qONM75^Cj^}V8AVI!+d3CL>Mjf2>+Omql1I&z~)LVBd zVg%*r3r|Qh0njaD#C;r-$3(EKgxR6}*v z{ly>jv;~mCGs27o?lqr5KD&`0{&J5OseE#aUq~0}qFRv{6eiWLWhQZ^vr<3N?cbO0 zv0?W~Q2px|)OwQC)*~^|`MOzAn!1`SxAuO-P4_?hZzCB=i-q|q%}7#*QCj&K3u#KA z|Gn4#CxQN0KX_=>eca0uli2cPt-ivKLs_PkTI+0e#pFL`SYnAP=EI1^!QF=*3^QLxA&RFjlN|w`?jdZGSTK!f6b2f{!0b-P+TZr3Ux4>xZ39uG(PZVBRZ&n z2mOL_<0Eci>X~l`OA^})H$ zj+G~RQ}(xd2R-V|-}*s_^_McqpYL^D_B&#jZ+(o5(N@@btmz9lc5{)o*!o*F;QQAS zrFTAI7-ue8&EjTH3!GcgPn4ctj6ZDw+VIzm@s@h!AxS(#xHQP0bOWlc>`jiV8aL)$ zGZBw0-{$)m8CN;IvX(^3mnAEyILi=)j`Nt zF|;HH02U}Wl6aPHmViQe2U|zNF`U#gJNs@}2Lx-l=PQYUtCwHCkyh@zVhI-oI{aZiKBkZt@4-|I|b;A4AaVaYe(vqWUx8KX`-zRxidqp2~z|LKmcx@B>l68Fru^I zvYaXAFjU&O4DLEObR}H}YfwaDJtbHwu~wQ;Zdd|#xzNWsfov0TA6H$A`ppQpLS<%u z`V)?nSu0<{N$P^j6d8%iUtR5brLnh*(nN9CJL_F`X`*+`GA}XHr~im0-KhLMqNuUY zTizh?-gvnsS~f)b`kGeHSKD$Wp;=h*K=k6IEEHpgV6OfAsE!m#6ur5J@Y6TjIFdWF zvrQ}>V(*jW9MfJ^5SrceMn@%NzYTat&S4Y5l}e}aS`D%xL@NH{`@2*#MN936E)Ila z#*vv$yCS2Myd;cVIDABi)}CpJ^7S{vUi2Uj9od#0$C!mxK4`>83Z@6XSQE|R+yZW7 z<+NHcz?ht|5_p?zRh-F736-kv3D`Y@ceAq(cr?z-JZAW~Ao0RUVF+^S|(K?`rVj~sp z-Wv#fYb%=%A284~8g*piKxvK*J7x^~B17#F!R9-3*b$lhvLKwdruYP)UO;$RU8sWz zw}u>n?^qd$YOJHB?f!9*vWZIO$27!nnS8C#MaW?oBAB?dNuY9aP0i*EK=4I2dx!;_`%x0BdEW5bZ4NxG zA3sRlp`CA{K?;9YDHRG_KjXq4U+uCvTSMFbT7F#;_q3xu=2jrm*#Wdnb1F`2h8Q+N z1vq$hypDi=HXd*c7y`RLiM6Tx0LSG=M3M{H#uZdffZV914nhvjS0hfHvEc|}$t_s3 z^6Sy*F+q`uY2ZB*hvV80;7(2hz;XOXIsn);&y)a%7G1rFOFp)Db8r6w#!Kb(==VkV z;jOrWDQ?K2F#wOt44zDJLl3V3dYIS!%sh<|^uC}`jM4J!_~v2Hzrk&EN)7NY1n}EIPOfg1H2?cUc*@unGGHSI zHVR-vwTxXszn8;Q372wgq7Xm`B^bmD;YC9w(jl-82nGBdqyU2a0{c!*)=$NQSfhFa zrXkWC!YPWGSkbSTis>k)h3KePh46vxXns804=sWNk%K^-6etjg3rz_EaZ^!)Ks+?G zAkZZmIuMALnjQqY%m4v__&|&x5I^web0C2WB6vZBE_A}o04Kr%5K(F%Be4sEI1|80 zT<9cO0V2f)G$qYW3qq-YXh0OI070q&1gcIA!GS0=KoAgIlN<^n!;nKjh^y29(EADstjArC^sz<9x6v>`qr z7|qdA7mW6T;M+x^6nL>PG#B1H49$R7P6x#3FtjT4E1aUGBMgm%aDvEDE}=m|Zhj#S z&YrGdFbH+q?LXGlEhrdY7J=@;$v}aXSD^Ax1*jshM+o!^R0*mK1p#S+z#Lkdz~KJh z7aBqVqUGR$kyApD6xtU8Cl8DU5K#XKTrehpNBqZAfq7tDfJFYEgg`?tYT>`vC72+f zhigMjIF-;PAfqb~u21Og#sC_qSRyhIdQj+>g67eWC+ zQVYO%D8K-qVQ^k@Gzw3pG~i)cO=4GM&y`2Tj! zh5(HJy_jhEkWgk?Ui?rLx&cgykB&yODpK*m%u!$j!~gR}Q;?CPC?IfO3OJII59S2e z{--5085tRh41$6qsqmxGXl*)vG?W5L0lS2TqhKf~3JyWwvt!WoU~YVQ4BCtV3B4Fj zZVq771juPA&`>g33OqU%&B{#%27`f~Kv4)33_|vw8&5NkJgooX283|#a zB+&Q(B#Hq)aUVr4Mh6xL!>LdpFoXsS15rbPRHz}eP{5u3qVGr=VGvMVC=e2jMB`2J zQS5m8cr*i!4oD0Lp#TYkVU#G4D3}JxfCkfYQ-B3fUxz!?e#3v*rM3)BzzB4^;zLrV=u1Bs%*B48LA zC|o=$0WCnud~u41hyZ3FS-g4z`kF1vMYt%>f8jti{Qtv&qhkpBzoo?wqlE(%K`SYU zf{I)md6UvT1qq&*uj?sv?iDhubPOy21ei$646+G zX-dQuC?)I)j1oZ!lc1ITuY%bBt038nU^$?(6!_yrG$y6$F1n@aE~OniTvjs3-yiQp cpya?`AyCr8<^P{74OjesEC;@iK*{`n0IIN~_y7O^ diff --git a/tools/txs/src/txs_cli_vals.rs b/tools/txs/src/txs_cli_vals.rs index 6dbd3eef5..d12ec7f1c 100644 --- a/tools/txs/src/txs_cli_vals.rs +++ b/tools/txs/src/txs_cli_vals.rs @@ -6,8 +6,8 @@ use diem_genesis::config::OperatorConfiguration; use diem_types::account_address::AccountAddress; use libra_cached_packages::libra_stdlib::EntryFunctionCall::{ self, JailUnjailByVoucher, ProofOfFeePofRetractBid, ProofOfFeePofUpdateBid, - StakeUpdateNetworkAndFullnodeAddresses, ValidatorUniverseRegisterValidator, VouchRevoke, - VouchVouchFor, + ProofOfFeePofUpdateBidNetReward, StakeUpdateNetworkAndFullnodeAddresses, + ValidatorUniverseRegisterValidator, VouchRevoke, VouchVouchFor, }; use libra_config::validator_registration; use libra_types::global_config_dir; @@ -18,14 +18,17 @@ use std::{fs, path::PathBuf}; pub enum ValidatorTxs { /// Proof-of-Fee auction bidding Pof { + #[clap(short('r'), long)] + /// Estimated net reward you would like to receive each epoch + net_reward: u64, #[clap(short, long)] /// Percentage of the nominal reward you will bid to join the /// validator set, with three decimal places: 1.234 is 123.4% - bid_pct: f64, + bid_pct: Option, #[clap(short, long)] - /// Epoch until the bid is valid (will expire in `expiry` + 1) + /// Epoch until the bid is valid (will expire in `expiry` + 1). Max 30 epochs expiry: u64, - #[clap(short, long)] + #[clap(long)] /// Eliminates the bid. There are only a limited amount of retractions that can happen in an epoch retract: bool, }, @@ -69,23 +72,32 @@ impl ValidatorTxs { pub fn make_payload(&self) -> anyhow::Result { let p = match self { ValidatorTxs::Pof { + net_reward, bid_pct, - expiry: epoch_expiry, + expiry, retract, } => { if *retract { ProofOfFeePofRetractBid {} } else { - // TODO: the u64 will truncate, but without rounding it will drop the last digit. - let scaled_bid = (bid_pct * 1000.0).round() as u64; // scale to 10ˆ3. - if scaled_bid > 1100 { - bail!( + if let Some(b) = bid_pct { + // TODO: the u64 will truncate, but without rounding it will drop the last digit. + let scaled_bid = (b * 1000.0).round() as u64; // scale to 10ˆ3. + if scaled_bid > 1100 { + bail!( "a bid amount at 110.0% or above the epoch's reward, will be rejected" ); - } - ProofOfFeePofUpdateBid { - bid: scaled_bid, - epoch_expiry: *epoch_expiry, + } + ProofOfFeePofUpdateBid { + bid: scaled_bid, + epoch_expiry: *expiry, + } + } else { + // Default path is to update based on the expected net reward + ProofOfFeePofUpdateBidNetReward { + net_reward: *net_reward, + epoch_expiry: *expiry, + } } } } From 64a5323de47f7ec6788392b612c74753c2f731c5 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:22:19 -0400 Subject: [PATCH 03/34] implements minimal commit-reveal for bids --- .../sources/ol_sources/secret_bid.move | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 framework/libra-framework/sources/ol_sources/secret_bid.move diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move new file mode 100644 index 000000000..384657362 --- /dev/null +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -0,0 +1,102 @@ +///////////////////////////////////////////////////////////////////////// +// 0L Module +// Secret Bid +// Implements a commit-reveal scheme for more private bidding in PoF auction +/////////////////////////////////////////////////////////////////////////// + +module ol_framework::secret_bid { + use std::error; + use std::signer; + use std::vector; + use diem_std::ed25519; + use diem_framework::account; + use diem_framework::epoch_helper; + + /// User bidding not initialized + const ECOMMIT_BID_NOT_INITIALIZED: u64 = 0; + /// Specified current public key is not correct + const EWRONG_CURRENT_PUBLIC_KEY: u64 = 1; + /// Invalid signature on bid message + const EINVALID_SIGNATURE: u64 = 2; + /// Must reveal bid in same epoch as committed + const EMISMATCH_EPOCH: u64 = 3; + /// Must submit bid before reveal window opens, and reveal bid only after. + const ENOT_IN_REVEAL_WINDOW: u64 = 4; + + + struct CommittedBid has key { + reveal_net_reward: u64, + commit_epoch: u64, + commit_signed_message: vector + } + + struct Bid has drop { + net_reward: u64, + epoch: u64, + } + + /// check if we are within the reveal window + /// do not allow bids within the reveal window + /// allow reveal transaction to be submitted + fun in_reveal_window(): bool { + // get the timestamp + // get the epoch ending timestamp + // get 5 mins prior + true + } + + fun is_init(account: address): bool { + exists(account) + } + + /// user transaction for setting a signed message with the bid + public fun commit_net_reward(user: &signer, message: vector) acquires CommittedBid { + // don't allow commiting within reveal window + assert!(!in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); + + if (!is_init(signer::address_of(user))) { + move_to(user, CommittedBid { + reveal_net_reward: 0, + commit_epoch: 0, + commit_signed_message: vector::empty(), + }); + }; + + let state = borrow_global_mut(signer::address_of(user)); + state.commit_signed_message = message; + state.commit_epoch = epoch_helper::get_current_epoch(); + } + + /// user sends transaction which takes the committed signed message + /// submits the public key used to sign message, which we compare to the authentication key. + /// we use the epoch as the sequence number, so that messages are different on each submission. + public fun reveal_net_reward(user: &signer, pk: vector, net_reward: u64) acquires CommittedBid { + assert!(is_init(signer::address_of(user)), error::invalid_state(ECOMMIT_BID_NOT_INITIALIZED)); + + assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); + let state = borrow_global_mut(signer::address_of(user)); + + // must reveal within the current epoch of the bid + let epoch = epoch_helper::get_current_epoch(); + assert!(epoch == state.commit_epoch, error::invalid_state(EMISMATCH_EPOCH)); + + let bid = Bid { + net_reward, + epoch, + }; + + check_signature(user, pk, state.commit_signed_message, bid); + + state.reveal_net_reward = net_reward; + } + + fun check_signature(user: &signer, account_public_key_bytes: vector, signed_message_bytes: vector, bid_message: Bid) { + let pubkey = ed25519::new_unvalidated_public_key_from_bytes(account_public_key_bytes); + let expected_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&pubkey); + assert!(account::get_authentication_key(signer::address_of(user)) == expected_auth_key, error::invalid_argument(EWRONG_CURRENT_PUBLIC_KEY)); + + // confirm that the signature bytes belong to the message (the Bid struct) + let sig = ed25519::new_signature_from_bytes(signed_message_bytes); + assert!(ed25519::signature_verify_strict_t(&sig, &pubkey, bid_message), error::invalid_argument(EINVALID_SIGNATURE)); + } +} From fcf0c7ffcca7a3878cf0e71be9995b3e7928034b Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:40:35 -0400 Subject: [PATCH 04/34] test ability to read messages --- .../sources/ol_sources/secret_bid.move | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move index 384657362..a01e4c243 100644 --- a/framework/libra-framework/sources/ol_sources/secret_bid.move +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -30,7 +30,7 @@ module ol_framework::secret_bid { commit_signed_message: vector } - struct Bid has drop { + struct Bid has drop, copy { net_reward: u64, epoch: u64, } @@ -99,4 +99,31 @@ module ol_framework::secret_bid { let sig = ed25519::new_signature_from_bytes(signed_message_bytes); assert!(ed25519::signature_verify_strict_t(&sig, &pubkey, bid_message), error::invalid_argument(EINVALID_SIGNATURE)); } + + //////// TESTS //////// + #[test] + public entry fun test_sign_message() { + use diem_std::from_bcs; + + let (new_sk, new_pk) = ed25519::generate_keys(); + let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); + let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); + let new_addr = from_bcs::to_address(new_auth_key); + let _alice = account::create_account_for_test(new_addr); + + let message = Bid { + net_reward: 0, + epoch: 0, + }; + + let to_sig = ed25519::sign_struct(&new_sk, copy message); + let sig_bytes = ed25519::signature_to_bytes(&to_sig); + // end set-up + + // yes repetitive, but following the same workflow + let sig_again = ed25519::new_signature_from_bytes(sig_bytes); + + assert!(ed25519::signature_verify_strict_t(&sig_again, &new_pk_unvalidated, message), error::invalid_argument(EINVALID_SIGNATURE)); + + } } From 35f0a0d73d3cff13f8372d7eb8ee549c32691e3a Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:44:48 -0400 Subject: [PATCH 05/34] add external tx api --- .../sources/ol_sources/secret_bid.move | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move index a01e4c243..c98039929 100644 --- a/framework/libra-framework/sources/ol_sources/secret_bid.move +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -49,10 +49,20 @@ module ol_framework::secret_bid { exists(account) } - /// user transaction for setting a signed message with the bid - public fun commit_net_reward(user: &signer, message: vector) acquires CommittedBid { + public entry fun commit(user: &signer, message: vector) acquires CommittedBid { // don't allow commiting within reveal window assert!(!in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); + commit_net_reward_impl(user, message); + } + + public entry fun reveal(user: &signer, pk: vector, net_reward: u64) acquires CommittedBid { + // don't allow commiting within reveal window + assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); + reveal_net_reward_impl(user, pk, net_reward); + } + + /// user transaction for setting a signed message with the bid + fun commit_net_reward_impl(user: &signer, message: vector) acquires CommittedBid { if (!is_init(signer::address_of(user))) { move_to(user, CommittedBid { @@ -70,7 +80,7 @@ module ol_framework::secret_bid { /// user sends transaction which takes the committed signed message /// submits the public key used to sign message, which we compare to the authentication key. /// we use the epoch as the sequence number, so that messages are different on each submission. - public fun reveal_net_reward(user: &signer, pk: vector, net_reward: u64) acquires CommittedBid { + public fun reveal_net_reward_impl(user: &signer, pk: vector, net_reward: u64) acquires CommittedBid { assert!(is_init(signer::address_of(user)), error::invalid_state(ECOMMIT_BID_NOT_INITIALIZED)); assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); @@ -102,7 +112,34 @@ module ol_framework::secret_bid { //////// TESTS //////// #[test] - public entry fun test_sign_message() { + fun test_sign_message() { + use diem_std::from_bcs; + + let (new_sk, new_pk) = ed25519::generate_keys(); + let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); + let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); + let new_addr = from_bcs::to_address(new_auth_key); + let _alice = account::create_account_for_test(new_addr); + + let message = Bid { + net_reward: 0, + epoch: 0, + }; + + let to_sig = ed25519::sign_struct(&new_sk, copy message); + let sig_bytes = ed25519::signature_to_bytes(&to_sig); + // end set-up + + // yes repetitive, but following the same workflow + let sig_again = ed25519::new_signature_from_bytes(sig_bytes); + + assert!(ed25519::signature_verify_strict_t(&sig_again, &new_pk_unvalidated, message), error::invalid_argument(EINVALID_SIGNATURE)); + + } + + + #[test] + fun test_commit_message() { use diem_std::from_bcs; let (new_sk, new_pk) = ed25519::generate_keys(); From c2139ab737af30e0f7f8d05a390f4dbec58475d2 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:59:04 -0400 Subject: [PATCH 06/34] patch epoch help for tests --- .../sources/ol_sources/epoch_helper.move | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/epoch_helper.move b/framework/libra-framework/sources/ol_sources/epoch_helper.move index 4a6022b16..dd7e21242 100644 --- a/framework/libra-framework/sources/ol_sources/epoch_helper.move +++ b/framework/libra-framework/sources/ol_sources/epoch_helper.move @@ -1,13 +1,20 @@ module ol_framework::epoch_helper { + use diem_framework::system_addresses; + + friend diem_framework::genesis; friend diem_framework::reconfiguration; + // too many cyclic dependencies from reconfiguration being the only place to reach epoch. struct EpochHelper has key { epoch: u64 } - public fun initialize(root: &signer) { + public(friend) fun initialize(framework: &signer) { + // must be framework address + system_addresses::assert_diem_framework(framework); + if (!exists(@ol_framework)){ - move_to(root, EpochHelper { + move_to(framework, EpochHelper { epoch: 0 }) }; @@ -23,4 +30,12 @@ module ol_framework::epoch_helper { let state = borrow_global(@ol_framework); state.epoch } -} \ No newline at end of file + + #[test_only] + public fun test_set_epoch(framework: &signer, epoch: u64) acquires EpochHelper { + system_addresses::assert_diem_framework(framework); + initialize(framework); + set_epoch(epoch); + + } +} From 98595b2c52d7f756574a22c9519e60d8831947bf Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:59:15 -0400 Subject: [PATCH 07/34] add commit and reveal tests --- .../sources/ol_sources/secret_bid.move | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move index c98039929..73c9c049c 100644 --- a/framework/libra-framework/sources/ol_sources/secret_bid.move +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -138,18 +138,20 @@ module ol_framework::secret_bid { } - #[test] - fun test_commit_message() { + #[test(framework = @0x1)] + fun test_commit_message(framework: &signer) acquires CommittedBid { use diem_std::from_bcs; + epoch_helper::test_set_epoch(framework, 1); + let (new_sk, new_pk) = ed25519::generate_keys(); let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); let new_addr = from_bcs::to_address(new_auth_key); - let _alice = account::create_account_for_test(new_addr); + let alice = account::create_account_for_test(new_addr); let message = Bid { - net_reward: 0, + net_reward: 5, epoch: 0, }; @@ -157,10 +159,33 @@ module ol_framework::secret_bid { let sig_bytes = ed25519::signature_to_bytes(&to_sig); // end set-up - // yes repetitive, but following the same workflow - let sig_again = ed25519::new_signature_from_bytes(sig_bytes); + commit_net_reward_impl(&alice, sig_bytes); + } - assert!(ed25519::signature_verify_strict_t(&sig_again, &new_pk_unvalidated, message), error::invalid_argument(EINVALID_SIGNATURE)); + #[test(framework = @0x1)] + fun test_reveal(framework: &signer) acquires CommittedBid { + use diem_std::from_bcs; + + epoch_helper::test_set_epoch(framework, 1); + + let (new_sk, new_pk) = ed25519::generate_keys(); + let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); + let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); + let new_addr = from_bcs::to_address(new_auth_key); + let alice = account::create_account_for_test(new_addr); + + let message = Bid { + net_reward: 5, + epoch: 0, + }; + + let to_sig = ed25519::sign_struct(&new_sk, copy message); + let sig_bytes = ed25519::signature_to_bytes(&to_sig); + // end set-up + + commit_net_reward_impl(&alice, sig_bytes); + let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); + reveal_net_reward_impl(&alice, pk_bytes, 5); } } From 87bd97a1ed61f82caf4a6cfaab1586933a39303e Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:18:00 -0400 Subject: [PATCH 08/34] reveal test passing, and checking correct aborts --- .../sources/ol_sources/secret_bid.move | 114 +++++++++++++++++- 1 file changed, 109 insertions(+), 5 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move index 73c9c049c..485bde793 100644 --- a/framework/libra-framework/sources/ol_sources/secret_bid.move +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -12,6 +12,8 @@ module ol_framework::secret_bid { use diem_framework::account; use diem_framework::epoch_helper; + // use diem_framework::debug::print; + /// User bidding not initialized const ECOMMIT_BID_NOT_INITIALIZED: u64 = 0; /// Specified current public key is not correct @@ -83,7 +85,6 @@ module ol_framework::secret_bid { public fun reveal_net_reward_impl(user: &signer, pk: vector, net_reward: u64) acquires CommittedBid { assert!(is_init(signer::address_of(user)), error::invalid_state(ECOMMIT_BID_NOT_INITIALIZED)); - assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); let state = borrow_global_mut(signer::address_of(user)); // must reveal within the current epoch of the bid @@ -135,6 +136,73 @@ module ol_framework::secret_bid { assert!(ed25519::signature_verify_strict_t(&sig_again, &new_pk_unvalidated, message), error::invalid_argument(EINVALID_SIGNATURE)); + + } + + #[test] + fun test_check_signature() { + use diem_std::from_bcs; + + let (new_sk, new_pk) = ed25519::generate_keys(); + let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); + let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); + let new_addr = from_bcs::to_address(new_auth_key); + let alice = account::create_account_for_test(new_addr); + + let message = Bid { + net_reward: 0, + epoch: 0, + }; + + let to_sig = ed25519::sign_struct(&new_sk, copy message); + let sig_bytes = ed25519::signature_to_bytes(&to_sig); + // end set-up + + // yes repetitive, but following the same workflow + let sig_again = ed25519::new_signature_from_bytes(sig_bytes); + + assert!(ed25519::signature_verify_strict_t(&sig_again, &new_pk_unvalidated, copy message), error::invalid_argument(EINVALID_SIGNATURE)); + + let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); + + check_signature(&alice, pk_bytes, sig_bytes, message); + + } + + #[test] + #[expected_failure(abort_code = 65538, location = Self)] + fun test_check_signature_sad() { + use diem_std::from_bcs; + + let (new_sk, new_pk) = ed25519::generate_keys(); + let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); + let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); + let new_addr = from_bcs::to_address(new_auth_key); + let alice = account::create_account_for_test(new_addr); + + let message = Bid { + net_reward: 0, + epoch: 0, + }; + + let to_sig = ed25519::sign_struct(&new_sk, copy message); + let sig_bytes = ed25519::signature_to_bytes(&to_sig); + // end set-up + + // yes repetitive, but following the same workflow + let sig_again = ed25519::new_signature_from_bytes(sig_bytes); + + assert!(ed25519::signature_verify_strict_t(&sig_again, &new_pk_unvalidated, copy message), error::invalid_argument(EINVALID_SIGNATURE)); + + let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); + + let message = Bid { + net_reward: 2, // incorrect + epoch: 0, + }; + + check_signature(&alice, pk_bytes, sig_bytes, message); + } @@ -142,7 +210,8 @@ module ol_framework::secret_bid { fun test_commit_message(framework: &signer) acquires CommittedBid { use diem_std::from_bcs; - epoch_helper::test_set_epoch(framework, 1); + let this_epoch = 1; + epoch_helper::test_set_epoch(framework, this_epoch); let (new_sk, new_pk) = ed25519::generate_keys(); let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); @@ -162,11 +231,12 @@ module ol_framework::secret_bid { commit_net_reward_impl(&alice, sig_bytes); } - #[test(framework = @0x1)] + #[test(framework = @0x1)] fun test_reveal(framework: &signer) acquires CommittedBid { use diem_std::from_bcs; - epoch_helper::test_set_epoch(framework, 1); + let this_epoch = 1; + epoch_helper::test_set_epoch(framework, this_epoch); let (new_sk, new_pk) = ed25519::generate_keys(); let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); @@ -176,7 +246,7 @@ module ol_framework::secret_bid { let message = Bid { net_reward: 5, - epoch: 0, + epoch: this_epoch, }; let to_sig = ed25519::sign_struct(&new_sk, copy message); @@ -186,6 +256,40 @@ module ol_framework::secret_bid { commit_net_reward_impl(&alice, sig_bytes); let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); + + check_signature(&alice, pk_bytes, sig_bytes, message); + + reveal_net_reward_impl(&alice, pk_bytes, 5); + } + + #[test(framework = @0x1)] + #[expected_failure(abort_code = 65538, location = Self)] + fun test_reveal_sad_wrong_epoch(framework: &signer) acquires CommittedBid { + use diem_std::from_bcs; + let this_epoch = 1; + epoch_helper::test_set_epoch(framework, this_epoch); + + let (new_sk, new_pk) = ed25519::generate_keys(); + let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); + let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); + let new_addr = from_bcs::to_address(new_auth_key); + let alice = account::create_account_for_test(new_addr); + + let message = Bid { + net_reward: 5, + epoch: 100, // wrong epoch, we are at 1 + }; + + let to_sig = ed25519::sign_struct(&new_sk, copy message); + let sig_bytes = ed25519::signature_to_bytes(&to_sig); + // end set-up + + commit_net_reward_impl(&alice, sig_bytes); + + let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); + + check_signature(&alice, pk_bytes, sig_bytes, message); + reveal_net_reward_impl(&alice, pk_bytes, 5); } } From 4ee76ea071442f8c6d6f1c31d88e4387425bb6ca Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:26:10 -0400 Subject: [PATCH 09/34] patch block.move on epoch interval adjustment --- framework/libra-framework/sources/block.move | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/framework/libra-framework/sources/block.move b/framework/libra-framework/sources/block.move index 8df83ea52..baaaa667b 100644 --- a/framework/libra-framework/sources/block.move +++ b/framework/libra-framework/sources/block.move @@ -81,10 +81,10 @@ module diem_framework::block { /// Update the epoch interval. /// Can only be called as part of the Diem governance proposal process established by the DiemGovernance module. public(friend) fun update_epoch_interval_microsecs( - _diem_framework: &signer, + diem_framework: &signer, new_epoch_interval: u64, ) acquires BlockResource { - //system_addresses::assert_vm(diem_framework); //TODO: remove after testing fork + system_addresses::assert_diem_framework(diem_framework); //TODO: remove after testing fork assert!(new_epoch_interval > 0, error::invalid_argument(EZERO_EPOCH_INTERVAL)); let block_resource = borrow_global_mut(@diem_framework); @@ -244,8 +244,6 @@ module diem_framework::block { }; if (timestamp - reconfiguration::last_reconfiguration_time() >= block_metadata_ref.epoch_interval) { - // if (!features::epoch_trigger_enabled() || - // testnet::is_testnet()) { if (testnet::is_testnet()) { epoch_boundary::epoch_boundary( vm, @@ -284,8 +282,7 @@ module diem_framework::block { } #[test(diem_framework = @diem_framework, account = @0x123)] - //#[expected_failure(abort_code = 0x50003, location = diem_framework::system_addresses)] //TODO: remove after testing fork - #[ignore] //TODO: remove after testing fork + #[expected_failure(abort_code = 0x50003, location = diem_framework::system_addresses)] public entry fun test_update_epoch_interval_unauthorized_should_fail( diem_framework: signer, account: signer, From 66ba523f00396cf0585cefb8a48203ac81f4c9af Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:40:13 -0400 Subject: [PATCH 10/34] adds convenience function to check remaining seconds in epoch, and implements the reveal window --- framework/libra-framework/sources/block.move | 24 +++++++++++++++++++ .../sources/ol_sources/secret_bid.move | 10 ++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/framework/libra-framework/sources/block.move b/framework/libra-framework/sources/block.move index baaaa667b..a111bd005 100644 --- a/framework/libra-framework/sources/block.move +++ b/framework/libra-framework/sources/block.move @@ -103,6 +103,30 @@ module diem_framework::block { borrow_global(@diem_framework).epoch_interval / 1000000 } + #[view] + /// Return rough remaining seconds in epoch + public fun get_remaining_epoch_secs(): u64 acquires BlockResource { + let now = timestamp::now_seconds(); + let last_epoch_secs = reconfiguration::last_reconfiguration_time() / 1000000; + let interval = get_epoch_interval_secs(); + if (now < last_epoch_secs) { // impossible underflow, some thign bad, or tests + return 0 + }; + + let deadline = last_epoch_secs + interval; + + if (now > deadline) { // we've run over the deadline + return 0 + }; + + // belt and suspenders + if (deadline > now) { + return deadline - now + }; + + return 0 + + } /// Set the metadata for the current block. /// The runtime always runs this before executing the transactions in a block. fun block_prologue( diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move index 485bde793..b2928bb23 100644 --- a/framework/libra-framework/sources/ol_sources/secret_bid.move +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -11,6 +11,8 @@ module ol_framework::secret_bid { use diem_std::ed25519; use diem_framework::account; use diem_framework::epoch_helper; + use diem_framework::block; + // use diem_framework::debug::print; @@ -42,8 +44,12 @@ module ol_framework::secret_bid { /// allow reveal transaction to be submitted fun in_reveal_window(): bool { // get the timestamp - // get the epoch ending timestamp - // get 5 mins prior + // NOTE: this might cause a dependency cycle issue in the future + let remaining_secs = block::get_remaining_epoch_secs(); + let five_mins = 60*5; + if (remaining_secs > five_mins) { + return false + }; true } From 16209ab0938db8dcf1a786415c6275e9fcd229e4 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Wed, 18 Sep 2024 18:48:38 -0400 Subject: [PATCH 11/34] finally implement the hash commit --- .../sources/ol_sources/secret_bid.move | 86 +++++++++++++------ 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move index b2928bb23..f24b4a5ad 100644 --- a/framework/libra-framework/sources/ol_sources/secret_bid.move +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -5,10 +5,14 @@ /////////////////////////////////////////////////////////////////////////// module ol_framework::secret_bid { + use std::bcs; use std::error; + use std::hash; use std::signer; use std::vector; + use diem_std::ed25519; + use diem_std::comparator; use diem_framework::account; use diem_framework::epoch_helper; use diem_framework::block; @@ -26,10 +30,13 @@ module ol_framework::secret_bid { const EMISMATCH_EPOCH: u64 = 3; /// Must submit bid before reveal window opens, and reveal bid only after. const ENOT_IN_REVEAL_WINDOW: u64 = 4; + /// Bad Alice, the reveal does not match the commit + const ECOMMIT_HASH_NOT_EQUAL: u64 = 5; struct CommittedBid has key { reveal_net_reward: u64, + commit_hash: vector, commit_epoch: u64, commit_signed_message: vector } @@ -57,52 +64,75 @@ module ol_framework::secret_bid { exists(account) } - public entry fun commit(user: &signer, message: vector) acquires CommittedBid { + public entry fun commit(user: &signer, hash: vector, signed_msg: vector) acquires CommittedBid { // don't allow commiting within reveal window assert!(!in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); - commit_net_reward_impl(user, message); + commit_net_reward_impl(user, hash, signed_msg); } - public entry fun reveal(user: &signer, pk: vector, net_reward: u64) acquires CommittedBid { + // TODO: the public key could be the consensus_pubkey that is already registered for the validator. That way the operator does not need the root key for bidding strategy. Note it's a BLS key. + public entry fun reveal(user: &signer, pk: vector, net_reward: u64, signed_msg: vector) acquires CommittedBid { // don't allow commiting within reveal window assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); - reveal_net_reward_impl(user, pk, net_reward); + reveal_net_reward_impl(user, pk, net_reward, signed_msg); } /// user transaction for setting a signed message with the bid - fun commit_net_reward_impl(user: &signer, message: vector) acquires CommittedBid { + fun commit_net_reward_impl(user: &signer, hash: vector, signed_bid: vector) acquires CommittedBid { if (!is_init(signer::address_of(user))) { move_to(user, CommittedBid { reveal_net_reward: 0, + commit_hash: vector::empty(), commit_epoch: 0, commit_signed_message: vector::empty(), }); }; let state = borrow_global_mut(signer::address_of(user)); - state.commit_signed_message = message; + state.commit_hash = hash; + state.commit_signed_message = signed_bid; state.commit_epoch = epoch_helper::get_current_epoch(); } + //NOTE: instead of a nonce, we are using the signed message for hash salting which would be private to the validator before the reveal. + fun make_hash(net_reward: u64, epoch: u64, signed_message: vector): vector{ + let bid = Bid { + net_reward, + epoch, + }; + + let bid_bytes = bcs::to_bytes(&bid); + vector::append(&mut bid_bytes, copy signed_message); + let commitment = hash::sha3_256(bid_bytes); + commitment + } + /// user sends transaction which takes the committed signed message /// submits the public key used to sign message, which we compare to the authentication key. /// we use the epoch as the sequence number, so that messages are different on each submission. - public fun reveal_net_reward_impl(user: &signer, pk: vector, net_reward: u64) acquires CommittedBid { + public fun reveal_net_reward_impl(user: &signer, pk: vector, net_reward: u64, signed_msg: vector) acquires CommittedBid { assert!(is_init(signer::address_of(user)), error::invalid_state(ECOMMIT_BID_NOT_INITIALIZED)); let state = borrow_global_mut(signer::address_of(user)); + // must reveal within the current epoch of the bid let epoch = epoch_helper::get_current_epoch(); assert!(epoch == state.commit_epoch, error::invalid_state(EMISMATCH_EPOCH)); + + let commitment = make_hash(net_reward, epoch, signed_msg); + let bid = Bid { net_reward, epoch, }; - check_signature(user, pk, state.commit_signed_message, bid); + check_signature(user, pk, signed_msg, bid); + + assert!(comparator::is_equal(&comparator::compare(&commitment, &state.commit_hash)), error::invalid_argument(ECOMMIT_HASH_NOT_EQUAL)); + state.reveal_net_reward = net_reward; } @@ -224,78 +254,86 @@ module ol_framework::secret_bid { let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); let new_addr = from_bcs::to_address(new_auth_key); let alice = account::create_account_for_test(new_addr); + let net_reward = 5; + let epoch = 0; let message = Bid { - net_reward: 5, - epoch: 0, + net_reward, + epoch, }; let to_sig = ed25519::sign_struct(&new_sk, copy message); let sig_bytes = ed25519::signature_to_bytes(&to_sig); + let hash = make_hash(net_reward, epoch, sig_bytes); + // end set-up - commit_net_reward_impl(&alice, sig_bytes); + commit_net_reward_impl(&alice, hash, sig_bytes); } #[test(framework = @0x1)] fun test_reveal(framework: &signer) acquires CommittedBid { use diem_std::from_bcs; - let this_epoch = 1; - epoch_helper::test_set_epoch(framework, this_epoch); + let epoch = 1; + epoch_helper::test_set_epoch(framework, epoch); let (new_sk, new_pk) = ed25519::generate_keys(); let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); let new_addr = from_bcs::to_address(new_auth_key); let alice = account::create_account_for_test(new_addr); - + let net_reward = 5; let message = Bid { - net_reward: 5, - epoch: this_epoch, + net_reward, + epoch, }; let to_sig = ed25519::sign_struct(&new_sk, copy message); let sig_bytes = ed25519::signature_to_bytes(&to_sig); + let hash = make_hash(net_reward, epoch, sig_bytes); // end set-up - commit_net_reward_impl(&alice, sig_bytes); + commit_net_reward_impl(&alice, hash, sig_bytes); let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); check_signature(&alice, pk_bytes, sig_bytes, message); - reveal_net_reward_impl(&alice, pk_bytes, 5); + reveal_net_reward_impl(&alice, pk_bytes, 5, sig_bytes); } #[test(framework = @0x1)] #[expected_failure(abort_code = 65538, location = Self)] fun test_reveal_sad_wrong_epoch(framework: &signer) acquires CommittedBid { use diem_std::from_bcs; - let this_epoch = 1; - epoch_helper::test_set_epoch(framework, this_epoch); + let epoch = 1; + epoch_helper::test_set_epoch(framework, epoch); let (new_sk, new_pk) = ed25519::generate_keys(); let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); let new_addr = from_bcs::to_address(new_auth_key); let alice = account::create_account_for_test(new_addr); + let net_reward = 5; + let wrong_epoch = 100; let message = Bid { - net_reward: 5, - epoch: 100, // wrong epoch, we are at 1 + net_reward, + epoch: wrong_epoch, // wrong epoch, we are at 1 }; let to_sig = ed25519::sign_struct(&new_sk, copy message); let sig_bytes = ed25519::signature_to_bytes(&to_sig); + let hash = make_hash(net_reward, epoch, sig_bytes); // end set-up - commit_net_reward_impl(&alice, sig_bytes); + commit_net_reward_impl(&alice, hash, sig_bytes); let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); check_signature(&alice, pk_bytes, sig_bytes, message); - reveal_net_reward_impl(&alice, pk_bytes, 5); + reveal_net_reward_impl(&alice, pk_bytes, 5, sig_bytes); } } From d207ee8bbe7535482d3f8bd40082598fe489f5fe Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:39:22 -0400 Subject: [PATCH 12/34] remove signed message state, don't require signing keypair to be of the user sending. --- .../sources/ol_sources/secret_bid.move | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move index f24b4a5ad..ccb2894a7 100644 --- a/framework/libra-framework/sources/ol_sources/secret_bid.move +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -13,17 +13,15 @@ module ol_framework::secret_bid { use diem_std::ed25519; use diem_std::comparator; - use diem_framework::account; use diem_framework::epoch_helper; use diem_framework::block; - + #[test_only] + use diem_framework::account; // use diem_framework::debug::print; /// User bidding not initialized - const ECOMMIT_BID_NOT_INITIALIZED: u64 = 0; - /// Specified current public key is not correct - const EWRONG_CURRENT_PUBLIC_KEY: u64 = 1; + const ECOMMIT_BID_NOT_INITIALIZED: u64 = 1; /// Invalid signature on bid message const EINVALID_SIGNATURE: u64 = 2; /// Must reveal bid in same epoch as committed @@ -31,14 +29,13 @@ module ol_framework::secret_bid { /// Must submit bid before reveal window opens, and reveal bid only after. const ENOT_IN_REVEAL_WINDOW: u64 = 4; /// Bad Alice, the reveal does not match the commit - const ECOMMIT_HASH_NOT_EQUAL: u64 = 5; + const ECOMMIT_DIGEST_NOT_EQUAL: u64 = 5; struct CommittedBid has key { reveal_net_reward: u64, - commit_hash: vector, + commit_digest: vector, commit_epoch: u64, - commit_signed_message: vector } struct Bid has drop, copy { @@ -64,10 +61,10 @@ module ol_framework::secret_bid { exists(account) } - public entry fun commit(user: &signer, hash: vector, signed_msg: vector) acquires CommittedBid { + public entry fun commit(user: &signer, digest: vector) acquires CommittedBid { // don't allow commiting within reveal window assert!(!in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); - commit_net_reward_impl(user, hash, signed_msg); + commit_net_reward_impl(user, digest); } // TODO: the public key could be the consensus_pubkey that is already registered for the validator. That way the operator does not need the root key for bidding strategy. Note it's a BLS key. @@ -78,24 +75,24 @@ module ol_framework::secret_bid { } /// user transaction for setting a signed message with the bid - fun commit_net_reward_impl(user: &signer, hash: vector, signed_bid: vector) acquires CommittedBid { + fun commit_net_reward_impl(user: &signer, digest: vector) acquires CommittedBid { if (!is_init(signer::address_of(user))) { move_to(user, CommittedBid { reveal_net_reward: 0, - commit_hash: vector::empty(), + commit_digest: vector::empty(), commit_epoch: 0, - commit_signed_message: vector::empty(), }); }; let state = borrow_global_mut(signer::address_of(user)); - state.commit_hash = hash; - state.commit_signed_message = signed_bid; + state.commit_digest = digest; state.commit_epoch = epoch_helper::get_current_epoch(); } - //NOTE: instead of a nonce, we are using the signed message for hash salting which would be private to the validator before the reveal. + /// The hashing protocol which the client will be submitting commitments + /// instead of a sequence number, we are using the signed message for hashing nonce, which would be private to the validator before the reveal. + // TODO: should we also use a salt or overkill for these purposes? fun make_hash(net_reward: u64, epoch: u64, signed_message: vector): vector{ let bid = Bid { net_reward, @@ -129,22 +126,27 @@ module ol_framework::secret_bid { epoch, }; - check_signature(user, pk, signed_msg, bid); + check_signature(pk, signed_msg, bid); - assert!(comparator::is_equal(&comparator::compare(&commitment, &state.commit_hash)), error::invalid_argument(ECOMMIT_HASH_NOT_EQUAL)); + assert!(comparator::is_equal(&comparator::compare(&commitment, &state.commit_digest)), error::invalid_argument(ECOMMIT_DIGEST_NOT_EQUAL)); state.reveal_net_reward = net_reward; } - fun check_signature(user: &signer, account_public_key_bytes: vector, signed_message_bytes: vector, bid_message: Bid) { + fun check_signature(account_public_key_bytes: vector, signed_message_bytes: vector, bid_message: Bid) { let pubkey = ed25519::new_unvalidated_public_key_from_bytes(account_public_key_bytes); - let expected_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&pubkey); - assert!(account::get_authentication_key(signer::address_of(user)) == expected_auth_key, error::invalid_argument(EWRONG_CURRENT_PUBLIC_KEY)); // confirm that the signature bytes belong to the message (the Bid struct) let sig = ed25519::new_signature_from_bytes(signed_message_bytes); assert!(ed25519::signature_verify_strict_t(&sig, &pubkey, bid_message), error::invalid_argument(EINVALID_SIGNATURE)); + + ///////// + // NOTE: previously we would check if the public key for the signed message + // belonged to this account. However that is not necessary for prevention of the replay attack. Any ed25519 key pair would be acceptable for producing the nonce, and the user may prefer to use one that is different than their main account's key. + // let expected_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&pubkey); + // assert!(account::get_authentication_key(signer::address_of(user)) == expected_auth_key, error::invalid_argument(EWRONG_CURRENT_PUBLIC_KEY)); + //////// } //////// TESTS //////// @@ -183,7 +185,7 @@ module ol_framework::secret_bid { let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); let new_addr = from_bcs::to_address(new_auth_key); - let alice = account::create_account_for_test(new_addr); + let _alice = account::create_account_for_test(new_addr); let message = Bid { net_reward: 0, @@ -201,7 +203,7 @@ module ol_framework::secret_bid { let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); - check_signature(&alice, pk_bytes, sig_bytes, message); + check_signature(pk_bytes, sig_bytes, message); } @@ -214,7 +216,7 @@ module ol_framework::secret_bid { let new_pk_unvalidated = ed25519::public_key_to_unvalidated(&new_pk); let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); let new_addr = from_bcs::to_address(new_auth_key); - let alice = account::create_account_for_test(new_addr); + let _alice = account::create_account_for_test(new_addr); let message = Bid { net_reward: 0, @@ -237,7 +239,7 @@ module ol_framework::secret_bid { epoch: 0, }; - check_signature(&alice, pk_bytes, sig_bytes, message); + check_signature(pk_bytes, sig_bytes, message); } @@ -264,11 +266,10 @@ module ol_framework::secret_bid { let to_sig = ed25519::sign_struct(&new_sk, copy message); let sig_bytes = ed25519::signature_to_bytes(&to_sig); - let hash = make_hash(net_reward, epoch, sig_bytes); - + let digest = make_hash(net_reward, epoch, sig_bytes); // end set-up - commit_net_reward_impl(&alice, hash, sig_bytes); + commit_net_reward_impl(&alice, digest); } #[test(framework = @0x1)] @@ -291,14 +292,14 @@ module ol_framework::secret_bid { let to_sig = ed25519::sign_struct(&new_sk, copy message); let sig_bytes = ed25519::signature_to_bytes(&to_sig); - let hash = make_hash(net_reward, epoch, sig_bytes); + let digest = make_hash(net_reward, epoch, sig_bytes); // end set-up - commit_net_reward_impl(&alice, hash, sig_bytes); + commit_net_reward_impl(&alice, digest); let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); - check_signature(&alice, pk_bytes, sig_bytes, message); + check_signature(pk_bytes, sig_bytes, message); reveal_net_reward_impl(&alice, pk_bytes, 5, sig_bytes); } @@ -325,14 +326,14 @@ module ol_framework::secret_bid { let to_sig = ed25519::sign_struct(&new_sk, copy message); let sig_bytes = ed25519::signature_to_bytes(&to_sig); - let hash = make_hash(net_reward, epoch, sig_bytes); + let digest = make_hash(net_reward, epoch, sig_bytes); // end set-up - commit_net_reward_impl(&alice, hash, sig_bytes); + commit_net_reward_impl(&alice, digest); let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); - check_signature(&alice, pk_bytes, sig_bytes, message); + check_signature(pk_bytes, sig_bytes, message); reveal_net_reward_impl(&alice, pk_bytes, 5, sig_bytes); } From 814dbe4360113a10fe9b162022f0e794daac46cb Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Thu, 19 Sep 2024 12:39:32 -0400 Subject: [PATCH 13/34] randomness test was a noop --- framework/libra-framework/sources/randomness.move | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/framework/libra-framework/sources/randomness.move b/framework/libra-framework/sources/randomness.move index 682d14076..f9d998fcd 100644 --- a/framework/libra-framework/sources/randomness.move +++ b/framework/libra-framework/sources/randomness.move @@ -29,8 +29,8 @@ module diem_framework::randomness { use std::vector; use diem_framework::system_addresses; use diem_framework::transaction_context; - #[test_only] - use diem_std::debug; + // #[test_only] + // use diem_std::debug; #[test_only] use diem_std::table_with_length; @@ -450,13 +450,15 @@ module diem_framework::randomness { } #[test(fx = @diem_framework)] - fun randomness_smoke_test(fx: signer) acquires PerBlockRandomness { + fun randomness_integer(fx: signer) acquires PerBlockRandomness { initialize(&fx); set_seed(x"0000000000000000000000000000000000000000000000000000000000000000"); // Test cases should always have no bias for any randomness call. // assert!(is_unbiasable(), 0); let num = u64_integer(); - debug::print(&num); + assert!(num > 0, 0); + let num2 = u64_integer(); + assert!(num != num2, 1); } // #[test_only] From a0dc6e26dafc7452f0e439d22bb3e4e66896ee23 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:37:41 -0400 Subject: [PATCH 14/34] storing entry_fee instead of net reward --- .../sources/ol_sources/secret_bid.move | 75 +++++++++++-------- tools/txs/tests/trigger_epoch.rs | 2 +- 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move index ccb2894a7..12e403bbb 100644 --- a/framework/libra-framework/sources/ol_sources/secret_bid.move +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -16,6 +16,8 @@ module ol_framework::secret_bid { use diem_framework::epoch_helper; use diem_framework::block; + use ol_framework::testnet; + #[test_only] use diem_framework::account; // use diem_framework::debug::print; @@ -33,13 +35,13 @@ module ol_framework::secret_bid { struct CommittedBid has key { - reveal_net_reward: u64, + reveal_entry_fee: u64, commit_digest: vector, commit_epoch: u64, } struct Bid has drop, copy { - net_reward: u64, + entry_fee: u64, epoch: u64, } @@ -50,8 +52,15 @@ module ol_framework::secret_bid { // get the timestamp // NOTE: this might cause a dependency cycle issue in the future let remaining_secs = block::get_remaining_epoch_secs(); - let five_mins = 60*5; - if (remaining_secs > five_mins) { + let window = if (testnet::is_testnet()) { + // ten secs + 10 + } else { + // five mins + 60*5 + }; + + if (remaining_secs > window) { return false }; true @@ -64,22 +73,22 @@ module ol_framework::secret_bid { public entry fun commit(user: &signer, digest: vector) acquires CommittedBid { // don't allow commiting within reveal window assert!(!in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); - commit_net_reward_impl(user, digest); + commit_entry_fee_impl(user, digest); } // TODO: the public key could be the consensus_pubkey that is already registered for the validator. That way the operator does not need the root key for bidding strategy. Note it's a BLS key. - public entry fun reveal(user: &signer, pk: vector, net_reward: u64, signed_msg: vector) acquires CommittedBid { + public entry fun reveal(user: &signer, pk: vector, entry_fee: u64, signed_msg: vector) acquires CommittedBid { // don't allow commiting within reveal window assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); - reveal_net_reward_impl(user, pk, net_reward, signed_msg); + reveal_entry_fee_impl(user, pk, entry_fee, signed_msg); } /// user transaction for setting a signed message with the bid - fun commit_net_reward_impl(user: &signer, digest: vector) acquires CommittedBid { + fun commit_entry_fee_impl(user: &signer, digest: vector) acquires CommittedBid { if (!is_init(signer::address_of(user))) { move_to(user, CommittedBid { - reveal_net_reward: 0, + reveal_entry_fee: 0, commit_digest: vector::empty(), commit_epoch: 0, }); @@ -93,9 +102,9 @@ module ol_framework::secret_bid { /// The hashing protocol which the client will be submitting commitments /// instead of a sequence number, we are using the signed message for hashing nonce, which would be private to the validator before the reveal. // TODO: should we also use a salt or overkill for these purposes? - fun make_hash(net_reward: u64, epoch: u64, signed_message: vector): vector{ + fun make_hash(entry_fee: u64, epoch: u64, signed_message: vector): vector{ let bid = Bid { - net_reward, + entry_fee, epoch, }; @@ -108,7 +117,7 @@ module ol_framework::secret_bid { /// user sends transaction which takes the committed signed message /// submits the public key used to sign message, which we compare to the authentication key. /// we use the epoch as the sequence number, so that messages are different on each submission. - public fun reveal_net_reward_impl(user: &signer, pk: vector, net_reward: u64, signed_msg: vector) acquires CommittedBid { + public fun reveal_entry_fee_impl(user: &signer, pk: vector, entry_fee: u64, signed_msg: vector) acquires CommittedBid { assert!(is_init(signer::address_of(user)), error::invalid_state(ECOMMIT_BID_NOT_INITIALIZED)); let state = borrow_global_mut(signer::address_of(user)); @@ -119,10 +128,10 @@ module ol_framework::secret_bid { assert!(epoch == state.commit_epoch, error::invalid_state(EMISMATCH_EPOCH)); - let commitment = make_hash(net_reward, epoch, signed_msg); + let commitment = make_hash(entry_fee, epoch, signed_msg); let bid = Bid { - net_reward, + entry_fee, epoch, }; @@ -131,7 +140,7 @@ module ol_framework::secret_bid { assert!(comparator::is_equal(&comparator::compare(&commitment, &state.commit_digest)), error::invalid_argument(ECOMMIT_DIGEST_NOT_EQUAL)); - state.reveal_net_reward = net_reward; + state.reveal_entry_fee = entry_fee; } fun check_signature(account_public_key_bytes: vector, signed_message_bytes: vector, bid_message: Bid) { @@ -161,7 +170,7 @@ module ol_framework::secret_bid { let _alice = account::create_account_for_test(new_addr); let message = Bid { - net_reward: 0, + entry_fee: 0, epoch: 0, }; @@ -188,7 +197,7 @@ module ol_framework::secret_bid { let _alice = account::create_account_for_test(new_addr); let message = Bid { - net_reward: 0, + entry_fee: 0, epoch: 0, }; @@ -219,7 +228,7 @@ module ol_framework::secret_bid { let _alice = account::create_account_for_test(new_addr); let message = Bid { - net_reward: 0, + entry_fee: 0, epoch: 0, }; @@ -235,7 +244,7 @@ module ol_framework::secret_bid { let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); let message = Bid { - net_reward: 2, // incorrect + entry_fee: 2, // incorrect epoch: 0, }; @@ -256,20 +265,20 @@ module ol_framework::secret_bid { let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); let new_addr = from_bcs::to_address(new_auth_key); let alice = account::create_account_for_test(new_addr); - let net_reward = 5; + let entry_fee = 5; let epoch = 0; let message = Bid { - net_reward, + entry_fee, epoch, }; let to_sig = ed25519::sign_struct(&new_sk, copy message); let sig_bytes = ed25519::signature_to_bytes(&to_sig); - let digest = make_hash(net_reward, epoch, sig_bytes); + let digest = make_hash(entry_fee, epoch, sig_bytes); // end set-up - commit_net_reward_impl(&alice, digest); + commit_entry_fee_impl(&alice, digest); } #[test(framework = @0x1)] @@ -284,24 +293,24 @@ module ol_framework::secret_bid { let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); let new_addr = from_bcs::to_address(new_auth_key); let alice = account::create_account_for_test(new_addr); - let net_reward = 5; + let entry_fee = 5; let message = Bid { - net_reward, + entry_fee, epoch, }; let to_sig = ed25519::sign_struct(&new_sk, copy message); let sig_bytes = ed25519::signature_to_bytes(&to_sig); - let digest = make_hash(net_reward, epoch, sig_bytes); + let digest = make_hash(entry_fee, epoch, sig_bytes); // end set-up - commit_net_reward_impl(&alice, digest); + commit_entry_fee_impl(&alice, digest); let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); check_signature(pk_bytes, sig_bytes, message); - reveal_net_reward_impl(&alice, pk_bytes, 5, sig_bytes); + reveal_entry_fee_impl(&alice, pk_bytes, 5, sig_bytes); } #[test(framework = @0x1)] @@ -316,25 +325,25 @@ module ol_framework::secret_bid { let new_auth_key = ed25519::unvalidated_public_key_to_authentication_key(&new_pk_unvalidated); let new_addr = from_bcs::to_address(new_auth_key); let alice = account::create_account_for_test(new_addr); - let net_reward = 5; + let entry_fee = 5; let wrong_epoch = 100; let message = Bid { - net_reward, + entry_fee, epoch: wrong_epoch, // wrong epoch, we are at 1 }; let to_sig = ed25519::sign_struct(&new_sk, copy message); let sig_bytes = ed25519::signature_to_bytes(&to_sig); - let digest = make_hash(net_reward, epoch, sig_bytes); + let digest = make_hash(entry_fee, epoch, sig_bytes); // end set-up - commit_net_reward_impl(&alice, digest); + commit_entry_fee_impl(&alice, digest); let pk_bytes = ed25519::unvalidated_public_key_to_bytes(&new_pk_unvalidated); check_signature(pk_bytes, sig_bytes, message); - reveal_net_reward_impl(&alice, pk_bytes, 5, sig_bytes); + reveal_entry_fee_impl(&alice, pk_bytes, 5, sig_bytes); } } diff --git a/tools/txs/tests/trigger_epoch.rs b/tools/txs/tests/trigger_epoch.rs index ba1450884..72454c0e1 100644 --- a/tools/txs/tests/trigger_epoch.rs +++ b/tools/txs/tests/trigger_epoch.rs @@ -5,7 +5,7 @@ use libra_smoke_tests::libra_smoke::LibraSmoke; use libra_txs::{submit_transaction::Sender, txs_cli_governance::GovernanceTxs}; /// Test triggering a new epoch #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -#[ignore] // TODO +// #[ignore] // TODO async fn trigger_epoch() -> anyhow::Result<()> { // create libra swarm and get app config for the validator let mut ls = LibraSmoke::new(Some(1), None) From 247a9d1f11331a436cda2d8f2a5d7755b39a11b5 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Wed, 25 Sep 2024 12:00:58 -0400 Subject: [PATCH 15/34] check for current bidding, and save historical --- .../sources/ol_sources/secret_bid.move | 101 ++++++++++++------ 1 file changed, 70 insertions(+), 31 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move index 12e403bbb..b0dd9a4d0 100644 --- a/framework/libra-framework/sources/ol_sources/secret_bid.move +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -36,6 +36,7 @@ module ol_framework::secret_bid { struct CommittedBid has key { reveal_entry_fee: u64, + entry_fee_history: vector, // keep previous 7 days bids commit_digest: vector, commit_epoch: u64, } @@ -45,38 +46,14 @@ module ol_framework::secret_bid { epoch: u64, } - /// check if we are within the reveal window - /// do not allow bids within the reveal window - /// allow reveal transaction to be submitted - fun in_reveal_window(): bool { - // get the timestamp - // NOTE: this might cause a dependency cycle issue in the future - let remaining_secs = block::get_remaining_epoch_secs(); - let window = if (testnet::is_testnet()) { - // ten secs - 10 - } else { - // five mins - 60*5 - }; - - if (remaining_secs > window) { - return false - }; - true - } - - fun is_init(account: address): bool { - exists(account) - } - + /// Transaction entry function for committing bid public entry fun commit(user: &signer, digest: vector) acquires CommittedBid { // don't allow commiting within reveal window assert!(!in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); commit_entry_fee_impl(user, digest); } - // TODO: the public key could be the consensus_pubkey that is already registered for the validator. That way the operator does not need the root key for bidding strategy. Note it's a BLS key. + /// Transaction entry function for revealing bid public entry fun reveal(user: &signer, pk: vector, entry_fee: u64, signed_msg: vector) acquires CommittedBid { // don't allow commiting within reveal window assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); @@ -89,14 +66,33 @@ module ol_framework::secret_bid { if (!is_init(signer::address_of(user))) { move_to(user, CommittedBid { reveal_entry_fee: 0, + entry_fee_history: vector::empty(), commit_digest: vector::empty(), commit_epoch: 0, }); }; let state = borrow_global_mut(signer::address_of(user)); + // if first commit in an epoch reset the counters + maybe_reset_bids(state); + state.commit_digest = digest; - state.commit_epoch = epoch_helper::get_current_epoch(); + } + + /// if this is the first commit in the epoch then we can reset bids + fun maybe_reset_bids(state: &mut CommittedBid) { + if (epoch_helper::get_current_epoch() > state.commit_epoch) { + // restart bidding + state.commit_epoch = epoch_helper::get_current_epoch(); + + vector::push_back(&mut state.entry_fee_history, state.reveal_entry_fee); + + if (vector::length(&state.entry_fee_history) > 7) { + vector::trim(&mut state.entry_fee_history, 7); + }; + + state.reveal_entry_fee = 0; + } } /// The hashing protocol which the client will be submitting commitments @@ -121,13 +117,10 @@ module ol_framework::secret_bid { assert!(is_init(signer::address_of(user)), error::invalid_state(ECOMMIT_BID_NOT_INITIALIZED)); let state = borrow_global_mut(signer::address_of(user)); - - // must reveal within the current epoch of the bid let epoch = epoch_helper::get_current_epoch(); assert!(epoch == state.commit_epoch, error::invalid_state(EMISMATCH_EPOCH)); - let commitment = make_hash(entry_fee, epoch, signed_msg); let bid = Bid { @@ -139,7 +132,6 @@ module ol_framework::secret_bid { assert!(comparator::is_equal(&comparator::compare(&commitment, &state.commit_digest)), error::invalid_argument(ECOMMIT_DIGEST_NOT_EQUAL)); - state.reveal_entry_fee = entry_fee; } @@ -158,6 +150,53 @@ module ol_framework::secret_bid { //////// } + ///////// GETTERS //////// + + #[view] + /// check if we are within the reveal window + /// do not allow bids within the reveal window + /// allow reveal transaction to be submitted + public fun in_reveal_window(): bool { + // get the timestamp + // NOTE: using block:: might cause a dependency cycle issue in the future + let remaining_secs = block::get_remaining_epoch_secs(); + let window = if (testnet::is_testnet()) { + // ten secs + 10 + } else { + // five mins + 60*5 + }; + + if (remaining_secs > window) { + return false + }; + true + } + + #[view] + public fun is_init(account: address): bool { + exists(account) + } + + #[view] + /// get the current bid, and exclude bids that are stale + public fun current_revealed_bid(user: address): u64 acquires CommittedBid { + // if we are not in reveal window this information will be confusing. + assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); + + let state = borrow_global(user); + if (state.commit_epoch != epoch_helper::get_current_epoch()) return 0; + state.reveal_entry_fee + } + + #[view] + /// get the current bid, and exclude bids that are stale + public fun historical_bids(user: address): vector acquires CommittedBid { + let state = borrow_global(user); + state.entry_fee_history + } + //////// TESTS //////// #[test] fun test_sign_message() { From 053fbce30208e39ffc986840f7fb4f869142b9bc Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:50:28 -0400 Subject: [PATCH 16/34] sorting of list of secret bids --- .../sources/ol_sources/secret_bid.move | 67 +++++++++++++++++-- .../sources/ol_sources/testnet.move | 7 ++ 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move index b0dd9a4d0..369740c8f 100644 --- a/framework/libra-framework/sources/ol_sources/secret_bid.move +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -17,10 +17,13 @@ module ol_framework::secret_bid { use diem_framework::block; use ol_framework::testnet; + use ol_framework::address_utils; #[test_only] use diem_framework::account; - // use diem_framework::debug::print; + #[test_only] + use diem_framework::system_addresses; + /// User bidding not initialized const ECOMMIT_BID_NOT_INITIALIZED: u64 = 1; @@ -150,8 +153,32 @@ module ol_framework::secret_bid { //////// } + /// populate the list of bids, but don't sort + fun get_bids_for_account_list(list: vector
): (vector
, vector) acquires CommittedBid { + let bids_vec = vector[]; + vector::for_each_ref(&list, |el| { + let b = get_bid_unchecked(*el); + vector::push_back(&mut bids_vec, b); + }); + + (list, bids_vec) + } + + /// get a sorted list of the addresses and their entry fee + public(friend) fun get_bids_by_account_sort_low_high(list: vector
): (vector
, vector) acquires CommittedBid { + let (addr, bids) = get_bids_for_account_list(list); + address_utils::sort_by_values(&mut addr, &mut bids); + (addr, bids) + } + ///////// GETTERS //////// + fun get_bid_unchecked(user: address): u64 acquires CommittedBid { + let state = borrow_global(user); + if (state.commit_epoch != epoch_helper::get_current_epoch()) return 0; + state.reveal_entry_fee + } + #[view] /// check if we are within the reveal window /// do not allow bids within the reveal window @@ -185,9 +212,7 @@ module ol_framework::secret_bid { // if we are not in reveal window this information will be confusing. assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); - let state = borrow_global(user); - if (state.commit_epoch != epoch_helper::get_current_epoch()) return 0; - state.reveal_entry_fee + get_bid_unchecked(user) } #[view] @@ -198,6 +223,19 @@ module ol_framework::secret_bid { } //////// TESTS //////// + + #[test_only] + public(friend) fun mock_revealed_bid(framework: &signer, user: &signer, reveal_entry_fee: u64, commit_epoch: u64) { + system_addresses::assert_diem_framework(framework); + testnet::assert_testnet(framework); + move_to(user, CommittedBid { + reveal_entry_fee, + entry_fee_history: vector[0], + commit_digest: vector[0], + commit_epoch, + }); + } + #[test] fun test_sign_message() { use diem_std::from_bcs; @@ -385,4 +423,25 @@ module ol_framework::secret_bid { reveal_entry_fee_impl(&alice, pk_bytes, 5, sig_bytes); } + + #[test(framework = @0x1, alice = @0x10001, bob = @0x10002, carol = @0x10003)] + fun test_sorting_secret_bids(framework: &signer, alice: &signer, bob: &signer, carol: &signer) acquires CommittedBid { + testnet::initialize(framework); + + let this_epoch = 1; + epoch_helper::test_set_epoch(framework, this_epoch); + + mock_revealed_bid(framework, alice, 234, this_epoch); + mock_revealed_bid(framework, bob, 1, this_epoch); + mock_revealed_bid(framework, carol, 30, this_epoch); + + let (accounts, bids) = get_bids_for_account_list(vector[@0x10001, @0x10002, @0x10003]); + + assert!(*vector::borrow(&accounts, 0) == @0x10001, 713570001); + assert!(*vector::borrow(&bids, 0) == 234, 713570002); + + let (sorted_accounts, sorted_bids) = get_bids_by_account_sort_low_high(vector[@0x10001, @0x10002, @0x10003]); + assert!(*vector::borrow(&sorted_accounts, 0) == @0x10002, 713570003); + assert!(*vector::borrow(&sorted_bids, 0) == 1, 713570004); + } } diff --git a/framework/libra-framework/sources/ol_sources/testnet.move b/framework/libra-framework/sources/ol_sources/testnet.move index 1f0aca795..88ea53e1e 100644 --- a/framework/libra-framework/sources/ol_sources/testnet.move +++ b/framework/libra-framework/sources/ol_sources/testnet.move @@ -33,6 +33,13 @@ module ol_framework::testnet { chain_id::get() == 2 // TESTNET named chain } + #[test_only] + public fun initialize(vm: &signer) { + use diem_framework::system_addresses; + system_addresses::assert_ol(vm); + chain_id::initialize_for_test(vm, 4); + } + #[test_only] public fun unset(vm: &signer) { use diem_framework::system_addresses; From 5125830eefa62dabb69f49d8b14c3b7f58701606 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:49:16 -0400 Subject: [PATCH 17/34] major surgery to replace secret_bid with pof auction state 16/383 tests failing --- .../drop-user-tools/last_goodbye.test.move | 4 +- framework/libra-framework/sources/block.move | 38 ++------ .../libra-framework/sources/block.spec.move | 4 - .../sources/ol_sources/epoch_boundary.move | 2 +- .../sources/ol_sources/mock.move | 33 +++---- .../sources/ol_sources/proof_of_fee.move | 86 +++++++++---------- .../sources/ol_sources/secret_bid.move | 60 ++++++++++--- .../ol_sources/tests/_meta_epoch.test.move | 10 +-- .../ol_sources/tests/boundary.test.move | 32 +++---- .../ol_sources/tests/proof_of_fee.test.move | 79 ++++++++--------- .../ol_sources/tests/slow_wallet.test.move | 2 +- .../sources/ol_sources/tests/stake.test.move | 2 +- .../tests/validator_reward.test.move | 8 +- .../tests/vote_lib/multi_action.test.move | 84 +++++++++--------- .../sources/ol_sources/tests/vouch.test.move | 4 + .../vote_lib/tally/binary_tally.move | 2 +- .../sources/reconfiguration.move | 57 ++++++++++-- 17 files changed, 276 insertions(+), 231 deletions(-) diff --git a/framework/drop-user-tools/last_goodbye.test.move b/framework/drop-user-tools/last_goodbye.test.move index b79ed9380..731dcfde3 100644 --- a/framework/drop-user-tools/last_goodbye.test.move +++ b/framework/drop-user-tools/last_goodbye.test.move @@ -14,7 +14,7 @@ module ol_framework::test_last_goodbye { let _vals = mock::genesis_n_vals(framework, 1); // we are at epoch 0 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 0, 7357001); last_goodbye::danger_test_last_goodby(vm, bob); @@ -23,7 +23,7 @@ module ol_framework::test_last_goodbye { last_goodbye::danger_user_gc(vm, bob); mock::trigger_epoch(framework); // epoch 1 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 1, 7357002); let vals = stake::get_current_validators(); diff --git a/framework/libra-framework/sources/block.move b/framework/libra-framework/sources/block.move index a111bd005..89310c800 100644 --- a/framework/libra-framework/sources/block.move +++ b/framework/libra-framework/sources/block.move @@ -67,6 +67,8 @@ module diem_framework::block { system_addresses::assert_diem_framework(diem_framework); assert!(epoch_interval_microsecs > 0, error::invalid_argument(EZERO_EPOCH_INTERVAL)); + reconfiguration::set_epoch_interval(diem_framework, epoch_interval_microsecs); + move_to( diem_framework, BlockResource { @@ -91,42 +93,14 @@ module diem_framework::block { let old_epoch_interval = block_resource.epoch_interval; block_resource.epoch_interval = new_epoch_interval; + reconfiguration::set_epoch_interval(diem_framework, new_epoch_interval); + event::emit_event( &mut block_resource.update_epoch_interval_events, UpdateEpochIntervalEvent { old_epoch_interval, new_epoch_interval }, ); } - #[view] - /// Return epoch interval in seconds. - public fun get_epoch_interval_secs(): u64 acquires BlockResource { - borrow_global(@diem_framework).epoch_interval / 1000000 - } - - #[view] - /// Return rough remaining seconds in epoch - public fun get_remaining_epoch_secs(): u64 acquires BlockResource { - let now = timestamp::now_seconds(); - let last_epoch_secs = reconfiguration::last_reconfiguration_time() / 1000000; - let interval = get_epoch_interval_secs(); - if (now < last_epoch_secs) { // impossible underflow, some thign bad, or tests - return 0 - }; - - let deadline = last_epoch_secs + interval; - - if (now > deadline) { // we've run over the deadline - return 0 - }; - - // belt and suspenders - if (deadline > now) { - return deadline - now - }; - - return 0 - - } /// Set the metadata for the current block. /// The runtime always runs this before executing the transactions in a block. fun block_prologue( @@ -271,11 +245,11 @@ module diem_framework::block { if (testnet::is_testnet()) { epoch_boundary::epoch_boundary( vm, - reconfiguration::get_current_epoch(), + reconfiguration::current_epoch(), round ); } else { - epoch_boundary::enable_epoch_trigger(vm, reconfiguration::get_current_epoch()); + epoch_boundary::enable_epoch_trigger(vm, reconfiguration::current_epoch()); } } } diff --git a/framework/libra-framework/sources/block.spec.move b/framework/libra-framework/sources/block.spec.move index 0b5cd0f58..c0a6b7b6d 100644 --- a/framework/libra-framework/sources/block.spec.move +++ b/framework/libra-framework/sources/block.spec.move @@ -115,10 +115,6 @@ spec diem_framework::block { ensures block_resource.epoch_interval == new_epoch_interval; } - spec get_epoch_interval_secs(): u64 { - aborts_if !exists(@diem_framework); - } - spec get_current_block_height(): u64 { aborts_if !exists(@diem_framework); } diff --git a/framework/libra-framework/sources/ol_sources/epoch_boundary.move b/framework/libra-framework/sources/ol_sources/epoch_boundary.move index f16de9b44..dadfcaa14 100644 --- a/framework/libra-framework/sources/ol_sources/epoch_boundary.move +++ b/framework/libra-framework/sources/ol_sources/epoch_boundary.move @@ -260,7 +260,7 @@ module diem_framework::epoch_boundary { assert!(state.ready, ETRIGGER_NOT_READY); // greater than, in case there is an epoch change due to an epoch bump in // testnet Twin tools, or a rescue operation. - assert!(state.closing_epoch <= reconfiguration::get_current_epoch(), + assert!(state.closing_epoch <= reconfiguration::current_epoch(), ENOT_SAME_EPOCH); true } diff --git a/framework/libra-framework/sources/ol_sources/mock.move b/framework/libra-framework/sources/ol_sources/mock.move index 8bf813019..3cd2b4a6d 100644 --- a/framework/libra-framework/sources/ol_sources/mock.move +++ b/framework/libra-framework/sources/ol_sources/mock.move @@ -23,6 +23,7 @@ module ol_framework::mock { use ol_framework::epoch_helper; use ol_framework::musical_chairs; use ol_framework::pledge_accounts; + use ol_framework::secret_bid; // use diem_std::debug::print; @@ -94,43 +95,41 @@ module ol_framework::mock { //////// PROOF OF FEE //////// #[test_only] - public fun pof_default(): (vector
, vector, vector){ + public fun pof_default(framework: &signer): (vector
, vector){ let vals = stake::get_current_validators(); - let (bids, expiry) = mock_bids(&vals); + let bids = mock_bids(framework, &vals); // make all validators pay auction fee // the clearing price in the fibonacci sequence is is 1 - let (alice_bid, _) = proof_of_fee::current_bid(*vector::borrow(&vals, 0)); + let alice_bid = secret_bid::get_bid_unchecked(*vector::borrow(&vals, 0)); + assert!(alice_bid == 1, 03); - (vals, bids, expiry) + (vals, bids) } #[test_only] - public fun mock_bids(vals: &vector
): (vector, vector) { - // system_addresses::assert_ol(vm); + public fun mock_bids(framework: &signer, vals: &vector
): vector { let bids = vector::empty(); - let expiry = vector::empty(); let i = 0; let prev = 0; let fib = 1; while (i < vector::length(vals)) { - - vector::push_back(&mut expiry, 1000); let b = prev + fib; vector::push_back(&mut bids, b); let a = vector::borrow(vals, i); let sig = account::create_signer_for_test(*a); // initialize and set. - proof_of_fee::pof_update_bid(&sig, b, 1000); + let epoch = 0; + secret_bid::mock_revealed_bid(framework, &sig, b, epoch); prev = fib; fib = b; i = i + 1; }; - (bids, expiry) + bids } use diem_framework::chain_status; @@ -290,6 +289,8 @@ module ol_framework::mock { i = i + 1; }; + + pof_default(root); } #[test_only] @@ -304,7 +305,7 @@ module ol_framework::mock { public fun trigger_epoch(root: &signer) { trigger_epoch_exactly_at( root, - reconfiguration::get_current_epoch(), + reconfiguration::current_epoch(), block::get_current_block_height() ); } @@ -314,11 +315,11 @@ module ol_framework::mock { epoch_boundary::ol_reconfigure_for_test(root, old_epoch, round); // always advance - assert!(reconfiguration::get_current_epoch() > old_epoch, + assert!(reconfiguration::current_epoch() > old_epoch, EDID_NOT_ADVANCE_EPOCH); // epoch helper should always be in sync - assert!(reconfiguration::get_current_epoch() == epoch_helper::get_current_epoch(), 666); + assert!(reconfiguration::current_epoch() == epoch_helper::get_current_epoch(), 666); } @@ -345,7 +346,7 @@ module ol_framework::mock { // will assert! case_1 mock_case_1(&root, *addr); - pof_default(); + pof_default(&root); // will assert! case_4 mock_case_4(&root, *addr); @@ -378,7 +379,7 @@ module ol_framework::mock { // Scenario: unit testing that pof_default results in a usable auction let n_vals = 5; let vals = genesis_n_vals(root, n_vals); // need to include eve to init funds - pof_default(); + pof_default(root); proof_of_fee::fill_seats_and_get_price(root, n_vals, &vals, &vals); 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 a8e90e62d..14a6b7e03 100644 --- a/framework/libra-framework/sources/ol_sources/proof_of_fee.move +++ b/framework/libra-framework/sources/ol_sources/proof_of_fee.move @@ -23,6 +23,7 @@ module ol_framework::proof_of_fee { use ol_framework::slow_wallet; use ol_framework::epoch_helper; use ol_framework::address_utils; + use ol_framework::secret_bid; //use diem_std::debug::print; friend diem_framework::genesis; @@ -164,7 +165,7 @@ module ol_framework::proof_of_fee { vm: &signer, outgoing_compliant_set: &vector
, mc_set_size: u64 // musical chairs set size suggestion - ): (vector
, vector
, vector
, u64) acquires ProofOfFeeAuction, ConsensusReward { + ): (vector
, vector
, vector
, u64) acquires ConsensusReward { system_addresses::assert_ol(vm); let all_bidders = get_bidders(false); @@ -256,7 +257,7 @@ module ol_framework::proof_of_fee { #[view] - public fun get_bidders(remove_unqualified: bool): vector
acquires ProofOfFeeAuction, ConsensusReward { + public fun get_bidders(remove_unqualified: bool): vector
acquires ConsensusReward { let eligible_validators = validator_universe::get_eligible_validators(); let (bidders, _) = sort_vals_impl(&eligible_validators, remove_unqualified); bidders @@ -264,12 +265,12 @@ module ol_framework::proof_of_fee { #[view] // same as get bidders, but returns the bid - public fun get_bidders_and_bids(remove_unqualified: bool): (vector
, vector) acquires ProofOfFeeAuction, ConsensusReward { + public fun get_bidders_and_bids(remove_unqualified: bool): (vector
, vector) acquires ConsensusReward { let eligible_validators = validator_universe::get_eligible_validators(); sort_vals_impl(&eligible_validators, remove_unqualified) } // returns two lists: ordered bidder addresss and the list of bids bid - fun sort_vals_impl(eligible_validators: &vector
, remove_unqualified: bool): (vector
, vector) acquires ProofOfFeeAuction, ConsensusReward { + fun sort_vals_impl(eligible_validators: &vector
, remove_unqualified: bool): (vector
, vector) acquires ConsensusReward { // let eligible_validators = validator_universe::get_eligible_validators(); let length = vector::length
(eligible_validators); @@ -280,13 +281,13 @@ module ol_framework::proof_of_fee { while (k < length) { // TODO: Ensure that this address is an active validator let cur_address = *vector::borrow
(eligible_validators, k); - let (bid, _expire) = current_bid(cur_address); + let entry_fee = secret_bid::current_revealed_bid(cur_address); let (_, qualified) = audit_qualification(cur_address); if (remove_unqualified && !qualified) { k = k + 1; continue }; - vector::push_back(&mut bids, bid); + vector::push_back(&mut bids, entry_fee); vector::push_back
(&mut filtered_vals, cur_address); k = k + 1; }; @@ -398,7 +399,7 @@ module ol_framework::proof_of_fee { final_set_size: u64, sorted_vals_by_bid: &vector
, proven_nodes: &vector
- ): (vector
, u64, u64, vector
, vector
) acquires ProofOfFeeAuction, ConsensusReward { + ): (vector
, u64, u64, vector
, vector
) acquires ConsensusReward { system_addresses::assert_ol(vm); // NOTE: this is duplicate work, but we are double checking we are getting a proper sort. @@ -459,7 +460,7 @@ module ol_framework::proof_of_fee { // Find the clearing price which all validators will pay let lowest_bidder = vector::borrow(&proposed_validators, vector::length(&proposed_validators) - 1); - let (lowest_bid_pct, _) = current_bid(*lowest_bidder); + let lowest_bid_pct = secret_bid::current_revealed_bid(*lowest_bidder); // update the clearing price let cr = borrow_global_mut(@ol_framework); @@ -485,7 +486,7 @@ module ol_framework::proof_of_fee { #[view] /// consolidate all the checks for a validator to be seated - public fun audit_qualification(val: address): (vector, bool) acquires ProofOfFeeAuction, ConsensusReward { + public fun audit_qualification(val: address): (vector, bool) acquires ConsensusReward { let errors = vector::empty(); // Safety check: node has valid configs @@ -502,12 +503,13 @@ module ol_framework::proof_of_fee { if (!is_above_thresh) vector::push_back(&mut errors, ETOO_FEW_VOUCHES); // 14 // check if current BIDS are valid - let (bid_pct, expire) = current_bid(val); - if (bid_pct == 0) vector::push_back(&mut errors, EBID_IS_ZERO); // 15 + let entry_fee_bid = secret_bid::current_revealed_bid(val); + if (entry_fee_bid == 0) vector::push_back(&mut errors, EBID_IS_ZERO); // 15 // Skip if the bid expired. belt and suspenders, this should have been checked in the sorting above. // TODO: make this it's own function so it can be publicly callable, it's useful generally, and for debugging. - if (epoch_helper::get_current_epoch() > expire) vector::push_back(&mut errors, EBID_EXPIRED); // 16 + let valid = secret_bid::has_valid_bid(val); + if (!valid) vector::push_back(&mut errors, EBID_EXPIRED); // 16 // skip the user if they don't have sufficient UNLOCKED funds // or if the bid expired. let unlocked_coins = slow_wallet::unlocked_amount(val); @@ -607,7 +609,7 @@ module ol_framework::proof_of_fee { /// find the median bid to push to history // this is needed for reward_thermostat - fun set_history(vm: &signer, proposed_validators: &vector
) acquires ProofOfFeeAuction, ConsensusReward { + fun set_history(vm: &signer, proposed_validators: &vector
) acquires ConsensusReward { system_addresses::assert_ol(vm); let median_bid = get_median(proposed_validators); @@ -624,7 +626,7 @@ module ol_framework::proof_of_fee { }; } - fun get_median(proposed_validators: &vector
):u64 acquires ProofOfFeeAuction { + fun get_median(proposed_validators: &vector
):u64 { // TODO: the list is sorted above, so // we assume the median is the middle element let len = vector::length(proposed_validators); @@ -636,8 +638,8 @@ module ol_framework::proof_of_fee { } else { vector::borrow(proposed_validators, 0) }; - let (median_bid, _) = current_bid(*median_bidder); - return median_bid + + secret_bid::current_revealed_bid(*median_bidder) } //////////////// GETTERS //////////////// @@ -654,22 +656,23 @@ module ol_framework::proof_of_fee { // CONSENSUS CRITICAL // Proof of Fee returns the current bid of the validator during the auction for upcoming epoch seats. // returns (current bid, expiration epoch) - #[view] - public fun current_bid(node_addr: address): (u64, u64) acquires ProofOfFeeAuction { - if (exists(node_addr)) { - let pof = borrow_global(node_addr); - let e = epoch_helper::get_current_epoch(); - // check the expiration of the bid - // the bid is zero if it expires. - // The expiration epoch number is inclusive of the epoch. - // i.e. the bid expires on e + 1. - if (pof.epoch_expiration >= e || pof.epoch_expiration == 0) { - return (pof.bid, pof.epoch_expiration) - }; - return (0, pof.epoch_expiration) - }; - return (0, 0) - } + // #[view] + // public fun current_bid(node_addr: address): (u64, u64) acquires ProofOfFeeAuction { + // if (exists(node_addr)) { + // let pof = borrow_global(node_addr); + // let e = epoch_helper::get_current_epoch(); + // // check the expiration of the bid + // // the bid is zero if it expires. + // // The expiration epoch number is inclusive of the epoch. + // // i.e. the bid expires on e + 1. + // if (pof.epoch_expiration >= e || pof.epoch_expiration == 0) { + // return (pof.bid, pof.epoch_expiration) + // }; + // return (0, pof.epoch_expiration) + // }; + // return (0, 0) + // } + #[view] /// Convenience function to calculate the implied net reward @@ -677,23 +680,16 @@ module ol_framework::proof_of_fee { /// @returns the unscaled coin value (not human readable) of the net reward /// the user expects public fun user_net_reward(node_addr: address): u64 acquires - ConsensusReward, ProofOfFeeAuction { + ConsensusReward { // get the user percentage rate - let (bid_pct, _) = current_bid(node_addr); - if (bid_pct == 0) return 0; + let user_entry_fee = secret_bid::current_revealed_bid(node_addr); + if (user_entry_fee == 0) return 0; // get the current nominal reward let (nominal_reward, _, _ , _) = get_consensus_reward(); + if (user_entry_fee >= nominal_reward) return 0; - let user_entry_fee = bid_pct * nominal_reward; - if (user_entry_fee == 0) return 0; - user_entry_fee = user_entry_fee / 10; - - if (user_entry_fee < nominal_reward) { - return nominal_reward - user_entry_fee - }; - - return 0 + return nominal_reward - user_entry_fee } #[view] @@ -727,7 +723,7 @@ module ol_framework::proof_of_fee { // Get the top N validators by bid, this is FILTERED by default - public(friend) fun top_n_accounts(account: &signer, n: u64, unfiltered: bool): vector
acquires ProofOfFeeAuction, ConsensusReward { + public(friend) fun top_n_accounts(account: &signer, n: u64, unfiltered: bool): vector
acquires ConsensusReward { system_addresses::assert_vm(account); let eligible_validators = get_bidders(unfiltered); diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move index 369740c8f..61c78dff2 100644 --- a/framework/libra-framework/sources/ol_sources/secret_bid.move +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -14,16 +14,22 @@ module ol_framework::secret_bid { use diem_std::ed25519; use diem_std::comparator; use diem_framework::epoch_helper; - use diem_framework::block; + use diem_framework::reconfiguration; use ol_framework::testnet; use ol_framework::address_utils; + friend ol_framework::proof_of_fee; + #[test_only] use diem_framework::account; #[test_only] use diem_framework::system_addresses; + #[test_only] + friend ol_framework::test_pof; + #[test_only] + friend ol_framework::mock; /// User bidding not initialized const ECOMMIT_BID_NOT_INITIALIZED: u64 = 1; @@ -35,7 +41,8 @@ module ol_framework::secret_bid { const ENOT_IN_REVEAL_WINDOW: u64 = 4; /// Bad Alice, the reveal does not match the commit const ECOMMIT_DIGEST_NOT_EQUAL: u64 = 5; - + /// Bid is for different epoch, expired + const EBID_EXPIRED: u64 = 6; struct CommittedBid has key { reveal_entry_fee: u64, @@ -173,9 +180,9 @@ module ol_framework::secret_bid { ///////// GETTERS //////// - fun get_bid_unchecked(user: address): u64 acquires CommittedBid { + public(friend) fun get_bid_unchecked(user: address): u64 acquires CommittedBid { let state = borrow_global(user); - if (state.commit_epoch != epoch_helper::get_current_epoch()) return 0; + state.reveal_entry_fee } @@ -185,8 +192,7 @@ module ol_framework::secret_bid { /// allow reveal transaction to be submitted public fun in_reveal_window(): bool { // get the timestamp - // NOTE: using block:: might cause a dependency cycle issue in the future - let remaining_secs = block::get_remaining_epoch_secs(); + let remaining_secs = reconfiguration::get_remaining_epoch_secs(); let window = if (testnet::is_testnet()) { // ten secs 10 @@ -209,10 +215,28 @@ module ol_framework::secret_bid { #[view] /// get the current bid, and exclude bids that are stale public fun current_revealed_bid(user: address): u64 acquires CommittedBid { + // // if we are not in reveal window this information will be confusing. + // assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); + + // let state = borrow_global(user); + // assert!(state.commit_epoch == epoch_helper::get_current_epoch(), error::invalid_state(EBID_EXPIRED)); + + // state.reveal_entry_fee + get_bid_unchecked(user) + } + + /// does the user have a current bid + public(friend) fun has_valid_bid(user: address): bool acquires CommittedBid { + let state = borrow_global(user); + state.commit_epoch == epoch_helper::get_current_epoch() + } + + /// will abort if bid is not valid + fun assert_valid_bid(user: address) acquires CommittedBid { // if we are not in reveal window this information will be confusing. assert!(in_reveal_window(), error::invalid_state(ENOT_IN_REVEAL_WINDOW)); - get_bid_unchecked(user) + assert!(has_valid_bid(user), error::invalid_state(EBID_EXPIRED)); } #[view] @@ -225,15 +249,23 @@ module ol_framework::secret_bid { //////// TESTS //////// #[test_only] - public(friend) fun mock_revealed_bid(framework: &signer, user: &signer, reveal_entry_fee: u64, commit_epoch: u64) { + public(friend) fun mock_revealed_bid(framework: &signer, user: &signer, reveal_entry_fee: u64, commit_epoch: u64) acquires CommittedBid { system_addresses::assert_diem_framework(framework); testnet::assert_testnet(framework); - move_to(user, CommittedBid { - reveal_entry_fee, - entry_fee_history: vector[0], - commit_digest: vector[0], - commit_epoch, - }); + let user_addr = signer::address_of(user); + if (!exists(user_addr)) { + move_to(user, CommittedBid { + reveal_entry_fee, + entry_fee_history: vector[0], + commit_digest: vector[0], + commit_epoch, + }); + } else { + let state = borrow_global_mut(user_addr); + state.reveal_entry_fee = reveal_entry_fee; + state.commit_epoch = commit_epoch; + } + } #[test] diff --git a/framework/libra-framework/sources/ol_sources/tests/_meta_epoch.test.move b/framework/libra-framework/sources/ol_sources/tests/_meta_epoch.test.move index a09f0493d..d148b1460 100644 --- a/framework/libra-framework/sources/ol_sources/tests/_meta_epoch.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/_meta_epoch.test.move @@ -11,17 +11,17 @@ module ol_framework::test_meta { let _vals = mock::genesis_n_vals(root, 2); // we are at epoch 0 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 0, 7357001); mock::trigger_epoch(root); // epoch 1 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 1, 7357002); mock::trigger_epoch(root); // epoch 2 mock::trigger_epoch(root); // epoch 3 mock::trigger_epoch(root); // epoch 4 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 4, 7357003); } @@ -33,11 +33,11 @@ module ol_framework::test_meta { // NOTE: genesis_n_vals, DOES trigger a genesis END event. mock::genesis_n_vals(&root, 4); - let a = reconfiguration::get_current_epoch(); + let a = reconfiguration::current_epoch(); // create a new epoch reconfiguration::reconfigure_for_test(); - let b = reconfiguration::get_current_epoch(); + let b = reconfiguration::current_epoch(); assert!(a == 0, 10001); assert!(b == 1, 10002); diff --git a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move index 659f207d1..81dba3370 100644 --- a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move @@ -36,7 +36,7 @@ module ol_framework::test_boundary { let set = mock::genesis_n_vals(root, 10); mock::ol_initialize_coin_and_fund_vals(root, 500000, true); mock::mock_all_vals_good_performance(root); - mock::pof_default(); + mock::pof_default(root); 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 @@ -99,7 +99,7 @@ module ol_framework::test_boundary { let vals = validator_universe::get_eligible_validators(); assert!(vector::length(&vals) == 11, 7357001); - mock::mock_bids(&vals); + mock::mock_bids(&root, &vals); // MARLON HAS MANY FRIENDS vouch::test_set_buddies(@0x12345, vals); @@ -181,8 +181,7 @@ module ol_framework::test_boundary { #[test(root = @ol_framework, alice = @0x1000a, marlon_rando = @0x12345)] fun e2e_add_validator_sad_vouches(root: signer, alice: signer, marlon_rando: signer) { let _vals = common_test_setup(&root); - // this test requires prod settings, since we don't check vouches on testing - testnet::unset(&root); + // generate credentials for validator registration ol_account::transfer(&alice, @0x12345, 200000); let (_sk, pk, pop) = stake::generate_identity(); @@ -192,7 +191,10 @@ module ol_framework::test_boundary { let vals = validator_universe::get_eligible_validators(); assert!(vector::length(&vals) == 11, 7357000); - mock::mock_bids(&vals); + + mock::mock_bids(&root, &vals); + // this test requires prod settings, since we don't check vouches on testing + testnet::unset(&root); mock::trigger_epoch(&root); @@ -256,14 +258,14 @@ module ol_framework::test_boundary { // testing mainnet, so change the chainid testnet::unset(root); // test setup advances to epoch #2 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 2, 7357001); epoch_boundary::test_set_boundary_ready(root, epoch); // test the APIs as root timestamp::fast_forward_seconds(1); // needed for reconfig epoch_boundary::test_trigger(root); - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 3, 7357002); // scenario: marlon has no privileges @@ -272,7 +274,7 @@ module ol_framework::test_boundary { epoch_boundary::test_set_boundary_ready(root, epoch); timestamp::fast_forward_seconds(1); // needed for reconfig diem_governance::trigger_epoch(marlon); - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 4, 7357003); } @@ -343,14 +345,14 @@ module ol_framework::test_boundary { // testing mainnet, so change the chainid testnet::unset(root); // test setup advances to epoch #2 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 2, 7357001); epoch_boundary::test_set_boundary_ready(root, epoch); // test the APIs as root timestamp::fast_forward_seconds(1); // needed for reconfig epoch_boundary::test_trigger(root); - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 3, 7357002); // scenario: marlon has no privileges @@ -378,7 +380,7 @@ module ol_framework::test_boundary { // assert!(!features::epoch_trigger_enabled(), 101); // // test setup advances to epoch #2 - // let epoch = reconfiguration::get_current_epoch(); + // let epoch = reconfiguration::current_epoch(); // assert!(epoch == 2, 7357001); // epoch_boundary::test_set_boundary_ready(root, epoch); @@ -388,7 +390,7 @@ module ol_framework::test_boundary { // block::test_maybe_advance_epoch(root, 602000001, 602000000); // // test epoch advances - // let epoch = reconfiguration::get_current_epoch(); + // let epoch = reconfiguration::current_epoch(); // assert!(epoch == 3, 7357002); // } @@ -399,7 +401,7 @@ module ol_framework::test_boundary { // testing mainnet, so change the chainid testnet::unset(root); // test setup advances to epoch #2 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 2, 7357001); epoch_boundary::test_set_boundary_ready(root, epoch); @@ -409,12 +411,12 @@ module ol_framework::test_boundary { block::test_maybe_advance_epoch(root, 603000001, 602000000); // test epoch did not advance and needs to be triggered - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 2, 7357002); // test epoch can be triggered and advances epoch_boundary::test_trigger(root); - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 3, 7357002); } diff --git a/framework/libra-framework/sources/ol_sources/tests/proof_of_fee.test.move b/framework/libra-framework/sources/ol_sources/tests/proof_of_fee.test.move index f1aa0fe21..605cfce33 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 @@ -9,11 +9,12 @@ module ol_framework::test_pof { use ol_framework::vouch; use ol_framework::testnet; use ol_framework::globals; + use ol_framework::secret_bid; use diem_framework::stake; use diem_framework::chain_id; use std::vector; - //use diem_std::debug::print; + use diem_std::debug::print; const Alice: address = @0x1000a; const Bob: address = @0x1000b; @@ -23,19 +24,18 @@ module ol_framework::test_pof { const Frank: address = @0x1000f; #[test_only] - fun mock_good_bid(_root: &signer, alice: &address) { + fun mock_good_bid(root: &signer, alice: &address) { let a_sig = account::create_signer_for_test(*alice); - // mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); - proof_of_fee::pof_update_bid(&a_sig, 1, 10000); - let (bid, expires) = proof_of_fee::current_bid(*alice); + let epoch = 0; // we are at genesis in tests + secret_bid::mock_revealed_bid(root, &a_sig, 1, epoch); + let bid = secret_bid::get_bid_unchecked(*alice); assert!(bid == 1, 1001); - assert!(expires == 10000, 1002); let coin = slow_wallet::unlocked_amount(*alice); let (r, _, _, _) = proof_of_fee::get_consensus_reward(); let bid_cost = (bid * r) / 1000; - assert!(coin > bid_cost, 1005); + assert!(coin > bid_cost, 1002); } #[test(root = @ol_framework)] @@ -51,30 +51,29 @@ module ol_framework::test_pof { let a_sig = account::create_signer_for_test(*alice); proof_of_fee::init_bidding(&a_sig); - let (bid, expires) = proof_of_fee::current_bid(*alice); + let bid = secret_bid::get_bid_unchecked(*alice); assert!(bid == 0, 1001); - assert!(expires == 0, 1002); - proof_of_fee::pof_update_bid(&a_sig, 100, 0); - let (bid, expires) = proof_of_fee::current_bid(*alice); - assert!(bid == 100, 1003); - assert!(expires == 0, 1004); + secret_bid::mock_revealed_bid(&root, &a_sig, 100, 1); + let bid = secret_bid::get_bid_unchecked(*alice); + assert!(bid == 100, 1002); // now retract proof_of_fee::pof_retract_bid(a_sig); - let (bid, expires) = proof_of_fee::current_bid(*alice); + let bid = secret_bid::get_bid_unchecked(*alice); let (is_rectracted, epoch) = proof_of_fee::is_already_retracted(*alice); - assert!(is_rectracted, 1007); + assert!(is_rectracted, 1004); - let this_epoch = reconfiguration::get_current_epoch(); + let this_epoch = reconfiguration::current_epoch(); - assert!(epoch == this_epoch, 1008); - assert!(bid == 0, 1009); - assert!(expires == 0, 10010); + assert!(epoch == this_epoch, 1005); + assert!(bid == 0, 1006); } #[test(root = @ol_framework)] fun audit_happy (root: signer) { + use ol_framework::epoch_helper; + let set = mock::genesis_n_vals(&root, 4); mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); @@ -85,7 +84,9 @@ module ol_framework::test_pof { mock_good_bid(&root, &alice); - let (_, pass) = proof_of_fee::audit_qualification(alice); + let (err, pass) = proof_of_fee::audit_qualification(alice); + print(&epoch_helper::get_current_epoch()); + print(&err); assert!(pass, 1006); } @@ -133,10 +134,9 @@ module ol_framework::test_pof { assert!(!jail::is_jailed(alice), 7357001); let a_sig = account::create_signer_for_test(alice); - proof_of_fee::pof_update_bid(&a_sig, 100, 10000); // 10 pct - let (bid, expires) = proof_of_fee::current_bid(alice); + secret_bid::mock_revealed_bid(&root, &a_sig, 100, 1); + let bid = secret_bid::get_bid_unchecked(alice); assert!(bid == 100, 7357001); - assert!(expires == 10000, 7357002); // NOT ENOUGH FUNDS WERE UNLOCKED slow_wallet::slow_wallet_epoch_drip(&root, 500); @@ -177,10 +177,11 @@ module ol_framework::test_pof { let a_sig = account::create_signer_for_test(alice); // set a bid in the past - proof_of_fee::pof_update_bid(&a_sig, 1, 0); - let (bid, expires) = proof_of_fee::current_bid(alice); + secret_bid::mock_revealed_bid(&root, &a_sig, 100, 0); + + let bid = secret_bid::get_bid_unchecked(alice); assert!(bid == 1, 1006); - assert!(expires == 0, 1007); + // should NOT pass audit. let (_err, pass) = proof_of_fee::audit_qualification(alice); assert!(!pass, 1008); @@ -227,7 +228,7 @@ module ol_framework::test_pof { i = i + 1; }; // mock_good_bid(&root, alice); - let (val_universe, _their_bids, _their_expiry) = mock::pof_default(); + let (val_universe, _their_bids) = mock::pof_default(&root); let sorted = proof_of_fee::get_bidders(false); let len = vector::length(&sorted); @@ -242,7 +243,7 @@ module ol_framework::test_pof { #[test(root= @ol_framework)] fun sorted_vals_none_qualify(root: signer) { let vals = mock::genesis_n_vals(&root, 4); - let (val_universe, _their_bids, _their_expiry) = mock::pof_default(); + let (val_universe, _their_bids) = mock::pof_default(&root); // calculate the auction proof_of_fee::fill_seats_and_get_price(&root, 4, &vals, &vals); @@ -274,10 +275,10 @@ module ol_framework::test_pof { let alice = vector::borrow(&set, 0); jail::jail(&root, *alice); - let (val_universe, _their_bids, _their_expiry) = mock::pof_default(); + let (val_universe, _their_bids) = mock::pof_default(&root); - // let (val_universe, _their_bids, _their_expiry) = mock::pof_default(); + // let (val_universe, _their_bids, _their_expiry) = mock::pof_default(&root); let sorted = proof_of_fee::get_bidders(false); let len = vector::length(&sorted); assert!(len == vector::length(&val_universe), 1000); @@ -298,7 +299,7 @@ module ol_framework::test_pof { let set = mock::genesis_n_vals(&root, 4); mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); - let (val_universe, _their_bids, _their_expiry) = mock::pof_default(); + let (val_universe, _their_bids) = mock::pof_default(&root); let len = vector::length(&set); let i = 0; @@ -337,7 +338,7 @@ module ol_framework::test_pof { let set = mock::genesis_n_vals(&root, 5); let len = vector::length(&set); mock::ol_initialize_coin_and_fund_vals(&root, 500000, true); - mock::pof_default(); + mock::pof_default(&root); slow_wallet::slow_wallet_epoch_drip(&root, 500000); @@ -360,7 +361,7 @@ module ol_framework::test_pof { fun fill_seats_happy_and_noop_thermostat(root: signer) { let set = mock::genesis_n_vals(&root, 5); mock::ol_initialize_coin_and_fund_vals(&root, 500000, true); - mock::pof_default(); + mock::pof_default(&root); slow_wallet::slow_wallet_epoch_drip(&root, 500000); @@ -400,7 +401,7 @@ module ol_framework::test_pof { fun fill_seats_few_bidders(root: signer) { let set = mock::genesis_n_vals(&root, 5); mock::ol_initialize_coin_and_fund_vals(&root, 500000, true); - mock::pof_default(); + mock::pof_default(&root); // Ok now EVE changes her mind. Will force the bid to expire. let a_sig = account::create_signer_for_test(*vector::borrow(&set, 4)); @@ -441,7 +442,7 @@ module ol_framework::test_pof { #[test(root = @ol_framework)] fun fill_seats_many_bidders(root: signer) { let set = mock::genesis_n_vals(&root, 5); - mock::pof_default(); + mock::pof_default(&root); mock::ol_initialize_coin_and_fund_vals(&root, 500000, true); let sorted = proof_of_fee::get_bidders(true); @@ -496,7 +497,7 @@ module ol_framework::test_pof { // we need 6 seats so that we can have 4 proven, and 2 unproven slots let set = mock::genesis_n_vals(&root, 6); mock::ol_initialize_coin_and_fund_vals(&root, 500000, true); - mock::pof_default(); + mock::pof_default(&root); let sorted = proof_of_fee::get_bidders(true); @@ -554,7 +555,7 @@ module ol_framework::test_pof { // we need 6 seats so that we can have 4 proven, and 2 unproven slots let set = mock::genesis_n_vals(&root, 6); mock::ol_initialize_coin_and_fund_vals(&root, 500000, true); - mock::pof_default(); + mock::pof_default(&root); let sorted = proof_of_fee::get_bidders(true); @@ -566,9 +567,9 @@ module ol_framework::test_pof { vector::push_back(&mut proven_vals, Carol); vector::push_back(&mut proven_vals, Dave); - let (alice_bid, _) = proof_of_fee::current_bid(Alice); + let alice_bid = secret_bid::get_bid_unchecked(Alice); assert!(alice_bid == 1, 1001); - let (frank_bid, _) = proof_of_fee::current_bid(Frank); + let frank_bid = secret_bid::get_bid_unchecked(Frank); assert!(alice_bid < frank_bid, 1002); let (seats, _, _, _, _) = proof_of_fee::fill_seats_and_get_price(&root, set_size, &sorted, &proven_vals); diff --git a/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move b/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move index e4b9103c6..06ff3027f 100644 --- a/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move @@ -238,7 +238,7 @@ module ol_framework::test_slow_wallet { // mock::ol_initialize_coin(&root); let a = vector::borrow(&set, 0); assert!(slow_wallet::unlocked_amount(*a) == 0, 735701); - epoch_boundary::ol_reconfigure_for_test(&root, reconfiguration::get_current_epoch(), block::get_current_block_height()) + epoch_boundary::ol_reconfigure_for_test(&root, reconfiguration::current_epoch(), block::get_current_block_height()) } diff --git a/framework/libra-framework/sources/ol_sources/tests/stake.test.move b/framework/libra-framework/sources/ol_sources/tests/stake.test.move index 047ec5ae8..7d46d0163 100644 --- a/framework/libra-framework/sources/ol_sources/tests/stake.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/stake.test.move @@ -161,7 +161,7 @@ module ol_framework::test_stake { mock::genesis_n_vals(&root, 6); mock::mock_all_vals_good_performance(&root); // all validators bid - mock::pof_default(); + mock::pof_default(&root); // now make Eve not compliant let eve = @0x1000e; diff --git a/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move b/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move index bcccaba78..9ce9231e9 100644 --- a/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move @@ -22,7 +22,7 @@ module ol_framework::test_reconfiguration { fun reconfig_reward_happy_case(root: signer) { let vals = mock::genesis_n_vals(&root, 5); - mock::pof_default(); + mock::pof_default(&root); assert!(vector::length(&vals) == 5, 7357001); let vals = stake::get_current_validators(); assert!(vector::length(&vals) == 5, 7357002); @@ -84,7 +84,7 @@ module ol_framework::test_reconfiguration { fun drop_non_performing(root: signer) { let _vals = mock::genesis_n_vals(&root, 5); // mock::ol_initialize_coin(&root); - mock::pof_default(); + mock::pof_default(&root); assert!(libra_coin::balance(@0x1000a) == 0, 7357000); // NOTE: epoch 0 and 1 are a special case, we don't run performance grades on that one. Need to move two epochs ahead @@ -127,9 +127,7 @@ module ol_framework::test_reconfiguration { #[test(root = @ol_framework)] fun reconfig_failover(root: signer) { let vals = mock::genesis_n_vals(&root, 5); - // mock::ol_initialize_coin(&root); - - mock::pof_default(); + mock::pof_default(&root); // validators did not perform. let i = 0; diff --git a/framework/libra-framework/sources/ol_sources/tests/vote_lib/multi_action.test.move b/framework/libra-framework/sources/ol_sources/tests/vote_lib/multi_action.test.move index 66e5dd5b6..7e5f651c0 100644 --- a/framework/libra-framework/sources/ol_sources/tests/vote_lib/multi_action.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/vote_lib/multi_action.test.move @@ -16,7 +16,7 @@ module ol_framework::test_multi_action { // print //use std::debug::print; - struct DummyType has drop, store {} + struct DummyType has drop, store {} #[test(root = @ol_framework, carol = @0x1000c)] fun init_multi_action(root: &signer, carol: &signer) { @@ -36,7 +36,7 @@ module ol_framework::test_multi_action { fun propose_offer(root: &signer, carol: &signer) { mock::genesis_n_vals(root, 4); let carol_address = @0x1000c; - + // check the offer does not exist assert!(!multi_action::exists_offer(carol_address), 7357001); assert!(!multi_action::is_multi_action(carol_address), 7357002); @@ -140,7 +140,7 @@ module ol_framework::test_multi_action { // check the account is multi_action assert!(multi_action::is_multi_action(carol_address), 7357002); - + // check authorities let authorities = multi_action::get_authorities(carol_address); assert!(authorities == vector[@0x1000a, @0x1000b], 7357003); @@ -173,7 +173,7 @@ module ol_framework::test_multi_action { // check the account is multi_action assert!(multi_action::is_multi_action(carol_address), 7357002); - + // check authorities let authorities = multi_action::get_authorities(carol_address); assert!(authorities == vector[@0x1000a, @0x1000b], 7357003); @@ -388,7 +388,7 @@ module ol_framework::test_multi_action { mock::genesis_n_vals(root, 3); let carol_address = @0x1000c; multi_action::init_gov(carol); - + // invite bob multi_action::propose_offer(carol, vector[@0x1000b], option::none()); @@ -402,13 +402,13 @@ module ol_framework::test_multi_action { fun claim_expired_offer(root: &signer, carol: &signer, alice: &signer, bob: &signer) { mock::genesis_n_vals(root, 3); multi_action::init_gov(carol); - + // offer to alice and bob multi_action::propose_offer(carol, vector[@0x1000a, @0x1000b], option::some(2)); // alice claim the offer multi_action::claim_offer(alice, @0x1000c); - + mock::trigger_epoch(root); // epoch 1 valid mock::trigger_epoch(root); // epoch 2 valid mock::trigger_epoch(root); // epoch 3 expired @@ -418,7 +418,7 @@ module ol_framework::test_multi_action { } // Try to claim offer of an account without proposal - #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b)] + #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b)] #[expected_failure(abort_code = 0x60011, location = ol_framework::multi_action)] fun claim_offer_without_proposal(root: &signer, alice: &signer) { mock::genesis_n_vals(root, 2); @@ -462,7 +462,7 @@ module ol_framework::test_multi_action { fun finalize_without_enough_claimed(root: &signer, alice: &signer, bob: &signer) { mock::genesis_n_vals(root, 3); multi_action::init_gov(alice); - + // offer to bob and carol authority on the alice account multi_action::propose_offer(alice, vector[@0x1000b, @0x1000c], option::none()); @@ -480,7 +480,7 @@ module ol_framework::test_multi_action { mock::genesis_n_vals(root, 3); let alice_address = @0x1000a; multi_action::init_gov(alice); - + // offer bob and carol authority on the alice account multi_action::propose_offer(alice, vector[@0x1000b, @0x1000c], option::none()); @@ -494,7 +494,7 @@ module ol_framework::test_multi_action { } // Governance Tests - + // Happy Day: propose a new action and check zero votes #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c)] fun propose_action(root: &signer, alice: &signer, bob: &signer, carol: &signer) { @@ -505,11 +505,11 @@ module ol_framework::test_multi_action { multi_action::init_gov(alice); multi_action::init_type(alice, true); multi_action::propose_offer(alice, vector[@0x1000b, @0x1000c], option::none()); - + // bob and alice claim the offer multi_action::claim_offer(bob, alice_address); - multi_action::claim_offer(carol, alice_address); - + multi_action::claim_offer(carol, alice_address); + // alice finalize multi action workflow to release control of the account multi_action::finalize_and_cage(alice, 2); @@ -546,8 +546,8 @@ module ol_framework::test_multi_action { // bob and alice claim the offer multi_action::claim_offer(bob, alice_address); - multi_action::claim_offer(carol, alice_address); - + multi_action::claim_offer(carol, alice_address); + // alice finalize multi action workflow to release control of the account multi_action::finalize_and_cage(alice, 2); @@ -590,10 +590,10 @@ module ol_framework::test_multi_action { let alice_address = @0x1000a; multi_action::init_gov(alice); // Ths is a simple multi_action: there is no capability being stored - multi_action::init_type(alice, false); + multi_action::init_type(alice, false); multi_action::propose_offer(alice, vector[@0x1000b, @0x1000c], option::none()); multi_action::claim_offer(bob, alice_address); - multi_action::claim_offer(carol, alice_address); + multi_action::claim_offer(carol, alice_address); multi_action::finalize_and_cage(alice, 2); // bob create a proposal @@ -628,7 +628,7 @@ module ol_framework::test_multi_action { multi_action::init_type(alice, true); multi_action::propose_offer(alice, vector[@0x1000b, @0x1000c], option::none()); multi_action::claim_offer(bob, alice_address); - multi_action::claim_offer(carol, alice_address); + multi_action::claim_offer(carol, alice_address); multi_action::finalize_and_cage(alice, 2); // bob create a proposal and vote @@ -650,7 +650,7 @@ module ol_framework::test_multi_action { assert!(option::is_some(&cap_opt), 7357003); let cap = option::extract(&mut cap_opt); let c = ol_account::withdraw_with_capability(&cap, 42,); - // deposit to erik account + // deposit to erik account ol_account::create_account(root, @0x1000e); ol_account::deposit_coins(@0x1000e, c); option::fill(&mut cap_opt, cap); @@ -671,7 +671,7 @@ module ol_framework::test_multi_action { mock::ol_initialize_coin_and_fund_vals(root, 10000000, true); // we are at epoch 0 - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 0, 7357001); // dave creates erik resource account. He is not one of the validators, and is not an authority in the multisig. @@ -681,15 +681,15 @@ module ol_framework::test_multi_action { // fund the account ol_account::transfer(alice, erik_address, 100); - + // offer alice and bob authority on the safe safe::init_payment_multisig(&erik, vector[@0x1000a, @0x1000b]); // both need to sign multi_action::claim_offer(alice, erik_address); - multi_action::claim_offer(bob, erik_address); + multi_action::claim_offer(bob, erik_address); multi_action::finalize_and_cage(&erik, 2); // make a proposal for governance, expires in 2 epoch from now - let id = multi_action::propose_governance(alice, erik_address, vector::empty(), true, + let id = multi_action::propose_governance(alice, erik_address, vector::empty(), true, option::some(1), option::some(2)); mock::trigger_epoch(root); // epoch 1 @@ -697,12 +697,12 @@ module ol_framework::test_multi_action { mock::trigger_epoch(root); // epoch 3 -- voting ended here mock::trigger_epoch(root); // epoch 4 -- now expired - let epoch = reconfiguration::get_current_epoch(); + let epoch = reconfiguration::current_epoch(); assert!(epoch == 4, 7357003); // trying to vote on a closed ballot will error let _passed = multi_action::vote_governance(bob, erik_address, &id); - } + } // Happy day: change the authorities of a multisig #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d)] @@ -715,7 +715,7 @@ module ol_framework::test_multi_action { mock::ol_initialize_coin_and_fund_vals(root, 10000000, true); let carol_address = @0x1000c; let dave_address = @0x1000d; - + // fund the account ol_account::transfer(alice, carol_address, 100); // offer alice and bob authority on the safe @@ -724,7 +724,7 @@ module ol_framework::test_multi_action { let authorities = vector[@0x1000a, @0x1000b]; multi_action::propose_offer(carol, authorities, option::none()); multi_action::claim_offer(alice, carol_address); - multi_action::claim_offer(bob, carol_address); + multi_action::claim_offer(bob, carol_address); multi_action::finalize_and_cage(carol, 2); // alice is going to propose to change the authorities to add dave and increase the threshold to 3 @@ -786,7 +786,7 @@ module ol_framework::test_multi_action { // Happy day: change the threshold of a multisig #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d)] fun governance_change_threshold(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { - // Scenario: The multisig gets initiated with the 2 bob and carol as the only authorities. + // Scenario: The multisig gets initiated with the 2 bob and carol as the only authorities. // It takes 2-of-2 to sign. // They decide next only 1-of-2 will be needed. // Then they decide to invite dave and make it 3-of-3. @@ -807,12 +807,12 @@ module ol_framework::test_multi_action { multi_action::init_type(&resource_sig, false); multi_action::propose_offer(&resource_sig, vector[@0x1000b, @0x1000c], option::none()); multi_action::claim_offer(carol, new_resource_address); - multi_action::claim_offer(bob, new_resource_address); + multi_action::claim_offer(bob, new_resource_address); multi_action::finalize_and_cage(&resource_sig, 2); // carol is going to propose to change the threshold to 1 let id = multi_action::propose_governance(carol, new_resource_address, vector::empty(), true, option::some(1), option::none()); - + // check authorities and threshold let authorities = multi_action::get_authorities(new_resource_address); assert!(authorities == vector[@0x1000c, @0x1000b], 7357002); // no change @@ -844,7 +844,7 @@ module ol_framework::test_multi_action { // now bob decide to invite dave and make it 3-of-3. multi_action::propose_governance(bob, new_resource_address, vector[signer::address_of(dave)], true, option::some(3), option::none()); - + // check authorities and threshold did not change let authorities = multi_action::get_authorities(new_resource_address); assert!(vector::length(&authorities) == 2, 7357011); @@ -892,10 +892,10 @@ module ol_framework::test_multi_action { // carol is going to propose to change the authorities to add dave let id = multi_action::propose_governance(carol, alice_address, vector[@0x1000d], true, option::none(), option::none()); - + // bob votes and dave does not claims the offer multi_action::vote_governance(bob, alice_address, &id); - + // check authorities and threshold assert!(multi_action::get_authorities(alice_address) == vector[@0x1000b, @0x1000c], 7357001); let (n, _m) = multi_action::get_threshold(alice_address); @@ -930,7 +930,7 @@ module ol_framework::test_multi_action { #[expected_failure(abort_code = 0x3000C, location = ol_framework::multi_action)] fun vote_action_twice(root: &signer, alice: &signer, bob: &signer, carol: &signer) { // Scenario: Testing that a vote cannot be done twice on the same ballot. - + mock::genesis_n_vals(root, 4); mock::ol_initialize_coin_and_fund_vals(root, 10000000, true); let carol_address = @0x1000c; @@ -942,11 +942,11 @@ module ol_framework::test_multi_action { multi_action::init_type(carol, true); multi_action::propose_offer(carol, vector[@0x1000a, @0x1000b], option::none()); multi_action::claim_offer(alice, carol_address); - multi_action::claim_offer(bob, carol_address); + multi_action::claim_offer(bob, carol_address); multi_action::finalize_and_cage(carol, 2); // alice is going to propose to change the authorities to add dave - let id = multi_action::propose_governance(alice, carol_address, + let id = multi_action::propose_governance(alice, carol_address, vector[dave_address], true, option::none(), option::none()); // bob votes @@ -973,7 +973,7 @@ module ol_framework::test_multi_action { // carol is going to propose to change the authorities to add dave let id = multi_action::propose_governance(carol, alice_address, vector[@0xCAFE], true, option::none(), option::none()); - + // bob votes and dave does not claims the offer multi_action::vote_governance(bob, alice_address, &id); } @@ -993,7 +993,7 @@ module ol_framework::test_multi_action { multi_action::finalize_and_cage(alice, 2); // carol is going to propose to change the authorities to add dave twice - let _id = multi_action::propose_governance(carol, alice_address, + let _id = multi_action::propose_governance(carol, alice_address, vector[@0x1000d, @0x1000d], true, option::none(), option::none()); } @@ -1048,13 +1048,13 @@ module ol_framework::test_multi_action { multi_action::finalize_and_cage(alice, 2); // carol is going to propose to remove dave - let _id = multi_action::propose_governance(carol, alice_address, + let _id = multi_action::propose_governance(carol, alice_address, vector[@0x1000d], false, option::none(), option::none()); } // Two governance proposals at the same time #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d, eve = @0x1000e)] - fun two_simultaneous_governance_vote(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer, eve: &signer) { + fun two_simultaneous_governance_vote(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer, eve: &signer) { mock::genesis_n_vals(root, 5); let alice_address = @0x1000a; @@ -1067,7 +1067,7 @@ module ol_framework::test_multi_action { // carol is going to propose to change the authorities to add dave let id_set_n_3 = multi_action::propose_governance(carol, alice_address, vector[@0x1000d], true, option::some(3), option::none()); - + // bob is going to propose to change the authorities to add erik let id_set_n_1 = multi_action::propose_governance(bob, alice_address, vector[@0x1000e], true, option::some(1), option::none()); diff --git a/framework/libra-framework/sources/ol_sources/tests/vouch.test.move b/framework/libra-framework/sources/ol_sources/tests/vouch.test.move index 17a1acde8..5dbcc78dd 100644 --- a/framework/libra-framework/sources/ol_sources/tests/vouch.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/vouch.test.move @@ -41,6 +41,8 @@ module ol_framework::test_vouch { // fast forward to epoch 1 mock::trigger_epoch(root); + vouch::set_vouch_price(root, 0); + // carol vouches for bob vouch::vouch_for(carol, @0x1000b); @@ -146,6 +148,8 @@ module ol_framework::test_vouch { // fast forward to epoch 1 mock::trigger_epoch(root); + vouch::set_vouch_price(root, 0); + // alice vouches for bob again vouch::vouch_for(alice, @0x1000b); diff --git a/framework/libra-framework/sources/ol_sources/vote_lib/tally/binary_tally.move b/framework/libra-framework/sources/ol_sources/vote_lib/tally/binary_tally.move index 852e99102..d1005b870 100644 --- a/framework/libra-framework/sources/ol_sources/vote_lib/tally/binary_tally.move +++ b/framework/libra-framework/sources/ol_sources/vote_lib/tally/binary_tally.move @@ -199,7 +199,7 @@ fun maybe_tally(t: &mut BinaryTally): Option { - if (reconfiguration::get_current_epoch() > t.deadline_epoch) { + if (reconfiguration::current_epoch() > t.deadline_epoch) { if (t.votes_for > t.votes_against) { t.tally_result = option::some(true); diff --git a/framework/libra-framework/sources/reconfiguration.move b/framework/libra-framework/sources/reconfiguration.move index 908bc2099..282dff273 100644 --- a/framework/libra-framework/sources/reconfiguration.move +++ b/framework/libra-framework/sources/reconfiguration.move @@ -24,6 +24,7 @@ module diem_framework::reconfiguration { friend diem_framework::gas_schedule; friend diem_framework::genesis; friend diem_framework::version; + friend diem_framework::block; /// Event that signals consensus to start a new epoch, /// with new configuration information. This is also called a @@ -42,6 +43,10 @@ module diem_framework::reconfiguration { events: event::EventHandle, } + struct EpochInterval has key { + epoch_interval: u64 + } + /// Reconfiguration will be disabled if this resource is published under the /// diem_framework system address struct DisableReconfiguration has key {} @@ -58,14 +63,6 @@ module diem_framework::reconfiguration { const EINVALID_GUID_FOR_EVENT: u64 = 5; - //////// 0L //////// - #[view] - /// Returns the current epoch number - public fun get_current_epoch(): u64 acquires Configuration { - let config_ref = borrow_global(@diem_framework); - config_ref.epoch - } - /// Only called during genesis. /// Publishes `Configuration` resource. Can only be invoked by diem framework account, and only a single time in Genesis. public(friend) fun initialize(diem_framework: &signer) { @@ -194,6 +191,50 @@ module diem_framework::reconfiguration { ); } + // convenience to hold the epoch interval outside of the block:: resource + public(friend) fun set_epoch_interval(framework: &signer, epoch_interval: u64) acquires EpochInterval { + if (!exists(@ol_framework)) { + move_to(framework, EpochInterval { + epoch_interval + }) + } else { + let state = borrow_global_mut(@ol_framework); + state.epoch_interval = epoch_interval + } + } + + #[view] + /// Return epoch interval in seconds. + public fun get_epoch_interval_secs(): u64 acquires EpochInterval { + let state = borrow_global_mut(@ol_framework); + state.epoch_interval / 1000000 + } + + #[view] + /// Return rough remaining seconds in epoch + public fun get_remaining_epoch_secs(): u64 acquires Configuration, EpochInterval { + let now = timestamp::now_seconds(); + let last_epoch_secs = last_reconfiguration_time() / 1000000; + let interval = get_epoch_interval_secs(); + if (now < last_epoch_secs) { // impossible underflow, some thign bad, or tests + return 0 + }; + + let deadline = last_epoch_secs + interval; + + if (now > deadline) { // we've run over the deadline + return 0 + }; + + // belt and suspenders + if (deadline > now) { + return deadline - now + }; + + return 0 + + } + // For tests, skips the guid validation. #[test_only] public fun initialize_for_test(account: &signer) { From 933a6901aa5ef7e418718e298898c1358af6e076 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Fri, 27 Sep 2024 12:42:25 -0400 Subject: [PATCH 18/34] patching tests --- .../libra-framework/sources/genesis.move | 5 +++- .../sources/ol_sources/proof_of_fee.move | 9 ++++---- .../sources/ol_sources/secret_bid.move | 22 +++++++++++++++--- .../ol_sources/tests/boundary.test.move | 5 ++-- .../ol_sources/tests/proof_of_fee.test.move | 23 +++++++++++-------- 5 files changed, 44 insertions(+), 20 deletions(-) diff --git a/framework/libra-framework/sources/genesis.move b/framework/libra-framework/sources/genesis.move index f61594a56..ab419a90a 100644 --- a/framework/libra-framework/sources/genesis.move +++ b/framework/libra-framework/sources/genesis.move @@ -31,6 +31,7 @@ module diem_framework::genesis { use ol_framework::ol_account; use ol_framework::musical_chairs; use ol_framework::proof_of_fee; + use ol_framework::secret_bid; use ol_framework::slow_wallet; use ol_framework::libra_coin; use ol_framework::infra_escrow; @@ -256,7 +257,9 @@ module diem_framework::genesis { // default 90% to align with thermostatic rule in the PoF paper. // otherwise the thermostatic rule starts kicking-in immediately let sig = create_signer(validator.validator_config.owner_address); - proof_of_fee::pof_update_bid(&sig, 0900, 1000); // make the genesis + + // genesis validators need a bid struct + secret_bid::genesis_helper(diem_framework, &sig, 100); if (testnet::is_not_mainnet()) { // TODO: this is for testnet purposes only diff --git a/framework/libra-framework/sources/ol_sources/proof_of_fee.move b/framework/libra-framework/sources/ol_sources/proof_of_fee.move index 14a6b7e03..052f7107c 100644 --- a/framework/libra-framework/sources/ol_sources/proof_of_fee.move +++ b/framework/libra-framework/sources/ol_sources/proof_of_fee.move @@ -833,10 +833,11 @@ module ol_framework::proof_of_fee { } /// update the bid for the sender - public entry fun pof_update_bid(sender: &signer, bid: u64, epoch_expiry: u64) acquires ProofOfFeeAuction { - // update the bid, initializes if not already. - set_bid(sender, bid, epoch_expiry); - } + // Deprecated + // public entry fun pof_update_bid(sender: &signer, bid: u64, epoch_expiry: u64) acquires ProofOfFeeAuction { + // // update the bid, initializes if not already. + // set_bid(sender, bid, epoch_expiry); + // } /// update the bid using estimated net reward instead of the internal bid variables public entry fun pof_update_bid_net_reward(sender: &signer, net_reward: u64, diff --git a/framework/libra-framework/sources/ol_sources/secret_bid.move b/framework/libra-framework/sources/ol_sources/secret_bid.move index 61c78dff2..105797288 100644 --- a/framework/libra-framework/sources/ol_sources/secret_bid.move +++ b/framework/libra-framework/sources/ol_sources/secret_bid.move @@ -15,21 +15,23 @@ module ol_framework::secret_bid { use diem_std::comparator; use diem_framework::epoch_helper; use diem_framework::reconfiguration; + use diem_framework::system_addresses; use ol_framework::testnet; use ol_framework::address_utils; + friend ol_framework::genesis; friend ol_framework::proof_of_fee; #[test_only] use diem_framework::account; - #[test_only] - use diem_framework::system_addresses; #[test_only] - friend ol_framework::test_pof; + friend ol_framework::test_boundary; #[test_only] friend ol_framework::mock; + #[test_only] + friend ol_framework::test_pof; /// User bidding not initialized const ECOMMIT_BID_NOT_INITIALIZED: u64 = 1; @@ -56,6 +58,14 @@ module ol_framework::secret_bid { epoch: u64, } + public(friend) fun genesis_helper(framework: &signer, validator: &signer, entry_fee: u64) acquires CommittedBid { + system_addresses::assert_diem_framework(framework); + commit_entry_fee_impl(validator, b"genesis"); + + let state = borrow_global_mut(signer::address_of(validator)); + state.reveal_entry_fee = entry_fee; + } + /// Transaction entry function for committing bid public entry fun commit(user: &signer, digest: vector) acquires CommittedBid { // don't allow commiting within reveal window @@ -225,6 +235,12 @@ module ol_framework::secret_bid { get_bid_unchecked(user) } + /// Find the most recent epoch the validator has placed a bid on. + public(friend) fun latest_epoch_bid(user: address): u64 acquires CommittedBid { + let state = borrow_global(user); + state.commit_epoch + } + /// does the user have a current bid public(friend) fun has_valid_bid(user: address): bool acquires CommittedBid { let state = borrow_global(user); 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 81dba3370..79b4c752c 100644 --- a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move @@ -13,6 +13,7 @@ module ol_framework::test_boundary { use ol_framework::burn; use ol_framework::mock; use ol_framework::proof_of_fee; + use ol_framework::secret_bid; use ol_framework::jail; use ol_framework::slow_wallet; use ol_framework::vouch; @@ -285,7 +286,7 @@ module ol_framework::test_boundary { // mock bids vector::for_each(vals, |a| { let sig = account::create_signer_for_test(a); - proof_of_fee::pof_update_bid(&sig, 0100, 30); // 10% for 30 epochs + secret_bid::mock_revealed_bid(root, &sig, 0100, 30); // 10% for 30 epochs }); // mock bid history to increase thermostat by 5% @@ -315,7 +316,7 @@ module ol_framework::test_boundary { // mock bids vector::for_each(vals, |a| { let sig = account::create_signer_for_test(a); - proof_of_fee::pof_update_bid(&sig, 0970, 30); // 97% for 30 epochs + secret_bid::mock_revealed_bid(root, &sig, 0970, 30); // 97% for 30 epochs }); // mock bid history to decrease thermostat by 5% 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 605cfce33..d87203820 100644 --- a/framework/libra-framework/sources/ol_sources/tests/proof_of_fee.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/proof_of_fee.test.move @@ -2,6 +2,7 @@ module ol_framework::test_pof { use ol_framework::mock; use ol_framework::account; + use ol_framework::epoch_helper; use ol_framework::proof_of_fee; use ol_framework::reconfiguration; use ol_framework::jail; @@ -14,7 +15,7 @@ module ol_framework::test_pof { use diem_framework::chain_id; use std::vector; - use diem_std::debug::print; + // use diem_std::debug::print; const Alice: address = @0x1000a; const Bob: address = @0x1000b; @@ -85,8 +86,6 @@ module ol_framework::test_pof { mock_good_bid(&root, &alice); let (err, pass) = proof_of_fee::audit_qualification(alice); - print(&epoch_helper::get_current_epoch()); - print(&err); assert!(pass, 1006); } @@ -134,7 +133,8 @@ module ol_framework::test_pof { assert!(!jail::is_jailed(alice), 7357001); let a_sig = account::create_signer_for_test(alice); - secret_bid::mock_revealed_bid(&root, &a_sig, 100, 1); + let epoch = epoch_helper::get_current_epoch(); + secret_bid::mock_revealed_bid(&root, &a_sig, 100, epoch); let bid = secret_bid::get_bid_unchecked(alice); assert!(bid == 100, 7357001); @@ -157,6 +157,7 @@ module ol_framework::test_pof { #[test(root = @ol_framework)] fun audit_expired(root: signer) { + use ol_framework::epoch_helper; let set = mock::genesis_n_vals(&root, 7); mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); let alice = *vector::borrow(&set, 0); @@ -180,17 +181,19 @@ module ol_framework::test_pof { secret_bid::mock_revealed_bid(&root, &a_sig, 100, 0); let bid = secret_bid::get_bid_unchecked(alice); - assert!(bid == 1, 1006); + assert!(bid == 100, 1003); // should NOT pass audit. let (_err, pass) = proof_of_fee::audit_qualification(alice); - assert!(!pass, 1008); + assert!(!pass, 1004); // fix it - proof_of_fee::pof_update_bid(&a_sig, 1, 1000); + let epoch = epoch_helper::get_current_epoch(); + secret_bid::mock_revealed_bid(&root, &a_sig, 100, epoch); + let (_err, pass) = proof_of_fee::audit_qualification(alice); - assert!(pass, 1009); + assert!(pass, 1005); } @@ -312,7 +315,7 @@ module ol_framework::test_pof { // set an expired bid for alice let alice = vector::borrow(&set, 0); let alice_sig = account::create_signer_for_test(*alice); - proof_of_fee::pof_update_bid(&alice_sig, 55, 1); + secret_bid::mock_revealed_bid(&root, &alice_sig, 55, 1); // advance the epoch 2x, so the previous bid is expired. mock::mock_all_vals_good_performance(&root); @@ -405,7 +408,7 @@ module ol_framework::test_pof { // Ok now EVE changes her mind. Will force the bid to expire. let a_sig = account::create_signer_for_test(*vector::borrow(&set, 4)); - proof_of_fee::pof_update_bid(&a_sig, 0, 0); + secret_bid::mock_revealed_bid(&root, &a_sig, 0, 0); slow_wallet::slow_wallet_epoch_drip(&root, 500000); let sorted = proof_of_fee::get_bidders(true); From 1a32338fe050848e7f1d8707f16a58ca97bcb9ed Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:27:46 -0400 Subject: [PATCH 19/34] create some default supply constants for testsuite --- .../sources/ol_sources/mock.move | 53 ++++++++++++++----- .../ol_sources/tests/boundary.test.move | 24 +++++---- .../sources/ol_sources/tests/burn.test.move | 39 +++++++------- .../ol_sources/tests/proof_of_fee.test.move | 3 +- 4 files changed, 77 insertions(+), 42 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/mock.move b/framework/libra-framework/sources/ol_sources/mock.move index 3cd2b4a6d..74152ff66 100644 --- a/framework/libra-framework/sources/ol_sources/mock.move +++ b/framework/libra-framework/sources/ol_sources/mock.move @@ -32,6 +32,30 @@ module ol_framework::mock { /// coin supply does not match expected const ESUPPLY_MISMATCH: u64 = 3; + //////// STATIC //////// + /// The starting nominal reward. Also how much each validator will have in their starting balance; as a genesis reward at start of network + const EPOCH_REWARD: u64 = 1_000_000; /// 1M + /// What is the fixed and final supply of the network at start + const FINAL_SUPPLY_AT_GENESIS: u64 = 100_000_000_000; // 100B + /// Place some coins in the system transaction fee account; + const TX_FEE_ACCOUNT_AT_GENESIS: u64 = 500_000_000; // 500M + /// Tbe starting entry fee for validators + const ENTRY_FEE: u64 = 1_000; // 1K + + #[test_only] + public fun default_epoch_reward(): u64 { EPOCH_REWARD } + + #[test_only] + /// What is the fixed and final supply of the network at start + public fun default_final_supply_at_genesis(): u64 { FINAL_SUPPLY_AT_GENESIS } + + #[test_only] + public fun default_tx_fee_account_at_genesis(): u64 { TX_FEE_ACCOUNT_AT_GENESIS } + + #[test_only] + public fun default_entry_fee(): u64 { ENTRY_FEE } + + #[test_only] public fun reset_val_perf_one(vm: &signer, addr: address) { stake::mock_performance(vm, addr, 0, 0); @@ -121,8 +145,8 @@ module ol_framework::mock { let a = vector::borrow(vals, i); let sig = account::create_signer_for_test(*a); - // initialize and set. - let epoch = 0; + // initialize and set. Will likely by epoch 0, genesis. + let epoch = epoch_helper::get_current_epoch(); secret_bid::mock_revealed_bid(framework, &sig, b, epoch); prev = fib; fib = b; @@ -216,14 +240,19 @@ module ol_framework::mock { let tx_fees = coin::test_mint(initial_fees, &mint_cap); transaction_fee::vm_pay_fee(root, @ol_framework, tx_fees); - // Forge Bruce - let fortune = 100_000_000_000; + let final_supply = 100_000_000_000; + + // We need to simulate a long running network + // where the Infra Pledge account accumulated. + // Here we use Wayne Enterprises as the sole sponsor of the + // test environment Infra Pedge let bruce_address = @0xBA7; ol_account::create_account(root, bruce_address); // Bruce mints a fortune + // TODO: change this so that the FINAL supply does not exceed 100B let bruce = account::create_signer_for_test(bruce_address); - let fortune_mint = coin::test_mint(fortune, &mint_cap); + let fortune_mint = coin::test_mint(final_supply, &mint_cap); ol_account::deposit_coins(bruce_address, fortune_mint); // Bruce funds infra escrow @@ -231,7 +260,7 @@ module ol_framework::mock { pledge_accounts::user_pledge(&bruce, framework, 37_000_000_000); let supply_pre = libra_coin::supply(); - assert!(supply_pre == (initial_fees + fortune), ESUPPLY_MISMATCH); + assert!(supply_pre == (initial_fees + final_supply), ESUPPLY_MISMATCH); libra_coin::test_set_final_supply(root, initial_fees); mint_cap @@ -365,12 +394,10 @@ module ol_framework::mock { let n_vals = 5; let _vals = genesis_n_vals(root, n_vals); // need to include eve to init funds - let genesis_mint = 1_000_000; - ol_initialize_coin_and_fund_vals(root, genesis_mint, true); + + ol_initialize_coin_and_fund_vals(root, EPOCH_REWARD, true); let supply_pre = libra_coin::supply(); - let bruce_fortune = 100_000_000_000; - let mocked_tx_fees = 5_000_000 * 100; - assert!(supply_pre == bruce_fortune + mocked_tx_fees + (n_vals * genesis_mint), 73570001); + assert!(supply_pre == FINAL_SUPPLY_AT_GENESIS + TX_FEE_ACCOUNT_AT_GENESIS + (n_vals * EPOCH_REWARD), 73570001); } @@ -385,9 +412,9 @@ module ol_framework::mock { let (nominal_reward, entry_fee, clearing_percent, median_bid ) = proof_of_fee::get_consensus_reward(); - assert!(nominal_reward == 1_000_000, 73570001); + assert!(nominal_reward == EPOCH_REWARD, 73570001); assert!(clearing_percent == 1, 73570002); - assert!(entry_fee == 1_000, 73570003); + assert!(entry_fee == ENTRY_FEE, 73570003); assert!(median_bid == 3, 73570004); } } diff --git a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move index 79b4c752c..ca59767bb 100644 --- a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move @@ -23,7 +23,7 @@ module ol_framework::test_boundary { use ol_framework::block; use ol_framework::ol_account; - use diem_std::debug::print; + // use diem_std::debug::print; const Alice: address = @0x1000a; const Bob: address = @0x1000b; @@ -80,17 +80,20 @@ module ol_framework::test_boundary { // check subsidy for new rewards and fees collected // fees collected = 10 * 1_000_000 + 10 * 1_000 = 10_010_000 - print(&transaction_fee::system_fees_collected()); assert!(transaction_fee::system_fees_collected() == 10_010_000, 7357006); } #[test(root = @ol_framework, alice = @0x1000a, marlon_rando = @0x12345)] fun e2e_add_validator_happy(root: signer, alice: signer, marlon_rando: signer) { + let epoch_reward = 1_000_000; + let entry_fee = 1_000; + let marlon_starting_balance = 200_000; + let initial_vals = common_test_setup(&root); // generate credentials for validator registration - ol_account::transfer(&alice, @0x12345, 200000); + ol_account::transfer(&alice, @0x12345, marlon_starting_balance); let (_sk, pk, pop) = stake::generate_identity(); let pk_bytes = bls12381::public_key_to_bytes(&pk); let pop_bytes = bls12381::proof_of_possession_to_bytes(&pop); @@ -132,20 +135,23 @@ module ol_framework::test_boundary { while (i < vector::length(&initial_vals)) { let (_unlocked, current) = ol_account::balance(*vector::borrow(&initial_vals, i)); let previous = *vector::borrow(&balances, i); - assert!(current == (previous + 1_000_000 - 1_000), 7357007); + assert!(current == (previous + epoch_reward - entry_fee), 7357007); i = i + 1; }; // check Marlon's balance: 200_000 - 1_000 = 199_000 let (_unlocked, marlon_balance) = ol_account::balance(@0x12345); - assert!(marlon_balance == 199_000, 7357008); + assert!(marlon_balance == (marlon_starting_balance - entry_fee), 7357008); // check subsidy for new rewards and fees collected - // fees collected = 11 * 1_000_000 + 11 * 1_000 = 11_011_000 + // fees collected = (11 * 1_000_000) + (11 * 1_000) = 11_011_000 + assert!((11* epoch_reward) + (11 * entry_fee) == 11_011_000, 7357009); assert!(transaction_fee::system_fees_collected() == 11_011_000, 7357009); // another epoch and everyone is compliant as well mock::mock_all_vals_good_performance(&root); + mock::mock_bids(&root, &vals); + mock::trigger_epoch(&root); assert!(epoch_boundary::get_seats_offered() == 12, 7357010); @@ -157,13 +163,13 @@ module ol_framework::test_boundary { while (i < vector::length(&initial_vals)) { let (_unlocked, current) = ol_account::balance(*vector::borrow(&initial_vals, i)); let previous = *vector::borrow(&balances, i); - assert!(current == (previous + 2_000_000 - 2_000), 7357012); + assert!(current == (previous + (2*epoch_reward) - (2*entry_fee)), 7357012); i = i + 1; }; - // check Marlon's balance: 200_000 + 1_000_000 - 2_000 = 1_198_000 + // check Marlon's balance = 1_000_000 + 200_000 - 2_000 = 1_198_000 let (_unlocked, marlon_balance) = ol_account::balance(@0x12345); - assert!(marlon_balance == 1_198_000, 7357013); + assert!(marlon_balance == (epoch_reward + 198_000), 7357013); // CHECK BURNT FEES diff --git a/framework/libra-framework/sources/ol_sources/tests/burn.test.move b/framework/libra-framework/sources/ol_sources/tests/burn.test.move index 51c66a9f6..bf5a9abaf 100644 --- a/framework/libra-framework/sources/ol_sources/tests/burn.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/burn.test.move @@ -1,7 +1,7 @@ #[test_only] module ol_framework::test_burn { - use ol_framework::mock; + use ol_framework::mock::{Self, default_epoch_reward, default_final_supply_at_genesis, default_tx_fee_account_at_genesis, default_entry_fee }; use ol_framework::libra_coin; use ol_framework::ol_account; use ol_framework::match_index; @@ -16,7 +16,7 @@ module ol_framework::test_burn { use std::option; use std::fixed_point32; - // use diem_std::debug::print; + use diem_std::debug::print; #[test(root = @ol_framework, alice = @0x1000a)] fun burn_reduces_supply(root: &signer, alice: &signer) { @@ -73,8 +73,9 @@ module ol_framework::test_burn { // EVE donates to both. But mostly to Alice. // The Match Index, should reflect that. - let vals = mock::genesis_n_vals(root, 5); // need to include eve to init funds - mock::ol_initialize_coin_and_fund_vals(root, 1000000, true); + let vals = mock::genesis_n_vals(root, 5); // need to include eve to init funds) + + mock::ol_initialize_coin_and_fund_vals(root, default_epoch_reward(), true); // start at epoch 1, since turnout tally needs epoch info, and 0 may cause issues mock::trigger_epoch(root); @@ -137,7 +138,7 @@ module ol_framework::test_burn { // The Match Index, should reflect that. let vals = mock::genesis_n_vals(root, 5); // need to include eve to init funds - mock::ol_initialize_coin_and_fund_vals(root, 1000000, true); + mock::ol_initialize_coin_and_fund_vals(root, default_epoch_reward(), true); // start at epoch 1, since turnout tally needs epoch info, and 0 may cause issues mock::trigger_epoch(root); @@ -200,26 +201,28 @@ module ol_framework::test_burn { // and the supply should be lower let n_vals = 5; - let _vals = mock::genesis_n_vals(root, n_vals); // need to include eve to init funds - let genesis_mint = 1_000_000; // 1 coin per - let epoch_reward = genesis_mint; // just to be explicit - - mock::ol_initialize_coin_and_fund_vals(root, genesis_mint, true); + let epoch_reward = default_epoch_reward(); // 1 coin per validator + let genesis_reward_per_val = epoch_reward; // just to be explicit let supply_pre = libra_coin::supply(); - let bruce_fortune = 100_000_000_000; - let mocked_tx_fees = 5_000_000 * 100; // 100 coins in tx fee account - assert!(supply_pre == mocked_tx_fees + bruce_fortune + (n_vals * genesis_mint), 73570001); + let genesis_supply = default_final_supply_at_genesis(); + let mocked_tx_fees = default_tx_fee_account_at_genesis(); + + let _vals = mock::genesis_n_vals(root, n_vals); + mock::ol_initialize_coin_and_fund_vals(root, genesis_reward_per_val, true); + assert!(supply_pre == mocked_tx_fees + genesis_supply + (n_vals * genesis_reward_per_val), 73570001); + // The only fees in the tX fee account at genesis should be what was mocked by default let fees = transaction_fee::system_fees_collected(); assert!(fees == mocked_tx_fees, 73570002); // start at epoch 1. NOTE Validators WILL GET PAID FOR EPOCH 1 - mock::trigger_epoch(root); // under 1000 rounds we don't + mock::trigger_epoch(root); + // NOTE: under 1000 rounds we don't evaluate performance of validators - // evaluate performance of validators let fees = transaction_fee::system_fees_collected(); + print(&fees); // There should be only entry fee left in tx fee wallet - assert!(fees == 5_000_000, 73570003); + assert!(fees == (5 * default_entry_fee()), 73570003); let validator_rewards = epoch_reward * n_vals; @@ -235,8 +238,8 @@ module ol_framework::test_burn { // this ALSO means that the only supply left in this example // is the rewards to validators from genesis, and from epoch 1 - // and the bruce fortune used to pledge infra escrow - assert!(supply_post == validator_rewards + (n_vals * genesis_mint) + bruce_fortune, 73570003); + // and the starting supply of the network + assert!(supply_post == validator_rewards + (n_vals * genesis_reward_per_val) + genesis_supply, 73570003); } 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 d87203820..3bf416c0b 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 @@ -73,7 +73,6 @@ module ol_framework::test_pof { #[test(root = @ol_framework)] fun audit_happy (root: signer) { - use ol_framework::epoch_helper; let set = mock::genesis_n_vals(&root, 4); mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); @@ -85,7 +84,7 @@ module ol_framework::test_pof { mock_good_bid(&root, &alice); - let (err, pass) = proof_of_fee::audit_qualification(alice); + let (_err, pass) = proof_of_fee::audit_qualification(alice); assert!(pass, 1006); } From aad93fa8cd4072cd479da2efe1db2a4c55e32d3f Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:29:04 -0400 Subject: [PATCH 20/34] standardize mock supply initialization, epoch burn sanity tested --- .../sources/ol_sources/burn.move | 2 + .../sources/ol_sources/epoch_boundary.move | 15 ++++ .../sources/ol_sources/infra_escrow.move | 47 ++++++++++-- .../sources/ol_sources/mock.move | 63 ++++++++------- .../sources/ol_sources/pledge_accounts.move | 16 ++-- .../sources/ol_sources/tests/burn.test.move | 76 +++++++++++-------- 6 files changed, 149 insertions(+), 70 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/burn.move b/framework/libra-framework/sources/ol_sources/burn.move index 0fbfa81f7..9ad668975 100644 --- a/framework/libra-framework/sources/ol_sources/burn.move +++ b/framework/libra-framework/sources/ol_sources/burn.move @@ -29,6 +29,8 @@ module ol_framework::burn { friend ol_framework::pledge_accounts; friend ol_framework::make_whole; + #[test_only] + friend ol_framework::mock; #[test_only] friend ol_framework::test_burn; #[test_only] diff --git a/framework/libra-framework/sources/ol_sources/epoch_boundary.move b/framework/libra-framework/sources/ol_sources/epoch_boundary.move index dadfcaa14..a671322db 100644 --- a/framework/libra-framework/sources/ol_sources/epoch_boundary.move +++ b/framework/libra-framework/sources/ol_sources/epoch_boundary.move @@ -45,6 +45,8 @@ module diem_framework::epoch_boundary { const ETRIGGER_NOT_READY: u64 = 2; /// Epoch number mismatch const ENOT_SAME_EPOCH: u64 = 3; + /// Supply should not change until burns + const ESUPPLY_SHOULD_NOT_CHANGE: u64 = 4; /////// Constants //////// /// How many PoF baseline rewards to we set aside for the miners. @@ -277,7 +279,11 @@ module diem_framework::epoch_boundary { // and prevents dependency cycling. public(friend) fun epoch_boundary(root: &signer, closing_epoch: u64, epoch_round: u64) acquires BoundaryStatus { + print(&string::utf8(b"EPOCH BOUNDARY BEGINS")); + // assert the supply does not change until there are burns. + let supply_a = libra_coin::supply(); + // either 0x0 or 0x1 can call, but we will always use framework signer system_addresses::assert_ol(root); let root = &create_signer::create_signer(@ol_framework); @@ -311,6 +317,10 @@ module diem_framework::epoch_boundary { status.incoming_compliant = compliant_vals; status.incoming_seats_offered = n_seats; + // up to this point supply should remain unchanged. + let supply_b = libra_coin::supply(); + assert!(supply_b == supply_a, ESUPPLY_SHOULD_NOT_CHANGE); + print(&string::utf8(b"settle_accounts")); settle_accounts(root, compliant_vals, status); @@ -352,6 +362,7 @@ module diem_framework::epoch_boundary { /// withdraw coins and settle accounts for validators and oracles /// returns the list of compliant_vals fun settle_accounts(root: &signer, compliant_vals: vector
, status: &mut BoundaryStatus): vector
{ + let supply_a = libra_coin::supply(); assert!(transaction_fee::is_fees_collection_enabled(), error::invalid_state(ETX_FEES_NOT_INITIALIZED)); if (transaction_fee::system_fees_collected() > 0) { @@ -377,6 +388,10 @@ module diem_framework::epoch_boundary { status.outgoing_vals_success = total_reward == (vector::length(&compliant_vals) * nominal_reward_to_vals) }; + // up to this point supply should remain unchanged. + let supply_b = libra_coin::supply(); + assert!(supply_b == supply_a, ESUPPLY_SHOULD_NOT_CHANGE); + // Commit note: deprecated with tower mining. // remainder gets burnt according to fee maker preferences diff --git a/framework/libra-framework/sources/ol_sources/infra_escrow.move b/framework/libra-framework/sources/ol_sources/infra_escrow.move index b91fbd34d..f9adc02b2 100644 --- a/framework/libra-framework/sources/ol_sources/infra_escrow.move +++ b/framework/libra-framework/sources/ol_sources/infra_escrow.move @@ -18,15 +18,29 @@ module ol_framework::infra_escrow{ use ol_framework::ol_account; use ol_framework::libra_coin::LibraCoin; use ol_framework::pledge_accounts; - // use ol_framework::slow_wallet; - // use std::fixed_point32; - // use std::signer; - // use diem_std::debug::print; + use ol_framework::testnet; + use ol_framework::epoch_helper; + + // use diem_framework::debug::print; friend diem_framework::genesis; friend ol_framework::epoch_boundary; + #[test_only] + use diem_framework::debug::print; + + #[test_only] + friend ol_framework::mock; + + + /// Not enough supply for genesis reward const EGENESIS_REWARD: u64 = 0; + + /// I'm sorry dave, this is only for testing + const EWITHDRAW_NOT_ON_MAINNET: u64 = 1; + + + /// for use on genesis, creates the infra escrow pledge policy struct public(friend) fun initialize(framework: &signer) { // NOTE: THIS MUST BE THE 0x0 address, because on epoch boundary it is that address @vm_reserved which will be calling the functions. @@ -95,13 +109,32 @@ module ol_framework::infra_escrow{ // and add them with mock.move when we need it. public(friend) fun genesis_coin_validator(framework: &signer, to: address) { system_addresses::assert_diem_framework(framework); - let bootstrap_amount = 1000000000; - if (infra_escrow_balance() > bootstrap_amount) { - let c_opt = infra_pledge_withdraw(framework, bootstrap_amount); + assert!(epoch_helper::get_current_epoch() == 0, EGENESIS_REWARD); + let bootstrap_amount = 1_000_000_000; // 1K scaled + framework_fund_account(framework, to, bootstrap_amount); + } + + // DANGER: keep this private, and only used in testnet + fun framework_fund_account(framework: &signer, to: address, amount: u64) { + system_addresses::assert_diem_framework(framework); + assert!(testnet::is_not_mainnet(), error::invalid_state(EWITHDRAW_NOT_ON_MAINNET)); + + if (infra_escrow_balance() > amount) { + let c_opt = infra_pledge_withdraw(framework, amount); assert!(option::is_some(&c_opt), error::invalid_state(EGENESIS_REWARD)); let coin = option::extract(&mut c_opt); ol_account::deposit_coins(to, coin); option::destroy_none(c_opt); } } + + #[test_only] + public(friend) fun test_fund_account_from_infra(framework: &signer, to: address, amount: u64) { + print(&amount); + // belt and suspenders + system_addresses::assert_diem_framework(framework); + assert!(testnet::is_not_mainnet(), error::invalid_state(EWITHDRAW_NOT_ON_MAINNET)); + + framework_fund_account(framework, to, amount); + } } diff --git a/framework/libra-framework/sources/ol_sources/mock.move b/framework/libra-framework/sources/ol_sources/mock.move index 74152ff66..b93591d92 100644 --- a/framework/libra-framework/sources/ol_sources/mock.move +++ b/framework/libra-framework/sources/ol_sources/mock.move @@ -2,7 +2,6 @@ #[test_only] module ol_framework::mock { use std::vector; - use std::signer; use diem_framework::coin; use diem_framework::block; use diem_framework::stake; @@ -25,7 +24,7 @@ module ol_framework::mock { use ol_framework::pledge_accounts; use ol_framework::secret_bid; - // use diem_std::debug::print; + use diem_std::debug::print; const ENO_GENESIS_END_MARKER: u64 = 1; const EDID_NOT_ADVANCE_EPOCH: u64 = 2; @@ -38,9 +37,11 @@ module ol_framework::mock { /// What is the fixed and final supply of the network at start const FINAL_SUPPLY_AT_GENESIS: u64 = 100_000_000_000; // 100B /// Place some coins in the system transaction fee account; - const TX_FEE_ACCOUNT_AT_GENESIS: u64 = 500_000_000; // 500M + const TX_FEE_ACCOUNT_AT_GENESIS: u64 = 100_000_000; // 100M /// Tbe starting entry fee for validators const ENTRY_FEE: u64 = 1_000; // 1K + /// Initial funding of infra-escrow + const INFRA_ESCROW_START: u64 = 37_000_000_000; // 37B #[test_only] public fun default_epoch_reward(): u64 { EPOCH_REWARD } @@ -200,22 +201,28 @@ module ol_framework::mock { #[test_only] public fun ol_initialize_coin_and_fund_vals(root: &signer, amount: u64, drip: bool) { - system_addresses::assert_ol(root); - - let mint_cap = if (coin::is_coin_initialized()) { - libra_coin::extract_mint_cap(root) - } else { - init_coin_impl(root) + if (!coin::is_coin_initialized()) { + let mint_cap = init_coin_impl(root); + libra_coin::restore_mint_cap(root, mint_cap); }; + fund_validators(root, amount, drip); + } + + fun fund_validators(root: &signer, amount: u64, + drip: bool) { + system_addresses::assert_ol(root); + let vals = stake::get_current_validators(); let i = 0; while (i < vector::length(&vals)) { let addr = vector::borrow(&vals, i); - let c = coin::test_mint(amount, &mint_cap); - ol_account::deposit_coins(*addr, c); + + let coin = pledge_accounts::test_single_withdrawal(root, @0xBA7, amount); + ol_account::deposit_coins(*addr, coin); let b = libra_coin::balance(*addr); + assert!(b == amount, 0001); i = i + 1; @@ -224,9 +231,14 @@ module ol_framework::mock { if (drip) { slow_wallet::slow_wallet_epoch_drip(root, amount); }; - libra_coin::restore_mint_cap(root, mint_cap); } + public fun mock_tx_fees_in_account(root: &signer, amount: u64) { + let tx_fees_start = pledge_accounts::test_single_withdrawal(root, @0xBA7, amount); + transaction_fee::vm_pay_fee(root, @ol_framework, tx_fees_start); + } + + #[test_only] fun init_coin_impl(root: &signer): coin::MintCapability { system_addresses::assert_ol(root); @@ -236,11 +248,10 @@ module ol_framework::mock { transaction_fee::initialize_fee_collection_and_distribution(root, 0); - let initial_fees = 5_000_000 * 100; // coin scaling * 100 coins - let tx_fees = coin::test_mint(initial_fees, &mint_cap); - transaction_fee::vm_pay_fee(root, @ol_framework, tx_fees); + let genesis_mint = coin::test_mint(FINAL_SUPPLY_AT_GENESIS, &mint_cap); + libra_coin::test_set_final_supply(root, FINAL_SUPPLY_AT_GENESIS); + assert!(libra_coin::supply() == FINAL_SUPPLY_AT_GENESIS, ESUPPLY_MISMATCH); - let final_supply = 100_000_000_000; // We need to simulate a long running network // where the Infra Pledge account accumulated. @@ -248,20 +259,14 @@ module ol_framework::mock { // test environment Infra Pedge let bruce_address = @0xBA7; ol_account::create_account(root, bruce_address); + // Bruce inherits a fortune, the remainder of genesis mint + ol_account::deposit_coins(bruce_address, genesis_mint); - // Bruce mints a fortune - // TODO: change this so that the FINAL supply does not exceed 100B + // Bruce pledges 37B to infra escrow let bruce = account::create_signer_for_test(bruce_address); - let fortune_mint = coin::test_mint(final_supply, &mint_cap); - ol_account::deposit_coins(bruce_address, fortune_mint); + pledge_accounts::user_pledge(&bruce, @ol_framework, INFRA_ESCROW_START); - // Bruce funds infra escrow - let framework = signer::address_of(root); - pledge_accounts::user_pledge(&bruce, framework, 37_000_000_000); - - let supply_pre = libra_coin::supply(); - assert!(supply_pre == (initial_fees + final_supply), ESUPPLY_MISMATCH); - libra_coin::test_set_final_supply(root, initial_fees); + assert!(libra_coin::supply() == FINAL_SUPPLY_AT_GENESIS, ESUPPLY_MISMATCH); mint_cap } @@ -397,7 +402,9 @@ module ol_framework::mock { ol_initialize_coin_and_fund_vals(root, EPOCH_REWARD, true); let supply_pre = libra_coin::supply(); - assert!(supply_pre == FINAL_SUPPLY_AT_GENESIS + TX_FEE_ACCOUNT_AT_GENESIS + (n_vals * EPOCH_REWARD), 73570001); + print(&supply_pre); + + assert!(libra_coin::supply() == FINAL_SUPPLY_AT_GENESIS, 73570001); } diff --git a/framework/libra-framework/sources/ol_sources/pledge_accounts.move b/framework/libra-framework/sources/ol_sources/pledge_accounts.move index c25cf387c..e575182db 100644 --- a/framework/libra-framework/sources/ol_sources/pledge_accounts.move +++ b/framework/libra-framework/sources/ol_sources/pledge_accounts.move @@ -528,7 +528,6 @@ save_pledge(user_sig, beneficiary, coin); } - ////////// GETTERS ////////// fun pledge_at_idx(account: &address, address_of_beneficiary: &address): (bool, u64) acquires MyPledges { @@ -656,9 +655,16 @@ //////// TEST HELPERS /////// #[test_only] // Danger! withdraws from an account. - public fun test_single_withdrawal(vm: &signer, bene: &address, donor: &address, amount: u64): option::Option> acquires MyPledges, BeneficiaryPolicy{ - system_addresses::assert_ol(vm); - // testnet::assert_testnet(vm); - withdraw_from_one_pledge_account(bene, donor, amount) + public fun test_single_withdrawal(framework: &signer, donor: address, amount: u64): coin::Coin acquires MyPledges, BeneficiaryPolicy{ + use ol_framework::testnet; + system_addresses::assert_diem_framework(framework); + testnet::assert_testnet(framework); + + let opt = withdraw_from_one_pledge_account(&@ol_framework, &donor, amount); + assert!(option::is_some(&opt), 0); + + let c = option::extract(&mut opt); + option::destroy_none(opt); + return c } } diff --git a/framework/libra-framework/sources/ol_sources/tests/burn.test.move b/framework/libra-framework/sources/ol_sources/tests/burn.test.move index bf5a9abaf..3e287c15e 100644 --- a/framework/libra-framework/sources/ol_sources/tests/burn.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/burn.test.move @@ -1,7 +1,7 @@ #[test_only] module ol_framework::test_burn { - use ol_framework::mock::{Self, default_epoch_reward, default_final_supply_at_genesis, default_tx_fee_account_at_genesis, default_entry_fee }; + use ol_framework::mock::{Self, default_epoch_reward, default_final_supply_at_genesis, default_entry_fee }; use ol_framework::libra_coin; use ol_framework::ol_account; use ol_framework::match_index; @@ -16,7 +16,7 @@ module ol_framework::test_burn { use std::option; use std::fixed_point32; - use diem_std::debug::print; + // use diem_std::debug::print; #[test(root = @ol_framework, alice = @0x1000a)] fun burn_reduces_supply(root: &signer, alice: &signer) { @@ -192,54 +192,70 @@ module ol_framework::test_burn { assert!(balance == eve_donation_to_A, 7357006); } + #[test(root = @ol_framework)] fun epoch_fees_burn(root: &signer) { // Scenario: - // the network starts. - // we mock some tx fees going into the collection - // the epoch turns and the fee account should be empty + // the network starts + // we mock some tx fees going into the fee account + // validators have ZERO balance, so they cannot pay into tx fee account + // the validators perform correctly, and the epoch changes (they + // are allowed to continue because of failover rules) + // Expected Outcome: + // The fees from the previous epoch were used to pay validators. + // however there was an excess amount in Tx account, and this amount + // would be burned. + // and the supply should be lower let n_vals = 5; - let epoch_reward = default_epoch_reward(); // 1 coin per validator - let genesis_reward_per_val = epoch_reward; // just to be explicit - let supply_pre = libra_coin::supply(); - let genesis_supply = default_final_supply_at_genesis(); - let mocked_tx_fees = default_tx_fee_account_at_genesis(); + let genesis_balance_per_val = 0; // simplify calcs, no validators funded at start + + let exact_amount_for_reward = n_vals * default_epoch_reward(); + let excess_in_tx_fee_account = 30_000_000; + + let starting_tx_fees = excess_in_tx_fee_account + exact_amount_for_reward; let _vals = mock::genesis_n_vals(root, n_vals); - mock::ol_initialize_coin_and_fund_vals(root, genesis_reward_per_val, true); + mock::ol_initialize_coin_and_fund_vals(root, genesis_balance_per_val, true); + let supply_pre = libra_coin::supply(); + mock::mock_tx_fees_in_account(root, starting_tx_fees); + + // supply at genesis always be equal to final supply + assert!(supply_pre == default_final_supply_at_genesis(), 73570001); - assert!(supply_pre == mocked_tx_fees + genesis_supply + (n_vals * genesis_reward_per_val), 73570001); // The only fees in the tX fee account at genesis should be what was mocked by default - let fees = transaction_fee::system_fees_collected(); - assert!(fees == mocked_tx_fees, 73570002); + assert!(transaction_fee::system_fees_collected() == starting_tx_fees, 73570002); + + // no change in supply, since these coins came from infra pledge + assert!(libra_coin::supply() == default_final_supply_at_genesis(), 73570001); // start at epoch 1. NOTE Validators WILL GET PAID FOR EPOCH 1 + // but they have no balance to contribute to next epochs funds (for simplicity of calcs) mock::trigger_epoch(root); // NOTE: under 1000 rounds we don't evaluate performance of validators - let fees = transaction_fee::system_fees_collected(); - print(&fees); - // There should be only entry fee left in tx fee wallet - assert!(fees == (5 * default_entry_fee()), 73570003); + // supply should be lower + let supply_post = libra_coin::supply(); - let validator_rewards = epoch_reward * n_vals; + let fees_account_balance = transaction_fee::system_fees_collected(); + let infra_pledge_subsidy = n_vals * default_epoch_reward(); + let entry_fees_total = n_vals * default_entry_fee(); - // of the initial supply, - // we expect to burn everything which was in the TX FEEs AFTER PAYING VALIDATOR REWARDS - let amount_burned_excess_tx_account = mocked_tx_fees - validator_rewards; + assert!(supply_post < default_final_supply_at_genesis(), 73570001); - // So the current supply should be lower, - // The the old supply was reduced by what was burned (the excess in tx bucket) - let supply_post = libra_coin::supply(); - assert!(supply_post == supply_pre - amount_burned_excess_tx_account, 73570003); + // Now for the follwing epoch the tx fee account should have: + // a) the entry fees of validators (they can afford it now) + // b) the infra pledge subsidy * vals + + assert!(fees_account_balance == (infra_pledge_subsidy + entry_fees_total), 73570003); + + // In this scenario + // we expect to burn everything which was in the txs after paying validator rewards. + // we funded the tx fee account with an excess, beyond what was needed for the validator reward. - // this ALSO means that the only supply left in this example - // is the rewards to validators from genesis, and from epoch 1 - // and the starting supply of the network - assert!(supply_post == validator_rewards + (n_vals * genesis_reward_per_val) + genesis_supply, 73570003); + assert!(supply_post == supply_pre - excess_in_tx_fee_account, 73570003); } From 2471ce90cc751f0a56f892530754f800987641bd Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:50:46 -0400 Subject: [PATCH 21/34] patch track_fees test --- .../sources/ol_sources/tests/burn.test.move | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/tests/burn.test.move b/framework/libra-framework/sources/ol_sources/tests/burn.test.move index 3e287c15e..e79ff2f15 100644 --- a/framework/libra-framework/sources/ol_sources/tests/burn.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/burn.test.move @@ -1,7 +1,7 @@ #[test_only] module ol_framework::test_burn { - use ol_framework::mock::{Self, default_epoch_reward, default_final_supply_at_genesis, default_entry_fee }; + use ol_framework::mock::{Self, default_epoch_reward, default_final_supply_at_genesis, default_entry_fee, default_tx_fee_account_at_genesis}; use ol_framework::libra_coin; use ol_framework::ol_account; use ol_framework::match_index; @@ -228,13 +228,15 @@ module ol_framework::test_burn { assert!(transaction_fee::system_fees_collected() == starting_tx_fees, 73570002); // no change in supply, since these coins came from infra pledge - assert!(libra_coin::supply() == default_final_supply_at_genesis(), 73570001); + assert!(libra_coin::supply() == default_final_supply_at_genesis(), 73570003); // start at epoch 1. NOTE Validators WILL GET PAID FOR EPOCH 1 // but they have no balance to contribute to next epochs funds (for simplicity of calcs) + let (lifetime_burn_pre, _lifetime_match) = burn::get_lifetime_tracker(); mock::trigger_epoch(root); // NOTE: under 1000 rounds we don't evaluate performance of validators - + let (lifetime_burn_post, _lifetime_match) = burn::get_lifetime_tracker(); + assert!(lifetime_burn_post > lifetime_burn_pre, 73570004); // supply should be lower let supply_post = libra_coin::supply(); @@ -242,20 +244,20 @@ module ol_framework::test_burn { let infra_pledge_subsidy = n_vals * default_epoch_reward(); let entry_fees_total = n_vals * default_entry_fee(); - assert!(supply_post < default_final_supply_at_genesis(), 73570001); + assert!(supply_post < default_final_supply_at_genesis(), 73570005); // Now for the follwing epoch the tx fee account should have: // a) the entry fees of validators (they can afford it now) // b) the infra pledge subsidy * vals - assert!(fees_account_balance == (infra_pledge_subsidy + entry_fees_total), 73570003); + assert!(fees_account_balance == (infra_pledge_subsidy + entry_fees_total), 73570006); // In this scenario // we expect to burn everything which was in the txs after paying validator rewards. // we funded the tx fee account with an excess, beyond what was needed for the validator reward. - assert!(supply_post == supply_pre - excess_in_tx_fee_account, 73570003); + assert!(supply_post == supply_pre - excess_in_tx_fee_account, 73570007); } @@ -338,9 +340,13 @@ module ol_framework::test_burn { #[test(root=@ol_framework, alice=@0x1000a)] fun track_fees(root: &signer, alice: address) { - // use ol_framework::libra_coin; - let _vals = mock::genesis_n_vals(root, 1); // need to include eve to init funds - mock::ol_initialize_coin_and_fund_vals(root, 10000, true); + + let _vals = mock::genesis_n_vals(root, 1); + mock::ol_initialize_coin_and_fund_vals(root, default_epoch_reward(), true); + mock::mock_tx_fees_in_account(root, default_tx_fee_account_at_genesis()); + + let fees = transaction_fee::system_fees_collected(); + assert!(fees == default_tx_fee_account_at_genesis(), 7357001); let marlon_rando = @0x12345; ol_account::create_account(root, marlon_rando); @@ -356,25 +362,37 @@ module ol_framework::test_burn { option::destroy_none(coin_option); let fees = transaction_fee::system_fees_collected(); - assert!(fees == 500_000_005, 7357001); - // Fees will include the initialization by MOCK. ol_initialize_coin_and_fund_vals + assert!(fees == (default_tx_fee_account_at_genesis() + rando_money), 7357001); // marlon is the only fee maker (since genesis) let fee_makers = fee_maker::get_fee_makers(); - // includes 0x1 which makes a deposit on assert!(vector::length(&fee_makers)==1, 7357002); + let (_found, idx) = vector::index_of(&fee_makers, &marlon_rando); + assert!( idx == 0, 7357003); let marlon_fees_made = fee_maker::get_user_fees_made(marlon_rando); - assert!(marlon_fees_made == 5, 7357003); + assert!(marlon_fees_made == 5, 7357004); + let (lifetime_burn_pre, _lifetime_match) = burn::get_lifetime_tracker(); mock::trigger_epoch(root); + // NOTE: under 1000 rounds we don't evaluate performance of validators + let (lifetime_burn_post, _lifetime_match) = burn::get_lifetime_tracker(); + assert!(lifetime_burn_post > lifetime_burn_pre, 0); let marlon_fees_made = fee_maker::get_user_fees_made(marlon_rando); - assert!(marlon_fees_made == 0, 7357004); + assert!(marlon_fees_made == 0, 7357005); + + // However, Alice (the only validator) did pay an entry fee during Proof of Fee, + // and that fee should be tracked let fee_makers = fee_maker::get_fee_makers(); - assert!(vector::length(&fee_makers) == 0, 7357005); + + assert!(vector::length(&fee_makers) == 1, 7357006); + let (_found, idx) = vector::index_of(&fee_makers, &alice); + assert!( idx == 0, 7357007); + + let fees = transaction_fee::system_fees_collected(); - assert!(fees == 1_000_000, 7357006); // val set entry fee + assert!(fees == default_entry_fee() + default_epoch_reward(), 7357008); // val set entry fee } #[test(root = @ol_framework, alice_val = @0x1000a)] From e89dbb5bf5362c45f384b1edbf64454c68408971 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:56:38 -0400 Subject: [PATCH 22/34] patch burn_tracker_increment --- .../libra-framework/sources/ol_sources/tests/burn.test.move | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/libra-framework/sources/ol_sources/tests/burn.test.move b/framework/libra-framework/sources/ol_sources/tests/burn.test.move index e79ff2f15..583aaf623 100644 --- a/framework/libra-framework/sources/ol_sources/tests/burn.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/burn.test.move @@ -437,6 +437,7 @@ module ol_framework::test_burn { assert!(cumu_burn == 0, 7357004); // simulate epoch boundary burn + mock::mock_tx_fees_in_account(root, 60_000_000); let all_fees = transaction_fee::test_root_withdraw_all(root); burn::epoch_burn_fees(root, &mut all_fees); coin::destroy_zero(all_fees); // destroy the hot potato From 8b799d2a56dea9a2be91769a02c8f00f9ceb5233 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:01:44 -0400 Subject: [PATCH 23/34] patch test_epoch_drip --- .../sources/ol_sources/tests/slow_wallet.test.move | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move b/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move index 06ff3027f..078302378 100644 --- a/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move @@ -5,7 +5,7 @@ module ol_framework::test_slow_wallet { use diem_framework::stake; use diem_framework::account; use ol_framework::slow_wallet; - use ol_framework::mock; + use ol_framework::mock::{Self, default_tx_fee_account_at_genesis}; use ol_framework::ol_account; use ol_framework::libra_coin; use ol_framework::epoch_boundary; @@ -112,6 +112,7 @@ module ol_framework::test_slow_wallet { fun test_epoch_drip(root: signer) { let set = mock::genesis_n_vals(&root, 4); mock::ol_initialize_coin_and_fund_vals(&root, 100, false); + mock::mock_tx_fees_in_account(&root, default_tx_fee_account_at_genesis()); let a = *vector::borrow(&set, 0); assert!(slow_wallet::is_slow(a), 7357000); @@ -122,12 +123,13 @@ module ol_framework::test_slow_wallet { let (u, b) = ol_account::balance(a); print(&b); - assert!(b==500_000_100, 735702); + assert!(b==(default_tx_fee_account_at_genesis() + 100), 735702); assert!(u==100, 735703); slow_wallet::slow_wallet_epoch_drip(&root, 233); let (u, b) = ol_account::balance(a); - assert!(b==500_000_100, 735704); + // no change total balances + assert!(b==(default_tx_fee_account_at_genesis() + 100), 735704); assert!(u==333, 735705); } From 45daa9e0aa3f1dbabaddc5396bfac0f849b5c410 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:46:55 -0400 Subject: [PATCH 24/34] patch safe_root_security_fee --- .../sources/ol_sources/tests/vote_lib/safe.test.move | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/tests/vote_lib/safe.test.move b/framework/libra-framework/sources/ol_sources/tests/vote_lib/safe.test.move index e6c4aaedc..edbb400e6 100644 --- a/framework/libra-framework/sources/ol_sources/tests/vote_lib/safe.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/vote_lib/safe.test.move @@ -93,14 +93,15 @@ module ol_framework::test_safe { fun safe_root_security_fee(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { let vals = mock::genesis_n_vals(root, 3); - mock::ol_initialize_coin_and_fund_vals(root, 1000000000000, true); + let balance = 100_000_000; + mock::ol_initialize_coin_and_fund_vals(root, balance, true); // Dave creates the resource account. HE is not one of the validators, and is not an authority in the multisig. let (resource_sig, _cap) = ol_account::test_ol_create_resource_account(dave, b"0x1"); let new_resource_address = signer::address_of(&resource_sig); assert!(resource_account::is_resource_account(new_resource_address), 0); - + safe::init_payment_multisig(&resource_sig, vals); // requires 3 - + // vals claim the offer multi_action::claim_offer(alice, new_resource_address); multi_action::claim_offer(bob, new_resource_address); @@ -110,9 +111,12 @@ module ol_framework::test_safe { multi_action::finalize_and_cage(&resource_sig, 2); // fund the account - ol_account::transfer(alice, new_resource_address, 1000000); + ol_account::transfer(alice, new_resource_address, 100_000); let (_, bal) = ol_account::balance(new_resource_address); + mock::trigger_epoch(root); + + // Use of Safe should debit the account at epoch boundary let (_, new_bal) = ol_account::balance(new_resource_address); assert!(new_bal < bal, 735701); } From 292b658f4d8d789f41952083f142e1f12ac88f00 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:10:16 -0400 Subject: [PATCH 25/34] patch reward_happy_case --- .../sources/ol_sources/mock.move | 4 +- .../sources/ol_sources/ol_account.move | 2 + .../tests/validator_reward.test.move | 66 +++++-------------- 3 files changed, 21 insertions(+), 51 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/mock.move b/framework/libra-framework/sources/ol_sources/mock.move index b93591d92..428924b99 100644 --- a/framework/libra-framework/sources/ol_sources/mock.move +++ b/framework/libra-framework/sources/ol_sources/mock.move @@ -219,7 +219,7 @@ module ol_framework::mock { let addr = vector::borrow(&vals, i); let coin = pledge_accounts::test_single_withdrawal(root, @0xBA7, amount); - ol_account::deposit_coins(*addr, coin); + ol_account::vm_deposit_coins_locked(root, *addr, coin); let b = libra_coin::balance(*addr); @@ -363,7 +363,7 @@ module ol_framework::mock { public fun meta_epoch(root: signer) { ol_test_genesis(&root); musical_chairs::initialize(&root, 10); - // ol_initialize_coin(&root); + let epoch = reconfiguration::current_epoch(); trigger_epoch(&root); let new_epoch = reconfiguration::current_epoch(); diff --git a/framework/libra-framework/sources/ol_sources/ol_account.move b/framework/libra-framework/sources/ol_sources/ol_account.move index 7fe1da168..fe302de99 100644 --- a/framework/libra-framework/sources/ol_sources/ol_account.move +++ b/framework/libra-framework/sources/ol_sources/ol_account.move @@ -38,6 +38,8 @@ module ol_framework::ol_account { friend ol_framework::test_multi_action; #[test_only] friend ol_framework::test_slow_wallet; + #[test_only] + friend ol_framework::mock; /// Account does not exist. const EACCOUNT_NOT_FOUND: u64 = 1; diff --git a/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move b/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move index 9ce9231e9..acb386893 100644 --- a/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move @@ -4,86 +4,54 @@ module ol_framework::test_reconfiguration { use std::vector; use diem_framework::stake; - use diem_framework::transaction_fee; - // use diem_framework::coin; - use ol_framework::mock; + // use diem_framework::transaction_fee; + use ol_framework::mock::{Self, default_tx_fee_account_at_genesis}; use ol_framework::testnet; use ol_framework::libra_coin; use ol_framework::proof_of_fee; use diem_framework::reconfiguration; use ol_framework::epoch_helper; use ol_framework::ol_account; - use ol_framework::infra_escrow; - use diem_std::debug::print; + // use diem_std::debug::print; // Scenario: all genesis validators make it to next epoch #[test(root = @ol_framework)] fun reconfig_reward_happy_case(root: signer) { - let vals = mock::genesis_n_vals(&root, 5); - - mock::pof_default(&root); - assert!(vector::length(&vals) == 5, 7357001); - let vals = stake::get_current_validators(); - assert!(vector::length(&vals) == 5, 7357002); - // all vals compliant - mock::mock_all_vals_good_performance(&root); + let _vals = mock::genesis_n_vals(&root, 5); - let (unlocked, alice_bal) = ol_account::balance(@0x1000a); - assert!(unlocked==0, 7367001); - assert!(alice_bal==0, 7357002); + let starting_balance = 500_000; + mock::ol_initialize_coin_and_fund_vals(&root, starting_balance, false); + mock::mock_tx_fees_in_account(&root, default_tx_fee_account_at_genesis()); - let (reward_one, _entry_fee, _, _ ) = proof_of_fee::get_consensus_reward(); + let (reward_one, genesis_entry_fee, _, _ ) = proof_of_fee::get_consensus_reward(); + // entry fee at genesis is zero + assert!(genesis_entry_fee == 0, 7357004); // The epoch's reward BEFORE reconfiguration - assert!(reward_one == 1000000, 7357004); - - let infra = infra_escrow::infra_escrow_balance(); - print(&555); - print(&infra); - - let subsidy = transaction_fee::system_fees_collected(); - print(&666); - print(&subsidy); + assert!(reward_one == 1_000_000, 7357004); // run ol reconfiguration mock::trigger_epoch(&root); - let infra = infra_escrow::infra_escrow_balance(); - print(&5552); - print(&infra); - - let subsidy = transaction_fee::system_fees_collected(); - print(&6662); - print(&subsidy); - + // magic: in test mode and under block 100 all validators perform let vals = stake::get_current_validators(); assert!(vector::length(&vals) == 5, 7357005); - // let alice_bal = libra_coin::balance(@0x1000a); + let (_unlocked, alice_bal) = ol_account::balance(@0x1000a); let (_, entry_fee, _, _ ) = proof_of_fee::get_consensus_reward(); - // need to check that the user paid an PoF entry fee for next epoch. - // which means the balance will be the nominal reward, net of the PoF clearing price bid - assert!(alice_bal == (reward_one - entry_fee), 7357006); - - // test new subsidy - let (reward_two, _entry_fee, _, _ ) = proof_of_fee::get_consensus_reward(); - print(&777); - print(&reward_two); - let new_budget = reward_two * 5; - print(&new_budget); - let subsidy = transaction_fee::system_fees_collected(); - print(&888); - print(&subsidy); + // need to check that the user paid an PoF entry fee for next epoch. + // which means the balance will be: + // the nominal reward minus, entry fee (clearing price), plus any existing balance. + assert!(alice_bal == (reward_one - entry_fee + starting_balance), 7357006); } #[test(root = @ol_framework)] fun drop_non_performing(root: signer) { let _vals = mock::genesis_n_vals(&root, 5); - // mock::ol_initialize_coin(&root); mock::pof_default(&root); assert!(libra_coin::balance(@0x1000a) == 0, 7357000); From d009df27d6baac9320e47b056cca3b3cc04ae1d6 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:14:34 -0400 Subject: [PATCH 26/34] patch epoch drip again --- .../ol_sources/tests/slow_wallet.test.move | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move b/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move index 078302378..7d96d5749 100644 --- a/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move @@ -18,7 +18,7 @@ module ol_framework::test_slow_wallet { use std::vector; use std::signer; - use diem_std::debug::print; + // use diem_std::debug::print; #[test(root = @ol_framework)] // we are testing that genesis creates the needed struct @@ -111,26 +111,28 @@ module ol_framework::test_slow_wallet { #[test(root = @ol_framework)] fun test_epoch_drip(root: signer) { let set = mock::genesis_n_vals(&root, 4); - mock::ol_initialize_coin_and_fund_vals(&root, 100, false); + let should_drip = false; + mock::ol_initialize_coin_and_fund_vals(&root, 100, should_drip); mock::mock_tx_fees_in_account(&root, default_tx_fee_account_at_genesis()); let a = *vector::borrow(&set, 0); assert!(slow_wallet::is_slow(a), 7357000); - assert!(slow_wallet::unlocked_amount(a) == 100, 735701); + // mock:: does not drip by default + assert!(slow_wallet::unlocked_amount(a) == 0, 735701); let coin = transaction_fee::test_root_withdraw_all(&root); rewards::test_helper_pay_reward(&root, a, coin, 0); - let (u, b) = ol_account::balance(a); - print(&b); - assert!(b==(default_tx_fee_account_at_genesis() + 100), 735702); - assert!(u==100, 735703); + let (unlocked, total_balance) = ol_account::balance(a); + + assert!(total_balance == (default_tx_fee_account_at_genesis() + 100), 735702); + assert!(unlocked == 0, 735703); slow_wallet::slow_wallet_epoch_drip(&root, 233); - let (u, b) = ol_account::balance(a); + let (unlocked, total_balance) = ol_account::balance(a); // no change total balances - assert!(b==(default_tx_fee_account_at_genesis() + 100), 735704); - assert!(u==333, 735705); + assert!(total_balance==(default_tx_fee_account_at_genesis() + 100), 735704); + assert!(unlocked==233, 735705); } #[test(root = @ol_framework, alice = @0x123, bob = @0x456)] From 00fcf1b5a63fd7fc8e194c994dbc99e24f1fe619 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:37:40 -0400 Subject: [PATCH 27/34] patch donor voice tests, had an incorrect assertion --- .../ol_sources/tests/boundary.test.move | 34 ++++++++++-------- .../ol_sources/tests/donor_voice.test.move | 35 +++++++------------ 2 files changed, 31 insertions(+), 38 deletions(-) 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 ca59767bb..12962ab73 100644 --- a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move @@ -11,6 +11,7 @@ module ol_framework::test_boundary { use diem_framework::transaction_fee; use diem_framework::account; use ol_framework::burn; + use ol_framework::epoch_helper; use ol_framework::mock; use ol_framework::proof_of_fee; use ol_framework::secret_bid; @@ -23,7 +24,7 @@ module ol_framework::test_boundary { use ol_framework::block; use ol_framework::ol_account; - // use diem_std::debug::print; + use diem_std::debug::print; const Alice: address = @0x1000a; const Bob: address = @0x1000b; @@ -288,50 +289,53 @@ module ol_framework::test_boundary { #[test(root = @ol_framework)] fun epoch_increase_thermostat(root: &signer) { let vals = common_test_setup(root); - + let this_epoch = epoch_helper::get_current_epoch(); + let entry_fee_bid = 0100; // mock bids vector::for_each(vals, |a| { let sig = account::create_signer_for_test(a); - secret_bid::mock_revealed_bid(root, &sig, 0100, 30); // 10% for 30 epochs + secret_bid::mock_revealed_bid(root, &sig, entry_fee_bid, this_epoch); // 10% for 30 epochs }); // mock bid history to increase thermostat by 5% proof_of_fee::test_mock_reward( root, - 1000, // nominal reward - 0100, // clearing bid - 0100, // median win bid - vector[ 0100, 0100, 0100, 0100, 0100, 0100 ] // median history bellow 50% + 100_000, // nominal reward + entry_fee_bid, // clearing bid + entry_fee_bid, // median win bid + vector[ entry_fee_bid, entry_fee_bid, entry_fee_bid, entry_fee_bid, entry_fee_bid, entry_fee_bid, ] // median history bellow 50% ); // trigger epoch mock::trigger_epoch(root); - + print(&transaction_fee::system_fees_collected()); // check subsidy increased by 5% // fees collected = entry fee + reward * 105% - // entry fee = 100 * 10 = 1_000 - // reward = 1050 * 10 = 10_500 + // entry fee = 100_000 * 0.1 * 10 = 100_000 + // reward = 100_000 * 1.05 * 10 = 1_050_000 // fees collected = 11_500 - assert!(transaction_fee::system_fees_collected() == 11_500, 7357001); + assert!(transaction_fee::system_fees_collected() == 1150000, 7357001); } #[test(root = @ol_framework)] fun epoch_decrease_thermostat(root: &signer) { let vals = common_test_setup(root); + let entry_fee_bid = 0970; // mock bids + let this_epoch = epoch_helper::get_current_epoch(); vector::for_each(vals, |a| { let sig = account::create_signer_for_test(a); - secret_bid::mock_revealed_bid(root, &sig, 0970, 30); // 97% for 30 epochs + secret_bid::mock_revealed_bid(root, &sig, entry_fee_bid, this_epoch); }); // mock bid history to decrease thermostat by 5% proof_of_fee::test_mock_reward( root, 100_000, // nominal reward - 0970, // clearing bid - 0970, // median win bid - vector[ 0970, 0970, 0970, 0970, 0970, 0970 ] // median history above 95% + entry_fee_bid, // clearing bid + entry_fee_bid, // median win bid + vector[ entry_fee_bid, entry_fee_bid, entry_fee_bid, entry_fee_bid, 0970, entry_fee_bid ] // median history above 95% ); // trigger epoch diff --git a/framework/libra-framework/sources/ol_sources/tests/donor_voice.test.move b/framework/libra-framework/sources/ol_sources/tests/donor_voice.test.move index 2b39cd964..83b5050c5 100644 --- a/framework/libra-framework/sources/ol_sources/tests/donor_voice.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/donor_voice.test.move @@ -12,7 +12,6 @@ module ol_framework::test_donor_voice { use ol_framework::donor_voice_governance; use ol_framework::community_wallet_init; use ol_framework::community_wallet; - use ol_framework::burn; use ol_framework::slow_wallet; use diem_framework::multisig_account; use std::guid; @@ -22,7 +21,7 @@ module ol_framework::test_donor_voice { use diem_std::debug::print; #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b)] - fun dd_init(root: &signer, alice: &signer, bob: &signer) { + fun dv_init(root: &signer, alice: &signer, bob: &signer) { let vals = mock::genesis_n_vals(root, 2); let (resource_sig, _cap) = ol_account::test_ol_create_resource_account(alice, b"0x1"); @@ -48,7 +47,7 @@ module ol_framework::test_donor_voice { } #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b)] - fun dd_propose_payment(root: &signer, alice: &signer, bob: &signer) { + fun dv_propose_payment(root: &signer, alice: &signer, bob: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -78,7 +77,7 @@ module ol_framework::test_donor_voice { } #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d)] - fun dd_schedule_happy(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { + fun dv_schedule_happy(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -124,7 +123,7 @@ module ol_framework::test_donor_voice { } #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d, eve = @0x1000e)] - fun dd_propose_and_veto(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer, eve: &signer) { + fun dv_propose_and_veto(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer, eve: &signer) { // Scenario: Eve wants to veto a transaction on a donor directed account. // Alice creates a donor directed account where Alice, Bob and Carol, are admins. // Dave and Eve make a donation and so are able to have some voting on that account. The veto can only happen after Alice Bob and Carol are able to schedule a tx. @@ -213,7 +212,7 @@ module ol_framework::test_donor_voice { // should not be able sign a tx twice #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d)] #[expected_failure(abort_code = 65550, location = 0x1::multi_action)] - fun dd_reject_duplicate_proposal(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { + fun dv_reject_duplicate_proposal(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -252,7 +251,7 @@ module ol_framework::test_donor_voice { #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d)] - fun dd_process_unit(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { + fun dv_process_unit(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -328,7 +327,7 @@ module ol_framework::test_donor_voice { } #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, marlon_rando = @0x123456)] - fun dd_process_epoch_boundary(root: &signer, alice: &signer, bob: &signer, carol: &signer, marlon_rando: &signer) { + fun dv_process_epoch_boundary(root: &signer, alice: &signer, bob: &signer, carol: &signer, marlon_rando: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -397,7 +396,7 @@ module ol_framework::test_donor_voice { } #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d, marlon_rando = @0x123456)] - fun dd_process_multi_same_epoch(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer, marlon_rando: &signer) { + fun dv_process_multi_same_epoch(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer, marlon_rando: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -544,7 +543,7 @@ module ol_framework::test_donor_voice { #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, marlon_rando = @0x123456)] - fun dd_process_multi_different_epochs(root: &signer, alice: &signer, bob: &signer, carol: &signer, marlon_rando: &signer) { + fun dv_process_multi_different_epochs(root: &signer, alice: &signer, bob: &signer, carol: &signer, marlon_rando: &signer) { // Scenario: Alice creates a resource_account which will be a donor directed account. She will not be one of the authorities of the account. // only bob, carol, and dave with be authorities @@ -669,7 +668,7 @@ module ol_framework::test_donor_voice { #[test(root = @ol_framework, _alice = @0x1000a, dave = @0x1000d, eve = @0x1000e, donor_voice = @0x1000f)] - fun dd_liquidate_to_donor(root: &signer, _alice: &signer, dave: &signer, eve: &signer, donor_voice: &signer) { + fun dv_liquidate_to_donor(root: &signer, _alice: &signer, dave: &signer, eve: &signer, donor_voice: &signer) { // Scenario: // Alice creates a donor directed account where Alice, Bob and Carol, are admins. // Dave and Eve make a donation and so are able to have some voting on that account. @@ -680,8 +679,6 @@ module ol_framework::test_donor_voice { // start at epoch 1, since turnout tally needs epoch info, and 0 may cause // issues mock::trigger_epoch(root); - // a burn happend in the epoch above, so let's compare it to end of epoch - let (lifetime_burn_pre, _) = burn::get_lifetime_tracker(); let donor_voice_address = signer::address_of(donor_voice); // the account needs basic donor directed structs @@ -745,13 +742,10 @@ module ol_framework::test_donor_voice { // eve shoul have received funds back assert!(eve_balance > eve_balance_pre, 7357010); - // nothing should have been burned, it was a refund - let (lifetime_burn_now, _) = burn::get_lifetime_tracker(); - assert!(lifetime_burn_now == lifetime_burn_pre, 7357011); } #[test(root = @ol_framework, alice = @0x1000a, dave = @0x1000d, eve = @0x1000e)] - fun dd_liquidate_to_match_index(root: &signer, alice: &signer, dave: &signer, eve: &signer) { + fun dv_liquidate_to_match_index(root: &signer, alice: &signer, dave: &signer, eve: &signer) { // Scenario: // Alice creates a donor directed account where Alice, Bob and Carol, are admins. // Dave and Eve make a donation and so are able to have some voting on that account. @@ -761,8 +755,7 @@ module ol_framework::test_donor_voice { mock::ol_initialize_coin_and_fund_vals(root, 100000, true); // start at epoch 1, since turnout tally needs epoch info, and 0 may cause issues mock::trigger_epoch(root); - // a burn happend in the epoch above, so let's compare it to end of epoch - let (lifetime_burn_pre, _) = burn::get_lifetime_tracker(); + let (resource_sig, _cap) = ol_account::test_ol_create_resource_account(alice, b"0x1"); let donor_voice_address = signer::address_of(&resource_sig); @@ -832,10 +825,6 @@ module ol_framework::test_donor_voice { // program says it goes to the matching index. assert!(eve_balance == eve_balance_pre, 7357010); - let (lifetime_burn_now, _lifetime_match) = burn::get_lifetime_tracker(); - - // nothing should have been burned, it was a refund - assert!(lifetime_burn_now == lifetime_burn_pre, 7357011); } #[test(root = @ol_framework, community = @0x10011, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c)] From 5b7bb9f740479ab377797e52e9e52d3bf07a7d04 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:49:04 -0400 Subject: [PATCH 28/34] remove retract bid test --- .../sources/ol_sources/proof_of_fee.move | 16 ------ .../ol_sources/tests/boundary.test.move | 5 -- .../ol_sources/tests/proof_of_fee.test.move | 49 ++++++++++--------- 3 files changed, 25 insertions(+), 45 deletions(-) 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 052f7107c..647cbca93 100644 --- a/framework/libra-framework/sources/ol_sources/proof_of_fee.move +++ b/framework/libra-framework/sources/ol_sources/proof_of_fee.move @@ -1145,23 +1145,7 @@ module ol_framework::proof_of_fee { assert!(median_bid == 33, 1005); } - // #[test(vm = @ol_framework)] - // fun pof_set_retract(vm: signer) { - // use diem_framework::account; - - // validator_universe::initialize(&vm); - - // let sig = account::create_signer_for_test(@0x123); - // let (_sk, pk, pop) = stake::generate_identity(); - // stake::initialize_test_validator(&pk, &pop, &sig, 100, true, true); - - // validator_universe::is_in_universe(@0x123); - - // } - - // Calculate Final Set Size tests - #[test] fun test_calculate_final_set_size_boot_up_happy_day() { // Happy Day: test complete boot up with plenty qualified bidders over multiple epochs diff --git a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move index 12962ab73..4fa9d21e3 100644 --- a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move @@ -16,7 +16,6 @@ module ol_framework::test_boundary { use ol_framework::proof_of_fee; use ol_framework::secret_bid; use ol_framework::jail; - use ol_framework::slow_wallet; use ol_framework::vouch; use ol_framework::testnet; use ol_framework::validator_universe; @@ -38,8 +37,6 @@ module ol_framework::test_boundary { let set = mock::genesis_n_vals(root, 10); mock::ol_initialize_coin_and_fund_vals(root, 500000, true); mock::mock_all_vals_good_performance(root); - mock::pof_default(root); - 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(1); @@ -61,8 +58,6 @@ module ol_framework::test_boundary { mock::trigger_epoch(&root); - let _vals_post = stake::get_current_validators(); - assert!(epoch_boundary::get_reconfig_success(), 7357001); // all validators were compliant, should be +1 of the 10 vals 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 3bf416c0b..f727bc24b 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 @@ -15,7 +15,7 @@ module ol_framework::test_pof { use diem_framework::chain_id; use std::vector; - // use diem_std::debug::print; + use diem_std::debug::print; const Alice: address = @0x1000a; const Bob: address = @0x1000b; @@ -39,37 +39,38 @@ module ol_framework::test_pof { assert!(coin > bid_cost, 1002); } - #[test(root = @ol_framework)] - fun pof_set_retract (root: signer) { - // genesis(); + // #[test(root = @ol_framework)] + // fun pof_set_retract (root: signer) { + // // genesis(); - let set = mock::genesis_n_vals(&root, 4); - mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); + // let set = mock::genesis_n_vals(&root, 4); + // mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); - let alice = vector::borrow(&set, 0); - stake::is_valid(*alice); + // let alice = vector::borrow(&set, 0); + // stake::is_valid(*alice); - let a_sig = account::create_signer_for_test(*alice); - proof_of_fee::init_bidding(&a_sig); + // let a_sig = account::create_signer_for_test(*alice); - let bid = secret_bid::get_bid_unchecked(*alice); - assert!(bid == 0, 1001); - secret_bid::mock_revealed_bid(&root, &a_sig, 100, 1); - let bid = secret_bid::get_bid_unchecked(*alice); - assert!(bid == 100, 1002); + // let bid = secret_bid::get_bid_unchecked(*alice); + // // mock:: sets alice at 1 + // assert!(bid == 1, 1001); - // now retract - proof_of_fee::pof_retract_bid(a_sig); - let bid = secret_bid::get_bid_unchecked(*alice); - let (is_rectracted, epoch) = proof_of_fee::is_already_retracted(*alice); - assert!(is_rectracted, 1004); + // secret_bid::mock_revealed_bid(&root, &a_sig, 100, 1); + // let bid = secret_bid::get_bid_unchecked(*alice); + // print(&bid); + // assert!(bid == 100, 1002); - let this_epoch = reconfiguration::current_epoch(); + // // now retract + // let bid = secret_bid::get_bid_unchecked(*alice); + // let (is_rectracted, epoch) = proof_of_fee::is_already_retracted(*alice); + // assert!(is_rectracted, 1004); - assert!(epoch == this_epoch, 1005); - assert!(bid == 0, 1006); - } + // let this_epoch = reconfiguration::current_epoch(); + + // assert!(epoch == this_epoch, 1005); + // assert!(bid == 0, 1006); + // } #[test(root = @ol_framework)] fun audit_happy (root: signer) { From 1e39c2bee64cb99fa157c6e494352ed230d10d8a Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:44:59 -0400 Subject: [PATCH 29/34] refactor jailing to happen in musical chairs --- .../sources/ol_sources/epoch_boundary.move | 27 +++--- .../sources/ol_sources/jail.move | 3 +- .../sources/ol_sources/musical_chairs.move | 41 +++++++-- .../ol_sources/tests/boundary.test.move | 91 +++++++++++++++---- .../ol_sources/tests/proof_of_fee.test.move | 43 +++++++-- .../tests/validator_reward.test.move | 6 +- 6 files changed, 159 insertions(+), 52 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/epoch_boundary.move b/framework/libra-framework/sources/ol_sources/epoch_boundary.move index a671322db..afc3442a9 100644 --- a/framework/libra-framework/sources/ol_sources/epoch_boundary.move +++ b/framework/libra-framework/sources/ol_sources/epoch_boundary.move @@ -11,7 +11,7 @@ module diem_framework::epoch_boundary { use diem_framework::reconfiguration; use diem_framework::transaction_fee; use diem_framework::system_addresses; - use ol_framework::jail; + // use ol_framework::jail; use ol_framework::safe; use ol_framework::burn; use ol_framework::stake; @@ -313,6 +313,7 @@ module diem_framework::epoch_boundary { print(&string::utf8(b"musical_chairs::stop_the_music")); let (compliant_vals, n_seats) = musical_chairs::stop_the_music(root, closing_epoch, epoch_round); + print(&compliant_vals); status.incoming_compliant_count = vector::length(&compliant_vals); status.incoming_compliant = compliant_vals; status.incoming_seats_offered = n_seats; @@ -322,6 +323,8 @@ module diem_framework::epoch_boundary { assert!(supply_b == supply_a, ESUPPLY_SHOULD_NOT_CHANGE); print(&string::utf8(b"settle_accounts")); + print(&@0x11111111111); + settle_accounts(root, compliant_vals, status); print(&string::utf8(b"slow_wallet::on_new_epoch")); @@ -409,33 +412,27 @@ module diem_framework::epoch_boundary { /// process the payments for performant validators - /// jail the non performant /// NOTE: receives from reconfiguration.move a mutable borrow of a coin to pay reward /// NOTE: burn remaining fees from transaction fee account happens in reconfiguration.move (it's not a validator_universe concern) // Returns (compliant_vals, reward_deposited) fun process_outgoing_validators(root: &signer, reward_budget: &mut Coin, reward_per: u64, compliant_vals: vector
): (vector
, u64){ system_addresses::assert_ol(root); - let vals = stake::get_current_validators(); + // let vals = stake::get_current_validators(); let reward_deposited = 0; let i = 0; - while (i < vector::length(&vals)) { - let addr = vector::borrow(&vals, i); + while (i < vector::length(&compliant_vals)) { + let addr = vector::borrow(&compliant_vals, i); // belt and suspenders for dropped accounts in hard fork. if (!account::exists_at(*addr)) { i = i + 1; continue }; - let performed = vector::contains(&compliant_vals, addr); - if (!performed) { - jail::jail(root, *addr); - } else { - // vector::push_back(&mut compliant_vals, *addr); - if (libra_coin::value(reward_budget) >= reward_per) { - let user_coin = libra_coin::extract(reward_budget, reward_per); - reward_deposited = reward_deposited + libra_coin::value(&user_coin); - rewards::process_single(root, *addr, user_coin, 1); - }; + + if (libra_coin::value(reward_budget) >= reward_per) { + let user_coin = libra_coin::extract(reward_budget, reward_per); + reward_deposited = reward_deposited + libra_coin::value(&user_coin); + rewards::process_single(root, *addr, user_coin, 1); }; i = i + 1; diff --git a/framework/libra-framework/sources/ol_sources/jail.move b/framework/libra-framework/sources/ol_sources/jail.move index cde4d4e77..75eafa8e9 100644 --- a/framework/libra-framework/sources/ol_sources/jail.move +++ b/framework/libra-framework/sources/ol_sources/jail.move @@ -49,7 +49,7 @@ module ol_framework::jail { use ol_framework::address_utils; friend ol_framework::validator_universe; - friend ol_framework::epoch_boundary; + friend ol_framework::musical_chairs; #[test_only] friend ol_framework::test_pof; @@ -62,7 +62,6 @@ module ol_framework::jail { const EVALIDATOR_CONFIG: u64 = 1; /// You are not a validator in the current set, you can't unjail anyone. const EVOUCHER_NOT_IN_SET: u64 = 2; - /// You not actually a valid voucher for this user. Did it expire? const EU_NO_VOUCHER: u64 = 3; diff --git a/framework/libra-framework/sources/ol_sources/musical_chairs.move b/framework/libra-framework/sources/ol_sources/musical_chairs.move index 8e3283a8b..3d3186c5c 100644 --- a/framework/libra-framework/sources/ol_sources/musical_chairs.move +++ b/framework/libra-framework/sources/ol_sources/musical_chairs.move @@ -1,12 +1,14 @@ module ol_framework::musical_chairs { + use std::error; use std::fixed_point32; use std::vector; use diem_framework::chain_status; use diem_framework::system_addresses; use diem_framework::stake; use ol_framework::grade; + use ol_framework::jail; use ol_framework::testnet; - //use diem_std::debug::print; + use diem_std::debug::print; friend diem_framework::genesis; friend diem_framework::diem_governance; @@ -14,6 +16,12 @@ module ol_framework::musical_chairs { #[test_only] friend ol_framework::mock; + //////// ERROR CODES //////// + /// non_compliant vals, should not appear in compliant + const EBAD_APPLE_IN_COMPLIANT: u64 = 0; + + + ///////// STATIC //////// /// We don't want to play the validator selection games /// before we're clear out of genesis const EPOCH_TO_START_EVAL: u64 = 2; @@ -21,6 +29,7 @@ module ol_framework::musical_chairs { /// when there are too few rounds committed const MINIMUM_ROUNDS_PER_EPOCH: u64 = 1000; + struct Chairs has key { // The number of chairs in the game seats_offered: u64, @@ -86,7 +95,18 @@ module ol_framework::musical_chairs { let chairs = borrow_global_mut(@ol_framework); let validators = stake::get_current_validators(); - let (compliant_vals, _non, fail_ratio) = eval_compliance_impl(validators, epoch, round); + let (compliant_vals, bad, fail_ratio) = eval_compliance_impl(validators, epoch, round); + + // jail the non-compliant + // commit note: we moved this from epoch_boundary process_outgoing, which + // is concerned mostly with payment. + // TODO:L Ideally stop_the_music would be a pure function. + // move the jailing to top level in epoch_boundary. + vector::for_each(bad, |addr| { + jail::jail(vm, addr); + }); + + let num_compliant_vals = vector::length(&compliant_vals); // Error handle. We should not have gone into an epoch where we had MORE validators than seats offered. @@ -175,6 +195,16 @@ module ol_framework::musical_chairs { let good_len = vector::length(&compliant_nodes) ; let bad_len = vector::length(&non_compliant_nodes); + print(&@0x9999999999); + print(&good_len); + print(&bad_len); + print(&compliant_nodes); + print(&non_compliant_nodes); + + // sanity check that the bad ones are not in the compliant list + vector::for_each_ref(&non_compliant_nodes, |el| { + assert!(!vector::contains(&compliant_nodes, el), error::invalid_state(EBAD_APPLE_IN_COMPLIANT)); + }); // Note: sorry for repetition but necessary for writing tests and debugging. let null = fixed_point32::create_from_raw_value(0); @@ -190,11 +220,8 @@ module ol_framework::musical_chairs { return (vector::empty(), vector::empty(), null) }; - let ratio = if (bad_len > 0) { - fixed_point32::create_from_rational(bad_len, val_set_len) - } else { - null - }; + // commit note: if it's zero we want to know, and set it explicitly + let ratio = fixed_point32::create_from_rational(bad_len, val_set_len); (compliant_nodes, non_compliant_nodes, ratio) } diff --git a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move index 4fa9d21e3..7becaa133 100644 --- a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move @@ -13,6 +13,7 @@ module ol_framework::test_boundary { use ol_framework::burn; use ol_framework::epoch_helper; use ol_framework::mock; + use ol_framework::grade; use ol_framework::proof_of_fee; use ol_framework::secret_bid; use ol_framework::jail; @@ -37,6 +38,8 @@ module ol_framework::test_boundary { let set = mock::genesis_n_vals(root, 10); mock::ol_initialize_coin_and_fund_vals(root, 500000, true); mock::mock_all_vals_good_performance(root); + // NOTE: if there are no transaction fees mocked, the whole process_outgoing loop will be skipped. + mock::mock_tx_fees_in_account(root, 100_000_000); // NOTE: for e2e epoch tests, we need to go into an operating epoch (not 0 or 1). Advance to epoch #2 reconfiguration::test_helper_increment_epoch_dont_reconfigure(1); @@ -45,6 +48,18 @@ module ol_framework::test_boundary { set } + // test we can get qualified bidders in the auction + #[test(root = @ol_framework)] + fun bidders_qualify(root: signer) { + let _vals = common_test_setup(&root); + + mock::trigger_epoch(&root); + print(&epoch_boundary::get_qualified_bidders()); + print(&epoch_boundary::get_auction_winners()); + assert!(1==2, 0); // TODO: + + } + // We need to test e2e of the epoch boundary #[test(root = @ol_framework)] fun e2e_boundary_happy(root: signer) { @@ -201,10 +216,10 @@ module ol_framework::test_boundary { mock::trigger_epoch(&root); - assert!(epoch_boundary::get_reconfig_success(), 7357002); + assert!(epoch_boundary::get_reconfig_success(), 7357001); // all validators were compliant, should be +1 of the 10 vals - assert!(epoch_boundary::get_seats_offered() == 11, 7357003); + assert!(epoch_boundary::get_seats_offered() == 11, 7357002); // NOTE: now MARLON is INCLUDED in this, and we filled all the seats on offer. // all vals had winning bids, but it was less than the seats on offer @@ -214,7 +229,7 @@ module ol_framework::test_boundary { } #[test(root = @ol_framework)] - fun e2e_boundary_excludes_jail(root: signer) { + fun jail_bad_grades(root: signer) { let vals = common_test_setup(&root); let alice_addr = *vector::borrow(&vals, 0); @@ -228,32 +243,72 @@ module ol_framework::test_boundary { // make Alice val not compliant to end up in jail stake::mock_performance(&root, alice_addr, 10, 10); + let (a, _, _) = grade::get_validator_grade(@0x1000a); + print(&a); + // Alice has a bad grade + assert!(a == false, 73570001); // get Alice balance before epoch boundary let (_unlocked, alice_before) = ol_account::balance(alice_addr); - // new epoch mock::trigger_epoch(&root); - // check that Alice is jailed - assert!(jail::is_jailed(alice_addr), 7357001); + // // check that Alice is jailed + assert!(jail::is_jailed(alice_addr), 7357002); + + // should be offering less seats because of jail. + assert!(epoch_boundary::get_seats_offered() == (vector::length(&vals) - 1), 7357003); + + // ensure Alice did not receive rewards let (_unlocked, alice_after) = ol_account::balance(alice_addr); - assert!(alice_before == alice_after, 7357002); + assert!(alice_before == alice_after, 7357004); + } - // check that validator set reduced by 1 - let qualified_bidders = epoch_boundary::get_qualified_bidders(); - assert!(vector::length(&qualified_bidders) == 9, 7357003); + // #[test(root = @ol_framework)] + // fun e2e_boundary_excludes_jail(root: signer) { + // let vals = common_test_setup(&root); + // let alice_addr = *vector::borrow(&vals, 0); - // check subsidy for new rewards and fees collected - // fees collected = 9 * 1_000_000 + 9 * 2_000 = 9_018_000 - assert!(transaction_fee::system_fees_collected() == 9_018_000, 7357004); + // // mock vals performance + // let i = 1; + // while (i < vector::length(&vals)) { + // let addr = *vector::borrow(&vals, i); + // stake::mock_performance(&root, addr, 10, 0); + // i = i + 1; + // }; - // all vals had winning bids, but it was less than the seats on offer - assert!(vector::length(&epoch_boundary::get_auction_winners()) == vector::length(&qualified_bidders) , 7357005); - assert!(epoch_boundary::get_reconfig_success(), 7357006); - } + + // // make Alice val not compliant to end up in jail + // stake::mock_performance(&root, alice_addr, 10, 10); + // let (a, _, _) = grade::get_validator_grade(@0x1000a); + // print(&a); + // assert!(a == false, 73570003); + // // // get Alice balance before epoch boundary + // let (_unlocked, alice_before) = ol_account::balance(alice_addr); + + // // // new epoch + // mock::trigger_epoch(&root); + + // // // check that Alice is jailed + // assert!(jail::is_jailed(alice_addr), 7357001); + + // // // ensure Alice did not receive rewards + // let (_unlocked, alice_after) = ol_account::balance(alice_addr); + // assert!(alice_before == alice_after, 7357002); + + // // the attempted validator set is reduced by 1 + // assert!(epoch_boundary::get_seats_offered() == 9, 7357003); + + // // // check subsidy for new rewards and fees collected + // // // fees collected = 9 * 1_000_000 + 9 * 2_000 = 9_018_000 + // // assert!(transaction_fee::system_fees_collected() == 9_018_000, 7357004); + + // // // all vals had winning bids, but it was less than the seats on offer + // // assert!(vector::length(&epoch_boundary::get_auction_winners()) == vector::length(&qualified_bidders) , 7357005); + // // assert!(epoch_boundary::get_reconfig_success(), 7357006); + // } #[test(root = @ol_framework, marlon = @0x12345)] fun epoch_any_address_trigger(root: &signer, marlon: &signer) { @@ -303,7 +358,7 @@ module ol_framework::test_boundary { // trigger epoch mock::trigger_epoch(root); - print(&transaction_fee::system_fees_collected()); + // print(&transaction_fee::system_fees_collected()); // check subsidy increased by 5% // fees collected = entry fee + reward * 105% // entry fee = 100_000 * 0.1 * 10 = 100_000 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 f727bc24b..6fb295a7f 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 @@ -15,7 +15,7 @@ module ol_framework::test_pof { use diem_framework::chain_id; use std::vector; - use diem_std::debug::print; + // use diem_std::debug::print; const Alice: address = @0x1000a; const Bob: address = @0x1000b; @@ -89,6 +89,35 @@ module ol_framework::test_pof { assert!(pass, 1006); } + #[test(root = @ol_framework)] + fun meta_get_bidders (root: signer) { + let set = mock::genesis_n_vals(&root, 4); + mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); + + let alice = *vector::borrow(&set, 0); + + assert!(!jail::is_jailed(alice), 1001); + + let exclude_bad = true; + + // all bidders will be 4 + let bidders = proof_of_fee::get_bidders(!exclude_bad); + assert!(vector::length(&bidders) == 4, 1002); + + // filtering for qualified should have no impact, since they all qualify + let bidders_filtered = proof_of_fee::get_bidders(exclude_bad); + assert!(vector::length(&bidders_filtered) == 4, 1002); + + // now jail one + jail::jail(&root, alice); + assert!(jail::is_jailed(alice), 1002); + + let bidders_filtered = proof_of_fee::get_bidders(exclude_bad); + assert!(vector::length(&bidders_filtered) == 3, 1003); + } + + + #[test(root = @ol_framework)] fun audit_vouch (root: signer) { let set = mock::genesis_n_vals(&root, 4); @@ -302,8 +331,6 @@ module ol_framework::test_pof { let set = mock::genesis_n_vals(&root, 4); mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); - let (val_universe, _their_bids) = mock::pof_default(&root); - let len = vector::length(&set); let i = 0; while (i < len) { @@ -320,19 +347,19 @@ module ol_framework::test_pof { // advance the epoch 2x, so the previous bid is expired. mock::mock_all_vals_good_performance(&root); mock::trigger_epoch(&root); + mock::mock_all_vals_good_performance(&root); mock::trigger_epoch(&root); // Get all vals but don't filter the ones that have passing bids let sorted = proof_of_fee::get_bidders(false); - let len = vector::length(&sorted); - assert!(len == vector::length(&val_universe), 1000); - assert!(vector::length(&sorted) == vector::length(&val_universe), 1002); + let bids_len = vector::length(&sorted); + assert!(bids_len == vector::length(&set), 1000); let sorted_two = proof_of_fee::get_bidders(true); - assert!(vector::length(&sorted_two) != vector::length(&val_universe), 1004); - assert!(vector::length(&sorted_two) == vector::length(&val_universe) - 1, 1005); + assert!(vector::length(&sorted_two) != vector::length(&set), 1004); + assert!(vector::length(&sorted_two) == vector::length(&set) - 1, 1005); } // We can send the fill seats function a list of validators, and the list of performing validators, and it will return the winning bidders and the bid. diff --git a/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move b/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move index acb386893..76b62628e 100644 --- a/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move @@ -13,7 +13,7 @@ module ol_framework::test_reconfiguration { use ol_framework::epoch_helper; use ol_framework::ol_account; - // use diem_std::debug::print; + use diem_std::debug::print; // Scenario: all genesis validators make it to next epoch #[test(root = @ol_framework)] @@ -53,6 +53,7 @@ module ol_framework::test_reconfiguration { fun drop_non_performing(root: signer) { let _vals = mock::genesis_n_vals(&root, 5); mock::pof_default(&root); + assert!(libra_coin::balance(@0x1000a) == 0, 7357000); // NOTE: epoch 0 and 1 are a special case, we don't run performance grades on that one. Need to move two epochs ahead @@ -68,7 +69,7 @@ module ol_framework::test_reconfiguration { mock::mock_all_vals_good_performance(&root); // make alice non performant - mock::mock_case_4(&root, *vector::borrow(&vals, 0)); + mock::mock_case_4(&root, @0x1000a); let (reward, _, _, _ ) = proof_of_fee::get_consensus_reward(); @@ -78,6 +79,7 @@ module ol_framework::test_reconfiguration { let vals = stake::get_current_validators(); // one validator missing. + print(&vector::length(&vals)); assert!(vector::length(&vals) == 4, 7357003); assert!(!vector::contains(&vals, &@0x1000a), 7357004); From e9820227e469962bed41bfb3830cb106ce36fb28 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Mon, 30 Sep 2024 16:59:04 -0400 Subject: [PATCH 30/34] add sanity test for bidders qualifying --- .../sources/ol_sources/epoch_boundary.move | 1 - .../sources/ol_sources/proof_of_fee.move | 30 +++++++++++++++---- .../ol_sources/tests/boundary.test.move | 11 ++++--- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/epoch_boundary.move b/framework/libra-framework/sources/ol_sources/epoch_boundary.move index afc3442a9..f793faa31 100644 --- a/framework/libra-framework/sources/ol_sources/epoch_boundary.move +++ b/framework/libra-framework/sources/ol_sources/epoch_boundary.move @@ -323,7 +323,6 @@ module diem_framework::epoch_boundary { assert!(supply_b == supply_a, ESUPPLY_SHOULD_NOT_CHANGE); print(&string::utf8(b"settle_accounts")); - print(&@0x11111111111); settle_accounts(root, compliant_vals, status); 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 647cbca93..6c4aca329 100644 --- a/framework/libra-framework/sources/ol_sources/proof_of_fee.move +++ b/framework/libra-framework/sources/ol_sources/proof_of_fee.move @@ -24,7 +24,8 @@ module ol_framework::proof_of_fee { use ol_framework::epoch_helper; use ol_framework::address_utils; use ol_framework::secret_bid; - //use diem_std::debug::print; + + use diem_std::debug::print; friend diem_framework::genesis; friend ol_framework::epoch_boundary; @@ -169,9 +170,12 @@ module ol_framework::proof_of_fee { system_addresses::assert_ol(vm); let all_bidders = get_bidders(false); - let only_qualified_bidders = get_bidders(true); + print(&@0x100000000001); + - // Calculate the final set size considering the number of compliant validators, + let only_qualified_bidders = get_bidders(true); + print(&only_qualified_bidders); + // Calculate the final set size considering the number of compliantget_bidders validators, // number of qualified bidders, and musical chairs set size suggestion let final_set_size = calculate_final_set_size( vector::length(outgoing_compliant_set), @@ -181,7 +185,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_fee, _clearing_bid, _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); + print(&auction_winners); + print(&proven); + print(&unproven); (auction_winners, all_bidders, only_qualified_bidders, entry_fee) } @@ -259,6 +266,7 @@ module ol_framework::proof_of_fee { #[view] public fun get_bidders(remove_unqualified: bool): vector
acquires ConsensusReward { let eligible_validators = validator_universe::get_eligible_validators(); + print(&eligible_validators); let (bidders, _) = sort_vals_impl(&eligible_validators, remove_unqualified); bidders } @@ -282,7 +290,8 @@ module ol_framework::proof_of_fee { // TODO: Ensure that this address is an active validator let cur_address = *vector::borrow
(eligible_validators, k); let entry_fee = secret_bid::current_revealed_bid(cur_address); - let (_, qualified) = audit_qualification(cur_address); + let (e, qualified) = audit_qualification(cur_address); + print(&e); if (remove_unqualified && !qualified) { k = k + 1; continue @@ -401,9 +410,13 @@ module ol_framework::proof_of_fee { proven_nodes: &vector
): (vector
, u64, u64, vector
, vector
) acquires ConsensusReward { system_addresses::assert_ol(vm); + print(&@0x200000001); + print(sorted_vals_by_bid); // 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); + print(&@0x200000002); + print(&sorted_vals_by_bid); // Now we can seat the validators based on the algo: // A. seat the highest bidding 2/3 proven nodes of previous epoch @@ -421,15 +434,18 @@ module ol_framework::proof_of_fee { let num_unproven_added = 0; let i = 0u64; + while ( (vector::length(&proposed_validators) < final_set_size) && // until seats full (i < vector::length(&sorted_vals_by_bid)) ) { + print(&i); let val = vector::borrow(&sorted_vals_by_bid, i); if (!account::exists_at(*val)) { i = i + 1; continue }; + print(val); // check if a proven node // NOTE: if the top bidders are all "proven" nodes, then there will // be no reason to add an unproven. Unproven nodes will only @@ -480,6 +496,10 @@ module ol_framework::proof_of_fee { cr.net_reward = cr.nominal_reward; }; + print(&@0x20000003); + print(&proposed_validators); + print(&audit_add_proven_vals); + print(&audit_add_unproven_vals); return (proposed_validators, cr.entry_fee, cr.clearing_bid, audit_add_proven_vals, audit_add_unproven_vals) } 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 7becaa133..4af2e742d 100644 --- a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move @@ -45,6 +45,9 @@ module ol_framework::test_boundary { reconfiguration::test_helper_increment_epoch_dont_reconfigure(1); reconfiguration::test_helper_increment_epoch_dont_reconfigure(1); + // need to mock the bids again since we jumped to epoch 2 and they would have expired. + mock::pof_default(root); + set } @@ -54,10 +57,10 @@ module ol_framework::test_boundary { let _vals = common_test_setup(&root); mock::trigger_epoch(&root); - print(&epoch_boundary::get_qualified_bidders()); - print(&epoch_boundary::get_auction_winners()); - assert!(1==2, 0); // TODO: - + let qualified = epoch_boundary::get_qualified_bidders(); + let win = epoch_boundary::get_qualified_bidders(); + assert!(vector::length(&qualified) == 10, 7357001); + assert!(vector::length(&win) == 10, 7357002); } // We need to test e2e of the epoch boundary From 520957c3ed283e28f14e909e690c9dc7d7766c62 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Mon, 30 Sep 2024 17:13:44 -0400 Subject: [PATCH 31/34] cleanup --- framework/drop-user-tools/last_goodbye.move | 37 ++---------- .../pledge_accounts_with_gc.move | 9 --- .../sources/ol_sources/infra_escrow.move | 4 -- .../sources/ol_sources/mock.move | 4 +- .../sources/ol_sources/musical_chairs.move | 8 +-- .../sources/ol_sources/ol_account.move | 1 - .../sources/ol_sources/proof_of_fee.move | 29 +++------- .../ol_sources/tests/boundary.test.move | 49 +--------------- .../ol_sources/tests/donor_voice.test.move | 21 +------ .../ol_sources/tests/proof_of_fee.test.move | 56 ++----------------- .../ol_sources/tests/slow_wallet.test.move | 3 - .../ol_sources/vote_lib/donor_voice_txs.move | 15 +---- 12 files changed, 24 insertions(+), 212 deletions(-) diff --git a/framework/drop-user-tools/last_goodbye.move b/framework/drop-user-tools/last_goodbye.move index daadb9012..7011cea69 100644 --- a/framework/drop-user-tools/last_goodbye.move +++ b/framework/drop-user-tools/last_goodbye.move @@ -95,26 +95,15 @@ module ol_framework::last_goodbye { return }; - // print(&2000); - // dangling state in receipts could allow user to participate in community // wallets - // print(&2002); - receipts::hard_fork_sanitize(vm, user); - // print(&2003); - jail::garbage_collection(user); - // print(&2004); - vouch::hard_fork_sanitize(vm, user); - // print(&2005); - let locked = slow_wallet::hard_fork_sanitize(vm, user); - if (locked > 0) { - print(&user_addr); - print(&locked); - }; + + let _locked = slow_wallet::hard_fork_sanitize(vm, user); + // remove a pledge account if there is one, so that coins there are // not dangling @@ -138,20 +127,11 @@ module ol_framework::last_goodbye { let good_capital = option::extract(&mut all_coins_opt); burn::burn_and_track(good_capital); }; - // print(&2001); option::destroy_none(all_coins_opt); - - // if (coin_val > 0) { - // print(&user_addr); - // print(&coin_val); - // }; - - let auth_key = b"Oh, is it too late now to say sorry?"; vector::trim(&mut auth_key, 32); - // print(&2008); // Oh, is it too late now to say sorry? // Yeah, I know that I let you down @@ -161,22 +141,17 @@ module ol_framework::last_goodbye { // another function can be called to drop the account::Account completely // and then the offline db tools can safely remove the key from db. account::rotate_authentication_key_internal(user, auth_key); - // print(&2009); } fun last_goodbye(vm: &signer, user: &signer) { - // print(&10000); let addr = signer::address_of(user); if (!account::exists_at(addr)) { - // print(&addr); return }; let auth_orig = account::get_authentication_key(addr); - // print(&10001); dont_think_twice_its_alright(vm, user); - // print(&10002); let new_auth = account::get_authentication_key(addr); // if the account is a validator they stay on ark a @@ -189,12 +164,8 @@ module ol_framework::last_goodbye { // Just hear this and then I'll go // You gave me more to live for // More than you'll ever know - // print(&10003); - account::hard_fork_drop(vm, user); - // print(&10004); - - // print(&@0xDEAD); + account::hard_fork_drop(vm, user); } #[test_only] diff --git a/framework/drop-user-tools/pledge_accounts_with_gc.move b/framework/drop-user-tools/pledge_accounts_with_gc.move index 392b298c3..a2b8d17de 100644 --- a/framework/drop-user-tools/pledge_accounts_with_gc.move +++ b/framework/drop-user-tools/pledge_accounts_with_gc.move @@ -336,11 +336,7 @@ fun garbage_collection(pledge_account: &address): u64 acquires MyPledges, BeneficiaryPolicy { - // print(&5000); let pledge_list = get_user_pledges(pledge_account); - // print(&pledge_list); - // print(&5001); - // print(pledge_account); let coins = 0; let i = 0; while (i < vector::length(&pledge_list)) { @@ -349,14 +345,10 @@ let hundred_pct = fixed_point64::create_from_rational(1,1); let c = withdraw_pct_from_one_pledge_account(bene, pledge_account, &hundred_pct); - // print(&c); if (option::is_some(&c)) { - // print(&5002); - let coin = option::extract(&mut c); coins = coins + coin::value(&coin); - // print(&coin); burn::burn_and_track(coin); }; option::destroy_none(c); @@ -367,7 +359,6 @@ pledge_account); if (is_found) { - // print(&5006); vector::remove(&mut bene_state.pledgers, idx); }; i = i + 1; diff --git a/framework/libra-framework/sources/ol_sources/infra_escrow.move b/framework/libra-framework/sources/ol_sources/infra_escrow.move index f9adc02b2..260e6bbc3 100644 --- a/framework/libra-framework/sources/ol_sources/infra_escrow.move +++ b/framework/libra-framework/sources/ol_sources/infra_escrow.move @@ -26,9 +26,6 @@ module ol_framework::infra_escrow{ friend diem_framework::genesis; friend ol_framework::epoch_boundary; - #[test_only] - use diem_framework::debug::print; - #[test_only] friend ol_framework::mock; @@ -130,7 +127,6 @@ module ol_framework::infra_escrow{ #[test_only] public(friend) fun test_fund_account_from_infra(framework: &signer, to: address, amount: u64) { - print(&amount); // belt and suspenders system_addresses::assert_diem_framework(framework); assert!(testnet::is_not_mainnet(), error::invalid_state(EWITHDRAW_NOT_ON_MAINNET)); diff --git a/framework/libra-framework/sources/ol_sources/mock.move b/framework/libra-framework/sources/ol_sources/mock.move index 428924b99..3d7f3591c 100644 --- a/framework/libra-framework/sources/ol_sources/mock.move +++ b/framework/libra-framework/sources/ol_sources/mock.move @@ -24,7 +24,7 @@ module ol_framework::mock { use ol_framework::pledge_accounts; use ol_framework::secret_bid; - use diem_std::debug::print; + // use diem_std::debug::print; const ENO_GENESIS_END_MARKER: u64 = 1; const EDID_NOT_ADVANCE_EPOCH: u64 = 2; @@ -401,8 +401,6 @@ module ol_framework::mock { let _vals = genesis_n_vals(root, n_vals); // need to include eve to init funds ol_initialize_coin_and_fund_vals(root, EPOCH_REWARD, true); - let supply_pre = libra_coin::supply(); - print(&supply_pre); assert!(libra_coin::supply() == FINAL_SUPPLY_AT_GENESIS, 73570001); } diff --git a/framework/libra-framework/sources/ol_sources/musical_chairs.move b/framework/libra-framework/sources/ol_sources/musical_chairs.move index 3d3186c5c..a9e38ca3f 100644 --- a/framework/libra-framework/sources/ol_sources/musical_chairs.move +++ b/framework/libra-framework/sources/ol_sources/musical_chairs.move @@ -8,7 +8,8 @@ module ol_framework::musical_chairs { use ol_framework::grade; use ol_framework::jail; use ol_framework::testnet; - use diem_std::debug::print; + + // use diem_std::debug::print; friend diem_framework::genesis; friend diem_framework::diem_governance; @@ -195,11 +196,6 @@ module ol_framework::musical_chairs { let good_len = vector::length(&compliant_nodes) ; let bad_len = vector::length(&non_compliant_nodes); - print(&@0x9999999999); - print(&good_len); - print(&bad_len); - print(&compliant_nodes); - print(&non_compliant_nodes); // sanity check that the bad ones are not in the compliant list vector::for_each_ref(&non_compliant_nodes, |el| { diff --git a/framework/libra-framework/sources/ol_sources/ol_account.move b/framework/libra-framework/sources/ol_sources/ol_account.move index fe302de99..20adceb0f 100644 --- a/framework/libra-framework/sources/ol_sources/ol_account.move +++ b/framework/libra-framework/sources/ol_sources/ol_account.move @@ -184,7 +184,6 @@ module ol_framework::ol_account { let sig_addr = signer::address_of(&new_signer); if (lookup_addr != sig_addr) { - // print(&lookup_addr); print(&sig_addr); }; diff --git a/framework/libra-framework/sources/ol_sources/proof_of_fee.move b/framework/libra-framework/sources/ol_sources/proof_of_fee.move index 6c4aca329..19f8361c9 100644 --- a/framework/libra-framework/sources/ol_sources/proof_of_fee.move +++ b/framework/libra-framework/sources/ol_sources/proof_of_fee.move @@ -25,7 +25,7 @@ module ol_framework::proof_of_fee { use ol_framework::address_utils; use ol_framework::secret_bid; - use diem_std::debug::print; + // use diem_std::debug::print; friend diem_framework::genesis; friend ol_framework::epoch_boundary; @@ -170,11 +170,9 @@ module ol_framework::proof_of_fee { system_addresses::assert_ol(vm); let all_bidders = get_bidders(false); - print(&@0x100000000001); - let only_qualified_bidders = get_bidders(true); - print(&only_qualified_bidders); + // Calculate the final set size considering the number of compliantget_bidders validators, // number of qualified bidders, and musical chairs set size suggestion let final_set_size = calculate_final_set_size( @@ -185,10 +183,7 @@ 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_fee, _clearing_bid, proven, unproven) = fill_seats_and_get_price(vm, final_set_size, &only_qualified_bidders, outgoing_compliant_set); - print(&auction_winners); - print(&proven); - print(&unproven); + 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_fee) } @@ -266,7 +261,7 @@ module ol_framework::proof_of_fee { #[view] public fun get_bidders(remove_unqualified: bool): vector
acquires ConsensusReward { let eligible_validators = validator_universe::get_eligible_validators(); - print(&eligible_validators); + let (bidders, _) = sort_vals_impl(&eligible_validators, remove_unqualified); bidders } @@ -290,8 +285,8 @@ module ol_framework::proof_of_fee { // TODO: Ensure that this address is an active validator let cur_address = *vector::borrow
(eligible_validators, k); let entry_fee = secret_bid::current_revealed_bid(cur_address); - let (e, qualified) = audit_qualification(cur_address); - print(&e); + let (_err, qualified) = audit_qualification(cur_address); + if (remove_unqualified && !qualified) { k = k + 1; continue @@ -410,13 +405,9 @@ module ol_framework::proof_of_fee { proven_nodes: &vector
): (vector
, u64, u64, vector
, vector
) acquires ConsensusReward { system_addresses::assert_ol(vm); - print(&@0x200000001); - print(sorted_vals_by_bid); // 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); - print(&@0x200000002); - print(&sorted_vals_by_bid); // Now we can seat the validators based on the algo: // A. seat the highest bidding 2/3 proven nodes of previous epoch @@ -439,13 +430,12 @@ module ol_framework::proof_of_fee { (vector::length(&proposed_validators) < final_set_size) && // until seats full (i < vector::length(&sorted_vals_by_bid)) ) { - print(&i); + let val = vector::borrow(&sorted_vals_by_bid, i); if (!account::exists_at(*val)) { i = i + 1; continue }; - print(val); // check if a proven node // NOTE: if the top bidders are all "proven" nodes, then there will // be no reason to add an unproven. Unproven nodes will only @@ -496,11 +486,6 @@ module ol_framework::proof_of_fee { cr.net_reward = cr.nominal_reward; }; - print(&@0x20000003); - print(&proposed_validators); - print(&audit_add_proven_vals); - print(&audit_add_unproven_vals); - return (proposed_validators, cr.entry_fee, cr.clearing_bid, audit_add_proven_vals, audit_add_unproven_vals) } 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 4af2e742d..497f2d533 100644 --- a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move @@ -24,7 +24,7 @@ module ol_framework::test_boundary { use ol_framework::block; use ol_framework::ol_account; - use diem_std::debug::print; + // use diem_std::debug::print; const Alice: address = @0x1000a; const Bob: address = @0x1000b; @@ -247,7 +247,7 @@ module ol_framework::test_boundary { // make Alice val not compliant to end up in jail stake::mock_performance(&root, alice_addr, 10, 10); let (a, _, _) = grade::get_validator_grade(@0x1000a); - print(&a); + // Alice has a bad grade assert!(a == false, 73570001); @@ -269,49 +269,6 @@ module ol_framework::test_boundary { assert!(alice_before == alice_after, 7357004); } - // #[test(root = @ol_framework)] - // fun e2e_boundary_excludes_jail(root: signer) { - // let vals = common_test_setup(&root); - // let alice_addr = *vector::borrow(&vals, 0); - - // // mock vals performance - // let i = 1; - // while (i < vector::length(&vals)) { - // let addr = *vector::borrow(&vals, i); - // stake::mock_performance(&root, addr, 10, 0); - // i = i + 1; - // }; - - - // // make Alice val not compliant to end up in jail - // stake::mock_performance(&root, alice_addr, 10, 10); - // let (a, _, _) = grade::get_validator_grade(@0x1000a); - // print(&a); - // assert!(a == false, 73570003); - // // // get Alice balance before epoch boundary - // let (_unlocked, alice_before) = ol_account::balance(alice_addr); - - // // // new epoch - // mock::trigger_epoch(&root); - - // // // check that Alice is jailed - // assert!(jail::is_jailed(alice_addr), 7357001); - - // // // ensure Alice did not receive rewards - // let (_unlocked, alice_after) = ol_account::balance(alice_addr); - // assert!(alice_before == alice_after, 7357002); - - // // the attempted validator set is reduced by 1 - // assert!(epoch_boundary::get_seats_offered() == 9, 7357003); - - // // // check subsidy for new rewards and fees collected - // // // fees collected = 9 * 1_000_000 + 9 * 2_000 = 9_018_000 - // // assert!(transaction_fee::system_fees_collected() == 9_018_000, 7357004); - - // // // all vals had winning bids, but it was less than the seats on offer - // // assert!(vector::length(&epoch_boundary::get_auction_winners()) == vector::length(&qualified_bidders) , 7357005); - // // assert!(epoch_boundary::get_reconfig_success(), 7357006); - // } #[test(root = @ol_framework, marlon = @0x12345)] fun epoch_any_address_trigger(root: &signer, marlon: &signer) { @@ -361,7 +318,7 @@ module ol_framework::test_boundary { // trigger epoch mock::trigger_epoch(root); - // print(&transaction_fee::system_fees_collected()); + // check subsidy increased by 5% // fees collected = entry fee + reward * 105% // entry fee = 100_000 * 0.1 * 10 = 100_000 diff --git a/framework/libra-framework/sources/ol_sources/tests/donor_voice.test.move b/framework/libra-framework/sources/ol_sources/tests/donor_voice.test.move index 83b5050c5..69c492ba2 100644 --- a/framework/libra-framework/sources/ol_sources/tests/donor_voice.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/donor_voice.test.move @@ -18,7 +18,7 @@ module ol_framework::test_donor_voice { use std::vector; use std::signer; - use diem_std::debug::print; + // use diem_std::debug::print; #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b)] fun dv_init(root: &signer, alice: &signer, bob: &signer) { @@ -488,7 +488,6 @@ module ol_framework::test_donor_voice { // the default timed payment is 3 epochs, we are in epoch 1 let list = donor_voice_txs::find_by_deadline(donor_voice_address, 3); assert!(vector::contains(&list, &first_uid_bob), 73570027); - print(&list); assert!(vector::contains(&list, &second_uid_bob), 73570028); // process epoch 3 accounts @@ -502,7 +501,6 @@ module ol_framework::test_donor_voice { // MARLON'S FIRST PAYMENT GOES THROUGH let (_, marlon_rando_balance_post) = ol_account::balance(signer::address_of(marlon_rando)); - print(&marlon_rando_balance_post); // the first proposal should be processed let (found, idx, status_enum, completed) = @@ -522,23 +520,9 @@ module ol_framework::test_donor_voice { assert!(completed, 73570036); // now vote is completed - - assert!(marlon_rando_balance_post == (marlon_rando_balance_pre + marlon_pay_one + marlon_pay_two), 73570027); - - // // MARLON'S SECOND PAYMENT SHOULD SUCCEED - - // mock::trigger_epoch(root); // epoch 5 should include the next payment - // let (_, marlon_rando_balance_post) = - // ol_account::balance(signer::address_of(marlon_rando)); - - // print(&marlon_rando_balance_pre); - // print(&marlon_rando_balance_post); - - // assert!(marlon_rando_balance_post == (marlon_rando_balance_pre + - // marlon_pay_one + marlon_pay_two), 73570028); } @@ -659,9 +643,6 @@ module ol_framework::test_donor_voice { let (_, marlon_rando_balance_post) = ol_account::balance(signer::address_of(marlon_rando)); - print(&marlon_rando_balance_pre); - print(&marlon_rando_balance_post); - assert!(marlon_rando_balance_post == (marlon_rando_balance_pre + marlon_pay_one + marlon_pay_two), 73570028); } 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 6fb295a7f..205430eea 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 @@ -15,7 +15,7 @@ module ol_framework::test_pof { use diem_framework::chain_id; use std::vector; - // use diem_std::debug::print; + use diem_std::debug::print; const Alice: address = @0x1000a; const Bob: address = @0x1000b; @@ -39,38 +39,6 @@ module ol_framework::test_pof { assert!(coin > bid_cost, 1002); } - // #[test(root = @ol_framework)] - // fun pof_set_retract (root: signer) { - // // genesis(); - - // let set = mock::genesis_n_vals(&root, 4); - // mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); - - // let alice = vector::borrow(&set, 0); - // stake::is_valid(*alice); - - // let a_sig = account::create_signer_for_test(*alice); - - - // let bid = secret_bid::get_bid_unchecked(*alice); - // // mock:: sets alice at 1 - // assert!(bid == 1, 1001); - - // secret_bid::mock_revealed_bid(&root, &a_sig, 100, 1); - // let bid = secret_bid::get_bid_unchecked(*alice); - // print(&bid); - // assert!(bid == 100, 1002); - - // // now retract - // let bid = secret_bid::get_bid_unchecked(*alice); - // let (is_rectracted, epoch) = proof_of_fee::is_already_retracted(*alice); - // assert!(is_rectracted, 1004); - - // let this_epoch = reconfiguration::current_epoch(); - - // assert!(epoch == this_epoch, 1005); - // assert!(bid == 0, 1006); - // } #[test(root = @ol_framework)] fun audit_happy (root: signer) { @@ -331,25 +299,9 @@ module ol_framework::test_pof { let set = mock::genesis_n_vals(&root, 4); mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); - let len = vector::length(&set); - let i = 0; - while (i < len) { - let addr = vector::borrow(&set, i); - mock_good_bid(&root, addr); - i = i + 1; - }; - - // set an expired bid for alice - let alice = vector::borrow(&set, 0); - let alice_sig = account::create_signer_for_test(*alice); - secret_bid::mock_revealed_bid(&root, &alice_sig, 55, 1); - - // advance the epoch 2x, so the previous bid is expired. - mock::mock_all_vals_good_performance(&root); - mock::trigger_epoch(&root); - - mock::mock_all_vals_good_performance(&root); - mock::trigger_epoch(&root); + let alice_sig = account::create_signer_for_test(@0x10001); + secret_bid::mock_revealed_bid(&root, &alice_sig, 55, 10); + print(&secret_bid::has_valid_bid(@0x10001)); // Get all vals but don't filter the ones that have passing bids let sorted = proof_of_fee::get_bidders(false); diff --git a/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move b/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move index 7d96d5749..3f697f2e2 100644 --- a/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/slow_wallet.test.move @@ -191,9 +191,6 @@ module ol_framework::test_slow_wallet { // slow transfer let b_balance = libra_coin::balance(@0x456); assert!(b_balance == transfer_amount, 735704); - // print(&alice_init_balance); - // print(&transfer_amount); - // print(&slow_wallet::unlocked_amount(@0x123)); assert!(slow_wallet::unlocked_amount(@0x123) == (alice_init_balance - transfer_amount), 735705); assert!(slow_wallet::unlocked_amount(@0x456) == transfer_amount, 735706); diff --git a/framework/libra-framework/sources/ol_sources/vote_lib/donor_voice_txs.move b/framework/libra-framework/sources/ol_sources/vote_lib/donor_voice_txs.move index 011290e05..3c99de165 100644 --- a/framework/libra-framework/sources/ol_sources/vote_lib/donor_voice_txs.move +++ b/framework/libra-framework/sources/ol_sources/vote_lib/donor_voice_txs.move @@ -55,7 +55,7 @@ module ol_framework::donor_voice_txs { use ol_framework::donor_voice; use ol_framework::slow_wallet; - use diem_std::debug::print; + // use diem_std::debug::print; friend ol_framework::community_wallet_init; friend ol_framework::epoch_boundary; @@ -343,14 +343,9 @@ module ol_framework::donor_voice_txs { let list = &mut state.scheduled; let split_point = vector::stable_partition(list, |e| { let e: &TimedTransfer = e; - // &tt.deadline > epoch - // print(&tt.uid); - print(&e.deadline); - print(&epoch); e.deadline > epoch }); - print(&split_point); - // vector::empty() + vector::trim(&mut state.scheduled, split_point) } @@ -364,7 +359,6 @@ module ol_framework::donor_voice_txs { let i = 0; let due_list = filter_scheduled_due(state, epoch); - print(&due_list); // find all Txs scheduled prior to this epoch. let len = vector::length(&due_list); @@ -386,13 +380,8 @@ module ol_framework::donor_voice_txs { amount_transferred = coin::value(&c); ol_account::vm_deposit_coins_locked(vm, t.tx.payee, c); // update the records (don't copy or drop) - print(&state.scheduled); - print(&state.paid); - vector::push_back(&mut state.paid, t); - print(&state.scheduled); - print(&state.paid); } else { // if it could not be paid because of low balance, // place it back on the scheduled list From f136275603f0e2d69130082c671cea6435e293fe Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Mon, 30 Sep 2024 17:29:41 -0400 Subject: [PATCH 32/34] all tests passing --- .../sources/ol_sources/proof_of_fee.move | 1 + .../ol_sources/tests/proof_of_fee.test.move | 16 ++++++----- .../tests/validator_reward.test.move | 27 +++++++------------ 3 files changed, 20 insertions(+), 24 deletions(-) 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 19f8361c9..7e9336413 100644 --- a/framework/libra-framework/sources/ol_sources/proof_of_fee.move +++ b/framework/libra-framework/sources/ol_sources/proof_of_fee.move @@ -514,6 +514,7 @@ module ol_framework::proof_of_fee { // TODO: make this it's own function so it can be publicly callable, it's useful generally, and for debugging. let valid = secret_bid::has_valid_bid(val); + if (!valid) vector::push_back(&mut errors, EBID_EXPIRED); // 16 // skip the user if they don't have sufficient UNLOCKED funds // or if the bid expired. 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 205430eea..3e4aec706 100644 --- a/framework/libra-framework/sources/ol_sources/tests/proof_of_fee.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/proof_of_fee.test.move @@ -15,7 +15,7 @@ module ol_framework::test_pof { use diem_framework::chain_id; use std::vector; - use diem_std::debug::print; + // use diem_std::debug::print; const Alice: address = @0x1000a; const Bob: address = @0x1000b; @@ -299,19 +299,21 @@ module ol_framework::test_pof { let set = mock::genesis_n_vals(&root, 4); mock::ol_initialize_coin_and_fund_vals(&root, 10000, true); - let alice_sig = account::create_signer_for_test(@0x10001); + let alice_addr = *vector::borrow(&set, 0); + let alice_sig = account::create_signer_for_test(alice_addr); secret_bid::mock_revealed_bid(&root, &alice_sig, 55, 10); - print(&secret_bid::has_valid_bid(@0x10001)); + assert!(secret_bid::has_valid_bid(alice_addr) == false, 7357001); - // Get all vals but don't filter the ones that have passing bids + // Get all bidders, but don't run audit let sorted = proof_of_fee::get_bidders(false); let bids_len = vector::length(&sorted); assert!(bids_len == vector::length(&set), 1000); + // now filter for expired bids, should have one missing + let sorted_audit = proof_of_fee::get_bidders(true); - let sorted_two = proof_of_fee::get_bidders(true); - assert!(vector::length(&sorted_two) != vector::length(&set), 1004); - assert!(vector::length(&sorted_two) == vector::length(&set) - 1, 1005); + assert!(vector::length(&sorted_audit) != vector::length(&set), 1004); + assert!(vector::length(&sorted_audit) == vector::length(&set) - 1, 1005); } // We can send the fill seats function a list of validators, and the list of performing validators, and it will return the winning bidders and the bid. diff --git a/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move b/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move index 76b62628e..3ffdff610 100644 --- a/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/validator_reward.test.move @@ -13,7 +13,7 @@ module ol_framework::test_reconfiguration { use ol_framework::epoch_helper; use ol_framework::ol_account; - use diem_std::debug::print; + // use diem_std::debug::print; // Scenario: all genesis validators make it to next epoch #[test(root = @ol_framework)] @@ -51,15 +51,18 @@ module ol_framework::test_reconfiguration { #[test(root = @ol_framework)] fun drop_non_performing(root: signer) { - let _vals = mock::genesis_n_vals(&root, 5); - mock::pof_default(&root); + let vals = mock::genesis_n_vals(&root, 5); + let alice = *vector::borrow(&vals, 0); - assert!(libra_coin::balance(@0x1000a) == 0, 7357000); + assert!(libra_coin::balance(alice) == 0, 7357000); // NOTE: epoch 0 and 1 are a special case, we don't run performance grades on that one. Need to move two epochs ahead reconfiguration::test_helper_increment_epoch_dont_reconfigure(1); reconfiguration::test_helper_increment_epoch_dont_reconfigure(1); + // epoch changed, need new valid bids + mock::pof_default(&root); + assert!(epoch_helper::get_current_epoch() == 2, 7357001); let vals = stake::get_current_validators(); @@ -67,28 +70,18 @@ module ol_framework::test_reconfiguration { // all vals compliant mock::mock_all_vals_good_performance(&root); - // make alice non performant - mock::mock_case_4(&root, @0x1000a); - - let (reward, _, _, _ ) = proof_of_fee::get_consensus_reward(); + mock::mock_case_4(&root, alice); // run ol reconfiguration mock::trigger_epoch(&root); - let vals = stake::get_current_validators(); // one validator missing. - print(&vector::length(&vals)); + let vals = stake::get_current_validators(); assert!(vector::length(&vals) == 4, 7357003); - assert!(!vector::contains(&vals, &@0x1000a), 7357004); - - let (_, entry_fee, _, _ ) = proof_of_fee::get_consensus_reward(); + assert!(!vector::contains(&vals, &alice), 7357004); - // alice doesn't get paid - assert!(libra_coin::balance(@0x1000a) == 0, 7357005); - // bob does - assert!(libra_coin::balance(@0x1000b) == (reward - entry_fee), 7357006); } From 7d83cc60d1ad43e6d465caea4f747639ca7143e8 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:16:04 -0500 Subject: [PATCH 33/34] clippy --- .../src/libra_framework_sdk_builder.rs | 124 +++++++++--------- tools/txs/src/txs_cli_vals.rs | 34 +++-- 2 files changed, 78 insertions(+), 80 deletions(-) diff --git a/framework/cached-packages/src/libra_framework_sdk_builder.rs b/framework/cached-packages/src/libra_framework_sdk_builder.rs index a2fdf941e..3c4909b23 100644 --- a/framework/cached-packages/src/libra_framework_sdk_builder.rs +++ b/framework/cached-packages/src/libra_framework_sdk_builder.rs @@ -2413,7 +2413,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::AccountOfferRotationCapability { - rotation_capability_sig_bytes: bcs::from_bytes(script.args().get(0)?).ok()?, + rotation_capability_sig_bytes: bcs::from_bytes(script.args().first()?).ok()?, account_scheme: bcs::from_bytes(script.args().get(1)?).ok()?, account_public_key_bytes: bcs::from_bytes(script.args().get(2)?).ok()?, recipient_address: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2428,7 +2428,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::AccountOfferSignerCapability { - signer_capability_sig_bytes: bcs::from_bytes(script.args().get(0)?).ok()?, + signer_capability_sig_bytes: bcs::from_bytes(script.args().first()?).ok()?, account_scheme: bcs::from_bytes(script.args().get(1)?).ok()?, account_public_key_bytes: bcs::from_bytes(script.args().get(2)?).ok()?, recipient_address: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2463,7 +2463,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::AccountRevokeRotationCapability { - to_be_revoked_address: bcs::from_bytes(script.args().get(0)?).ok()?, + to_be_revoked_address: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2475,7 +2475,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::AccountRevokeSignerCapability { - to_be_revoked_address: bcs::from_bytes(script.args().get(0)?).ok()?, + to_be_revoked_address: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2487,7 +2487,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::AccountRotateAuthenticationKey { - from_scheme: bcs::from_bytes(script.args().get(0)?).ok()?, + from_scheme: bcs::from_bytes(script.args().first()?).ok()?, from_public_key_bytes: bcs::from_bytes(script.args().get(1)?).ok()?, to_scheme: bcs::from_bytes(script.args().get(2)?).ok()?, to_public_key_bytes: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2505,7 +2505,7 @@ mod decoder { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::AccountRotateAuthenticationKeyWithRotationCapability { - rotation_cap_offerer_address: bcs::from_bytes(script.args().get(0)?).ok()?, + rotation_cap_offerer_address: bcs::from_bytes(script.args().first()?).ok()?, new_scheme: bcs::from_bytes(script.args().get(1)?).ok()?, new_public_key_bytes: bcs::from_bytes(script.args().get(2)?).ok()?, cap_update_table: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2519,7 +2519,7 @@ mod decoder { pub fn burn_set_send_community(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::BurnSetSendCommunity { - community: bcs::from_bytes(script.args().get(0)?).ok()?, + community: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2529,7 +2529,7 @@ mod decoder { pub fn code_publish_package_txn(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::CodePublishPackageTxn { - metadata_serialized: bcs::from_bytes(script.args().get(0)?).ok()?, + metadata_serialized: bcs::from_bytes(script.args().first()?).ok()?, code: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2540,8 +2540,8 @@ mod decoder { pub fn coin_transfer(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::CoinTransfer { - coin_type: script.ty_args().get(0)?.clone(), - to: bcs::from_bytes(script.args().get(0)?).ok()?, + coin_type: script.ty_args().first()?.clone(), + to: bcs::from_bytes(script.args().first()?).ok()?, amount: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2555,7 +2555,7 @@ mod decoder { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::CommunityWalletInitChangeSignerCommunityMultisig { - multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_address: bcs::from_bytes(script.args().first()?).ok()?, new_signer: bcs::from_bytes(script.args().get(1)?).ok()?, is_add_operation: bcs::from_bytes(script.args().get(2)?).ok()?, n_of_m: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2572,7 +2572,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::CommunityWalletInitFinalizeAndCage { - num_signers: bcs::from_bytes(script.args().get(0)?).ok()?, + num_signers: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2584,7 +2584,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::CommunityWalletInitInitCommunity { - initial_authorities: bcs::from_bytes(script.args().get(0)?).ok()?, + initial_authorities: bcs::from_bytes(script.args().first()?).ok()?, check_threshold: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2597,7 +2597,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::CommunityWalletInitProposeOffer { - new_signers: bcs::from_bytes(script.args().get(0)?).ok()?, + new_signers: bcs::from_bytes(script.args().first()?).ok()?, num_signers: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2611,7 +2611,7 @@ mod decoder { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::DiemGovernanceAddApprovedScriptHashScript { - proposal_id: bcs::from_bytes(script.args().get(0)?).ok()?, + proposal_id: bcs::from_bytes(script.args().first()?).ok()?, }, ) } else { @@ -2624,7 +2624,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DiemGovernanceAssertCanResolve { - proposal_id: bcs::from_bytes(script.args().get(0)?).ok()?, + proposal_id: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2636,7 +2636,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DiemGovernanceCreateProposalV2 { - execution_hash: bcs::from_bytes(script.args().get(0)?).ok()?, + execution_hash: bcs::from_bytes(script.args().first()?).ok()?, metadata_location: bcs::from_bytes(script.args().get(1)?).ok()?, metadata_hash: bcs::from_bytes(script.args().get(2)?).ok()?, is_multi_step_proposal: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2651,7 +2651,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DiemGovernanceOlCreateProposalV2 { - execution_hash: bcs::from_bytes(script.args().get(0)?).ok()?, + execution_hash: bcs::from_bytes(script.args().first()?).ok()?, metadata_location: bcs::from_bytes(script.args().get(1)?).ok()?, metadata_hash: bcs::from_bytes(script.args().get(2)?).ok()?, is_multi_step_proposal: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2664,7 +2664,7 @@ mod decoder { pub fn diem_governance_ol_vote(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DiemGovernanceOlVote { - proposal_id: bcs::from_bytes(script.args().get(0)?).ok()?, + proposal_id: bcs::from_bytes(script.args().first()?).ok()?, should_pass: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2695,7 +2695,7 @@ mod decoder { pub fn diem_governance_vote(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DiemGovernanceVote { - proposal_id: bcs::from_bytes(script.args().get(0)?).ok()?, + proposal_id: bcs::from_bytes(script.args().first()?).ok()?, should_pass: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2708,7 +2708,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DonorVoiceTxsProposeLiquidateTx { - multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_address: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2720,7 +2720,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DonorVoiceTxsProposePaymentTx { - multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_address: bcs::from_bytes(script.args().first()?).ok()?, payee: bcs::from_bytes(script.args().get(1)?).ok()?, value: bcs::from_bytes(script.args().get(2)?).ok()?, description: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2735,7 +2735,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DonorVoiceTxsProposeVetoTx { - multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_address: bcs::from_bytes(script.args().first()?).ok()?, id: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2748,7 +2748,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DonorVoiceTxsVoteLiquidationTx { - multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_address: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2758,7 +2758,7 @@ mod decoder { pub fn donor_voice_txs_vote_veto_tx(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::DonorVoiceTxsVoteVetoTx { - multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_address: bcs::from_bytes(script.args().first()?).ok()?, id: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2779,7 +2779,7 @@ 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()?, + addr: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2801,7 +2801,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::LibraCoinDelegateMintCapability { - to: bcs::from_bytes(script.args().get(0)?).ok()?, + to: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2811,7 +2811,7 @@ mod decoder { pub fn libra_coin_mint_to_impl(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::LibraCoinMintToImpl { - dst_addr: bcs::from_bytes(script.args().get(0)?).ok()?, + dst_addr: bcs::from_bytes(script.args().first()?).ok()?, amount: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2822,7 +2822,7 @@ mod decoder { pub fn multi_action_claim_offer(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultiActionClaimOffer { - multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_address: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2844,7 +2844,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultiActionMigrationMigrateOffer { - multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_address: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2854,7 +2854,7 @@ mod decoder { pub fn multisig_account_add_owner(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountAddOwner { - new_owner: bcs::from_bytes(script.args().get(0)?).ok()?, + new_owner: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2864,7 +2864,7 @@ mod decoder { pub fn multisig_account_add_owners(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountAddOwners { - new_owners: bcs::from_bytes(script.args().get(0)?).ok()?, + new_owners: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -2876,7 +2876,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountApproveTransaction { - multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_account: bcs::from_bytes(script.args().first()?).ok()?, sequence_number: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2887,7 +2887,7 @@ mod decoder { pub fn multisig_account_create(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountCreate { - num_signatures_required: bcs::from_bytes(script.args().get(0)?).ok()?, + num_signatures_required: bcs::from_bytes(script.args().first()?).ok()?, metadata_keys: bcs::from_bytes(script.args().get(1)?).ok()?, metadata_values: bcs::from_bytes(script.args().get(2)?).ok()?, }) @@ -2901,7 +2901,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountCreateTransaction { - multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_account: bcs::from_bytes(script.args().first()?).ok()?, payload: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -2915,7 +2915,7 @@ mod decoder { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::MultisigAccountCreateTransactionWithHash { - multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_account: bcs::from_bytes(script.args().first()?).ok()?, payload_hash: bcs::from_bytes(script.args().get(1)?).ok()?, }, ) @@ -2930,7 +2930,7 @@ mod decoder { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::MultisigAccountCreateWithExistingAccount { - multisig_address: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_address: bcs::from_bytes(script.args().first()?).ok()?, owners: bcs::from_bytes(script.args().get(1)?).ok()?, num_signatures_required: bcs::from_bytes(script.args().get(2)?).ok()?, account_scheme: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2951,7 +2951,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountCreateWithOwners { - additional_owners: bcs::from_bytes(script.args().get(0)?).ok()?, + additional_owners: bcs::from_bytes(script.args().first()?).ok()?, num_signatures_required: bcs::from_bytes(script.args().get(1)?).ok()?, metadata_keys: bcs::from_bytes(script.args().get(2)?).ok()?, metadata_values: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2967,7 +2967,7 @@ mod decoder { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::MultisigAccountExecuteRejectedTransaction { - multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_account: bcs::from_bytes(script.args().first()?).ok()?, }, ) } else { @@ -2980,7 +2980,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountMigrateWithOwners { - additional_owners: bcs::from_bytes(script.args().get(0)?).ok()?, + additional_owners: bcs::from_bytes(script.args().first()?).ok()?, num_signatures_required: bcs::from_bytes(script.args().get(1)?).ok()?, metadata_keys: bcs::from_bytes(script.args().get(2)?).ok()?, metadata_values: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -2995,7 +2995,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountRejectTransaction { - multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_account: bcs::from_bytes(script.args().first()?).ok()?, sequence_number: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -3008,7 +3008,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountRemoveOwner { - owner_to_remove: bcs::from_bytes(script.args().get(0)?).ok()?, + owner_to_remove: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -3020,7 +3020,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountRemoveOwners { - owners_to_remove: bcs::from_bytes(script.args().get(0)?).ok()?, + owners_to_remove: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -3032,7 +3032,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountUpdateMetadata { - keys: bcs::from_bytes(script.args().get(0)?).ok()?, + keys: bcs::from_bytes(script.args().first()?).ok()?, values: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -3045,7 +3045,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountUpdateSignaturesRequired { - new_num_signatures_required: bcs::from_bytes(script.args().get(0)?).ok()?, + new_num_signatures_required: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -3057,7 +3057,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::MultisigAccountVoteTransanction { - multisig_account: bcs::from_bytes(script.args().get(0)?).ok()?, + multisig_account: bcs::from_bytes(script.args().first()?).ok()?, sequence_number: bcs::from_bytes(script.args().get(1)?).ok()?, approved: bcs::from_bytes(script.args().get(2)?).ok()?, }) @@ -3069,7 +3069,7 @@ mod decoder { pub fn object_transfer_call(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::ObjectTransferCall { - object: bcs::from_bytes(script.args().get(0)?).ok()?, + object: bcs::from_bytes(script.args().first()?).ok()?, to: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -3080,7 +3080,7 @@ mod decoder { pub fn ol_account_create_account(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::OlAccountCreateAccount { - auth_key: bcs::from_bytes(script.args().get(0)?).ok()?, + auth_key: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -3092,7 +3092,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::OlAccountSetAllowDirectCoinTransfers { - allow: bcs::from_bytes(script.args().get(0)?).ok()?, + allow: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -3102,7 +3102,7 @@ mod decoder { pub fn ol_account_transfer(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::OlAccountTransfer { - to: bcs::from_bytes(script.args().get(0)?).ok()?, + to: bcs::from_bytes(script.args().first()?).ok()?, amount: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -3129,7 +3129,7 @@ mod decoder { pub fn proof_of_fee_pof_update_bid(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::ProofOfFeePofUpdateBid { - bid: bcs::from_bytes(script.args().get(0)?).ok()?, + bid: bcs::from_bytes(script.args().first()?).ok()?, epoch_expiry: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -3142,7 +3142,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::ProofOfFeePofUpdateBidNetReward { - net_reward: bcs::from_bytes(script.args().get(0)?).ok()?, + net_reward: bcs::from_bytes(script.args().first()?).ok()?, epoch_expiry: bcs::from_bytes(script.args().get(1)?).ok()?, }) } else { @@ -3153,7 +3153,7 @@ mod decoder { pub fn safe_init_payment_multisig(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::SafeInitPaymentMultisig { - authorities: bcs::from_bytes(script.args().get(0)?).ok()?, + authorities: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -3165,7 +3165,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::SlowWalletSmokeTestVmUnlock { - user_addr: bcs::from_bytes(script.args().get(0)?).ok()?, + user_addr: bcs::from_bytes(script.args().first()?).ok()?, unlocked: bcs::from_bytes(script.args().get(1)?).ok()?, transferred: bcs::from_bytes(script.args().get(2)?).ok()?, }) @@ -3185,7 +3185,7 @@ mod decoder { pub fn stake_initialize_validator(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::StakeInitializeValidator { - consensus_pubkey: bcs::from_bytes(script.args().get(0)?).ok()?, + consensus_pubkey: bcs::from_bytes(script.args().first()?).ok()?, proof_of_possession: bcs::from_bytes(script.args().get(1)?).ok()?, network_addresses: bcs::from_bytes(script.args().get(2)?).ok()?, fullnode_addresses: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -3198,7 +3198,7 @@ mod decoder { pub fn stake_rotate_consensus_key(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::StakeRotateConsensusKey { - validator_address: bcs::from_bytes(script.args().get(0)?).ok()?, + validator_address: bcs::from_bytes(script.args().first()?).ok()?, new_consensus_pubkey: bcs::from_bytes(script.args().get(1)?).ok()?, proof_of_possession: bcs::from_bytes(script.args().get(2)?).ok()?, }) @@ -3212,7 +3212,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::StakeUpdateNetworkAndFullnodeAddresses { - validator_address: bcs::from_bytes(script.args().get(0)?).ok()?, + validator_address: bcs::from_bytes(script.args().first()?).ok()?, new_network_addresses: bcs::from_bytes(script.args().get(1)?).ok()?, new_fullnode_addresses: bcs::from_bytes(script.args().get(2)?).ok()?, }) @@ -3226,7 +3226,7 @@ mod decoder { ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::ValidatorUniverseRegisterValidator { - consensus_pubkey: bcs::from_bytes(script.args().get(0)?).ok()?, + consensus_pubkey: bcs::from_bytes(script.args().first()?).ok()?, proof_of_possession: bcs::from_bytes(script.args().get(1)?).ok()?, network_addresses: bcs::from_bytes(script.args().get(2)?).ok()?, fullnode_addresses: bcs::from_bytes(script.args().get(3)?).ok()?, @@ -3239,7 +3239,7 @@ mod decoder { pub fn version_set_version(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::VersionSetVersion { - major: bcs::from_bytes(script.args().get(0)?).ok()?, + major: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -3249,7 +3249,7 @@ mod decoder { pub fn vouch_insist_vouch_for(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::VouchInsistVouchFor { - friend_account: bcs::from_bytes(script.args().get(0)?).ok()?, + friend_account: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -3259,7 +3259,7 @@ mod decoder { pub fn vouch_revoke(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::VouchRevoke { - friend_account: bcs::from_bytes(script.args().get(0)?).ok()?, + friend_account: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None @@ -3269,7 +3269,7 @@ mod decoder { pub fn vouch_vouch_for(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::VouchVouchFor { - friend_account: bcs::from_bytes(script.args().get(0)?).ok()?, + friend_account: bcs::from_bytes(script.args().first()?).ok()?, }) } else { None diff --git a/tools/txs/src/txs_cli_vals.rs b/tools/txs/src/txs_cli_vals.rs index d12ec7f1c..bfd34ce70 100644 --- a/tools/txs/src/txs_cli_vals.rs +++ b/tools/txs/src/txs_cli_vals.rs @@ -79,25 +79,23 @@ impl ValidatorTxs { } => { if *retract { ProofOfFeePofRetractBid {} + } else if let Some(b) = bid_pct { + // TODO: the u64 will truncate, but without rounding it will drop the last digit. + let scaled_bid = (b * 1000.0).round() as u64; // scale to 10ˆ3. + if scaled_bid > 1100 { + bail!( + "a bid amount at 110.0% or above the epoch's reward, will be rejected" + ); + } + ProofOfFeePofUpdateBid { + bid: scaled_bid, + epoch_expiry: *expiry, + } } else { - if let Some(b) = bid_pct { - // TODO: the u64 will truncate, but without rounding it will drop the last digit. - let scaled_bid = (b * 1000.0).round() as u64; // scale to 10ˆ3. - if scaled_bid > 1100 { - bail!( - "a bid amount at 110.0% or above the epoch's reward, will be rejected" - ); - } - ProofOfFeePofUpdateBid { - bid: scaled_bid, - epoch_expiry: *expiry, - } - } else { - // Default path is to update based on the expected net reward - ProofOfFeePofUpdateBidNetReward { - net_reward: *net_reward, - epoch_expiry: *expiry, - } + // Default path is to update based on the expected net reward + ProofOfFeePofUpdateBidNetReward { + net_reward: *net_reward, + epoch_expiry: *expiry, } } } From 119d6c7f33c9a73a3666cec17f345a0e70f5c1f8 Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:19:20 -0500 Subject: [PATCH 34/34] fmt --- tools/txs/src/txs_cli_vals.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/txs/src/txs_cli_vals.rs b/tools/txs/src/txs_cli_vals.rs index bfd34ce70..98d1ea749 100644 --- a/tools/txs/src/txs_cli_vals.rs +++ b/tools/txs/src/txs_cli_vals.rs @@ -84,8 +84,8 @@ impl ValidatorTxs { let scaled_bid = (b * 1000.0).round() as u64; // scale to 10ˆ3. if scaled_bid > 1100 { bail!( - "a bid amount at 110.0% or above the epoch's reward, will be rejected" - ); + "a bid amount at 110.0% or above the epoch's reward, will be rejected" + ); } ProofOfFeePofUpdateBid { bid: scaled_bid,