Skip to content

aptos_governance - [mainnet]

AptosGovernance represents the on-chain governance of the Aptos network. Voting power is calculated based on the current epoch’s voting power of the proposer or voter’s backing stake pool. In addition, for it to count, the stake pool’s lockup needs to be at least as long as the proposal’s duration.

It provides the following flow:

  1. Proposers can create a proposal by calling AptosGovernance::create_proposal. The proposer’s backing stake pool needs to have the minimum proposer stake required. Off-chain components can subscribe to CreateProposalEvent to track proposal creation and proposal ids.
  2. Voters can vote on a proposal. Their voting power is derived from the backing stake pool. A stake pool can vote on a proposal multiple times as long as the total voting power of these votes doesn’t exceed its total voting power.
use 0x1::account;
use 0x1::aptos_coin;
use 0x1::coin;
use 0x1::consensus_config;
use 0x1::error;
use 0x1::event;
use 0x1::features;
use 0x1::governance_proposal;
use 0x1::math64;
use 0x1::option;
use 0x1::permissioned_signer;
use 0x1::randomness_config;
use 0x1::reconfiguration_with_dkg;
use 0x1::signer;
use 0x1::simple_map;
use 0x1::smart_table;
use 0x1::stake;
use 0x1::staking_config;
use 0x1::string;
use 0x1::system_addresses;
use 0x1::table;
use 0x1::timestamp;
use 0x1::vector;
use 0x1::voting;

Constants

const MAX_U64: u64 = 18446744073709551615;

This matches the same enum const in voting. We have to duplicate it as Move doesn’t have support for enums yet.

const PROPOSAL_STATE_SUCCEEDED: u64 = 1;

The specified stake pool has already been used to vote on the same proposal

const EALREADY_VOTED: u64 = 4;

The specified stake pool does not have sufficient stake to create a proposal

const EINSUFFICIENT_PROPOSER_STAKE: u64 = 1;

The specified stake pool does not have long enough remaining lockup to create a proposal or vote

const EINSUFFICIENT_STAKE_LOCKUP: u64 = 3;

Metadata hash cannot be longer than 256 chars

const EMETADATA_HASH_TOO_LONG: u64 = 10;

Metadata location cannot be longer than 256 chars

const EMETADATA_LOCATION_TOO_LONG: u64 = 9;

This account is not the designated voter of the specified stake pool

const ENOT_DELEGATED_VOTER: u64 = 2;

The proposal in the argument is not a partial voting proposal.

const ENOT_PARTIAL_VOTING_PROPOSAL: u64 = 14;

Current permissioned signer cannot perform governance operations.

const ENO_GOVERNANCE_PERMISSION: u64 = 16;

The specified stake pool must be part of the validator set

const ENO_VOTING_POWER: u64 = 5;

Partial voting feature hasn’t been properly initialized.

const EPARTIAL_VOTING_NOT_INITIALIZED: u64 = 13;

The proposal has expired.

const EPROPOSAL_EXPIRED: u64 = 15;

Proposal is not ready to be resolved. Waiting on time or votes

const EPROPOSAL_NOT_RESOLVABLE_YET: u64 = 6;

The proposal has not been resolved yet

const EPROPOSAL_NOT_RESOLVED_YET: u64 = 8;

Account is not authorized to call this function.

const EUNAUTHORIZED: u64 = 11;

The stake pool is using voting power more than it has.

const EVOTING_POWER_OVERFLOW: u64 = 12;
const METADATA_HASH_KEY: vector<u8> = [109, 101, 116, 97, 100, 97, 116, 97, 95, 104, 97, 115, 104];

Proposal metadata attribute keys.

const METADATA_LOCATION_KEY: vector<u8> = [109, 101, 116, 97, 100, 97, 116, 97, 95, 108, 111, 99, 97, 116, 105, 111, 110];

Structs

RecordKey

struct RecordKey has copy, drop, store
Fields
stake_pool: address
proposal_id: u64

CreateProposalEvent

Event emitted when a proposal is created.

struct CreateProposalEvent has drop, store
Fields
proposer: address
stake_pool: address
proposal_id: u64
execution_hash: vector<u8>
proposal_metadata: simple_map::SimpleMap<string::String, vector<u8>>

VoteEvent

Event emitted when there’s a vote on a proposa;

struct VoteEvent has drop, store
Fields
proposal_id: u64
voter: address
stake_pool: address
num_votes: u64
should_pass: bool

UpdateConfigEvent

Event emitted when the governance configs are updated.

struct UpdateConfigEvent has drop, store
Fields
min_voting_threshold: u128
required_proposer_stake: u64
voting_duration_secs: u64

CreateProposal

Event emitted when a proposal is created.

#[event]
struct CreateProposal has drop, store
Fields
proposer: address
stake_pool: address
proposal_id: u64
execution_hash: vector<u8>
proposal_metadata: simple_map::SimpleMap<string::String, vector<u8>>

Vote

Event emitted when there’s a vote on a proposa;

#[event]
struct Vote has drop, store
Fields
proposal_id: u64
voter: address
stake_pool: address
num_votes: u64
should_pass: bool

UpdateConfig

Event emitted when the governance configs are updated.

#[event]
struct UpdateConfig has drop, store
Fields
min_voting_threshold: u128
required_proposer_stake: u64
voting_duration_secs: u64

GovernancePermission

struct GovernancePermission has copy, drop, store
Fields
dummy_field: bool

Resources

GovernanceResponsbility

Store the SignerCapabilities of accounts under the on-chain governance’s control.

struct GovernanceResponsbility has key
Fields
signer_caps: simple_map::SimpleMap<address, account::SignerCapability>

GovernanceConfig

Configurations of the AptosGovernance, set during Genesis and can be updated by the same process offered by this AptosGovernance module.

struct GovernanceConfig has key
Fields
min_voting_threshold: u128
required_proposer_stake: u64
voting_duration_secs: u64

VotingRecords

Records to track the proposals each stake pool has been used to vote on.

struct VotingRecords has key
Fields
votes: table::Table<aptos_governance::RecordKey, bool>

VotingRecordsV2

Records to track the voting power usage of each stake pool on each proposal.

struct VotingRecordsV2 has key
Fields
votes: smart_table::SmartTable<aptos_governance::RecordKey, u64>

ApprovedExecutionHashes

Used to track which execution script hashes have been approved by governance. This is required to bypass cases where the execution scripts exceed the size limit imposed by mempool.

struct ApprovedExecutionHashes has key
Fields
hashes: simple_map::SimpleMap<u64, vector<u8>>

GovernanceEvents

Events generated by interactions with the AptosGovernance module.

struct GovernanceEvents has key
Fields
create_proposal_events: event::EventHandle<aptos_governance::CreateProposalEvent>
update_config_events: event::EventHandle<aptos_governance::UpdateConfigEvent>
vote_events: event::EventHandle<aptos_governance::VoteEvent>

Functions

check_governance_permission

Permissions

fun check_governance_permission(s: &signer)
Implementation
inline fun check_governance_permission(s: &signer) {
assert!(
permissioned_signer::check_permission_exists(s, GovernancePermission {}),
error::permission_denied(ENO_GOVERNANCE_PERMISSION),
);
}

grant_permission

Grant permission to perform governance operations on behalf of the master signer.

public fun grant_permission(master: &signer, permissioned_signer: &signer)
Implementation
public fun grant_permission(master: &signer, permissioned_signer: &signer) {
permissioned_signer::authorize_unlimited(master, permissioned_signer, GovernancePermission {})
}

store_signer_cap

Can be called during genesis or by the governance itself. Stores the signer capability for a given address.

public fun store_signer_cap(aptos_framework: &signer, signer_address: address, signer_cap: account::SignerCapability)
Implementation
public fun store_signer_cap(
aptos_framework: &signer,
signer_address: address,
signer_cap: SignerCapability,
) acquires GovernanceResponsbility {
system_addresses::assert_aptos_framework(aptos_framework);
system_addresses::assert_framework_reserved(signer_address);
if (!exists<GovernanceResponsbility>(@aptos_framework)) {
move_to(
aptos_framework,
GovernanceResponsbility { signer_caps: simple_map::create<address, SignerCapability>() }
);
};
let signer_caps = &mut borrow_global_mut<GovernanceResponsbility>(@aptos_framework).signer_caps;
simple_map::add(signer_caps, signer_address, signer_cap);
}

initialize

Initializes the state for Aptos Governance. Can only be called during Genesis with a signer for the aptos_framework (0x1) account. This function is private because it’s called directly from the vm.

fun initialize(aptos_framework: &signer, min_voting_threshold: u128, required_proposer_stake: u64, voting_duration_secs: u64)
Implementation
fun initialize(
aptos_framework: &signer,
min_voting_threshold: u128,
required_proposer_stake: u64,
voting_duration_secs: u64,
) {
system_addresses::assert_aptos_framework(aptos_framework);
voting::register<GovernanceProposal>(aptos_framework);
initialize_partial_voting(aptos_framework);
move_to(aptos_framework, GovernanceConfig {
voting_duration_secs,
min_voting_threshold,
required_proposer_stake,
});
move_to(aptos_framework, GovernanceEvents {
create_proposal_events: account::new_event_handle<CreateProposalEvent>(aptos_framework),
update_config_events: account::new_event_handle<UpdateConfigEvent>(aptos_framework),
vote_events: account::new_event_handle<VoteEvent>(aptos_framework),
});
move_to(aptos_framework, VotingRecords {
votes: table::new(),
});
move_to(aptos_framework, ApprovedExecutionHashes {
hashes: simple_map::create<u64, vector<u8>>(),
})
}

update_governance_config

Update the governance configurations. This can only be called as part of resolving a proposal in this same AptosGovernance.

public fun update_governance_config(aptos_framework: &signer, min_voting_threshold: u128, required_proposer_stake: u64, voting_duration_secs: u64)
Implementation
public fun update_governance_config(
aptos_framework: &signer,
min_voting_threshold: u128,
required_proposer_stake: u64,
voting_duration_secs: u64,
) acquires GovernanceConfig, GovernanceEvents {
system_addresses::assert_aptos_framework(aptos_framework);
let governance_config = borrow_global_mut<GovernanceConfig>(@aptos_framework);
governance_config.voting_duration_secs = voting_duration_secs;
governance_config.min_voting_threshold = min_voting_threshold;
governance_config.required_proposer_stake = required_proposer_stake;
if (std::features::module_event_migration_enabled()) {
event::emit(
UpdateConfig {
min_voting_threshold,
required_proposer_stake,
voting_duration_secs
},
)
} else {
let events = borrow_global_mut<GovernanceEvents>(@aptos_framework);
event::emit_event<UpdateConfigEvent>(
&mut events.update_config_events,
UpdateConfigEvent {
min_voting_threshold,
required_proposer_stake,
voting_duration_secs
},
);
};
}

initialize_partial_voting

Initializes the state for Aptos Governance partial voting. Can only be called through Aptos governance proposals with a signer for the aptos_framework (0x1) account.

public fun initialize_partial_voting(aptos_framework: &signer)
Implementation
public fun initialize_partial_voting(
aptos_framework: &signer,
) {
system_addresses::assert_aptos_framework(aptos_framework);
move_to(aptos_framework, VotingRecordsV2 {
votes: smart_table::new(),
});
}

get_voting_duration_secs

#[view]
public fun get_voting_duration_secs(): u64
Implementation
public fun get_voting_duration_secs(): u64 acquires GovernanceConfig {
borrow_global<GovernanceConfig>(@aptos_framework).voting_duration_secs
}

get_min_voting_threshold

#[view]
public fun get_min_voting_threshold(): u128
Implementation
public fun get_min_voting_threshold(): u128 acquires GovernanceConfig {
borrow_global<GovernanceConfig>(@aptos_framework).min_voting_threshold
}

get_required_proposer_stake

#[view]
public fun get_required_proposer_stake(): u64
Implementation
public fun get_required_proposer_stake(): u64 acquires GovernanceConfig {
borrow_global<GovernanceConfig>(@aptos_framework).required_proposer_stake
}

has_entirely_voted

Return true if a stake pool has already voted on a proposal before partial governance voting is enabled.

#[view]
public fun has_entirely_voted(stake_pool: address, proposal_id: u64): bool
Implementation
public fun has_entirely_voted(stake_pool: address, proposal_id: u64): bool acquires VotingRecords {
let record_key = RecordKey {
stake_pool,
proposal_id,
};
// If a stake pool has already voted on a proposal before partial governance voting is enabled,
// there is a record in VotingRecords.
let voting_records = borrow_global<VotingRecords>(@aptos_framework);
table::contains(&voting_records.votes, record_key)
}

get_remaining_voting_power

Return remaining voting power of a stake pool on a proposal. Note: a stake pool’s voting power on a proposal could increase over time(e.g. rewards/new stake).

#[view]
public fun get_remaining_voting_power(stake_pool: address, proposal_id: u64): u64
Implementation
public fun get_remaining_voting_power(
stake_pool: address,
proposal_id: u64
): u64 acquires VotingRecords, VotingRecordsV2 {
assert_voting_initialization();
let proposal_expiration = voting::get_proposal_expiration_secs<GovernanceProposal>(
@aptos_framework,
proposal_id
);
let lockup_until = stake::get_lockup_secs(stake_pool);
// The voter's stake needs to be locked up at least as long as the proposal's expiration.
// Also no one can vote on a expired proposal.
if (proposal_expiration > lockup_until || timestamp::now_seconds() > proposal_expiration) {
return 0
};
// If a stake pool has already voted on a proposal before partial governance voting is enabled, the stake pool
// cannot vote on the proposal even after partial governance voting is enabled.
if (has_entirely_voted(stake_pool, proposal_id)) {
return 0
};
let record_key = RecordKey {
stake_pool,
proposal_id,
};
let used_voting_power = *VotingRecordsV2[@aptos_framework].votes.borrow_with_default(record_key, &0);
get_voting_power(stake_pool) - used_voting_power
}

assert_proposal_expiration

public fun assert_proposal_expiration(stake_pool: address, proposal_id: u64)
Implementation
public fun assert_proposal_expiration(stake_pool: address, proposal_id: u64) {
assert_voting_initialization();
let proposal_expiration = voting::get_proposal_expiration_secs<GovernanceProposal>(
@aptos_framework,
proposal_id
);
// The voter's stake needs to be locked up at least as long as the proposal's expiration.
assert!(
proposal_expiration <= stake::get_lockup_secs(stake_pool),
error::invalid_argument(EINSUFFICIENT_STAKE_LOCKUP),
);
assert!(
timestamp::now_seconds() <= proposal_expiration,
error::invalid_argument(EPROPOSAL_EXPIRED),
);
}

create_proposal

Create a single-step proposal with the backing stake_pool. @param execution_hash Required. This is the hash of the resolution script. When the proposal is resolved, only the exact script with matching hash can be successfully executed.

public entry fun create_proposal(proposer: &signer, stake_pool: address, execution_hash: vector<u8>, metadata_location: vector<u8>, metadata_hash: vector<u8>)
Implementation
public entry fun create_proposal(
proposer: &signer,
stake_pool: address,
execution_hash: vector<u8>,
metadata_location: vector<u8>,
metadata_hash: vector<u8>,
) acquires GovernanceConfig, GovernanceEvents {
create_proposal_v2(proposer, stake_pool, execution_hash, metadata_location, metadata_hash, false);
}

create_proposal_v2

Create a single-step or multi-step proposal with the backing stake_pool. @param execution_hash Required. This is the hash of the resolution script. When the proposal is resolved, only the exact script with matching hash can be successfully executed.

public entry fun create_proposal_v2(proposer: &signer, stake_pool: address, execution_hash: vector<u8>, metadata_location: vector<u8>, metadata_hash: vector<u8>, is_multi_step_proposal: bool)
Implementation
public entry fun create_proposal_v2(
proposer: &signer,
stake_pool: address,
execution_hash: vector<u8>,
metadata_location: vector<u8>,
metadata_hash: vector<u8>,
is_multi_step_proposal: bool,
) acquires GovernanceConfig, GovernanceEvents {
create_proposal_v2_impl(
proposer,
stake_pool,
execution_hash,
metadata_location,
metadata_hash,
is_multi_step_proposal
);
}

create_proposal_v2_impl

Create a single-step or multi-step proposal with the backing stake_pool. @param execution_hash Required. This is the hash of the resolution script. When the proposal is resolved, only the exact script with matching hash can be successfully executed. Return proposal_id when a proposal is successfully created.

public fun create_proposal_v2_impl(proposer: &signer, stake_pool: address, execution_hash: vector<u8>, metadata_location: vector<u8>, metadata_hash: vector<u8>, is_multi_step_proposal: bool): u64
Implementation
public fun create_proposal_v2_impl(
proposer: &signer,
stake_pool: address,
execution_hash: vector<u8>,
metadata_location: vector<u8>,
metadata_hash: vector<u8>,
is_multi_step_proposal: bool,
): u64 acquires GovernanceConfig, GovernanceEvents {
check_governance_permission(proposer);
let proposer_address = signer::address_of(proposer);
assert!(
stake::get_delegated_voter(stake_pool) == proposer_address,
error::invalid_argument(ENOT_DELEGATED_VOTER)
);
// The proposer's stake needs to be at least the required bond amount.
let governance_config = borrow_global<GovernanceConfig>(@aptos_framework);
let stake_balance = get_voting_power(stake_pool);
assert!(
stake_balance >= governance_config.required_proposer_stake,
error::invalid_argument(EINSUFFICIENT_PROPOSER_STAKE),
);
// The proposer's stake needs to be locked up at least as long as the proposal's voting period.
let current_time = timestamp::now_seconds();
let proposal_expiration = current_time + governance_config.voting_duration_secs;
assert!(
stake::get_lockup_secs(stake_pool) >= proposal_expiration,
error::invalid_argument(EINSUFFICIENT_STAKE_LOCKUP),
);
// Create and validate proposal metadata.
let proposal_metadata = create_proposal_metadata(metadata_location, metadata_hash);
// We want to allow early resolution of proposals if more than 50% of the total supply of the network coins
// has voted. This doesn't take into subsequent inflation/deflation (rewards are issued every epoch and gas fees
// are burnt after every transaction), but inflation/delation is very unlikely to have a major impact on total
// supply during the voting period.
let total_voting_token_supply = coin::supply<AptosCoin>();
let early_resolution_vote_threshold = option::none<u128>();
if (option::is_some(&total_voting_token_supply)) {
let total_supply = *option::borrow(&total_voting_token_supply);
// 50% + 1 to avoid rounding errors.
early_resolution_vote_threshold = option::some(total_supply / 2 + 1);
};
let proposal_id = voting::create_proposal_v2(
proposer_address,
@aptos_framework,
governance_proposal::create_proposal(),
execution_hash,
governance_config.min_voting_threshold,
proposal_expiration,
early_resolution_vote_threshold,
proposal_metadata,
is_multi_step_proposal,
);
if (std::features::module_event_migration_enabled()) {
event::emit(
CreateProposal {
proposal_id,
proposer: proposer_address,
stake_pool,
execution_hash,
proposal_metadata,
},
);
} else {
let events = borrow_global_mut<GovernanceEvents>(@aptos_framework);
event::emit_event<CreateProposalEvent>(
&mut events.create_proposal_events,
CreateProposalEvent {
proposal_id,
proposer: proposer_address,
stake_pool,
execution_hash,
proposal_metadata,
},
);
};
proposal_id
}

batch_vote

Vote on proposal with proposal_id and all voting power from multiple stake_pools.

public entry fun batch_vote(voter: &signer, stake_pools: vector<address>, proposal_id: u64, should_pass: bool)
Implementation
public entry fun batch_vote(
voter: &signer,
stake_pools: vector<address>,
proposal_id: u64,
should_pass: bool,
) acquires ApprovedExecutionHashes, VotingRecords, VotingRecordsV2, GovernanceEvents {
vector::for_each(stake_pools, |stake_pool| {
vote_internal(voter, stake_pool, proposal_id, MAX_U64, should_pass);
});
}

batch_partial_vote

Batch vote on proposal with proposal_id and specified voting power from multiple stake_pools.

public entry fun batch_partial_vote(voter: &signer, stake_pools: vector<address>, proposal_id: u64, voting_power: u64, should_pass: bool)
Implementation
public entry fun batch_partial_vote(
voter: &signer,
stake_pools: vector<address>,
proposal_id: u64,
voting_power: u64,
should_pass: bool,
) acquires ApprovedExecutionHashes, VotingRecords, VotingRecordsV2, GovernanceEvents {
vector::for_each(stake_pools, |stake_pool| {
vote_internal(voter, stake_pool, proposal_id, voting_power, should_pass);
});
}

vote

Vote on proposal with proposal_id and all voting power from stake_pool.

public entry fun vote(voter: &signer, stake_pool: address, proposal_id: u64, should_pass: bool)
Implementation
public entry fun vote(
voter: &signer,
stake_pool: address,
proposal_id: u64,
should_pass: bool,
) acquires ApprovedExecutionHashes, VotingRecords, VotingRecordsV2, GovernanceEvents {
vote_internal(voter, stake_pool, proposal_id, MAX_U64, should_pass);
}

partial_vote

Vote on proposal with proposal_id and specified voting power from stake_pool.

public entry fun partial_vote(voter: &signer, stake_pool: address, proposal_id: u64, voting_power: u64, should_pass: bool)
Implementation
public entry fun partial_vote(
voter: &signer,
stake_pool: address,
proposal_id: u64,
voting_power: u64,
should_pass: bool,
) acquires ApprovedExecutionHashes, VotingRecords, VotingRecordsV2, GovernanceEvents {
vote_internal(voter, stake_pool, proposal_id, voting_power, should_pass);
}

vote_internal

Vote on proposal with proposal_id and specified voting_power from stake_pool. If voting_power is more than all the left voting power of stake_pool, use all the left voting power. If a stake pool has already voted on a proposal before partial governance voting is enabled, the stake pool cannot vote on the proposal even after partial governance voting is enabled.

fun vote_internal(voter: &signer, stake_pool: address, proposal_id: u64, voting_power: u64, should_pass: bool)
Implementation
fun vote_internal(
voter: &signer,
stake_pool: address,
proposal_id: u64,
voting_power: u64,
should_pass: bool,
) acquires ApprovedExecutionHashes, VotingRecords, VotingRecordsV2, GovernanceEvents {
permissioned_signer::assert_master_signer(voter);
let voter_address = signer::address_of(voter);
assert!(stake::get_delegated_voter(stake_pool) == voter_address, error::invalid_argument(ENOT_DELEGATED_VOTER));
assert_proposal_expiration(stake_pool, proposal_id);
// If a stake pool has already voted on a proposal before partial governance voting is enabled,
// `get_remaining_voting_power` returns 0.
let staking_pool_voting_power = get_remaining_voting_power(stake_pool, proposal_id);
voting_power = min(voting_power, staking_pool_voting_power);
// Short-circuit if the voter has no voting power.
assert!(voting_power > 0, error::invalid_argument(ENO_VOTING_POWER));
voting::vote<GovernanceProposal>(
&governance_proposal::create_empty_proposal(),
@aptos_framework,
proposal_id,
voting_power,
should_pass,
);
let record_key = RecordKey {
stake_pool,
proposal_id,
};
let used_voting_power = VotingRecordsV2[@aptos_framework].votes.borrow_mut_with_default(record_key, 0);
// This calculation should never overflow because the used voting cannot exceed the total voting power of this stake pool.
*used_voting_power += voting_power;
if (std::features::module_event_migration_enabled()) {
event::emit(
Vote {
proposal_id,
voter: voter_address,
stake_pool,
num_votes: voting_power,
should_pass,
},
);
} else {
let events = &mut GovernanceEvents[@aptos_framework];
event::emit_event(
&mut events.vote_events,
VoteEvent {
proposal_id,
voter: voter_address,
stake_pool,
num_votes: voting_power,
should_pass,
},
);
};
let proposal_state = voting::get_proposal_state<GovernanceProposal>(@aptos_framework, proposal_id);
if (proposal_state == PROPOSAL_STATE_SUCCEEDED) {
add_approved_script_hash(proposal_id);
}
}

add_approved_script_hash_script

public entry fun add_approved_script_hash_script(proposal_id: u64)
Implementation
public entry fun add_approved_script_hash_script(proposal_id: u64) acquires ApprovedExecutionHashes {
add_approved_script_hash(proposal_id)
}

add_approved_script_hash

Add the execution script hash of a successful governance proposal to the approved list. This is needed to bypass the mempool transaction size limit for approved governance proposal transactions that are too large (e.g. module upgrades).

public fun add_approved_script_hash(proposal_id: u64)
Implementation
public fun add_approved_script_hash(proposal_id: u64) acquires ApprovedExecutionHashes {
let approved_hashes = borrow_global_mut<ApprovedExecutionHashes>(@aptos_framework);
// Ensure the proposal can be resolved.
let proposal_state = voting::get_proposal_state<GovernanceProposal>(@aptos_framework, proposal_id);
assert!(proposal_state == PROPOSAL_STATE_SUCCEEDED, error::invalid_argument(EPROPOSAL_NOT_RESOLVABLE_YET));
let execution_hash = voting::get_execution_hash<GovernanceProposal>(@aptos_framework, proposal_id);
// If this is a multi-step proposal, the proposal id will already exist in the ApprovedExecutionHashes map.
// We will update execution hash in ApprovedExecutionHashes to be the next_execution_hash.
if (simple_map::contains_key(&approved_hashes.hashes, &proposal_id)) {
let current_execution_hash = simple_map::borrow_mut(&mut approved_hashes.hashes, &proposal_id);
*current_execution_hash = execution_hash;
} else {
simple_map::add(&mut approved_hashes.hashes, proposal_id, execution_hash);
}
}

resolve

Resolve a successful single-step proposal. This would fail if the proposal is not successful (not enough votes or more no than yes).

public fun resolve(proposal_id: u64, signer_address: address): signer
Implementation
public fun resolve(
proposal_id: u64,
signer_address: address
): signer acquires ApprovedExecutionHashes, GovernanceResponsbility {
voting::resolve<GovernanceProposal>(@aptos_framework, proposal_id);
remove_approved_hash(proposal_id);
get_signer(signer_address)
}

resolve_multi_step_proposal

Resolve a successful multi-step proposal. This would fail if the proposal is not successful.

public fun resolve_multi_step_proposal(proposal_id: u64, signer_address: address, next_execution_hash: vector<u8>): signer
Implementation
public fun resolve_multi_step_proposal(
proposal_id: u64,
signer_address: address,
next_execution_hash: vector<u8>
): signer acquires GovernanceResponsbility, ApprovedExecutionHashes {
voting::resolve_proposal_v2<GovernanceProposal>(@aptos_framework, proposal_id, next_execution_hash);
// If the current step is the last step of this multi-step proposal,
// we will remove the execution hash from the ApprovedExecutionHashes map.
if (vector::length(&next_execution_hash) == 0) {
remove_approved_hash(proposal_id);
} else {
// If the current step is not the last step of this proposal,
// we replace the current execution hash with the next execution hash
// in the ApprovedExecutionHashes map.
add_approved_script_hash(proposal_id)
};
get_signer(signer_address)
}

remove_approved_hash

Remove an approved proposal’s execution script hash.

public fun remove_approved_hash(proposal_id: u64)
Implementation
public fun remove_approved_hash(proposal_id: u64) acquires ApprovedExecutionHashes {
assert!(
voting::is_resolved<GovernanceProposal>(@aptos_framework, proposal_id),
error::invalid_argument(EPROPOSAL_NOT_RESOLVED_YET),
);
let approved_hashes = &mut borrow_global_mut<ApprovedExecutionHashes>(@aptos_framework).hashes;
if (simple_map::contains_key(approved_hashes, &proposal_id)) {
simple_map::remove(approved_hashes, &proposal_id);
};
}

reconfigure

Manually reconfigure. Called at the end of a governance txn that alters on-chain configs.

WARNING: this function always ensures a reconfiguration starts, but when the reconfiguration finishes depends.

  • If feature RECONFIGURE_WITH_DKG is disabled, it finishes immediately.
  • At the end of the calling transaction, we will be in a new epoch.
  • If feature RECONFIGURE_WITH_DKG is enabled, it starts DKG, and the new epoch will start in a block prologue after DKG finishes.

This behavior affects when an update of an on-chain config (e.g. ConsensusConfig, Features) takes effect, since such updates are applied whenever we enter an new epoch.

public entry fun reconfigure(aptos_framework: &signer)
Implementation
public entry fun reconfigure(aptos_framework: &signer) {
system_addresses::assert_aptos_framework(aptos_framework);
if (consensus_config::validator_txn_enabled() && randomness_config::enabled()) {
reconfiguration_with_dkg::try_start();
} else {
reconfiguration_with_dkg::finish(aptos_framework);
}
}

force_end_epoch

Change epoch immediately. If RECONFIGURE_WITH_DKG is enabled and we are in the middle of a DKG, stop waiting for DKG and enter the new epoch without randomness.

WARNING: currently only used by tests. In most cases you should use reconfigure() instead. TODO: migrate these tests to be aware of async reconfiguration.

public entry fun force_end_epoch(aptos_framework: &signer)
Implementation
public entry fun force_end_epoch(aptos_framework: &signer) {
system_addresses::assert_aptos_framework(aptos_framework);
reconfiguration_with_dkg::finish(aptos_framework);
}

force_end_epoch_test_only

force_end_epoch() equivalent but only called in testnet, where the core resources account exists and has been granted power to mint Aptos coins.

public entry fun force_end_epoch_test_only(aptos_framework: &signer)
Implementation
public entry fun force_end_epoch_test_only(aptos_framework: &signer) acquires GovernanceResponsbility {
let core_signer = get_signer_testnet_only(aptos_framework, @0x1);
system_addresses::assert_aptos_framework(&core_signer);
reconfiguration_with_dkg::finish(&core_signer);
}

toggle_features

Update feature flags and also trigger reconfiguration.

public fun toggle_features(aptos_framework: &signer, enable: vector<u64>, disable: vector<u64>)
Implementation
public fun toggle_features(aptos_framework: &signer, enable: vector<u64>, disable: vector<u64>) {
system_addresses::assert_aptos_framework(aptos_framework);
features::change_feature_flags_for_next_epoch(aptos_framework, enable, disable);
reconfigure(aptos_framework);
}

get_signer_testnet_only

Only called in testnet where the core resources account exists and has been granted power to mint Aptos coins.

public fun get_signer_testnet_only(core_resources: &signer, signer_address: address): signer
Implementation
public fun get_signer_testnet_only(
core_resources: &signer, signer_address: address): signer acquires GovernanceResponsbility {
system_addresses::assert_core_resource(core_resources);
// Core resources account only has mint capability in tests/testnets.
assert!(aptos_coin::has_mint_capability(core_resources), error::unauthenticated(EUNAUTHORIZED));
get_signer(signer_address)
}

get_voting_power

Return the voting power a stake pool has with respect to governance proposals.

#[view]
public fun get_voting_power(pool_address: address): u64
Implementation
public fun get_voting_power(pool_address: address): u64 {
let allow_validator_set_change = staking_config::get_allow_validator_set_change(&staking_config::get());
if (allow_validator_set_change) {
let (active, _, pending_active, pending_inactive) = stake::get_stake(pool_address);
// We calculate the voting power as total non-inactive stakes of the pool. Even if the validator is not in the
// active validator set, as long as they have a lockup (separately checked in create_proposal and voting), their
// stake would still count in their voting power for governance proposals.
active + pending_active + pending_inactive
} else {
stake::get_current_epoch_voting_power(pool_address)
}
}

get_signer

Return a signer for making changes to 0x1 as part of on-chain governance proposal process.

fun get_signer(signer_address: address): signer
Implementation
fun get_signer(signer_address: address): signer acquires GovernanceResponsbility {
let governance_responsibility = borrow_global<GovernanceResponsbility>(@aptos_framework);
let signer_cap = simple_map::borrow(&governance_responsibility.signer_caps, &signer_address);
create_signer_with_capability(signer_cap)
}

create_proposal_metadata

fun create_proposal_metadata(metadata_location: vector<u8>, metadata_hash: vector<u8>): simple_map::SimpleMap<string::String, vector<u8>>
Implementation
fun create_proposal_metadata(
metadata_location: vector<u8>,
metadata_hash: vector<u8>
): SimpleMap<String, vector<u8>> {
assert!(string::length(&utf8(metadata_location)) <= 256, error::invalid_argument(EMETADATA_LOCATION_TOO_LONG));
assert!(string::length(&utf8(metadata_hash)) <= 256, error::invalid_argument(EMETADATA_HASH_TOO_LONG));
let metadata = simple_map::create<String, vector<u8>>();
simple_map::add(&mut metadata, utf8(METADATA_LOCATION_KEY), metadata_location);
simple_map::add(&mut metadata, utf8(METADATA_HASH_KEY), metadata_hash);
metadata
}

assert_voting_initialization

fun assert_voting_initialization()
Implementation
fun assert_voting_initialization() {
assert!(exists<VotingRecordsV2>(@aptos_framework), error::invalid_state(EPARTIAL_VOTING_NOT_INITIALIZED));
}

Specification

High-level Requirements

No.RequirementCriticalityImplementationEnforcement
1 The create proposal function calls create proposal v2. Low The create_proposal function internally calls create_proposal_v2. This is manually audited to ensure create_proposal_v2 is called in create_proposal.
2 The proposer must have a stake equal to or greater than the required bond amount. High The create_proposal_v2 function verifies that the stake balance equals or exceeds the required proposer stake amount. Formally verified in CreateProposalAbortsIf.
3 The Approved execution hashes resources that exist when the vote function is called. Low The Vote function acquires the Approved execution hashes resources. Formally verified in VoteAbortIf.
4 The execution script hash of a successful governance proposal is added to the approved list if the proposal can be resolved. Medium The add_approved_script_hash function asserts that proposal_state == PROPOSAL_STATE_SUCCEEDED. Formally verified in AddApprovedScriptHash.

Module-level Specification

pragma verify = false;
pragma aborts_if_is_partial;
schema AbortsIfPermissionedSigner {
s: signer;
let perm = GovernancePermission {};
aborts_if !permissioned_signer::spec_check_permission_exists(s, perm);
}

store_signer_cap

public fun store_signer_cap(aptos_framework: &signer, signer_address: address, signer_cap: account::SignerCapability)
aborts_if !system_addresses::is_aptos_framework_address(signer::address_of(aptos_framework));
aborts_if !system_addresses::is_framework_reserved_address(signer_address);
let signer_caps = global<GovernanceResponsbility>(@aptos_framework).signer_caps;
aborts_if exists<GovernanceResponsbility>(@aptos_framework) &&
simple_map::spec_contains_key(signer_caps, signer_address);
ensures exists<GovernanceResponsbility>(@aptos_framework);
let post post_signer_caps = global<GovernanceResponsbility>(@aptos_framework).signer_caps;
ensures simple_map::spec_contains_key(post_signer_caps, signer_address);

initialize

fun initialize(aptos_framework: &signer, min_voting_threshold: u128, required_proposer_stake: u64, voting_duration_secs: u64)

Signer address must be @aptos_framework. The signer does not allow these resources (GovernanceProposal, GovernanceConfig, GovernanceEvents, VotingRecords, ApprovedExecutionHashes) to exist. The signer must have an Account. Limit addition overflow.

pragma aborts_if_is_partial;
let addr = signer::address_of(aptos_framework);
let register_account = global<account::Account>(addr);
aborts_if exists<voting::VotingForum<GovernanceProposal>>(addr);
aborts_if !type_info::spec_is_struct<GovernanceProposal>();
include InitializeAbortIf;
ensures exists<voting::VotingForum<governance_proposal::GovernanceProposal>>(addr);
ensures exists<GovernanceConfig>(addr);
ensures exists<GovernanceEvents>(addr);
ensures exists<VotingRecords>(addr);
ensures exists<ApprovedExecutionHashes>(addr);
ensures exists<VotingRecordsV2>(addr);

update_governance_config

public fun update_governance_config(aptos_framework: &signer, min_voting_threshold: u128, required_proposer_stake: u64, voting_duration_secs: u64)

Signer address must be @aptos_framework. Address @aptos_framework must exist GovernanceConfig and GovernanceEvents.

let addr = signer::address_of(aptos_framework);
let governance_config = global<GovernanceConfig>(@aptos_framework);
let post new_governance_config = global<GovernanceConfig>(@aptos_framework);
aborts_if addr != @aptos_framework;
aborts_if !exists<GovernanceConfig>(@aptos_framework);
aborts_if !features::spec_is_enabled(features::MODULE_EVENT_MIGRATION) && !exists<GovernanceEvents>(
@aptos_framework
);
modifies global<GovernanceConfig>(addr);
ensures new_governance_config.voting_duration_secs == voting_duration_secs;
ensures new_governance_config.min_voting_threshold == min_voting_threshold;
ensures new_governance_config.required_proposer_stake == required_proposer_stake;

initialize_partial_voting

public fun initialize_partial_voting(aptos_framework: &signer)

Signer address must be @aptos_framework. Abort if structs have already been created.

let addr = signer::address_of(aptos_framework);
aborts_if addr != @aptos_framework;
aborts_if exists<VotingRecordsV2>(@aptos_framework);
ensures exists<VotingRecordsV2>(@aptos_framework);
schema InitializeAbortIf {
aptos_framework: &signer;
min_voting_threshold: u128;
required_proposer_stake: u64;
voting_duration_secs: u64;
let addr = signer::address_of(aptos_framework);
let account = global<account::Account>(addr);
aborts_if addr != @aptos_framework;
aborts_if exists<voting::VotingForum<governance_proposal::GovernanceProposal>>(addr);
aborts_if exists<GovernanceConfig>(addr);
aborts_if exists<GovernanceEvents>(addr);
aborts_if exists<VotingRecords>(addr);
aborts_if exists<ApprovedExecutionHashes>(addr);
aborts_if exists<VotingRecordsV2>(addr);
}

get_voting_duration_secs

#[view]
public fun get_voting_duration_secs(): u64
include AbortsIfNotGovernanceConfig;

get_min_voting_threshold

#[view]
public fun get_min_voting_threshold(): u128
include AbortsIfNotGovernanceConfig;

get_required_proposer_stake

#[view]
public fun get_required_proposer_stake(): u64
include AbortsIfNotGovernanceConfig;
schema AbortsIfNotGovernanceConfig {
aborts_if !exists<GovernanceConfig>(@aptos_framework);
}

has_entirely_voted

#[view]
public fun has_entirely_voted(stake_pool: address, proposal_id: u64): bool
aborts_if !exists<VotingRecords>(@aptos_framework);

get_remaining_voting_power

#[view]
public fun get_remaining_voting_power(stake_pool: address, proposal_id: u64): u64
aborts_if !exists<VotingRecordsV2>(@aptos_framework);
include voting::AbortsIfNotContainProposalID<GovernanceProposal> {
voting_forum_address: @aptos_framework
};
aborts_if !exists<stake::StakePool>(stake_pool);
aborts_if spec_proposal_expiration <= locked_until && !exists<timestamp::CurrentTimeMicroseconds>(@aptos_framework);
let spec_proposal_expiration = voting::spec_get_proposal_expiration_secs<GovernanceProposal>(@aptos_framework, proposal_id);
let locked_until = global<stake::StakePool>(stake_pool).locked_until_secs;
let remain_zero_1_cond = (spec_proposal_expiration > locked_until || timestamp::spec_now_seconds() > spec_proposal_expiration);
ensures remain_zero_1_cond ==> result == 0;
let record_key = RecordKey {
stake_pool,
proposal_id,
};
let entirely_voted = spec_has_entirely_voted(stake_pool, proposal_id, record_key);
aborts_if !remain_zero_1_cond && !exists<VotingRecords>(@aptos_framework);
include !remain_zero_1_cond && !entirely_voted ==> GetVotingPowerAbortsIf {
pool_address: stake_pool
};
let staking_config = global<staking_config::StakingConfig>(@aptos_framework);
let voting_power = spec_get_voting_power(stake_pool, staking_config);
let voting_records_v2 = borrow_global<VotingRecordsV2>(@aptos_framework);
let used_voting_power = if (smart_table::spec_contains(voting_records_v2.votes, record_key)) {
smart_table::spec_get(voting_records_v2.votes, record_key)
} else {
0
};
aborts_if !remain_zero_1_cond && !entirely_voted && used_voting_power > 0 && voting_power < used_voting_power;
ensures result == spec_get_remaining_voting_power(stake_pool, proposal_id);
fun spec_get_remaining_voting_power(stake_pool: address, proposal_id: u64): u64 {
let spec_proposal_expiration = voting::spec_get_proposal_expiration_secs<GovernanceProposal>(@aptos_framework, proposal_id);
let locked_until = global<stake::StakePool>(stake_pool).locked_until_secs;
let remain_zero_1_cond = (spec_proposal_expiration > locked_until || timestamp::spec_now_seconds() > spec_proposal_expiration);
let staking_config = global<staking_config::StakingConfig>(@aptos_framework);
let voting_records_v2 = borrow_global<VotingRecordsV2>(@aptos_framework);
let record_key = RecordKey {
stake_pool,
proposal_id,
};
let entirely_voted = spec_has_entirely_voted(stake_pool, proposal_id, record_key);
let voting_power = spec_get_voting_power(stake_pool, staking_config);
let used_voting_power = if (smart_table::spec_contains(voting_records_v2.votes, record_key)) {
smart_table::spec_get(voting_records_v2.votes, record_key)
} else {
0
};
if (remain_zero_1_cond) {
0
} else if (entirely_voted) {
0
} else {
voting_power - used_voting_power
}
}
fun spec_has_entirely_voted(stake_pool: address, proposal_id: u64, record_key: RecordKey): bool {
let voting_records = global<VotingRecords>(@aptos_framework);
table::spec_contains(voting_records.votes, record_key)
}
schema GetVotingPowerAbortsIf {
pool_address: address;
let staking_config = global<staking_config::StakingConfig>(@aptos_framework);
aborts_if !exists<staking_config::StakingConfig>(@aptos_framework);
let allow_validator_set_change = staking_config.allow_validator_set_change;
let stake_pool_res = global<stake::StakePool>(pool_address);
aborts_if allow_validator_set_change && (stake_pool_res.active.value + stake_pool_res.pending_active.value + stake_pool_res.pending_inactive.value) > MAX_U64;
aborts_if !exists<stake::StakePool>(pool_address);
aborts_if !allow_validator_set_change && !exists<stake::ValidatorSet>(@aptos_framework);
aborts_if !allow_validator_set_change && stake::spec_is_current_epoch_validator(pool_address) && stake_pool_res.active.value + stake_pool_res.pending_inactive.value > MAX_U64;
}

assert_proposal_expiration

public fun assert_proposal_expiration(stake_pool: address, proposal_id: u64)
include VotingInitializationAbortIfs;
include voting::AbortsIfNotContainProposalID<GovernanceProposal>{voting_forum_address: @aptos_framework};
let proposal_expiration = voting::spec_get_proposal_expiration_secs<GovernanceProposal>(@aptos_framework, proposal_id);
aborts_if !stake::stake_pool_exists(stake_pool);
aborts_if proposal_expiration > stake::spec_get_lockup_secs(stake_pool);
aborts_if !exists<timestamp::CurrentTimeMicroseconds>(@aptos_framework);
aborts_if timestamp::now_seconds() > proposal_expiration;

create_proposal

public entry fun create_proposal(proposer: &signer, stake_pool: address, execution_hash: vector<u8>, metadata_location: vector<u8>, metadata_hash: vector<u8>)

The same as spec of create_proposal_v2().

pragma verify_duration_estimate = 60;
requires chain_status::is_operating();
include CreateProposalAbortsIf;

create_proposal_v2

public entry fun create_proposal_v2(proposer: &signer, stake_pool: address, execution_hash: vector<u8>, metadata_location: vector<u8>, metadata_hash: vector<u8>, is_multi_step_proposal: bool)
pragma verify_duration_estimate = 60;
requires chain_status::is_operating();
include CreateProposalAbortsIf;

create_proposal_v2_impl

public fun create_proposal_v2_impl(proposer: &signer, stake_pool: address, execution_hash: vector<u8>, metadata_location: vector<u8>, metadata_hash: vector<u8>, is_multi_step_proposal: bool): u64
pragma verify_duration_estimate = 60;
requires chain_status::is_operating();
include CreateProposalAbortsIf;

stake_pool must exist StakePool. The delegated voter under the resource StakePool of the stake_pool must be the proposer address. Address @aptos_framework must exist GovernanceEvents.

schema CreateProposalAbortsIf {
proposer: &signer;
stake_pool: address;
execution_hash: vector<u8>;
metadata_location: vector<u8>;
metadata_hash: vector<u8>;
include VotingGetDelegatedVoterAbortsIf { sign: proposer };
include AbortsIfNotGovernanceConfig;
include GetVotingPowerAbortsIf { pool_address: stake_pool };
let staking_config = global<staking_config::StakingConfig>(@aptos_framework);
let allow_validator_set_change = staking_config.allow_validator_set_change;
let stake_pool_res = global<stake::StakePool>(stake_pool);
let stake_balance_0 = stake_pool_res.active.value + stake_pool_res.pending_active.value + stake_pool_res.pending_inactive.value;
let stake_balance_1 = stake_pool_res.active.value + stake_pool_res.pending_inactive.value;
let stake_balance_2 = 0;
let governance_config = global<GovernanceConfig>(@aptos_framework);
let required_proposer_stake = governance_config.required_proposer_stake;
// This enforces high-level requirement 2:
aborts_if allow_validator_set_change && stake_balance_0 < required_proposer_stake;
aborts_if !allow_validator_set_change && stake::spec_is_current_epoch_validator(stake_pool) && stake_balance_1 < required_proposer_stake;
aborts_if !allow_validator_set_change && !stake::spec_is_current_epoch_validator(stake_pool) && stake_balance_2 < required_proposer_stake;
aborts_if !exists<timestamp::CurrentTimeMicroseconds>(@aptos_framework);
let current_time = timestamp::spec_now_seconds();
let proposal_expiration = current_time + governance_config.voting_duration_secs;
aborts_if stake_pool_res.locked_until_secs < proposal_expiration;
include CreateProposalMetadataAbortsIf;
let addr = aptos_std::type_info::type_of<AptosCoin>().account_address;
aborts_if !exists<coin::CoinInfo<AptosCoin>>(addr);
let maybe_supply = global<coin::CoinInfo<AptosCoin>>(addr).supply;
let supply = option::spec_borrow(maybe_supply);
let total_supply = aptos_framework::optional_aggregator::optional_aggregator_value(supply);
let early_resolution_vote_threshold_value = total_supply / 2 + 1;
aborts_if option::spec_is_some(maybe_supply) && governance_config.min_voting_threshold > early_resolution_vote_threshold_value;
aborts_if len(execution_hash) == 0;
aborts_if !exists<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let proposal_id = voting_forum.next_proposal_id;
aborts_if proposal_id + 1 > MAX_U64;
let post post_voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let post post_next_proposal_id = post_voting_forum.next_proposal_id;
ensures post_next_proposal_id == proposal_id + 1;
aborts_if !string::spec_internal_check_utf8(voting::IS_MULTI_STEP_PROPOSAL_KEY);
aborts_if !string::spec_internal_check_utf8(voting::IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY);
aborts_if table::spec_contains(voting_forum.proposals,proposal_id);
ensures table::spec_contains(post_voting_forum.proposals, proposal_id);
aborts_if !exists<GovernanceEvents>(@aptos_framework);
}
schema VotingGetDelegatedVoterAbortsIf {
stake_pool: address;
sign: signer;
let addr = signer::address_of(sign);
let stake_pool_res = global<stake::StakePool>(stake_pool);
aborts_if !exists<stake::StakePool>(stake_pool);
aborts_if stake_pool_res.delegated_voter != addr;
}

batch_vote

public entry fun batch_vote(voter: &signer, stake_pools: vector<address>, proposal_id: u64, should_pass: bool)
pragma verify = false;

batch_partial_vote

public entry fun batch_partial_vote(voter: &signer, stake_pools: vector<address>, proposal_id: u64, voting_power: u64, should_pass: bool)
pragma verify = false;

vote

public entry fun vote(voter: &signer, stake_pool: address, proposal_id: u64, should_pass: bool)

stake_pool must exist StakePool. The delegated voter under the resource StakePool of the stake_pool must be the voter address. Address @aptos_framework must exist VotingRecords and GovernanceProposal.

pragma verify_duration_estimate = 60;
requires chain_status::is_operating();
include VoteAbortIf {
voting_power: MAX_U64
};

partial_vote

public entry fun partial_vote(voter: &signer, stake_pool: address, proposal_id: u64, voting_power: u64, should_pass: bool)

stake_pool must exist StakePool. The delegated voter under the resource StakePool of the stake_pool must be the voter address. Address @aptos_framework must exist VotingRecords and GovernanceProposal. Address @aptos_framework must exist VotingRecordsV2 if partial_governance_voting flag is enabled.

pragma verify_duration_estimate = 60;
requires chain_status::is_operating();
include VoteAbortIf;

vote_internal

fun vote_internal(voter: &signer, stake_pool: address, proposal_id: u64, voting_power: u64, should_pass: bool)

stake_pool must exist StakePool. The delegated voter under the resource StakePool of the stake_pool must be the voter address. Address @aptos_framework must exist VotingRecords and GovernanceProposal. Address @aptos_framework must exist VotingRecordsV2 if partial_governance_voting flag is enabled.

pragma verify_duration_estimate = 60;
requires chain_status::is_operating();
include VoteAbortIf;
schema VoteAbortIf {
voter: &signer;
stake_pool: address;
proposal_id: u64;
should_pass: bool;
voting_power: u64;
include VotingGetDelegatedVoterAbortsIf { sign: voter };
aborts_if spec_proposal_expiration <= locked_until && !exists<timestamp::CurrentTimeMicroseconds>(@aptos_framework);
let spec_proposal_expiration = voting::spec_get_proposal_expiration_secs<GovernanceProposal>(@aptos_framework, proposal_id);
let locked_until = global<stake::StakePool>(stake_pool).locked_until_secs;
let remain_zero_1_cond = (spec_proposal_expiration > locked_until || timestamp::spec_now_seconds() > spec_proposal_expiration);
let record_key = RecordKey {
stake_pool,
proposal_id,
};
let entirely_voted = spec_has_entirely_voted(stake_pool, proposal_id, record_key);
aborts_if !remain_zero_1_cond && !exists<VotingRecords>(@aptos_framework);
include !remain_zero_1_cond && !entirely_voted ==> GetVotingPowerAbortsIf {
pool_address: stake_pool
};
let staking_config = global<staking_config::StakingConfig>(@aptos_framework);
let spec_voting_power = spec_get_voting_power(stake_pool, staking_config);
let voting_records_v2 = borrow_global<VotingRecordsV2>(@aptos_framework);
let used_voting_power = if (smart_table::spec_contains(voting_records_v2.votes, record_key)) {
smart_table::spec_get(voting_records_v2.votes, record_key)
} else {
0
};
aborts_if !remain_zero_1_cond && !entirely_voted && used_voting_power > 0 && spec_voting_power < used_voting_power;
let remaining_power = spec_get_remaining_voting_power(stake_pool, proposal_id);
let real_voting_power = min(voting_power, remaining_power);
aborts_if !(real_voting_power > 0);
aborts_if !exists<VotingRecords>(@aptos_framework);
let voting_records = global<VotingRecords>(@aptos_framework);
let allow_validator_set_change = global<staking_config::StakingConfig>(@aptos_framework).allow_validator_set_change;
let stake_pool_res = global<stake::StakePool>(stake_pool);
aborts_if !exists<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let proposal = table::spec_get(voting_forum.proposals, proposal_id);
aborts_if !table::spec_contains(voting_forum.proposals, proposal_id);
let proposal_expiration = proposal.expiration_secs;
let locked_until_secs = global<stake::StakePool>(stake_pool).locked_until_secs;
aborts_if proposal_expiration > locked_until_secs;
aborts_if timestamp::now_seconds() > proposal_expiration;
aborts_if proposal.is_resolved;
aborts_if !string::spec_internal_check_utf8(voting::IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY);
let execution_key = utf8(voting::IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY);
aborts_if simple_map::spec_contains_key(proposal.metadata, execution_key) &&
simple_map::spec_get(proposal.metadata, execution_key) != std::bcs::to_bytes(false);
aborts_if
if (should_pass) { proposal.yes_votes + real_voting_power > MAX_U128 } else { proposal.no_votes + real_voting_power > MAX_U128 };
let post post_voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let post post_proposal = table::spec_get(post_voting_forum.proposals, proposal_id);
aborts_if !string::spec_internal_check_utf8(voting::RESOLVABLE_TIME_METADATA_KEY);
let key = utf8(voting::RESOLVABLE_TIME_METADATA_KEY);
ensures simple_map::spec_contains_key(post_proposal.metadata, key);
ensures simple_map::spec_get(post_proposal.metadata, key) == std::bcs::to_bytes(timestamp::now_seconds());
aborts_if used_voting_power + real_voting_power > MAX_U64;
aborts_if !exists<GovernanceEvents>(@aptos_framework);
let early_resolution_threshold = option::spec_borrow(proposal.early_resolution_vote_threshold);
let is_voting_period_over = timestamp::spec_now_seconds() > proposal_expiration;
let new_proposal_yes_votes_0 = proposal.yes_votes + real_voting_power;
let can_be_resolved_early_0 = option::spec_is_some(proposal.early_resolution_vote_threshold) &&
(new_proposal_yes_votes_0 >= early_resolution_threshold ||
proposal.no_votes >= early_resolution_threshold);
let is_voting_closed_0 = is_voting_period_over || can_be_resolved_early_0;
let proposal_state_successed_0 = is_voting_closed_0 && new_proposal_yes_votes_0 > proposal.no_votes &&
new_proposal_yes_votes_0 + proposal.no_votes >= proposal.min_vote_threshold;
let new_proposal_no_votes_0 = proposal.no_votes + real_voting_power;
let can_be_resolved_early_1 = option::spec_is_some(proposal.early_resolution_vote_threshold) &&
(proposal.yes_votes >= early_resolution_threshold ||
new_proposal_no_votes_0 >= early_resolution_threshold);
let is_voting_closed_1 = is_voting_period_over || can_be_resolved_early_1;
let proposal_state_successed_1 = is_voting_closed_1 && proposal.yes_votes > new_proposal_no_votes_0 &&
proposal.yes_votes + new_proposal_no_votes_0 >= proposal.min_vote_threshold;
let new_proposal_yes_votes_1 = proposal.yes_votes + real_voting_power;
let can_be_resolved_early_2 = option::spec_is_some(proposal.early_resolution_vote_threshold) &&
(new_proposal_yes_votes_1 >= early_resolution_threshold ||
proposal.no_votes >= early_resolution_threshold);
let is_voting_closed_2 = is_voting_period_over || can_be_resolved_early_2;
let proposal_state_successed_2 = is_voting_closed_2 && new_proposal_yes_votes_1 > proposal.no_votes &&
new_proposal_yes_votes_1 + proposal.no_votes >= proposal.min_vote_threshold;
let new_proposal_no_votes_1 = proposal.no_votes + real_voting_power;
let can_be_resolved_early_3 = option::spec_is_some(proposal.early_resolution_vote_threshold) &&
(proposal.yes_votes >= early_resolution_threshold ||
new_proposal_no_votes_1 >= early_resolution_threshold);
let is_voting_closed_3 = is_voting_period_over || can_be_resolved_early_3;
let proposal_state_successed_3 = is_voting_closed_3 && proposal.yes_votes > new_proposal_no_votes_1 &&
proposal.yes_votes + new_proposal_no_votes_1 >= proposal.min_vote_threshold;
let post can_be_resolved_early = option::spec_is_some(proposal.early_resolution_vote_threshold) &&
(post_proposal.yes_votes >= early_resolution_threshold ||
post_proposal.no_votes >= early_resolution_threshold);
let post is_voting_closed = is_voting_period_over || can_be_resolved_early;
let post proposal_state_successed = is_voting_closed && post_proposal.yes_votes > post_proposal.no_votes &&
post_proposal.yes_votes + post_proposal.no_votes >= proposal.min_vote_threshold;
let execution_hash = proposal.execution_hash;
let post post_approved_hashes = global<ApprovedExecutionHashes>(@aptos_framework);
// This enforces high-level requirement 3:
aborts_if
if (should_pass) {
proposal_state_successed_0 && !exists<ApprovedExecutionHashes>(@aptos_framework)
} else {
proposal_state_successed_1 && !exists<ApprovedExecutionHashes>(@aptos_framework)
};
aborts_if
if (should_pass) {
proposal_state_successed_2 && !exists<ApprovedExecutionHashes>(@aptos_framework)
} else {
proposal_state_successed_3 && !exists<ApprovedExecutionHashes>(@aptos_framework)
};
ensures proposal_state_successed ==> simple_map::spec_contains_key(post_approved_hashes.hashes, proposal_id) &&
simple_map::spec_get(post_approved_hashes.hashes, proposal_id) == execution_hash;
aborts_if !exists<VotingRecordsV2>(@aptos_framework);
}

add_approved_script_hash_script

public entry fun add_approved_script_hash_script(proposal_id: u64)
requires chain_status::is_operating();
include AddApprovedScriptHash;
schema AddApprovedScriptHash {
proposal_id: u64;
aborts_if !exists<ApprovedExecutionHashes>(@aptos_framework);
aborts_if !exists<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let proposal = table::spec_get(voting_forum.proposals, proposal_id);
aborts_if !table::spec_contains(voting_forum.proposals, proposal_id);
let early_resolution_threshold = option::spec_borrow(proposal.early_resolution_vote_threshold);
aborts_if timestamp::now_seconds() <= proposal.expiration_secs &&
(option::spec_is_none(proposal.early_resolution_vote_threshold) ||
proposal.yes_votes < early_resolution_threshold && proposal.no_votes < early_resolution_threshold);
aborts_if (timestamp::now_seconds() > proposal.expiration_secs ||
option::spec_is_some(proposal.early_resolution_vote_threshold) && (proposal.yes_votes >= early_resolution_threshold ||
proposal.no_votes >= early_resolution_threshold)) &&
(proposal.yes_votes <= proposal.no_votes || proposal.yes_votes + proposal.no_votes < proposal.min_vote_threshold);
let post post_approved_hashes = global<ApprovedExecutionHashes>(@aptos_framework);
// This enforces high-level requirement 4:
ensures simple_map::spec_contains_key(post_approved_hashes.hashes, proposal_id) &&
simple_map::spec_get(post_approved_hashes.hashes, proposal_id) == proposal.execution_hash;
}

add_approved_script_hash

public fun add_approved_script_hash(proposal_id: u64)
requires chain_status::is_operating();
include AddApprovedScriptHash;

resolve

public fun resolve(proposal_id: u64, signer_address: address): signer

Address @aptos_framework must exist ApprovedExecutionHashes and GovernanceProposal and GovernanceResponsbility.

requires chain_status::is_operating();
include VotingIsProposalResolvableAbortsif;
let voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let proposal = table::spec_get(voting_forum.proposals, proposal_id);
let multi_step_key = utf8(voting::IS_MULTI_STEP_PROPOSAL_KEY);
let has_multi_step_key = simple_map::spec_contains_key(proposal.metadata, multi_step_key);
let is_multi_step_proposal = aptos_std::from_bcs::deserialize<bool>(simple_map::spec_get(proposal.metadata, multi_step_key));
aborts_if has_multi_step_key && !aptos_std::from_bcs::deserializable<bool>(simple_map::spec_get(proposal.metadata, multi_step_key));
aborts_if !string::spec_internal_check_utf8(voting::IS_MULTI_STEP_PROPOSAL_KEY);
aborts_if has_multi_step_key && is_multi_step_proposal;
let post post_voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let post post_proposal = table::spec_get(post_voting_forum.proposals, proposal_id);
ensures post_proposal.is_resolved == true && post_proposal.resolution_time_secs == timestamp::now_seconds();
aborts_if option::spec_is_none(proposal.execution_content);
aborts_if !exists<ApprovedExecutionHashes>(@aptos_framework);
let post post_approved_hashes = global<ApprovedExecutionHashes>(@aptos_framework).hashes;
ensures !simple_map::spec_contains_key(post_approved_hashes, proposal_id);
include GetSignerAbortsIf;
let governance_responsibility = global<GovernanceResponsbility>(@aptos_framework);
let signer_cap = simple_map::spec_get(governance_responsibility.signer_caps, signer_address);
let addr = signer_cap.account;
ensures signer::address_of(result) == addr;

resolve_multi_step_proposal

public fun resolve_multi_step_proposal(proposal_id: u64, signer_address: address, next_execution_hash: vector<u8>): signer
requires chain_status::is_operating();
pragma verify_duration_estimate = 120;
include VotingIsProposalResolvableAbortsif;
let voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let proposal = table::spec_get(voting_forum.proposals, proposal_id);
let post post_voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let post post_proposal = table::spec_get(post_voting_forum.proposals, proposal_id);
aborts_if !string::spec_internal_check_utf8(voting::IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY);
let multi_step_in_execution_key = utf8(voting::IS_MULTI_STEP_PROPOSAL_IN_EXECUTION_KEY);
let post is_multi_step_proposal_in_execution_value = simple_map::spec_get(post_proposal.metadata, multi_step_in_execution_key);
aborts_if !string::spec_internal_check_utf8(voting::IS_MULTI_STEP_PROPOSAL_KEY);
let multi_step_key = utf8(voting::IS_MULTI_STEP_PROPOSAL_KEY);
aborts_if simple_map::spec_contains_key(proposal.metadata, multi_step_key) &&
!aptos_std::from_bcs::deserializable<bool>(simple_map::spec_get(proposal.metadata, multi_step_key));
let is_multi_step = simple_map::spec_contains_key(proposal.metadata, multi_step_key) &&
aptos_std::from_bcs::deserialize<bool>(simple_map::spec_get(proposal.metadata, multi_step_key));
let next_execution_hash_is_empty = len(next_execution_hash) == 0;
aborts_if !is_multi_step && !next_execution_hash_is_empty;
aborts_if next_execution_hash_is_empty && is_multi_step && !simple_map::spec_contains_key(proposal.metadata, multi_step_in_execution_key);
ensures next_execution_hash_is_empty ==> post_proposal.is_resolved == true && post_proposal.resolution_time_secs == timestamp::spec_now_seconds() &&
if (is_multi_step) {
is_multi_step_proposal_in_execution_value == std::bcs::serialize(false)
} else {
simple_map::spec_contains_key(proposal.metadata, multi_step_in_execution_key) ==>
is_multi_step_proposal_in_execution_value == std::bcs::serialize(true)
};
ensures !next_execution_hash_is_empty ==> post_proposal.execution_hash == next_execution_hash;
aborts_if !exists<ApprovedExecutionHashes>(@aptos_framework);
let post post_approved_hashes = global<ApprovedExecutionHashes>(@aptos_framework).hashes;
ensures next_execution_hash_is_empty ==> !simple_map::spec_contains_key(post_approved_hashes, proposal_id);
ensures !next_execution_hash_is_empty ==>
simple_map::spec_get(post_approved_hashes, proposal_id) == next_execution_hash;
include GetSignerAbortsIf;
let governance_responsibility = global<GovernanceResponsbility>(@aptos_framework);
let signer_cap = simple_map::spec_get(governance_responsibility.signer_caps, signer_address);
let addr = signer_cap.account;
ensures signer::address_of(result) == addr;
schema VotingIsProposalResolvableAbortsif {
proposal_id: u64;
aborts_if !exists<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let proposal = table::spec_get(voting_forum.proposals, proposal_id);
aborts_if !table::spec_contains(voting_forum.proposals, proposal_id);
let early_resolution_threshold = option::spec_borrow(proposal.early_resolution_vote_threshold);
let voting_period_over = timestamp::now_seconds() > proposal.expiration_secs;
let be_resolved_early = option::spec_is_some(proposal.early_resolution_vote_threshold) &&
(proposal.yes_votes >= early_resolution_threshold ||
proposal.no_votes >= early_resolution_threshold);
let voting_closed = voting_period_over || be_resolved_early;
aborts_if voting_closed && (proposal.yes_votes <= proposal.no_votes || proposal.yes_votes + proposal.no_votes < proposal.min_vote_threshold);
aborts_if !voting_closed;
aborts_if proposal.is_resolved;
aborts_if !string::spec_internal_check_utf8(voting::RESOLVABLE_TIME_METADATA_KEY);
aborts_if !simple_map::spec_contains_key(proposal.metadata, utf8(voting::RESOLVABLE_TIME_METADATA_KEY));
let resolvable_time = aptos_std::from_bcs::deserialize<u64>(simple_map::spec_get(proposal.metadata, utf8(voting::RESOLVABLE_TIME_METADATA_KEY)));
aborts_if !aptos_std::from_bcs::deserializable<u64>(simple_map::spec_get(proposal.metadata, utf8(voting::RESOLVABLE_TIME_METADATA_KEY)));
aborts_if timestamp::now_seconds() <= resolvable_time;
aborts_if aptos_framework::transaction_context::spec_get_script_hash() != proposal.execution_hash;
}

remove_approved_hash

public fun remove_approved_hash(proposal_id: u64)

Address @aptos_framework must exist ApprovedExecutionHashes and GovernanceProposal.

aborts_if !exists<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
aborts_if !exists<ApprovedExecutionHashes>(@aptos_framework);
let voting_forum = global<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
aborts_if !table::spec_contains(voting_forum.proposals, proposal_id);
aborts_if !exists<voting::VotingForum<GovernanceProposal>>(@aptos_framework);
let proposal = table::spec_get(voting_forum.proposals, proposal_id);
aborts_if !proposal.is_resolved;
let post approved_hashes = global<ApprovedExecutionHashes>(@aptos_framework).hashes;
ensures !simple_map::spec_contains_key(approved_hashes, proposal_id);

reconfigure

public entry fun reconfigure(aptos_framework: &signer)
pragma verify = false;
aborts_if !system_addresses::is_aptos_framework_address(signer::address_of(aptos_framework));
include reconfiguration_with_dkg::FinishRequirement {
framework: aptos_framework
};
include stake::GetReconfigStartTimeRequirement;
requires chain_status::is_operating();
requires exists<CoinInfo<AptosCoin>>(@aptos_framework);
requires exists<staking_config::StakingRewardsConfig>(@aptos_framework);
include staking_config::StakingRewardsConfigRequirement;

force_end_epoch

public entry fun force_end_epoch(aptos_framework: &signer)
pragma verify = false;
let address = signer::address_of(aptos_framework);
include reconfiguration_with_dkg::FinishRequirement {
framework: aptos_framework
};
schema VotingInitializationAbortIfs {
aborts_if !exists<VotingRecordsV2>(@aptos_framework);
}

force_end_epoch_test_only

public entry fun force_end_epoch_test_only(aptos_framework: &signer)
pragma verify = false;

toggle_features

public fun toggle_features(aptos_framework: &signer, enable: vector<u64>, disable: vector<u64>)

Signer address must be @aptos_framework. Address @aptos_framework must exist GovernanceConfig and GovernanceEvents.

pragma verify = false;
let addr = signer::address_of(aptos_framework);
aborts_if addr != @aptos_framework;
include reconfiguration_with_dkg::FinishRequirement {
framework: aptos_framework
};
include stake::GetReconfigStartTimeRequirement;
requires chain_status::is_operating();
requires exists<CoinInfo<AptosCoin>>(@aptos_framework);
requires exists<staking_config::StakingRewardsConfig>(@aptos_framework);
include staking_config::StakingRewardsConfigRequirement;

get_signer_testnet_only

public fun get_signer_testnet_only(core_resources: &signer, signer_address: address): signer

Signer address must be @core_resources. signer must exist in MintCapStore. Address @aptos_framework must exist GovernanceResponsbility.

aborts_if signer::address_of(core_resources) != @core_resources;
aborts_if !exists<aptos_coin::MintCapStore>(signer::address_of(core_resources));
include GetSignerAbortsIf;

get_voting_power

#[view]
public fun get_voting_power(pool_address: address): u64

Address @aptos_framework must exist StakingConfig. limit addition overflow. pool_address must exist in StakePool.

include GetVotingPowerAbortsIf;
let staking_config = global<staking_config::StakingConfig>(@aptos_framework);
let allow_validator_set_change = staking_config.allow_validator_set_change;
let stake_pool_res = global<stake::StakePool>(pool_address);
ensures allow_validator_set_change ==> result == stake_pool_res.active.value + stake_pool_res.pending_active.value + stake_pool_res.pending_inactive.value;
ensures !allow_validator_set_change ==> if (stake::spec_is_current_epoch_validator(pool_address)) {
result == stake_pool_res.active.value + stake_pool_res.pending_inactive.value
} else {
result == 0
};
ensures result == spec_get_voting_power(pool_address, staking_config);
fun spec_get_voting_power(pool_address: address, staking_config: staking_config::StakingConfig): u64 {
let allow_validator_set_change = staking_config.allow_validator_set_change;
let stake_pool_res = global<stake::StakePool>(pool_address);
if (allow_validator_set_change) {
stake_pool_res.active.value + stake_pool_res.pending_active.value + stake_pool_res.pending_inactive.value
} else if (!allow_validator_set_change && (stake::spec_is_current_epoch_validator(pool_address))) {
stake_pool_res.active.value + stake_pool_res.pending_inactive.value
} else {
0
}
}

get_signer

fun get_signer(signer_address: address): signer
include GetSignerAbortsIf;
schema GetSignerAbortsIf {
signer_address: address;
aborts_if !exists<GovernanceResponsbility>(@aptos_framework);
let cap_map = global<GovernanceResponsbility>(@aptos_framework).signer_caps;
aborts_if !simple_map::spec_contains_key(cap_map, signer_address);
}

create_proposal_metadata

fun create_proposal_metadata(metadata_location: vector<u8>, metadata_hash: vector<u8>): simple_map::SimpleMap<string::String, vector<u8>>
include CreateProposalMetadataAbortsIf;
schema CreateProposalMetadataAbortsIf {
metadata_location: vector<u8>;
metadata_hash: vector<u8>;
aborts_if string::length(utf8(metadata_location)) > 256;
aborts_if string::length(utf8(metadata_hash)) > 256;
aborts_if !string::spec_internal_check_utf8(metadata_location);
aborts_if !string::spec_internal_check_utf8(metadata_hash);
aborts_if !string::spec_internal_check_utf8(METADATA_LOCATION_KEY);
aborts_if !string::spec_internal_check_utf8(METADATA_HASH_KEY);
}

assert_voting_initialization

fun assert_voting_initialization()
include VotingInitializationAbortIfs;