// SPDX-License-Identifier: Apache 2 /// This module implements the global state variables for Token Bridge as a /// shared object. The `State` object is used to perform anything that requires /// access to data that defines the Token Bridge contract. Examples of which are /// accessing registered assets and verifying `VAA` intended for Token Bridge by /// checking the emitter against its own registered emitters. module token_bridge::state { use sui::object::{Self, ID, UID}; use sui::package::{UpgradeCap, UpgradeReceipt, UpgradeTicket}; use sui::table::{Self, Table}; use sui::tx_context::{TxContext}; use wormhole::bytes32::{Self, Bytes32}; use wormhole::consumed_vaas::{Self, ConsumedVAAs}; use wormhole::emitter::{EmitterCap}; use wormhole::external_address::{ExternalAddress}; use wormhole::package_utils::{Self}; use wormhole::publish_message::{MessageTicket}; use token_bridge::token_registry::{Self, TokenRegistry, VerifiedAsset}; use token_bridge::version_control::{Self}; /// Build digest does not agree with current implementation. const E_INVALID_BUILD_DIGEST: u64 = 0; /// Specified version does not match this build's version. const E_VERSION_MISMATCH: u64 = 1; /// Emitter has already been used to emit Wormhole messages. const E_USED_EMITTER: u64 = 2; friend token_bridge::attest_token; friend token_bridge::complete_transfer; friend token_bridge::complete_transfer_with_payload; friend token_bridge::create_wrapped; friend token_bridge::migrate; friend token_bridge::register_chain; friend token_bridge::setup; friend token_bridge::transfer_tokens; friend token_bridge::transfer_tokens_with_payload; friend token_bridge::upgrade_contract; friend token_bridge::vaa; /// Capability reflecting that the current build version is used to invoke /// state methods. struct LatestOnly has drop {} /// Container for all state variables for Token Bridge. struct State has key, store { id: UID, /// Governance chain ID. governance_chain: u16, /// Governance contract address. governance_contract: ExternalAddress, /// Set of consumed VAA hashes. consumed_vaas: ConsumedVAAs, /// Emitter capability required to publish Wormhole messages. emitter_cap: EmitterCap, /// Registry for foreign Token Bridge contracts. emitter_registry: Table, /// Registry for native and wrapped assets. token_registry: TokenRegistry, /// Upgrade capability. upgrade_cap: UpgradeCap } /// Create new `State`. This is only executed using the `setup` module. public(friend) fun new( emitter_cap: EmitterCap, upgrade_cap: UpgradeCap, governance_chain: u16, governance_contract: ExternalAddress, ctx: &mut TxContext ): State { assert!(wormhole::emitter::sequence(&emitter_cap) == 0, E_USED_EMITTER); let state = State { id: object::new(ctx), governance_chain, governance_contract, consumed_vaas: consumed_vaas::new(ctx), emitter_cap, emitter_registry: table::new(ctx), token_registry: token_registry::new(ctx), upgrade_cap }; // Set first version and initialize package info. This will be used for // emitting information of successful migrations. let upgrade_cap = &state.upgrade_cap; package_utils::init_package_info( &mut state.id, version_control::current_version(), upgrade_cap ); state } //////////////////////////////////////////////////////////////////////////// // // Simple Getters // // These methods do not require `LatestOnly` for access. Anyone is free to // access these values. // //////////////////////////////////////////////////////////////////////////// /// Retrieve governance module name. public fun governance_module(): Bytes32 { // A.K.A. "TokenBridge". bytes32::new( x"000000000000000000000000000000000000000000546f6b656e427269646765" ) } /// Retrieve governance chain ID, which is governance's emitter chain ID. public fun governance_chain(self: &State): u16 { self.governance_chain } /// Retrieve governance emitter address. public fun governance_contract(self: &State): ExternalAddress { self.governance_contract } /// Retrieve immutable reference to `TokenRegistry`. public fun borrow_token_registry( self: &State ): &TokenRegistry { &self.token_registry } public fun borrow_emitter_registry( self: &State ): &Table { &self.emitter_registry } public fun verified_asset( self: &State ): VerifiedAsset { token_registry::assert_has(&self.token_registry); token_registry::verified_asset(&self.token_registry) } #[test_only] public fun borrow_mut_token_registry_test_only( self: &mut State ): &mut TokenRegistry { borrow_mut_token_registry(&assert_latest_only(self), self) } #[test_only] public fun migrate_version_test_only( self: &mut State, old_version: Old, new_version: New ) { wormhole::package_utils::update_version_type_test_only( &mut self.id, old_version, new_version ); } #[test_only] public fun test_upgrade(self: &mut State) { let test_digest = bytes32::from_bytes(b"new build"); let ticket = authorize_upgrade(self, test_digest); let receipt = sui::package::test_upgrade(ticket); commit_upgrade(self, receipt); } #[test_only] public fun reverse_migrate_version(self: &mut State) { package_utils::update_version_type_test_only( &mut self.id, version_control::current_version(), version_control::previous_version() ); } //////////////////////////////////////////////////////////////////////////// // // Privileged `State` Access // // This section of methods require a `LatestOnly`, which can only be // created within the Token Bridge package. This capability allows special // access to the `State` object where we require that the latest build is // used for these interactions. // // NOTE: A lot of these methods are still marked as `(friend)` as a safety // precaution. When a package is upgraded, friend modifiers can be // removed. // //////////////////////////////////////////////////////////////////////////// /// Obtain a capability to interact with `State` methods. This method checks /// that we are running the current build. /// /// NOTE: This method allows caching the current version check so we avoid /// multiple checks to dynamic fields. public(friend) fun assert_latest_only(self: &State): LatestOnly { package_utils::assert_version( &self.id, version_control::current_version() ); LatestOnly {} } /// Obtain a capability to interact with `State` methods. This method checks /// that we are running the current build and that the specified `Version` /// equals the current version. This method is useful when external modules /// invoke Token Bridge and we need to check that the external module's /// version is up-to-date (e.g. `create_wrapped::prepare_registration`). /// /// NOTE: This method allows caching the current version check so we avoid /// multiple checks to dynamic fields. public(friend) fun assert_latest_only_specified( self: &State ): LatestOnly { use std::type_name::{get}; // Explicitly check the type names. let current_type = package_utils::type_of_version(version_control::current_version()); assert!(current_type == get(), E_VERSION_MISMATCH); assert_latest_only(self) } /// Store `VAA` hash as a way to claim a VAA. This method prevents a VAA /// from being replayed. public(friend) fun borrow_mut_consumed_vaas( _: &LatestOnly, self: &mut State ): &mut ConsumedVAAs { borrow_mut_consumed_vaas_unchecked(self) } /// Store `VAA` hash as a way to claim a VAA. This method prevents a VAA /// from being replayed. /// /// NOTE: This method does not require `LatestOnly`. Only methods in the /// `upgrade_contract` module requires this to be unprotected to prevent /// a corrupted upgraded contract from bricking upgradability. public(friend) fun borrow_mut_consumed_vaas_unchecked( self: &mut State ): &mut ConsumedVAAs { &mut self.consumed_vaas } /// Publish Wormhole message using Token Bridge's `EmitterCap`. public(friend) fun prepare_wormhole_message( _: &LatestOnly, self: &mut State, nonce: u32, payload: vector ): MessageTicket { wormhole::publish_message::prepare_message( &mut self.emitter_cap, nonce, payload, ) } /// Retrieve mutable reference to `TokenRegistry`. public(friend) fun borrow_mut_token_registry( _: &LatestOnly, self: &mut State ): &mut TokenRegistry { &mut self.token_registry } public(friend) fun borrow_mut_emitter_registry( _: &LatestOnly, self: &mut State ): &mut Table { &mut self.emitter_registry } public(friend) fun current_package(_: &LatestOnly, self: &State): ID { package_utils::current_package(&self.id) } //////////////////////////////////////////////////////////////////////////// // // Upgradability // // A special space that controls upgrade logic. These methods are invoked // via the `upgrade_contract` module. // // Also in this section is managing contract migrations, which uses the // `migrate` module to officially roll state access to the latest build. // Only those methods that require `LatestOnly` will be affected by an // upgrade. // //////////////////////////////////////////////////////////////////////////// /// Issue an `UpgradeTicket` for the upgrade. /// /// NOTE: The Sui VM performs a check that this method is executed from the /// latest published package. If someone were to try to execute this using /// a stale build, the transaction will revert with `PackageUpgradeError`, /// specifically `PackageIDDoesNotMatch`. public(friend) fun authorize_upgrade( self: &mut State, package_digest: Bytes32 ): UpgradeTicket { let cap = &mut self.upgrade_cap; package_utils::authorize_upgrade(&mut self.id, cap, package_digest) } /// Finalize the upgrade that ran to produce the given `receipt`. /// /// NOTE: The Sui VM performs a check that this method is executed from the /// latest published package. If someone were to try to execute this using /// a stale build, the transaction will revert with `PackageUpgradeError`, /// specifically `PackageIDDoesNotMatch`. public(friend) fun commit_upgrade( self: &mut State, receipt: UpgradeReceipt ): (ID, ID) { let cap = &mut self.upgrade_cap; package_utils::commit_upgrade(&mut self.id, cap, receipt) } /// Method executed by the `migrate` module to roll access from one package /// to another. This method will be called from the upgraded package. public(friend) fun migrate_version(self: &mut State) { package_utils::migrate_version( &mut self.id, version_control::previous_version(), version_control::current_version() ); } /// As a part of the migration, we verify that the upgrade contract VAA's /// encoded package digest used in `migrate` equals the one used to conduct /// the upgrade. public(friend) fun assert_authorized_digest( _: &LatestOnly, self: &State, digest: Bytes32 ) { let authorized = package_utils::authorized_digest(&self.id); assert!(digest == authorized, E_INVALID_BUILD_DIGEST); } //////////////////////////////////////////////////////////////////////////// // // Special State Interaction via Migrate // // A VERY special space that manipulates `State` via calling `migrate`. // // PLEASE KEEP ANY METHODS HERE AS FRIENDS. We want the ability to remove // these for future builds. // //////////////////////////////////////////////////////////////////////////// /// This method is used to make modifications to `State` when `migrate` is /// called. This method name should change reflecting which version this /// contract is migrating to. /// /// NOTE: Please keep this method as public(friend) because we never want /// to expose this method as a public method. public(friend) fun migrate__v__0_2_0(_self: &mut State) { // Intentionally do nothing. } #[test_only] /// Bloody hack. /// /// This method is used to set up tests where we migrate to a new version, /// which is meant to test that modules protected by version control will /// break. public fun reverse_migrate__v__dummy(_self: &mut State) { // Intentionally do nothing. } //////////////////////////////////////////////////////////////////////////// // // Deprecated // // Dumping grounds for old structs and methods. These things should not // be used in future builds. // //////////////////////////////////////////////////////////////////////////// }