Module 0x1::init
Module initialization support. This is internally used by the compiler.
- Struct
ModuleId - Struct
ModuleState - Enum Resource
InitializationState - Constants
- Function
internal_maybe_initialize - Function
assert_may_self_initialize - Function
recorded_deploy_owner - Function
record_deploy_owner - Function
reset_initialized - Function
module_id_from_name - Function
check_and_set_initialized - Function
ensure_module_state - Function
get_caller_address_and_module_id - Specification
use 0x1::create_signer;
use 0x1::error;
use 0x1::features;
use 0x1::from_bcs;
use 0x1::hash;
use 0x1::object;
use 0x1::option;
use 0x1::ordered_map;
use 0x1::vector;
Struct ModuleId
Opaque representation of module names.
struct ModuleId has copy, drop, store
Fields
-
hash: u128
Struct ModuleState
Per-module initialization metadata.
only_once is the flag first passed at initialization: none before init, some(false)
re-inits after each upgrade (cleared by reset_initialized), some(true) inits only once.
deploy_owner is the object's transitive root owner recorded at this module's last publish
and gates object self-init (see assert_may_self_initialize); none for account addresses.
struct ModuleState has copy, drop, store
Fields
-
only_once: option::Option<bool> -
deploy_owner: option::Option<address>
Enum Resource InitializationState
Per-address initialization metadata, keyed by module so each module is gated on the owner at its own last publish -- republishing one module does not re-arm a sibling.
enum InitializationState has key
Variants
V1
Fields
Constants
Module initialization can only be requested by module code, not by scripts.
const EINVALID_INITIALIZE_CALLER: u64 = 1;
Lazy module initialization is not enabled (see the LAZY_MODULE_INITIALIZATION feature).
const ELAZY_MODULE_INITIALIZATION_NOT_ENABLED: u64 = 3;
A module on an object cannot self-initialize once the object changed owner, or was deleted, since that module's code was last published.
const EOWNER_CHANGED_SINCE_DEPLOY: u64 = 2;
Function internal_maybe_initialize
If the calling module needs initialization -- never initialized, or (with only_once false)
upgraded since -- marks it initialized and returns a signer for its address; else returns
none. Marking happens before the initializer runs, so a cyclic call terminates.
The caller module is derived by the VM from the call stack, so a module can only initialize
itself. Aborts if the feature is disabled (ELAZY_MODULE_INITIALIZATION_NOT_ENABLED), the
caller is not module code (EINVALID_INITIALIZE_CALLER), or an object-hosted module changed
owner since publish (EOWNER_CHANGED_SINCE_DEPLOY).
public fun internal_maybe_initialize(only_once: bool): option::Option<signer>
Implementation
public fun internal_maybe_initialize(only_once: bool): Option<signer> {
assert!(
features::is_lazy_module_initialization_enabled(),
error::invalid_state(ELAZY_MODULE_INITIALIZATION_NOT_ENABLED),
);
let (addr, module_id) = get_caller_address_and_module_id();
if (check_and_set_initialized(addr, module_id, only_once)) {
option::none()
} else {
// Guard only when actually minting: a legitimate transfer after initialization must not
// brick ordinary calls. An abort here rolls back the mark set above.
assert_may_self_initialize(addr, module_id);
option::some(create_signer::create_signer(addr))
}
}
Function assert_may_self_initialize
Aborts unless the module at addr may self-initialize now. Only object-hosted modules are
gated: an object must still have the transitive root owner recorded for this module at
publish, so a transfer of the object or an ancestor, or its deletion, blocks self-init; an
object with no record is fail-closed. Account addresses authorize their own code by publishing.
fun assert_may_self_initialize(addr: address, module_id: init::ModuleId)
Implementation
fun assert_may_self_initialize(addr: address, module_id: ModuleId) {
let recorded = recorded_deploy_owner(addr, module_id);
let ok = if (recorded.is_some()) {
object::is_object(addr)
&& recorded.destroy_some() == object::address_to_object<ObjectCore>(addr).root_owner()
} else {
!object::is_object(addr)
};
assert!(ok, error::permission_denied(EOWNER_CHANGED_SINCE_DEPLOY));
}
Function recorded_deploy_owner
The object root owner recorded for module_id at its last publish, or none if the module
has no such record (an account module, or an object module never recorded).
fun recorded_deploy_owner(addr: address, module_id: init::ModuleId): option::Option<address>
Implementation
fun recorded_deploy_owner(addr: address, module_id: ModuleId): Option<address> {
if (!exists<InitializationState>(addr)) return option::none();
let modules = &InitializationState[addr].modules;
if (modules.contains(&module_id)) modules.borrow(&module_id).deploy_owner else option::none()
}
Function record_deploy_owner
Records owner as the object root owner of the module named module_name at (re)publish, to
gate its later self-init (see assert_may_self_initialize). Called per module by
code::publish_package for object addresses only.
public(friend) fun record_deploy_owner(addr: address, module_name: vector<u8>, owner: address)
Implementation
package fun record_deploy_owner(addr: address, module_name: vector<u8>, owner: address) {
let module_id = module_id_from_name(module_name);
ensure_module_state(addr, module_id);
InitializationState[addr].modules.borrow_mut(&module_id).deploy_owner = option::some(owner);
}
Function reset_initialized
Called on code upgrade to request re-run of initialization for the named module. Skipped for
modules that used only_once = true when first initialized. Keeps the recorded deploy owner.
public(friend) fun reset_initialized(addr: address, module_name: vector<u8>)
Implementation
package fun reset_initialized(addr: address, module_name: vector<u8>) {
if (exists<InitializationState>(addr)) {
let modules = &mut InitializationState[addr].modules;
let module_id = module_id_from_name(module_name);
if (modules.contains(&module_id)) {
let state = modules.borrow_mut(&module_id);
if (state.only_once == option::some(false)) {
state.only_once = option::none();
}
}
}
}
Function module_id_from_name
Creates the id for a module name (see get_caller_address_and_module_id for the format).
fun module_id_from_name(name: vector<u8>): init::ModuleId
Implementation
fun module_id_from_name(name: vector<u8>): ModuleId {
let hash = hash::sha3_256(name);
hash.trim(16);
ModuleId { hash: from_bcs::to_u128(hash) }
}
Function check_and_set_initialized
Returns true if the module is already initialized. Otherwise marks it initialized, recording
only_once: if true the entry survives upgrades (initializer never re-runs); if false an
upgrade resets it (initializer re-runs).
fun check_and_set_initialized(addr: address, module_id: init::ModuleId, only_once: bool): bool
Implementation
fun check_and_set_initialized(addr: address, module_id: ModuleId, only_once: bool): bool {
ensure_module_state(addr, module_id);
let state = InitializationState[addr].modules.borrow_mut(&module_id);
if (state.only_once.is_some()) {
true
} else {
state.only_once = option::some(only_once);
false
}
}
Function ensure_module_state
Ensures an InitializationState exists at addr holding an entry for module_id.
fun ensure_module_state(addr: address, module_id: init::ModuleId)
Implementation
fun ensure_module_state(addr: address, module_id: ModuleId) {
if (!exists<InitializationState>(addr)) {
move_to(
&create_signer::create_signer(addr),
InitializationState::V1 { modules: ordered_map::new() },
);
};
let modules = &mut InitializationState[addr].modules;
if (!modules.contains(&module_id)) {
modules.add(module_id, ModuleState { only_once: option::none(), deploy_owner: option::none() });
};
}
Function get_caller_address_and_module_id
Returns the address and module id of the module that directly called the function invoking
this native. Aborts with EINVALID_INITIALIZE_CALLER if there is no such module (e.g. a
script). The module id is the sha3-256 of the module name, trimmed to 16 bytes and read as
the bcs encoding of a u128.
fun get_caller_address_and_module_id(): (address, init::ModuleId)
Implementation
native fun get_caller_address_and_module_id(): (address, ModuleId);
Specification
Function internal_maybe_initialize
public fun internal_maybe_initialize(only_once: bool): option::Option<signer>
pragma aborts_if_is_partial;
Function get_caller_address_and_module_id
fun get_caller_address_and_module_id(): (address, init::ModuleId)
pragma opaque;