Skip to content

Commit

Permalink
Merge branch 'governance-mode' into release-7.0.4
Browse files Browse the repository at this point in the history
  • Loading branch information
Bea Brock committed Feb 4, 2025
2 parents 19cea43 + 898ef46 commit ecbfcc7
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 92 deletions.
2 changes: 0 additions & 2 deletions framework/libra-framework/sources/block.move
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

module ol_framework::ol_features_constants {
use std::features;
///////// 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]
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)
}
}
9 changes: 7 additions & 2 deletions framework/libra-framework/sources/ol_sources/ol_account.move
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);

Expand All @@ -68,4 +70,26 @@ 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);
// 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));
coin::destroy_mint_cap(mint_cap);

ol_account::transfer(&alice_sig, bob_addr, 20);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -46,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) {
Expand Down Expand Up @@ -367,56 +389,4 @@ module ol_framework::test_boundary {
// aborts
}


// #[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);
// 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[features::get_epoch_trigger()], vector[]);
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);

// test epoch can be triggered and advances
epoch_boundary::test_trigger(root);
let epoch = reconfiguration::get_current_epoch();
assert!(epoch == 3, 7357002);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -63,4 +65,14 @@ module ol_framework::test_governance {
let (can_resolve, _err) = voting::check_resolvable_ex_hash<GovernanceProposal>(@ol_framework, prop_id);
assert!(can_resolve, 73570007);
}
}


#[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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -19,19 +19,19 @@

/// 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.

/// 5. Voting for all purposes are done on a pro-rata basis according to the amounts donated. Voting using ParticipationVote method, which in short, biases the threshold based on the turnout of the vote. TL;DR a low turnout of 12.5% would require 100% of the voters to veto, and lower thresholds for higher turnouts until 51%.

/// 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;
Expand All @@ -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.

Expand Down
Loading

0 comments on commit ecbfcc7

Please sign in to comment.