0x1::PackageTxnManager
The module provides strategies for module upgrading.
UpgradePlan
UpgradePlanCapability
UpgradePlanV2
ModuleUpgradeStrategy
TwoPhaseUpgrade
TwoPhaseUpgradeConfig
TwoPhaseUpgradeV2
UpgradeEvent
get_strategy_arbitrary
get_strategy_two_phase
get_strategy_new_module
get_strategy_freeze
get_default_min_time_limit
update_module_upgrade_strategy
account_address
destroy_upgrade_plan_cap
extract_submit_upgrade_plan_cap
convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2
submit_upgrade_plan_v2
submit_upgrade_plan_with_cap_v2
cancel_upgrade_plan
cancel_upgrade_plan_with_cap
get_module_upgrade_strategy
get_upgrade_plan
get_upgrade_plan_v2
check_package_txn
check_package_txn_v2
finish_upgrade_plan
package_txn_prologue
package_txn_prologue_v2
package_txn_epilogue
use 0x1::Config;
use 0x1::CoreAddresses;
use 0x1::Errors;
use 0x1::Event;
use 0x1::Option;
use 0x1::Signer;
use 0x1::Timestamp;
use 0x1::Version;
UpgradePlan
module upgrade plan
struct UpgradePlan has copy, drop, store
package_hash: vector<u8>
active_after_time: u64
version: u64
UpgradePlanCapability
The holder of UpgradePlanCapability for account_address can submit UpgradePlan for account_address.
struct UpgradePlanCapability has store, key
account_address: address
UpgradePlanV2
struct UpgradePlanV2 has copy, drop, store
package_hash: vector<u8>
active_after_time: u64
version: u64
enforced: bool
ModuleUpgradeStrategy
module upgrade strategy
struct ModuleUpgradeStrategy has store, key
strategy: u8
TwoPhaseUpgrade
data of two phase upgrade strategy.
struct TwoPhaseUpgrade has key
config: PackageTxnManager::TwoPhaseUpgradeConfig
plan: Option::Option<PackageTxnManager::UpgradePlan>
version_cap: Config::ModifyConfigCapability<Version::Version>
upgrade_event: Event::EventHandle<PackageTxnManager::UpgradeEvent>
TwoPhaseUpgradeConfig
config of two phase upgrade strategy.
struct TwoPhaseUpgradeConfig has copy, drop, store
min_time_limit: u64
TwoPhaseUpgradeV2
data of two phase upgrade strategy.
struct TwoPhaseUpgradeV2 has key
config: PackageTxnManager::TwoPhaseUpgradeConfig
plan: Option::Option<PackageTxnManager::UpgradePlanV2>
version_cap: Config::ModifyConfigCapability<Version::Version>
upgrade_event: Event::EventHandle<PackageTxnManager::UpgradeEvent>
UpgradeEvent
module upgrade event.
struct UpgradeEvent has drop, store
package_address: address
package_hash: vector<u8>
version: u64
const DEFAULT_MIN_TIME_LIMIT: u64 = 86400000;
const EACTIVE_TIME_INCORRECT: u64 = 104;
const EPACKAGE_HASH_INCORRECT: u64 = 103;
const ESENDER_AND_PACKAGE_ADDRESS_MISMATCH: u64 = 109;
const ESTRATEGY_FREEZED: u64 = 105;
const ESTRATEGY_INCORRECT: u64 = 106;
const ESTRATEGY_NOT_TWO_PHASE: u64 = 107;
const EUNKNOWN_STRATEGY: u64 = 108;
const EUPGRADE_PLAN_IS_NONE: u64 = 102;
const STRATEGY_ARBITRARY: u8 = 0;
const STRATEGY_FREEZE: u8 = 3;
const STRATEGY_NEW_MODULE: u8 = 2;
const STRATEGY_TWO_PHASE: u8 = 1;
get_strategy_arbitrary
arbitary stragegy
public fun get_strategy_arbitrary(): u8
public fun get_strategy_arbitrary(): u8 { STRATEGY_ARBITRARY }
get_strategy_two_phase
two phase stragegy
public fun get_strategy_two_phase(): u8
public fun get_strategy_two_phase(): u8 { STRATEGY_TWO_PHASE }
get_strategy_new_module
new module strategy
public fun get_strategy_new_module(): u8
public fun get_strategy_new_module(): u8 { STRATEGY_NEW_MODULE }
get_strategy_freeze
freezed strategy
public fun get_strategy_freeze(): u8
public fun get_strategy_freeze(): u8 { STRATEGY_FREEZE }
get_default_min_time_limit
default min time limit
public fun get_default_min_time_limit(): u64
public fun get_default_min_time_limit(): u64 { DEFAULT_MIN_TIME_LIMIT }
update_module_upgrade_strategy
Update account’s ModuleUpgradeStrategy
public fun update_module_upgrade_strategy(account: &signer, strategy: u8, min_time: Option::Option<u64>)
public fun update_module_upgrade_strategy(account: &signer, strategy: u8, min_time: Option<u64>) acquires ModuleUpgradeStrategy, TwoPhaseUpgrade, TwoPhaseUpgradeV2, UpgradePlanCapability{
assert!(strategy == STRATEGY_ARBITRARY || strategy == STRATEGY_TWO_PHASE || strategy == STRATEGY_NEW_MODULE || strategy == STRATEGY_FREEZE, Errors::invalid_argument(EUNKNOWN_STRATEGY));
let account_address = Signer::address_of(account);
let previous_strategy = get_module_upgrade_strategy(account_address);
assert!(strategy > previous_strategy, Errors::invalid_argument(ESTRATEGY_INCORRECT));
if (exists<ModuleUpgradeStrategy>(account_address)) {
borrow_global_mut<ModuleUpgradeStrategy>(account_address).strategy = strategy;
}else{
move_to(account, ModuleUpgradeStrategy{ strategy: strategy});
};
if (strategy == STRATEGY_TWO_PHASE){
let version_cap = Config::extract_modify_config_capability<Version::Version>(account);
let min_time_limit = Option::get_with_default(&min_time, DEFAULT_MIN_TIME_LIMIT);
move_to(account, UpgradePlanCapability{ account_address: account_address});
move_to(account, TwoPhaseUpgradeV2{
config: TwoPhaseUpgradeConfig{min_time_limit: min_time_limit},
plan: Option::none<UpgradePlanV2>(),
version_cap: version_cap,
upgrade_event: Event::new_event_handle<Self::UpgradeEvent>(account)}
);
};
//clean two phase upgrade resource
if (previous_strategy == STRATEGY_TWO_PHASE){
if (exists<TwoPhaseUpgrade>(account_address)) {
let tpu = move_from<TwoPhaseUpgrade>(account_address);
let TwoPhaseUpgrade{plan:_, version_cap, upgrade_event, config: _} = tpu;
Event::destroy_handle<Self::UpgradeEvent>(upgrade_event);
Config::destroy_modify_config_capability<Version::Version>(version_cap);
};
if (exists<TwoPhaseUpgradeV2>(account_address)) {
let tpu = move_from<TwoPhaseUpgradeV2>(account_address);
let TwoPhaseUpgradeV2{plan:_, version_cap, upgrade_event, config: _} = tpu;
Event::destroy_handle<Self::UpgradeEvent>(upgrade_event);
Config::destroy_modify_config_capability<Version::Version>(version_cap);
};
// UpgradePlanCapability may be extracted
if (exists<UpgradePlanCapability>(account_address)) {
let cap = move_from<UpgradePlanCapability>(account_address);
destroy_upgrade_plan_cap(cap);
};
};
}
pragma verify = false;
aborts_if strategy != 0 && strategy != 1 && strategy != 2 && strategy != 3;
aborts_if exists<ModuleUpgradeStrategy>(Signer::address_of(account)) && strategy <= global<ModuleUpgradeStrategy>(Signer::address_of(account)).strategy;
aborts_if !exists<ModuleUpgradeStrategy>(Signer::address_of(account)) && strategy == 0;
aborts_if strategy == 1 && exists<UpgradePlanCapability>(Signer::address_of(account));
aborts_if strategy == 1 && !exists<Config::ModifyConfigCapabilityHolder<Version::Version>>(Signer::address_of(account));
let holder = global<Config::ModifyConfigCapabilityHolder<Version::Version>>(Signer::address_of(account));
aborts_if strategy == 1 && Option::is_none<Config::ModifyConfigCapability<Version::Version>>(holder.cap);
aborts_if strategy == 1 && exists<TwoPhaseUpgrade>(Signer::address_of(account));
aborts_if exists<ModuleUpgradeStrategy>(Signer::address_of(account)) && global<ModuleUpgradeStrategy>(Signer::address_of(account)).strategy == 1
&& !exists<TwoPhaseUpgrade>(Signer::address_of(account));
account_address
Get account address of UpgradePlanCapability
public fun account_address(cap: &PackageTxnManager::UpgradePlanCapability): address
public fun account_address(cap: &UpgradePlanCapability): address {
cap.account_address
}
destroy_upgrade_plan_cap
destroy the given UpgradePlanCapability
public fun destroy_upgrade_plan_cap(cap: PackageTxnManager::UpgradePlanCapability)
public fun destroy_upgrade_plan_cap(cap: UpgradePlanCapability){
let UpgradePlanCapability{account_address:_} = cap;
}
aborts_if false;
extract_submit_upgrade_plan_cap
extract out UpgradePlanCapability from signer
.
public fun extract_submit_upgrade_plan_cap(account: &signer): PackageTxnManager::UpgradePlanCapability
public fun extract_submit_upgrade_plan_cap(account: &signer): UpgradePlanCapability acquires ModuleUpgradeStrategy, UpgradePlanCapability{
let account_address = Signer::address_of(account);
assert!(get_module_upgrade_strategy(account_address) == STRATEGY_TWO_PHASE, Errors::invalid_argument(ESTRATEGY_NOT_TWO_PHASE));
move_from<UpgradePlanCapability>(account_address)
}
aborts_if !exists<ModuleUpgradeStrategy>(Signer::address_of(account));
aborts_if global<ModuleUpgradeStrategy>(Signer::address_of(account)).strategy != 1;
aborts_if !exists<UpgradePlanCapability>(Signer::address_of(account));
convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2
public entry fun convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2(account: signer, package_address: address)
public entry fun convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2(account: signer, package_address: address) acquires TwoPhaseUpgrade {
let account_address = Signer::address_of(&account);
// sender should be package owner
assert!(account_address == package_address, Errors::requires_address(ESENDER_AND_PACKAGE_ADDRESS_MISMATCH));
let tpu = move_from<TwoPhaseUpgrade>(account_address);
let TwoPhaseUpgrade{config, plan, version_cap, upgrade_event} = tpu;
if (Option::is_some(&plan)) {
let old_plan = Option::borrow(&plan);
move_to(&account, TwoPhaseUpgradeV2{
config: config,
plan: Option::some(UpgradePlanV2 {
package_hash: *&old_plan.package_hash,
active_after_time: old_plan.active_after_time,
version: old_plan.version,
enforced: false }),
version_cap: version_cap,
upgrade_event: upgrade_event
});
} else {
move_to(&account, TwoPhaseUpgradeV2{
config: config,
plan: Option::none<UpgradePlanV2>(),
version_cap: version_cap,
upgrade_event: upgrade_event
});
};
}
pragma verify = false;
submit_upgrade_plan_v2
public fun submit_upgrade_plan_v2(account: &signer, package_hash: vector<u8>, version: u64, enforced: bool)
public fun submit_upgrade_plan_v2(account: &signer, package_hash: vector<u8>, version:u64, enforced: bool) acquires TwoPhaseUpgradeV2,UpgradePlanCapability,ModuleUpgradeStrategy{
let account_address = Signer::address_of(account);
let cap = borrow_global<UpgradePlanCapability>(account_address);
submit_upgrade_plan_with_cap_v2(cap, package_hash, version, enforced);
}
pragma verify = false;
aborts_if !exists<UpgradePlanCapability>(Signer::address_of(account));
include SubmitUpgradePlanWithCapAbortsIf{account: global<UpgradePlanCapability>(Signer::address_of(account)).account_address};
ensures Option::is_some(global<TwoPhaseUpgrade>(global<UpgradePlanCapability>(Signer::address_of(account)).account_address).plan);
submit_upgrade_plan_with_cap_v2
public fun submit_upgrade_plan_with_cap_v2(cap: &PackageTxnManager::UpgradePlanCapability, package_hash: vector<u8>, version: u64, enforced: bool)
public fun submit_upgrade_plan_with_cap_v2(cap: &UpgradePlanCapability, package_hash: vector<u8>, version: u64, enforced: bool) acquires TwoPhaseUpgradeV2,ModuleUpgradeStrategy{
let package_address = cap.account_address;
assert!(get_module_upgrade_strategy(package_address) == STRATEGY_TWO_PHASE, Errors::invalid_argument(ESTRATEGY_NOT_TWO_PHASE));
let tpu = borrow_global_mut<TwoPhaseUpgradeV2>(package_address);
let active_after_time = Timestamp::now_milliseconds() + tpu.config.min_time_limit;
tpu.plan = Option::some(UpgradePlanV2 { package_hash, active_after_time, version, enforced });
}
pragma verify = false;
include SubmitUpgradePlanWithCapAbortsIf{account: cap.account_address};
ensures Option::is_some(global<TwoPhaseUpgrade>(cap.account_address).plan);
schema SubmitUpgradePlanWithCapAbortsIf {
account: address;
aborts_if !exists<ModuleUpgradeStrategy>(account);
aborts_if global<ModuleUpgradeStrategy>(account).strategy != 1;
aborts_if !exists<TwoPhaseUpgrade>(account);
aborts_if !exists<Timestamp::CurrentTimeMilliseconds>(CoreAddresses::GENESIS_ADDRESS());
aborts_if Timestamp::now_milliseconds() + global<TwoPhaseUpgrade>(account).config.min_time_limit > max_u64();
}
cancel_upgrade_plan
Cancel a module upgrade plan.
public fun cancel_upgrade_plan(account: &signer)
public fun cancel_upgrade_plan(account: &signer) acquires TwoPhaseUpgradeV2,UpgradePlanCapability,ModuleUpgradeStrategy{
let account_address = Signer::address_of(account);
let cap = borrow_global<UpgradePlanCapability>(account_address);
cancel_upgrade_plan_with_cap(cap);
}
aborts_if !exists<UpgradePlanCapability>(Signer::address_of(account));
include CancelUpgradePlanWithCapAbortsIf{account: global<UpgradePlanCapability>(Signer::address_of(account)).account_address};
ensures Option::is_none(global<TwoPhaseUpgrade>(global<UpgradePlanCapability>(Signer::address_of(account)).account_address).plan);
cancel_upgrade_plan_with_cap
Cancel a module upgrade plan with given cap.
public fun cancel_upgrade_plan_with_cap(cap: &PackageTxnManager::UpgradePlanCapability)
public fun cancel_upgrade_plan_with_cap(cap: &UpgradePlanCapability) acquires TwoPhaseUpgradeV2,ModuleUpgradeStrategy{
let package_address = cap.account_address;
assert!(get_module_upgrade_strategy(package_address) == STRATEGY_TWO_PHASE, Errors::invalid_argument(ESTRATEGY_NOT_TWO_PHASE));
let tpu = borrow_global_mut<TwoPhaseUpgradeV2>(package_address);
assert!(Option::is_some(&tpu.plan), Errors::invalid_state(EUPGRADE_PLAN_IS_NONE));
tpu.plan = Option::none<UpgradePlanV2>();
}
include CancelUpgradePlanWithCapAbortsIf{account: cap.account_address};
ensures Option::is_none(global<TwoPhaseUpgrade>(cap.account_address).plan);
schema CancelUpgradePlanWithCapAbortsIf {
account: address;
aborts_if !exists<ModuleUpgradeStrategy>(account);
aborts_if global<ModuleUpgradeStrategy>(account).strategy != 1;
aborts_if !exists<TwoPhaseUpgrade>(account);
aborts_if !Option::is_some(global<TwoPhaseUpgrade>(account).plan);
}
get_module_upgrade_strategy
Get module upgrade strategy of an module address.
public fun get_module_upgrade_strategy(module_address: address): u8
public fun get_module_upgrade_strategy(module_address: address): u8 acquires ModuleUpgradeStrategy {
if (exists<ModuleUpgradeStrategy>(module_address)) {
borrow_global<ModuleUpgradeStrategy>(module_address).strategy
}else{
0
}
}
aborts_if false;
fun spec_get_module_upgrade_strategy(module_address: address): u8 {
if (exists<ModuleUpgradeStrategy>(module_address)) {
global<ModuleUpgradeStrategy>(module_address).strategy
}else{
0
}
}
get_upgrade_plan
Get module upgrade plan of an address.
public fun get_upgrade_plan(_module_address: address): Option::Option<PackageTxnManager::UpgradePlan>
public fun get_upgrade_plan(_module_address: address): Option<UpgradePlan> {
// DEPRECATED_CODE
Option::none<UpgradePlan>()
}
aborts_if false;
get_upgrade_plan_v2
Get module upgrade plan of an address.
public fun get_upgrade_plan_v2(module_address: address): Option::Option<PackageTxnManager::UpgradePlanV2>
public fun get_upgrade_plan_v2(module_address: address): Option<UpgradePlanV2> acquires TwoPhaseUpgradeV2 {
if (exists<TwoPhaseUpgradeV2>(module_address)) {
*&borrow_global<TwoPhaseUpgradeV2>(module_address).plan
} else {
Option::none<UpgradePlanV2>()
}
}
pragma verify = false;
aborts_if false;
fun spec_get_upgrade_plan_v2(module_address: address): Option<UpgradePlan> {
if (exists<TwoPhaseUpgrade>(module_address)) {
global<TwoPhaseUpgrade>(module_address).plan
}else{
Option::spec_none<UpgradePlan>()
}
}
check_package_txn
Check againest on the given package data.
public fun check_package_txn(package_address: address, package_hash: vector<u8>)
public fun check_package_txn(package_address: address, package_hash: vector<u8>) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy{
let strategy = get_module_upgrade_strategy(package_address);
if (strategy == STRATEGY_ARBITRARY){
//do nothing
}else if(strategy == STRATEGY_TWO_PHASE){
let plan_opt = get_upgrade_plan_v2(package_address);
assert!(Option::is_some(&plan_opt), Errors::invalid_argument(EUPGRADE_PLAN_IS_NONE));
let plan = Option::borrow(&plan_opt);
assert!(*&plan.package_hash == package_hash, Errors::invalid_argument(EPACKAGE_HASH_INCORRECT));
assert!(plan.active_after_time <= Timestamp::now_milliseconds(), Errors::invalid_argument(EACTIVE_TIME_INCORRECT));
}else if(strategy == STRATEGY_NEW_MODULE){
//do check at VM runtime.
}else if(strategy == STRATEGY_FREEZE){
Errors::invalid_argument(ESTRATEGY_FREEZED);
};
}
pragma verify = false;
include CheckPackageTxnAbortsIf;
check_package_txn_v2
public fun check_package_txn_v2(txn_sender: address, package_address: address, package_hash: vector<u8>)
public fun check_package_txn_v2(txn_sender: address, package_address: address, package_hash: vector<u8>) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy{
let strategy = get_module_upgrade_strategy(package_address);
if (strategy == STRATEGY_ARBITRARY){
assert!(txn_sender == package_address, Errors::requires_address(ESENDER_AND_PACKAGE_ADDRESS_MISMATCH));
}else if(strategy == STRATEGY_TWO_PHASE){
let plan_opt = get_upgrade_plan_v2(package_address);
assert!(Option::is_some(&plan_opt), Errors::invalid_argument(EUPGRADE_PLAN_IS_NONE));
let plan = Option::borrow(&plan_opt);
assert!(*&plan.package_hash == package_hash, Errors::invalid_argument(EPACKAGE_HASH_INCORRECT));
assert!(plan.active_after_time <= Timestamp::now_milliseconds(), Errors::invalid_argument(EACTIVE_TIME_INCORRECT));
}else if(strategy == STRATEGY_NEW_MODULE){
//do check at VM runtime.
assert!(txn_sender == package_address, Errors::requires_address(ESENDER_AND_PACKAGE_ADDRESS_MISMATCH));
}else if(strategy == STRATEGY_FREEZE){
Errors::invalid_argument(ESTRATEGY_FREEZED);
};
}
finish_upgrade_plan
fun finish_upgrade_plan(package_address: address)
fun finish_upgrade_plan(package_address: address) acquires TwoPhaseUpgradeV2 {
let tpu = borrow_global_mut<TwoPhaseUpgradeV2>(package_address);
if (Option::is_some(&tpu.plan)) {
let plan = Option::borrow(&tpu.plan);
Config::set_with_capability<Version::Version>(&mut tpu.version_cap, Version::new_version(plan.version));
Event::emit_event<Self::UpgradeEvent>(&mut tpu.upgrade_event, UpgradeEvent {
package_address: package_address,
package_hash: *&plan.package_hash,
version: plan.version});
};
tpu.plan = Option::none<UpgradePlanV2>();
}
pragma verify = false;
aborts_if !exists<TwoPhaseUpgrade>(package_address);
let tpu = global<TwoPhaseUpgrade>(package_address);
aborts_if Option::is_some(tpu.plan) && !exists<Config::Config<Version::Version>>(tpu.version_cap.account_address);
package_txn_prologue
Prologue of package transaction.
public fun package_txn_prologue(account: &signer, package_address: address, package_hash: vector<u8>)
public fun package_txn_prologue(account: &signer, package_address: address, package_hash: vector<u8>) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy {
// Can only be invoked by genesis account
CoreAddresses::assert_genesis_address(account);
check_package_txn(package_address, package_hash);
}
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
include CheckPackageTxnAbortsIf{};
package_txn_prologue_v2
public fun package_txn_prologue_v2(account: &signer, txn_sender: address, package_address: address, package_hash: vector<u8>)
public fun package_txn_prologue_v2(account: &signer, txn_sender: address, package_address: address, package_hash: vector<u8>) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy {
// Can only be invoked by genesis account
CoreAddresses::assert_genesis_address(account);
check_package_txn_v2(txn_sender, package_address, package_hash);
}
package_txn_epilogue
Package txn finished, and clean UpgradePlan
public fun package_txn_epilogue(account: &signer, _txn_sender: address, package_address: address, success: bool)
public fun package_txn_epilogue(account: &signer, _txn_sender: address, package_address: address, success: bool) acquires TwoPhaseUpgradeV2, ModuleUpgradeStrategy {
// Can only be invoked by genesis account
CoreAddresses::assert_genesis_address(account);
let strategy = get_module_upgrade_strategy(package_address);
if(strategy == STRATEGY_TWO_PHASE){
if (success) {
finish_upgrade_plan(package_address);
};
};
}
aborts_if Signer::address_of(account) != CoreAddresses::GENESIS_ADDRESS();
aborts_if spec_get_module_upgrade_strategy(package_address) == 1
&& success && !exists<TwoPhaseUpgrade>(package_address);
aborts_if spec_get_module_upgrade_strategy(package_address) == 1
&& success && Option::is_some(global<TwoPhaseUpgrade>(package_address).plan)
&& !exists<Config::Config<Version::Version>>(global<TwoPhaseUpgrade>(package_address).version_cap.account_address);
pragma verify = false;
pragma aborts_if_is_strict = true;