starcoin-framework

Module 0x1::PackageTxnManager

The module provides strategies for module upgrading.

use 0x1::Config;
use 0x1::CoreAddresses;
use 0x1::Errors;
use 0x1::Event;
use 0x1::Option;
use 0x1::Signer;
use 0x1::Timestamp;
use 0x1::Version;

Struct UpgradePlan

module upgrade plan

struct UpgradePlan has copy, drop, store
Fields
package_hash: vector<u8>
active_after_time: u64
version: u64

Resource UpgradePlanCapability

The holder of UpgradePlanCapability for account_address can submit UpgradePlan for account_address.

struct UpgradePlanCapability has store, key
Fields
account_address: address

Struct UpgradePlanV2

struct UpgradePlanV2 has copy, drop, store
Fields
package_hash: vector<u8>
active_after_time: u64
version: u64
enforced: bool

Resource ModuleUpgradeStrategy

module upgrade strategy

struct ModuleUpgradeStrategy has store, key
Fields
strategy: u8
0 arbitrary 1 two phase upgrade 2 only new module 3 freeze

Resource TwoPhaseUpgrade

data of two phase upgrade strategy.

struct TwoPhaseUpgrade has key
Fields
config: PackageTxnManager::TwoPhaseUpgradeConfig
plan: Option::Option<PackageTxnManager::UpgradePlan>
version_cap: Config::ModifyConfigCapability<Version::Version>
upgrade_event: Event::EventHandle<PackageTxnManager::UpgradeEvent>

Struct TwoPhaseUpgradeConfig

config of two phase upgrade strategy.

struct TwoPhaseUpgradeConfig has copy, drop, store
Fields
min_time_limit: u64

Resource TwoPhaseUpgradeV2

data of two phase upgrade strategy.

struct TwoPhaseUpgradeV2 has key
Fields
config: PackageTxnManager::TwoPhaseUpgradeConfig
plan: Option::Option<PackageTxnManager::UpgradePlanV2>
version_cap: Config::ModifyConfigCapability<Version::Version>
upgrade_event: Event::EventHandle<PackageTxnManager::UpgradeEvent>

Struct UpgradeEvent

module upgrade event.

struct UpgradeEvent has drop, store
Fields
package_address: address
package_hash: vector<u8>
version: u64

Constants

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;

Function get_strategy_arbitrary

arbitary stragegy

public fun get_strategy_arbitrary(): u8
Implementation
public fun get_strategy_arbitrary(): u8 { STRATEGY_ARBITRARY }

Function get_strategy_two_phase

two phase stragegy

public fun get_strategy_two_phase(): u8
Implementation
public fun get_strategy_two_phase(): u8 { STRATEGY_TWO_PHASE }

Function get_strategy_new_module

new module strategy

public fun get_strategy_new_module(): u8
Implementation
public fun get_strategy_new_module(): u8 { STRATEGY_NEW_MODULE }

Function get_strategy_freeze

freezed strategy

public fun get_strategy_freeze(): u8
Implementation
public fun get_strategy_freeze(): u8 { STRATEGY_FREEZE }

Function get_default_min_time_limit

default min time limit

public fun get_default_min_time_limit(): u64
Implementation
public fun get_default_min_time_limit(): u64 { DEFAULT_MIN_TIME_LIMIT }

Function update_module_upgrade_strategy

Update account’s ModuleUpgradeStrategy

public fun update_module_upgrade_strategy(account: &signer, strategy: u8, min_time: Option::Option<u64>)
Implementation
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);
        };
    };
}
Specification
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));

Function account_address

Get account address of UpgradePlanCapability

public fun account_address(cap: &PackageTxnManager::UpgradePlanCapability): address
Implementation
public fun account_address(cap: &UpgradePlanCapability): address {
    cap.account_address
}

Function destroy_upgrade_plan_cap

destroy the given UpgradePlanCapability

public fun destroy_upgrade_plan_cap(cap: PackageTxnManager::UpgradePlanCapability)
Implementation
public fun destroy_upgrade_plan_cap(cap: UpgradePlanCapability){
    let UpgradePlanCapability{account_address:_} = cap;
}
Specification
aborts_if false;

Function extract_submit_upgrade_plan_cap

extract out UpgradePlanCapability from signer.

public fun extract_submit_upgrade_plan_cap(account: &signer): PackageTxnManager::UpgradePlanCapability
Implementation
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)
}
Specification
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));

Function convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2

public(script) fun convert_TwoPhaseUpgrade_to_TwoPhaseUpgradeV2(account: signer, package_address: address)
Implementation
public(script) 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
        });
    };
}
Specification
pragma verify = false;

Function submit_upgrade_plan_v2

public fun submit_upgrade_plan_v2(account: &signer, package_hash: vector<u8>, version: u64, enforced: bool)
Implementation
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);
}
Specification
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);

Function 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)
Implementation
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 });
}
Specification
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();
}

Function cancel_upgrade_plan

Cancel a module upgrade plan.

public fun cancel_upgrade_plan(account: &signer)
Implementation
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);
}
Specification
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);

Function cancel_upgrade_plan_with_cap

Cancel a module upgrade plan with given cap.

public fun cancel_upgrade_plan_with_cap(cap: &PackageTxnManager::UpgradePlanCapability)
Implementation
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>();
}
Specification
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);
}

Function get_module_upgrade_strategy

Get module upgrade strategy of an module address.

public fun get_module_upgrade_strategy(module_address: address): u8
Implementation
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
    }
}
Specification
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
   }
}

Function get_upgrade_plan

Get module upgrade plan of an address.

public fun get_upgrade_plan(_module_address: address): Option::Option<PackageTxnManager::UpgradePlan>
Implementation
public fun get_upgrade_plan(_module_address: address): Option<UpgradePlan> {
    // DEPRECATED_CODE
    Option::none<UpgradePlan>()
}
Specification
aborts_if false;

Function get_upgrade_plan_v2

Get module upgrade plan of an address.

public fun get_upgrade_plan_v2(module_address: address): Option::Option<PackageTxnManager::UpgradePlanV2>
Implementation
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>()
    }
}
Specification
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>()
   }
}

Function check_package_txn

Check againest on the given package data.

public fun check_package_txn(package_address: address, package_hash: vector<u8>)
Implementation
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){
        abort(ESTRATEGY_FREEZED)
    };
}
Specification
pragma verify = false;
include CheckPackageTxnAbortsIf;

Function check_package_txn_v2

public fun check_package_txn_v2(txn_sender: address, package_address: address, package_hash: vector<u8>)
Implementation
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){
        abort(ESTRATEGY_FREEZED)
    };
}

Function finish_upgrade_plan

fun finish_upgrade_plan(package_address: address)
Implementation
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>();
}
Specification
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);

Function package_txn_prologue

Prologue of package transaction.

public fun package_txn_prologue(account: &signer, package_address: address, package_hash: vector<u8>)
Implementation
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);
}
Specification
aborts_if Signer::address_of(account) != CoreAddresses::SPEC_GENESIS_ADDRESS();
include CheckPackageTxnAbortsIf{};

Function package_txn_prologue_v2

public fun package_txn_prologue_v2(account: &signer, txn_sender: address, package_address: address, package_hash: vector<u8>)
Implementation
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);
}

Function package_txn_epilogue

Package txn finished, and clean UpgradePlan

public fun package_txn_epilogue(account: &signer, _txn_sender: address, package_address: address, success: bool)
Implementation
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);
        };
    };
}
Specification
aborts_if Signer::address_of(account) != CoreAddresses::SPEC_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);

Module Specification

pragma verify = false;
pragma aborts_if_is_strict = true;