code - [mainnet]
This module supports functionality related to code management.
use 0x1::copyable_any;use 0x1::error;use 0x1::event;use 0x1::features;use 0x1::object;use 0x1::option;use 0x1::permissioned_signer;use 0x1::signer;use 0x1::string;use 0x1::system_addresses;use 0x1::util;use 0x1::vector;
Constants
code_object
does not exist.
const ECODE_OBJECT_DOES_NOT_EXIST: u64 = 10;
A dependency to an arbitrary
package must be on the same address.
const EDEP_ARBITRARY_NOT_SAME_ADDRESS: u64 = 7;
A dependency cannot have a weaker upgrade policy.
const EDEP_WEAKER_POLICY: u64 = 6;
Creating a package with incompatible upgrade policy is disabled.
const EINCOMPATIBLE_POLICY_DISABLED: u64 = 8;
Cannot delete a module that was published in the same package
const EMODULE_MISSING: u64 = 4;
Package contains duplicate module names with existing modules publised in other packages on this address
const EMODULE_NAME_CLASH: u64 = 1;
Not the owner of the package registry.
const ENOT_PACKAGE_OWNER: u64 = 9;
Current permissioned signer cannot publish codes.
const ENO_CODE_PERMISSION: u64 = 11;
Dependency could not be resolved to any published package.
const EPACKAGE_DEP_MISSING: u64 = 5;
Cannot upgrade an immutable package
const EUPGRADE_IMMUTABLE: u64 = 2;
Cannot downgrade a package’s upgradability policy
const EUPGRADE_WEAKER_POLICY: u64 = 3;
Structs
PackageMetadata
Metadata for a package. All byte blobs are represented as base64-of-gzipped-bytes
struct PackageMetadata has copy, drop, store
Fields
-
name: string::String
- Name of this package.
-
upgrade_policy: code::UpgradePolicy
- The upgrade policy of this package.
-
upgrade_number: u64
- The numbers of times this module has been upgraded. Also serves as the on-chain version. This field will be automatically assigned on successful upgrade.
-
source_digest: string::String
- The source digest of the sources in the package. This is constructed by first building the sha256 of each individual source, than sorting them alphabetically, and sha256 them again.
-
manifest: vector<u8>
- The package manifest, in the Move.toml format. Gzipped text.
-
modules: vector<code::ModuleMetadata>
- The list of modules installed by this package.
-
deps: vector<code::PackageDep>
- Holds PackageDeps.
-
extension: option::Option<copyable_any::Any>
- For future extension
PackageDep
A dependency to a package published at address
struct PackageDep has copy, drop, store
Fields
-
account: address
-
package_name: string::String
ModuleMetadata
Metadata about a module in a package.
struct ModuleMetadata has copy, drop, store
Fields
-
name: string::String
- Name of the module.
-
source: vector<u8>
- Source text, gzipped String. Empty if not provided.
-
source_map: vector<u8>
- Source map, in compressed BCS. Empty if not provided.
-
extension: option::Option<copyable_any::Any>
- For future extensions.
UpgradePolicy
Describes an upgrade policy
struct UpgradePolicy has copy, drop, store
Fields
-
policy: u8
PublishPackage
Event emitted when code is published to an address.
#[event]struct PublishPackage has drop, store
Fields
-
code_address: address
-
is_upgrade: bool
CodePublishingPermission
struct CodePublishingPermission has copy, drop, store
Fields
-
dummy_field: bool
AllowedDep
A helper type for request_publish_with_allowed_deps
struct AllowedDep has drop
Fields
-
account: address
- Address of the module.
-
module_name: string::String
- Name of the module. If this is the empty string, then this serves as a wildcard for all modules from this address. This is used for speeding up dependency checking for packages from well-known framework addresses, where we can assume that there are no malicious packages.
Resources
PackageRegistry
The package registry at the given address.
struct PackageRegistry has drop, store, key
Fields
-
packages: vector<code::PackageMetadata>
- Packages installed at this address.
Functions
check_code_publishing_permission
Permissions
public(friend) fun check_code_publishing_permission(s: &signer)
Implementation
public(friend) fun check_code_publishing_permission(s: &signer) { assert!( permissioned_signer::check_permission_exists(s, CodePublishingPermission {}), error::permission_denied(ENO_CODE_PERMISSION), );}
grant_permission
Grant permission to publish code 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, CodePublishingPermission {})}
upgrade_policy_arbitrary
Whether unconditional code upgrade with no compatibility check is allowed. This publication mode should only be used for modules which aren’t shared with user others. The developer is responsible for not breaking memory layout of any resources he already stored on chain.
public fun upgrade_policy_arbitrary(): code::UpgradePolicy
Implementation
public fun upgrade_policy_arbitrary(): UpgradePolicy { UpgradePolicy { policy: 0 }}
upgrade_policy_compat
Whether a compatibility check should be performed for upgrades. The check only passes if a new module has (a) the same public functions (b) for existing resources, no layout change.
public fun upgrade_policy_compat(): code::UpgradePolicy
Implementation
public fun upgrade_policy_compat(): UpgradePolicy { UpgradePolicy { policy: 1 }}
upgrade_policy_immutable
Whether the modules in the package are immutable and cannot be upgraded.
public fun upgrade_policy_immutable(): code::UpgradePolicy
Implementation
public fun upgrade_policy_immutable(): UpgradePolicy { UpgradePolicy { policy: 2 }}
can_change_upgrade_policy_to
Whether the upgrade policy can be changed. In general, the policy can be only strengthened but not weakened.
public fun can_change_upgrade_policy_to(from: code::UpgradePolicy, to: code::UpgradePolicy): bool
Implementation
public fun can_change_upgrade_policy_to(from: UpgradePolicy, to: UpgradePolicy): bool { from.policy <= to.policy}
initialize
Initialize package metadata for Genesis.
fun initialize(aptos_framework: &signer, package_owner: &signer, metadata: code::PackageMetadata)
Implementation
fun initialize(aptos_framework: &signer, package_owner: &signer, metadata: PackageMetadata)acquires PackageRegistry { system_addresses::assert_aptos_framework(aptos_framework); let addr = signer::address_of(package_owner); if (!exists<PackageRegistry>(addr)) { move_to(package_owner, PackageRegistry { packages: vector[metadata] }) } else { vector::push_back(&mut borrow_global_mut<PackageRegistry>(addr).packages, metadata) }}
publish_package
Publishes a package at the given signer’s address. The caller must provide package metadata describing the package.
public fun publish_package(owner: &signer, pack: code::PackageMetadata, code: vector<vector<u8>>)
Implementation
public fun publish_package(owner: &signer, pack: PackageMetadata, code: vector<vector<u8>>) acquires PackageRegistry { check_code_publishing_permission(owner); // Disallow incompatible upgrade mode. Governance can decide later if this should be reconsidered. assert!( pack.upgrade_policy.policy > upgrade_policy_arbitrary().policy, error::invalid_argument(EINCOMPATIBLE_POLICY_DISABLED), );
let addr = signer::address_of(owner); if (!exists<PackageRegistry>(addr)) { move_to(owner, PackageRegistry { packages: vector::empty() }) };
// Checks for valid dependencies to other packages let allowed_deps = check_dependencies(addr, &pack);
// Check package against conflicts // To avoid prover compiler error on spec // the package need to be an immutable variable let module_names = get_module_names(&pack); let package_immutable = &borrow_global<PackageRegistry>(addr).packages; let len = vector::length(package_immutable); let index = len; let upgrade_number = 0; vector::enumerate_ref(package_immutable , |i, old| { let old: &PackageMetadata = old; if (old.name == pack.name) { upgrade_number = old.upgrade_number + 1; check_upgradability(old, &pack, &module_names); index = i; } else { check_coexistence(old, &module_names) }; });
// Assign the upgrade counter. pack.upgrade_number = upgrade_number;
let packages = &mut borrow_global_mut<PackageRegistry>(addr).packages; // Update registry let policy = pack.upgrade_policy; if (index < len) { *vector::borrow_mut(packages, index) = pack } else { vector::push_back(packages, pack) };
event::emit(PublishPackage { code_address: addr, is_upgrade: upgrade_number > 0 });
// Request publish if (features::code_dependency_check_enabled()) request_publish_with_allowed_deps(addr, module_names, allowed_deps, code, policy.policy) else // The new `request_publish_with_allowed_deps` has not yet rolled out, so call downwards // compatible code. request_publish(addr, module_names, code, policy.policy)}
freeze_code_object
public fun freeze_code_object(publisher: &signer, code_object: object::Object<code::PackageRegistry>)
Implementation
public fun freeze_code_object(publisher: &signer, code_object: Object<PackageRegistry>) acquires PackageRegistry { check_code_publishing_permission(publisher); let code_object_addr = object::object_address(&code_object); assert!(exists<PackageRegistry>(code_object_addr), error::not_found(ECODE_OBJECT_DOES_NOT_EXIST)); assert!( object::is_owner(code_object, signer::address_of(publisher)), error::permission_denied(ENOT_PACKAGE_OWNER) );
let registry = borrow_global_mut<PackageRegistry>(code_object_addr); vector::for_each_mut(&mut registry.packages, |pack| { let package: &mut PackageMetadata = pack; package.upgrade_policy = upgrade_policy_immutable(); });
// We unfortunately have to make a copy of each package to avoid borrow checker issues as check_dependencies // needs to borrow PackageRegistry from the dependency packages. // This would increase the amount of gas used, but this is a rare operation and it's rare to have many packages // in a single code object. vector::for_each(registry.packages, |pack| { check_dependencies(code_object_addr, &pack); });}
publish_package_txn
Same as publish_package
but as an entry function which can be called as a transaction. Because
of current restrictions for txn parameters, the metadata needs to be passed in serialized form.
public entry fun publish_package_txn(owner: &signer, metadata_serialized: vector<u8>, code: vector<vector<u8>>)
Implementation
public entry fun publish_package_txn(owner: &signer, metadata_serialized: vector<u8>, code: vector<vector<u8>>)acquires PackageRegistry { publish_package(owner, util::from_bytes<PackageMetadata>(metadata_serialized), code)}
check_upgradability
Checks whether the given package is upgradable, and returns true if a compatibility check is needed.
fun check_upgradability(old_pack: &code::PackageMetadata, new_pack: &code::PackageMetadata, new_modules: &vector<string::String>)
Implementation
fun check_upgradability( old_pack: &PackageMetadata, new_pack: &PackageMetadata, new_modules: &vector<String>) { assert!(old_pack.upgrade_policy.policy < upgrade_policy_immutable().policy, error::invalid_argument(EUPGRADE_IMMUTABLE)); assert!(can_change_upgrade_policy_to(old_pack.upgrade_policy, new_pack.upgrade_policy), error::invalid_argument(EUPGRADE_WEAKER_POLICY)); let old_modules = get_module_names(old_pack);
vector::for_each_ref(&old_modules, |old_module| { assert!( vector::contains(new_modules, old_module), EMODULE_MISSING ); });}
check_coexistence
Checks whether a new package with given names can co-exist with old package.
fun check_coexistence(old_pack: &code::PackageMetadata, new_modules: &vector<string::String>)
Implementation
fun check_coexistence(old_pack: &PackageMetadata, new_modules: &vector<String>) { // The modules introduced by each package must not overlap with `names`. vector::for_each_ref(&old_pack.modules, |old_mod| { let old_mod: &ModuleMetadata = old_mod; let j = 0; while (j < vector::length(new_modules)) { let name = vector::borrow(new_modules, j); assert!(&old_mod.name != name, error::already_exists(EMODULE_NAME_CLASH)); j = j + 1; }; });}
check_dependencies
Check that the upgrade policies of all packages are equal or higher quality than this package. Also compute the list of module dependencies which are allowed by the package metadata. The later is passed on to the native layer to verify that bytecode dependencies are actually what is pretended here.
fun check_dependencies(publish_address: address, pack: &code::PackageMetadata): vector<code::AllowedDep>
Implementation
fun check_dependencies(publish_address: address, pack: &PackageMetadata): vector<AllowedDep>acquires PackageRegistry { let allowed_module_deps = vector::empty(); let deps = &pack.deps; vector::for_each_ref(deps, |dep| { let dep: &PackageDep = dep; assert!(exists<PackageRegistry>(dep.account), error::not_found(EPACKAGE_DEP_MISSING)); if (is_policy_exempted_address(dep.account)) { // Allow all modules from this address, by using "" as a wildcard in the AllowedDep let account: address = dep.account; let module_name = string::utf8(b""); vector::push_back(&mut allowed_module_deps, AllowedDep { account, module_name }); } else { let registry = borrow_global<PackageRegistry>(dep.account); let found = vector::any(®istry.packages, |dep_pack| { let dep_pack: &PackageMetadata = dep_pack; if (dep_pack.name == dep.package_name) { // Check policy assert!( dep_pack.upgrade_policy.policy >= pack.upgrade_policy.policy, error::invalid_argument(EDEP_WEAKER_POLICY) ); if (dep_pack.upgrade_policy == upgrade_policy_arbitrary()) { assert!( dep.account == publish_address, error::invalid_argument(EDEP_ARBITRARY_NOT_SAME_ADDRESS) ) }; // Add allowed deps let account = dep.account; let k = 0; let r = vector::length(&dep_pack.modules); while (k < r) { let module_name = vector::borrow(&dep_pack.modules, k).name; vector::push_back(&mut allowed_module_deps, AllowedDep { account, module_name }); k = k + 1; }; true } else { false } }); assert!(found, error::not_found(EPACKAGE_DEP_MISSING)); }; }); allowed_module_deps}
is_policy_exempted_address
Core addresses which are exempted from the check that their policy matches the referring package. Without
this exemption, it would not be possible to define an immutable package based on the core system, which
requires to be upgradable for maintenance and evolution, and is configured to be compatible
.
fun is_policy_exempted_address(addr: address): bool
Implementation
fun is_policy_exempted_address(addr: address): bool { addr == @1 || addr == @2 || addr == @3 || addr == @4 || addr == @5 || addr == @6 || addr == @7 || addr == @8 || addr == @9 || addr == @10}
get_module_names
Get the names of the modules in a package.
fun get_module_names(pack: &code::PackageMetadata): vector<string::String>
Implementation
fun get_module_names(pack: &PackageMetadata): vector<String> { let module_names = vector::empty(); vector::for_each_ref(&pack.modules, |pack_module| { let pack_module: &ModuleMetadata = pack_module; vector::push_back(&mut module_names, pack_module.name); }); module_names}
request_publish
Native function to initiate module loading
fun request_publish(owner: address, expected_modules: vector<string::String>, bundle: vector<vector<u8>>, policy: u8)
Implementation
native fun request_publish( owner: address, expected_modules: vector<String>, bundle: vector<vector<u8>>, policy: u8);
request_publish_with_allowed_deps
Native function to initiate module loading, including a list of allowed dependencies.
fun request_publish_with_allowed_deps(owner: address, expected_modules: vector<string::String>, allowed_deps: vector<code::AllowedDep>, bundle: vector<vector<u8>>, policy: u8)
Implementation
native fun request_publish_with_allowed_deps( owner: address, expected_modules: vector<String>, allowed_deps: vector<AllowedDep>, bundle: vector<vector<u8>>, policy: u8);
Specification
High-level Requirements
No. | Requirement | Criticality | Implementation | Enforcement |
---|---|---|---|---|
1 | Updating a package should fail if the user is not the owner of it. | Critical | The publish_package function may only be able to update the package if the signer is the actual owner of the package. | The Aptos upgrade native functions have been manually audited. |
2 | The arbitrary upgrade policy should never be used. | Critical | There should never be a pass of an arbitrary upgrade policy to the request_publish native function. | Manually audited that it aborts if package.upgrade_policy.policy == 0. |
3 | Should perform accurate compatibility checks when the policy indicates compatibility, ensuring it meets the required conditions. | Critical | Specifies if it should perform compatibility checks for upgrades. The check only passes if a new module has (a) the same public functions, and (b) for existing resources, no layout change. | The Move upgradability patterns have been manually audited. |
4 | Package upgrades should abide by policy change rules. In particular, The new upgrade policy must be equal to or stricter when compared to the old one. The original upgrade policy must not be immutable. The new package must contain all modules contained in the old package. | Medium | A package may only be updated using the publish_package function when the check_upgradability function returns true. | This is audited by a manual review of the check_upgradability patterns. |
5 | The upgrade policy of a package must not exceed the strictness level imposed by its dependencies. | Medium | The upgrade_policy of a package may only be less than its dependencies throughout the upgrades. In addition, the native code properly restricts the use of dependencies outside the passed-in metadata. | This has been manually audited. |
6 | The extension for package metadata is currently unused. | Medium | The extension field in PackageMetadata should be unused. | Data invariant on the extension field has been manually audited. |
7 | The upgrade number of a package increases incrementally in a monotonic manner with each subsequent upgrade. | Low | On each upgrade of a particular package, the publish_package function updates the upgrade_number for that package. | Post condition on upgrade_number has been manually audited. |
Module-level Specification
pragma verify = true;pragma aborts_if_is_partial;
initialize
fun initialize(aptos_framework: &signer, package_owner: &signer, metadata: code::PackageMetadata)
let aptos_addr = signer::address_of(aptos_framework);let owner_addr = signer::address_of(package_owner);aborts_if !system_addresses::is_aptos_framework_address(aptos_addr);ensures exists<PackageRegistry>(owner_addr);
publish_package
public fun publish_package(owner: &signer, pack: code::PackageMetadata, code: vector<vector<u8>>)
pragma aborts_if_is_partial;let addr = signer::address_of(owner);modifies global<PackageRegistry>(addr);aborts_if pack.upgrade_policy.policy <= upgrade_policy_arbitrary().policy;
freeze_code_object
public fun freeze_code_object(publisher: &signer, code_object: object::Object<code::PackageRegistry>)
pragma aborts_if_is_partial;let code_object_addr = code_object.inner;aborts_if !exists<object::ObjectCore>(code_object_addr);aborts_if !exists<PackageRegistry>(code_object_addr);aborts_if !object::is_owner(code_object, signer::address_of(publisher));modifies global<PackageRegistry>(code_object_addr);
publish_package_txn
public entry fun publish_package_txn(owner: &signer, metadata_serialized: vector<u8>, code: vector<vector<u8>>)
pragma verify = false;
check_upgradability
fun check_upgradability(old_pack: &code::PackageMetadata, new_pack: &code::PackageMetadata, new_modules: &vector<string::String>)
pragma aborts_if_is_partial;aborts_if old_pack.upgrade_policy.policy >= upgrade_policy_immutable().policy;aborts_if !can_change_upgrade_policy_to(old_pack.upgrade_policy, new_pack.upgrade_policy);
check_coexistence
fun check_coexistence(old_pack: &code::PackageMetadata, new_modules: &vector<string::String>)
pragma verify = false;
check_dependencies
fun check_dependencies(publish_address: address, pack: &code::PackageMetadata): vector<code::AllowedDep>
pragma verify = false;
get_module_names
fun get_module_names(pack: &code::PackageMetadata): vector<string::String>
pragma opaque;aborts_if [abstract] false;ensures [abstract] len(result) == len(pack.modules);ensures [abstract] forall i in 0..len(result): result[i] == pack.modules[i].name;
request_publish
fun request_publish(owner: address, expected_modules: vector<string::String>, bundle: vector<vector<u8>>, policy: u8)
pragma opaque;
request_publish_with_allowed_deps
fun request_publish_with_allowed_deps(owner: address, expected_modules: vector<string::String>, allowed_deps: vector<code::AllowedDep>, bundle: vector<vector<u8>>, policy: u8)
pragma opaque;
schema AbortsIfPermissionedSigner { s: signer; let perm = CodePublishingPermission {}; aborts_if !permissioned_signer::spec_check_permission_exists(s, perm);}