From e15acb5469c0a1d729914b3d3baa5285863b6e2a Mon Sep 17 00:00:00 2001 From: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:05:21 -0500 Subject: [PATCH 1/7] separate feature flag definitions from evaluation --- framework/libra-framework/sources/block.move | 2 -- .../ol_sources/config/ol_features.move | 22 ++++++++++++ .../ol_sources/tests/boundary.test.move | 35 +++---------------- .../ol_sources/tests/governance.test.move | 14 +++++++- .../sources/reconfiguration.move | 1 - .../sources/transaction_validation.move | 1 - .../move-stdlib/sources/configs/features.move | 11 ++---- 7 files changed, 42 insertions(+), 44 deletions(-) create mode 100644 framework/libra-framework/sources/ol_sources/config/ol_features.move diff --git a/framework/libra-framework/sources/block.move b/framework/libra-framework/sources/block.move index 8df83ea52..bd8f10bff 100644 --- a/framework/libra-framework/sources/block.move +++ b/framework/libra-framework/sources/block.move @@ -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, diff --git a/framework/libra-framework/sources/ol_sources/config/ol_features.move b/framework/libra-framework/sources/ol_sources/config/ol_features.move new file mode 100644 index 000000000..27dc0a2a1 --- /dev/null +++ b/framework/libra-framework/sources/ol_sources/config/ol_features.move @@ -0,0 +1,22 @@ + +module ol_framework::ol_features_constants { + use std::features; + /// Whether the new epoch trigger logic is enabled. + /// Lifetime: transient + const EPOCH_TRIGGER_ENABLED: u64 = 24; + public fun get_epoch_trigger(): u64 { EPOCH_TRIGGER_ENABLED } + public fun epoch_trigger_enabled(): bool { + features::is_enabled(EPOCH_TRIGGER_ENABLED) + } + + + //////// TEST HELPERS //////// + #[test_only] + const TEST_DUMMY_FLAG: u64 = 8675309; + #[test_only] + public fun test_get_dummy_flag(): u64 { TEST_DUMMY_FLAG } + #[test_only] + public fun test_dummy_flag_enabled(): bool { + features::is_enabled(TEST_DUMMY_FLAG) + } +} 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..3f997878f 100644 --- a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move @@ -21,6 +21,7 @@ module ol_framework::test_boundary { use ol_framework::epoch_boundary; use ol_framework::block; use ol_framework::ol_account; + use ol_framework::ol_features_constants; use diem_std::debug::print; @@ -368,31 +369,6 @@ module ol_framework::test_boundary { } - // #[test(root = @ol_framework, alice = @0x1000a, marlon = @0x12345)] - // fun test_epoch_trigger_disabled(root: &signer) { - // common_test_setup(root); - // // testing mainnet, so change the chainid - // testnet::unset(root); - - // //verify trigger is not enabled - // assert!(!features::epoch_trigger_enabled(), 101); - - // // test setup advances to epoch #2 - // let epoch = reconfiguration::get_current_epoch(); - // assert!(epoch == 2, 7357001); - // epoch_boundary::test_set_boundary_ready(root, epoch); - - - // // case: trigger not set and flipped - // timestamp::fast_forward_seconds(1); // needed for reconfig - // block::test_maybe_advance_epoch(root, 602000001, 602000000); - - // // test epoch advances - // let epoch = reconfiguration::get_current_epoch(); - // assert!(epoch == 3, 7357002); - - // } - #[test(root = @ol_framework, alice = @0x1000a, marlon = @0x12345)] fun test_epoch_trigger_enabled(root: &signer) { common_test_setup(root); @@ -404,19 +380,18 @@ module ol_framework::test_boundary { epoch_boundary::test_set_boundary_ready(root, epoch); // case: epoch trigger set - features::change_feature_flags(root, vector[features::get_epoch_trigger()], vector[]); + features::change_feature_flags(root, vector[ol_features_constants::get_epoch_trigger()], vector[]); + assert!(features::is_enabled(ol_features_constants::get_epoch_trigger()), 7357002); timestamp::fast_forward_seconds(1); // needed for reconfig block::test_maybe_advance_epoch(root, 603000001, 602000000); // test epoch did not advance and needs to be triggered let epoch = reconfiguration::get_current_epoch(); - assert!(epoch == 2, 7357002); + assert!(epoch == 2, 7357003); // test epoch can be triggered and advances epoch_boundary::test_trigger(root); let epoch = reconfiguration::get_current_epoch(); - assert!(epoch == 3, 7357002); + assert!(epoch == 3, 7357004); } - - } diff --git a/framework/libra-framework/sources/ol_sources/tests/governance.test.move b/framework/libra-framework/sources/ol_sources/tests/governance.test.move index f22017e90..cd5c702a8 100644 --- a/framework/libra-framework/sources/ol_sources/tests/governance.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/governance.test.move @@ -6,6 +6,8 @@ module ol_framework::test_governance { use diem_framework::governance_proposal::GovernanceProposal; use diem_framework::voting; use diem_framework::timestamp; + use ol_framework::ol_features_constants; + use std::features; // use diem_std::debug::print; #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b)] @@ -63,4 +65,14 @@ module ol_framework::test_governance { let (can_resolve, _err) = voting::check_resolvable_ex_hash(@ol_framework, prop_id); assert!(can_resolve, 73570007); } -} \ No newline at end of file + + + #[test(root = @ol_framework, alice = @0x1000a, marlon = @0x12345)] + fun test_enable_ol_features(root: &signer) { + let _vals = mock::genesis_n_vals(root, 2); + // case: set a dummy feature flag + features::change_feature_flags(root, vector[ol_features_constants::test_get_dummy_flag()], vector[]); + assert!(features::is_enabled(ol_features_constants::test_get_dummy_flag()), 7357002); + assert!(ol_features_constants::test_dummy_flag_enabled(), 7357003); + } +} diff --git a/framework/libra-framework/sources/reconfiguration.move b/framework/libra-framework/sources/reconfiguration.move index 908bc2099..1c921123f 100644 --- a/framework/libra-framework/sources/reconfiguration.move +++ b/framework/libra-framework/sources/reconfiguration.move @@ -2,7 +2,6 @@ /// to synchronize configuration changes for the validators. module diem_framework::reconfiguration { use std::error; - // use std::features; use std::signer; use diem_framework::account; diff --git a/framework/libra-framework/sources/transaction_validation.move b/framework/libra-framework/sources/transaction_validation.move index 41b9ba611..6f3a33f94 100644 --- a/framework/libra-framework/sources/transaction_validation.move +++ b/framework/libra-framework/sources/transaction_validation.move @@ -1,6 +1,5 @@ module diem_framework::transaction_validation { use std::error; - // use std::features; use std::signer; use std::vector; diff --git a/framework/move-stdlib/sources/configs/features.move b/framework/move-stdlib/sources/configs/features.move index 8fda2ab06..05785d56e 100644 --- a/framework/move-stdlib/sources/configs/features.move +++ b/framework/move-stdlib/sources/configs/features.move @@ -209,14 +209,6 @@ module std::features { is_enabled(DIEM_UNIQUE_IDENTIFIERS) } - /// Whether the new epoch trigger logic is enabled. - /// Lifetime: transient - const EPOCH_TRIGGER_ENABLED: u64 = 24; - public fun get_epoch_trigger(): u64 { EPOCH_TRIGGER_ENABLED } - public fun epoch_trigger_enabled(): bool acquires Features { - is_enabled(EPOCH_TRIGGER_ENABLED) - } - // ============================================================================================ // Feature Flag Implementation @@ -244,8 +236,9 @@ module std::features { }); } + #[view] /// Check whether the feature is enabled. - fun is_enabled(feature: u64): bool acquires Features { + public fun is_enabled(feature: u64): bool acquires Features { exists(@std) && contains(&borrow_global(@std).features, feature) } From b37214e56c416cc16a6256f44635e66d9613bf14 Mon Sep 17 00:00:00 2001 From: Reginald Fitz Hart Date: Mon, 3 Feb 2025 16:49:53 -0500 Subject: [PATCH 2/7] remove deprecated test, replace with tooling tests --- .../ol_sources/tests/boundary.test.move | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 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 3f997878f..15c1fd044 100644 --- a/framework/libra-framework/sources/ol_sources/tests/boundary.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/boundary.test.move @@ -2,7 +2,6 @@ #[test_only] module ol_framework::test_boundary { use std::vector; - use std::features; use diem_std::bls12381; use diem_framework::stake; use diem_framework::timestamp; @@ -21,7 +20,6 @@ module ol_framework::test_boundary { use ol_framework::epoch_boundary; use ol_framework::block; use ol_framework::ol_account; - use ol_framework::ol_features_constants; use diem_std::debug::print; @@ -47,6 +45,29 @@ module ol_framework::test_boundary { set } + + #[test(root = @ol_framework, alice = @0x1000a, marlon = @0x12345)] + fun meta_test_override_epoch_change(root: &signer) { + common_test_setup(root); + // testing mainnet, so change the chain_id + testnet::unset(root); + // test setup advances to epoch #2 + let epoch = reconfiguration::get_current_epoch(); + assert!(epoch == 2, 7357001); + epoch_boundary::test_set_boundary_ready(root, epoch); + + timestamp::fast_forward_seconds(1); // needed for reconfig + block::test_maybe_advance_epoch(root, 603000001, 602000000); + + // test epoch did not advance and needs to be triggered + let epoch = reconfiguration::get_current_epoch(); + assert!(epoch == 2, 7357003); + + // test epoch can be triggered and advances + epoch_boundary::test_trigger(root); + let epoch = reconfiguration::get_current_epoch(); + assert!(epoch == 3, 7357004); + } // We need to test e2e of the epoch boundary #[test(root = @ol_framework)] fun e2e_boundary_happy(root: signer) { @@ -368,30 +389,4 @@ module ol_framework::test_boundary { // aborts } - - #[test(root = @ol_framework, alice = @0x1000a, marlon = @0x12345)] - fun test_epoch_trigger_enabled(root: &signer) { - common_test_setup(root); - // testing mainnet, so change the chainid - testnet::unset(root); - // test setup advances to epoch #2 - let epoch = reconfiguration::get_current_epoch(); - assert!(epoch == 2, 7357001); - epoch_boundary::test_set_boundary_ready(root, epoch); - - // case: epoch trigger set - features::change_feature_flags(root, vector[ol_features_constants::get_epoch_trigger()], vector[]); - assert!(features::is_enabled(ol_features_constants::get_epoch_trigger()), 7357002); - timestamp::fast_forward_seconds(1); // needed for reconfig - block::test_maybe_advance_epoch(root, 603000001, 602000000); - - // test epoch did not advance and needs to be triggered - let epoch = reconfiguration::get_current_epoch(); - assert!(epoch == 2, 7357003); - - // test epoch can be triggered and advances - epoch_boundary::test_trigger(root); - let epoch = reconfiguration::get_current_epoch(); - assert!(epoch == 3, 7357004); - } } From ecd2923880ebbceebad35e8d6411e85c195d4698 Mon Sep 17 00:00:00 2001 From: Cholmondeley De Tempo Date: Mon, 3 Feb 2025 16:54:34 -0500 Subject: [PATCH 3/7] add governance mode flag definition, tbd implementation --- .../ol_sources/config/ol_features.move | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/config/ol_features.move b/framework/libra-framework/sources/ol_sources/config/ol_features.move index 27dc0a2a1..a741276bd 100644 --- a/framework/libra-framework/sources/ol_sources/config/ol_features.move +++ b/framework/libra-framework/sources/ol_sources/config/ol_features.move @@ -1,14 +1,28 @@ module ol_framework::ol_features_constants { use std::features; - /// Whether the new epoch trigger logic is enabled. - /// Lifetime: transient - const EPOCH_TRIGGER_ENABLED: u64 = 24; - public fun get_epoch_trigger(): u64 { EPOCH_TRIGGER_ENABLED } - public fun epoch_trigger_enabled(): bool { - features::is_enabled(EPOCH_TRIGGER_ENABLED) - } + ///////// KEEP COMMENTED //////// + // NOTE: this feature is deprecated + // since epoch trigger was an experimental feature + // but is permanent since v7.0.3. + // The code and ID are kept here for reference + // /// Whether the new epoch trigger logic is enabled. + // /// Lifetime: transient + // const EPOCH_TRIGGER_ENABLED: u64 = 24; + // public fun get_epoch_trigger(): u64 { EPOCH_TRIGGER_ENABLED } + // public fun epoch_trigger_enabled(): bool { + // features::is_enabled(EPOCH_TRIGGER_ENABLED) + // } + + /// GOVERNANCE MODE + /// Certain transactions are disabled during deliberation and + /// execution of on-chain hot upgrades. + const GOVERNANCE_MODE_ENABLED: u64 = 25; + public fun get_governance_mode(): u64 { GOVERNANCE_MODE_ENABLED } + public fun is_governance_mode_enabled(): bool { + features::is_enabled(GOVERNANCE_MODE_ENABLED) + } //////// TEST HELPERS //////// #[test_only] From eecc4428126037681837d4d0684fc99318c35fe7 Mon Sep 17 00:00:00 2001 From: Bea Marten Date: Mon, 3 Feb 2025 17:07:44 -0500 Subject: [PATCH 4/7] governance mode prevents p2p transfers (no test) --- .../libra-framework/sources/ol_sources/ol_account.move | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/ol_account.move b/framework/libra-framework/sources/ol_sources/ol_account.move index 7fe1da168..d4493cb0d 100644 --- a/framework/libra-framework/sources/ol_sources/ol_account.move +++ b/framework/libra-framework/sources/ol_sources/ol_account.move @@ -13,6 +13,7 @@ module ol_framework::ol_account { use ol_framework::ancestry; + use ol_framework::ol_features_constants; use ol_framework::libra_coin::{Self, LibraCoin}; use ol_framework::slow_wallet; use ol_framework::receipts; @@ -75,11 +76,13 @@ module ol_framework::ol_account { /// This key cannot be used to create accounts. The address may have /// malformed state. And says, "My advice is to not let the boys in". - const ETOMBSTONE: u64 = 21; + const ETOMBSTONE: u64 = 13; /// This account is malformed, it does not have the necessary burn tracker struct - const ENO_TRACKER_INITIALIZED: u64 = 13; + const ENO_TRACKER_INITIALIZED: u64 = 14; + /// Governance mode: chain has restricted p2p transactions while upgrades are executed. + const EGOVERNANCE_MODE: u64 = 15; ///////// CONSTS ///////// /// what limit should be set for new account creation while using transfer() @@ -312,6 +315,8 @@ module ol_framework::ol_account { // actual implementation to allow for capability fun transfer_checks(payer: address, recipient: address, amount: u64) { + assert!(!ol_features_constants::is_governance_mode_enabled(), error::invalid_state(EGOVERNANCE_MODE)); + let limit = slow_wallet::unlocked_amount(payer); assert!(amount < limit, error::invalid_state(EINSUFFICIENT_BALANCE)); From 3ea541f19d21117305c6d32fe47b941019bf1165 Mon Sep 17 00:00:00 2001 From: Rupert Von Fortissimo Date: Mon, 3 Feb 2025 17:08:11 -0500 Subject: [PATCH 5/7] governance mode prevents donor_voice creation or schedules txs (no tests) --- .../ol_sources/vote_lib/donor_voice.move | 10 ++++---- .../ol_sources/vote_lib/donor_voice_txs.move | 23 ++++++++++++------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/vote_lib/donor_voice.move b/framework/libra-framework/sources/ol_sources/vote_lib/donor_voice.move index cf5d372e1..caac70ce9 100644 --- a/framework/libra-framework/sources/ol_sources/vote_lib/donor_voice.move +++ b/framework/libra-framework/sources/ol_sources/vote_lib/donor_voice.move @@ -9,7 +9,7 @@ /// transactions on behalf of Owner /// Depositors are called Donors, and the app gives depositors /// visibility of the transactions, and also limited authority over -/// specific actions: alterting the Owner and Depositors from +/// specific actions: alerting the Owner and Depositors from /// unauthorized transaction. /// The DonorVoice Account Lifecycle: @@ -19,11 +19,11 @@ /// By creating a TxSchedule wallet you are providing certain restrictions and guarantees to the users that interact with this wallet. -/// 1. The wallet's contents is propoperty of the owner. The owner is free to issue transactions which change the state of the wallet, including transferring funds. There are however time, and veto policies. +/// 1. The wallet's contents is property of the owner. The owner is free to issue transactions which change the state of the wallet, including transferring funds. There are however time, and veto policies. /// 2. All transfers out of the account are timed. Meaning, they will execute automatically after a set period of time passes. The VM address triggers these events at each epoch boundary. The purpose of the delayed transfers is that the transaction can be paused for analysis, and eventually rejected by the donors of the wallet. -/// 3. Every pending transaction can be "vetoed". The vetos delay the finalizing of the transaction, to allow more time for analysis. Each veto adds one day/epoch to the transaction PER DAY THAT A VETO OCCURRS. That is, two vetos happening in the same day, only extend the vote by one day. If a sufficient number of Donors vote on the Veto, then the transaction will be rejected. Since TxSchedule has an expiration time, as does ParticipationVote, each time there is a veto, the deadlines for both are syncronized, based on the new TxSchedule expiration time. +/// 3. Every pending transaction can be "vetoed". Each veto delays the finalizing of the transaction, to allow more time for analysis. Each veto adds one day/epoch to the transaction PER DAY THAT A VETO OCCURs. That is, two vetos happening in the same day, only extend the vote by one day. If a sufficient number of Donors vote on the Veto, then the transaction will be rejected. Since TxSchedule has an expiration time, as does ParticipationVote, each time there is a veto, the deadlines for both are synchronized, based on the new TxSchedule expiration time. /// 4. After three consecutive transaction rejections, the account will become frozen. The funds remain in the account but no operations are available until the Donors, un-freeze the account. @@ -31,7 +31,7 @@ /// 6. The donors can vote to liquidate a frozen TxSchedule account. The result will depend on the configuration of the TxSchedule account from when it was initialized: the funds by default return to the end user who was the donor. -/// 7. Third party contracts can wrap the Donor Voice wallet. The outcomes of the votes can be returned to a handler in a third party contract For example, liquidiation of a frozen account is programmable: a handler can be coded to determine the outcome of the Donor Voice wallet. See in CommunityWallets the funds return to the InfrastructureEscrow side-account of the user. +/// 7. Third party contracts can wrap the Donor Voice wallet. The outcomes of the votes can be returned to a handler in a third party contract For example, liquidation of a frozen account is programmable: a handler can be coded to determine the outcome of the Donor Voice wallet. See in CommunityWallets the funds return to the InfrastructureEscrow side-account of the user. module ol_framework::donor_voice { use std::vector; @@ -55,7 +55,7 @@ module ol_framework::donor_voice { } - //////// INIT REGISRTY OF DONOR VOICE ACCOUNTS //////// + //////// INIT REGISTRY OF DONOR VOICE ACCOUNTS //////// // Donor Voice Accounts are a root security service. So the root account needs to keep a registry of all Donor Voice accounts, using this service. 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..15541a69e 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 @@ -9,7 +9,7 @@ /// transactions on behalf of Owner /// Depositors are called Donors, and the app gives depositors /// visibility of the transactions, and also limited authority over -/// specific actions: alterting the Owner and Depositors from +/// specific actions: alerting the Owner and Depositors from /// unauthorized transaction. /// The DonorVoice Account Lifecycle: @@ -19,11 +19,11 @@ /// By creating a TxSchedule wallet you are providing certain restrictions and guarantees to the users that interact with this wallet. -/// 1. The wallet's contents is propoperty of the owner. The owner is free to issue transactions which change the state of the wallet, including transferring funds. There are however time, and veto policies. +/// 1. The wallet's contents is property of the owner. The owner is free to issue transactions which change the state of the wallet, including transferring funds. There are however time, and veto policies. /// 2. All transfers out of the account are timed. Meaning, they will execute automatically after a set period of time passes. The VM address triggers these events at each epoch boundary. The purpose of the delayed transfers is that the transaction can be paused for analysis, and eventually rejected by the donors of the wallet. -/// 3. Every pending transaction can be "vetoed". The vetos delay the finalizing of the transaction, to allow more time for analysis. Each veto adds one day/epoch to the transaction PER DAY THAT A VETO OCCURRS. That is, two vetos happening in the same day, only extend the vote by one day. If a sufficient number of Donors vote on the Veto, then the transaction will be rejected. Since TxSchedule has an expiration time, as does ParticipationVote, each time there is a veto, the deadlines for both are syncronized, based on the new TxSchedule expiration time. +/// 3. Every pending transaction can be "vetoed". Each veto delays the finalizing of the transaction, to allow more time for analysis. Each veto adds one day/epoch to the transaction PER DAY THAT A VETO OCCURS. That is, two vetos happening in the same day, only extend the vote by one day. If a sufficient number of Donors vote on the Veto, then the transaction will be rejected. Since TxSchedule has an expiration time, as does ParticipationVote, each time there is a veto, the deadlines for both are synchronized, based on the new TxSchedule expiration time. /// 4. After three consecutive transaction rejections, the account will become frozen. The funds remain in the account but no operations are available until the Donors, un-freeze the account. @@ -31,7 +31,7 @@ /// 6. The donors can vote to liquidate a frozen TxSchedule account. The result will depend on the configuration of the TxSchedule account from when it was initialized: the funds by default return to the end user who was the donor. -/// 7. Third party contracts can wrap the Donor Voice wallet. The outcomes of the votes can be returned to a handler in a third party contract For example, liquidiation of a frozen account is programmable: a handler can be coded to determine the outcome of the Donor Voice wallet. See in CommunityWallets the funds return to the InfrastructureEscrow side-account of the user. +/// 7. Third party contracts can wrap the Donor Voice wallet. The outcomes of the votes can be returned to a handler in a third party contract For example, liquidation of a frozen account is programmable: a handler can be coded to determine the outcome of the Donor Voice wallet. See in CommunityWallets the funds return to the InfrastructureEscrow side-account of the user. module ol_framework::donor_voice_txs { use std::vector; @@ -45,6 +45,7 @@ module ol_framework::donor_voice_txs { use ol_framework::ballot; use ol_framework::epoch_helper; use ol_framework::ol_account; + use ol_framework::ol_features_constants; use ol_framework::receipts; use ol_framework::multi_action; use ol_framework::account::{Self, WithdrawCapability}; @@ -80,6 +81,9 @@ module ol_framework::donor_voice_txs { /// No enum for this number const EPAYEE_NOT_SLOW: u64 = 7; + /// Governance mode: chain has restricted Donor Voice transactions while upgrades are executed. + const EGOVERNANCE_MODE: u64 = 8; + /// number of epochs to wait before a transaction is executed /// Veto can happen in this time /// at the end of the third epoch from when multisig gets consensus @@ -140,6 +144,8 @@ module ol_framework::donor_voice_txs { // 3. Once the MultiSig is initialized, the account needs to be caged, before the MultiSig can be used. public(friend) fun make_donor_voice(sponsor: &signer) { + assert!(!ol_features_constants::is_governance_mode_enabled(), error::invalid_state(EGOVERNANCE_MODE)); + // will not create if already exists (for migration) cumulative_deposits::init_cumulative_deposits(sponsor); @@ -211,6 +217,8 @@ module ol_framework::donor_voice_txs { value: u64, description: vector ): guid::ID acquires TxSchedule { + assert!(!ol_features_constants::is_governance_mode_enabled(), error::invalid_state(EGOVERNANCE_MODE)); + assert!(slow_wallet::is_slow(payee), error::invalid_argument(EPAYEE_NOT_SLOW)); let tx = Payment { @@ -412,8 +420,8 @@ module ol_framework::donor_voice_txs { (amount_processed, expected_amount, expected_amount == amount_processed) } - // COMMIT NOTE: ok to be public - public fun find_by_deadline(multisig_address: address, epoch: u64): vector acquires TxSchedule { + #[test_only] + public(friend) fun find_by_deadline(multisig_address: address, epoch: u64): vector acquires TxSchedule { let state = borrow_global_mut(multisig_address); let i = 0; let list = vector::empty(); @@ -736,9 +744,8 @@ module ol_framework::donor_voice_txs { multi_action::get_proposal_status_by_id(directed_address, uid) } - // COMMIT NOTE: OK to be public /// Get the status of a SCHEDULED payment which as already passed the multisig stage. - public fun get_schedule_state(directed_address: address, uid: &guid::ID): (bool, u64, u8) acquires TxSchedule { // (is_found, index, state) + fun get_schedule_state(directed_address: address, uid: &guid::ID): (bool, u64, u8) acquires TxSchedule { // (is_found, index, state) let state = borrow_global(directed_address); find_schedule_by_id(state, uid) } From 0e03ae1daf7395d025765fe84368928f875e39b5 Mon Sep 17 00:00:00 2001 From: Chiaretta McBittern Date: Mon, 3 Feb 2025 17:18:52 -0500 Subject: [PATCH 6/7] test: transfers fail in governance mode --- .../ol_sources/tests/account.test.move | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/framework/libra-framework/sources/ol_sources/tests/account.test.move b/framework/libra-framework/sources/ol_sources/tests/account.test.move index 4828544ea..87e721ad6 100644 --- a/framework/libra-framework/sources/ol_sources/tests/account.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/account.test.move @@ -3,23 +3,25 @@ /// tests for external apis, and where a dependency cycle with genesis is created. module ol_framework::test_account { use std::vector; + use std::features; use ol_framework::libra_coin; use ol_framework::mock; use ol_framework::ol_account; use ol_framework::ancestry; use diem_framework::coin; + use ol_framework::ol_features_constants; //use diem_std::debug::print; // scenario: testing trying send more funds than are unlocked - #[test(root = @ol_framework, alice_sig = @0x1000a)] - fun test_account_create(root: signer, alice_sig: signer) { + #[test(framework_sig = @ol_framework, alice_sig = @0x1000a)] + fun test_account_create(framework_sig: signer, alice_sig: signer) { let alice_addr = @0x1000a; let bob_addr = @0x1000b; - mock::ol_test_genesis(&root); + mock::ol_test_genesis(&framework_sig); - let mint_cap = libra_coin::extract_mint_cap(&root); - ol_account::create_account(&root, alice_addr); + let mint_cap = libra_coin::extract_mint_cap(&framework_sig); + ol_account::create_account(&framework_sig, alice_addr); ol_account::deposit_coins(alice_addr, coin::test_mint(100, &mint_cap)); coin::destroy_mint_cap(mint_cap); @@ -39,19 +41,19 @@ module ol_framework::test_account { } - #[test(root = @ol_framework, alice_sig = @0x1000a)] + #[test(framework_sig = @ol_framework, alice_sig = @0x1000a)] #[expected_failure(abort_code = 131082, location = 0x1::ol_account)] - fun test_account_reject_create(root: signer, alice_sig: signer) { + fun test_account_reject_create(framework_sig: signer, alice_sig: signer) { let alice_addr = @0x1000a; let bob_addr = @0x1000b; - mock::ol_test_genesis(&root); + mock::ol_test_genesis(&framework_sig); let alice_balance = 10000 * 1000000; // with scaling let bob_tx_too_much = 1100 * 1000000; // above limit - let mint_cap = libra_coin::extract_mint_cap(&root); - ol_account::create_account(&root, alice_addr); + let mint_cap = libra_coin::extract_mint_cap(&framework_sig); + ol_account::create_account(&framework_sig, alice_addr); ol_account::deposit_coins(alice_addr, coin::test_mint(alice_balance, &mint_cap)); coin::destroy_mint_cap(mint_cap); @@ -68,4 +70,24 @@ module ol_framework::test_account { assert!(vector::length(&addr_tree) > 1, 7357004); assert!(vector::contains(&addr_tree, &alice_addr), 7357005); } + + + // scenario: chain enters governance mode, transactions fail + #[test(framework_sig = @ol_framework, alice_sig = @0x1000a)] + #[expected_failure(abort_code = 196623, location = 0x1::ol_account)] + + fun governance_mode_tx_fail(framework_sig: signer, alice_sig: signer) { + let alice_addr = @0x1000a; + let bob_addr = @0x1000b; + + mock::ol_test_genesis(&framework_sig); + let gov_mode_id = ol_features_constants::get_governance_mode(); + features::change_feature_flags(&framework_sig, vector::singleton(gov_mode_id), vector::empty()); + let mint_cap = libra_coin::extract_mint_cap(&framework_sig); + ol_account::create_account(&framework_sig, alice_addr); + ol_account::deposit_coins(alice_addr, coin::test_mint(100, &mint_cap)); + coin::destroy_mint_cap(mint_cap); + + ol_account::transfer(&alice_sig, bob_addr, 20); + } } From 898ef46f631c218514af0da3af483e9ef9f0b367 Mon Sep 17 00:00:00 2001 From: Paoletta Saint Leveret Date: Mon, 3 Feb 2025 17:33:02 -0500 Subject: [PATCH 7/7] tx and donor voice tested with governance mode --- .../ol_sources/tests/account.test.move | 2 + .../ol_sources/tests/donor_voice.test.move | 52 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/framework/libra-framework/sources/ol_sources/tests/account.test.move b/framework/libra-framework/sources/ol_sources/tests/account.test.move index 87e721ad6..d6f1272dc 100644 --- a/framework/libra-framework/sources/ol_sources/tests/account.test.move +++ b/framework/libra-framework/sources/ol_sources/tests/account.test.move @@ -81,8 +81,10 @@ module ol_framework::test_account { let bob_addr = @0x1000b; mock::ol_test_genesis(&framework_sig); + // set governance mode let gov_mode_id = ol_features_constants::get_governance_mode(); features::change_feature_flags(&framework_sig, vector::singleton(gov_mode_id), vector::empty()); + let mint_cap = libra_coin::extract_mint_cap(&framework_sig); ol_account::create_account(&framework_sig, alice_addr); ol_account::deposit_coins(alice_addr, coin::test_mint(100, &mint_cap)); 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..9560bbed2 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 @@ -7,6 +7,7 @@ module ol_framework::test_donor_voice { use ol_framework::donor_voice_txs; use ol_framework::mock; use ol_framework::ol_account; + use ol_framework::ol_features_constants; use ol_framework::multi_action; use ol_framework::receipts; use ol_framework::donor_voice_governance; @@ -15,6 +16,7 @@ module ol_framework::test_donor_voice { use ol_framework::burn; use ol_framework::slow_wallet; use diem_framework::multisig_account; + use std::features; use std::guid; use std::vector; use std::signer; @@ -890,4 +892,54 @@ module ol_framework::test_donor_voice { workers } + + #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d)] + #[expected_failure(abort_code = 196616, location = 0x1::donor_voice_txs)] + + fun dv_init_fails_in_gov_mode(root: &signer, alice: &signer) { + // Scenario: Alice tries to make an account a DV account + // but fails because we are in governance mode. + + let vals = mock::genesis_n_vals(root, 4); + let (resource_sig, _cap) = ol_account::test_ol_create_resource_account(alice, b"0x1"); + let _donor_voice_address = signer::address_of(&resource_sig); + + //////// ENTER GOVERNANCE MODE //////// + let gov_mode_id = ol_features_constants::get_governance_mode(); + features::change_feature_flags(root, vector::singleton(gov_mode_id), vector::empty()); + + // the account needs basic donor directed structs + donor_voice_txs::test_helper_make_donor_voice(root, &resource_sig, vals); + } + + #[test(root = @ol_framework, alice = @0x1000a, bob = @0x1000b, carol = @0x1000c, dave = @0x1000d)] + #[expected_failure(abort_code = 196616, location = 0x1::donor_voice_txs)] + + fun dv_schedule_fails_in_gov_mode(root: &signer, alice: &signer, bob: &signer, carol: &signer, dave: &signer) { + // Scenario: Alice has a pre-existing DV account + // and tries to schedule a transactions but fails because + // we are in governance mode. + + let vals = mock::genesis_n_vals(root, 4); + let (resource_sig, _cap) = ol_account::test_ol_create_resource_account(alice, b"0x1"); + let donor_voice_address = signer::address_of(&resource_sig); + + // the account needs basic donor directed structs + donor_voice_txs::test_helper_make_donor_voice(root, &resource_sig, vals); + + // vals claim the offer + multi_action::claim_offer(alice, donor_voice_address); + multi_action::claim_offer(bob, donor_voice_address); + multi_action::claim_offer(carol, donor_voice_address); + multi_action::claim_offer(dave, donor_voice_address); + + //need to cage to finalize donor directed workflow and release control of the account + multi_action::finalize_and_cage(&resource_sig, 2); + + //////// ENTER GOVERNANCE MODE //////// + let gov_mode_id = ol_features_constants::get_governance_mode(); + features::change_feature_flags(root, vector::singleton(gov_mode_id), vector::empty()); + + let _uid = donor_voice_txs::test_propose_payment(bob, donor_voice_address, @0x1000b, 100, b"thanks bob"); + } }