| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- module pyth::state {
- use std::vector;
- use sui::object::{Self, UID, ID};
- use sui::tx_context::{Self, TxContext};
- use sui::package::{UpgradeCap, UpgradeTicket, UpgradeReceipt};
- use pyth::data_source::{Self, DataSource};
- use pyth::price_info::{Self};
- use pyth::price_identifier::{Self, PriceIdentifier};
- use pyth::version_control::{Self};
- use wormhole::consumed_vaas::{Self, ConsumedVAAs};
- use wormhole::bytes32::{Self, Bytes32};
- use wormhole::package_utils::{Self};
- use wormhole::external_address::{ExternalAddress};
- friend pyth::pyth;
- #[test_only]
- friend pyth::pyth_tests;
- friend pyth::governance_action;
- friend pyth::set_update_fee;
- friend pyth::set_stale_price_threshold;
- friend pyth::set_data_sources;
- friend pyth::governance;
- friend pyth::set_governance_data_source;
- friend pyth::migrate;
- friend pyth::contract_upgrade;
- friend pyth::set_fee_recipient;
- friend pyth::setup;
- /// Build digest does not agree with current implementation.
- const E_INVALID_BUILD_DIGEST: u64 = 0;
- /// Capability reflecting that the current build version is used to invoke
- /// state methods.
- struct LatestOnly has drop {}
- #[test_only]
- public fun create_latest_only_for_test():LatestOnly {
- LatestOnly{}
- }
- struct State has key, store {
- id: UID,
- governance_data_source: DataSource,
- stale_price_threshold: u64,
- base_update_fee: u64,
- fee_recipient_address: address,
- last_executed_governance_sequence: u64,
- consumed_vaas: ConsumedVAAs,
- // Upgrade capability.
- upgrade_cap: UpgradeCap
- }
- public(friend) fun new(
- upgrade_cap: UpgradeCap,
- sources: vector<DataSource>,
- governance_data_source: DataSource,
- stale_price_threshold: u64,
- base_update_fee: u64,
- ctx: &mut TxContext
- ): State {
- let uid = object::new(ctx);
- // Create a set that contains all registered data sources and
- // attach it to uid as a dynamic field to minimize the
- // size of State.
- data_source::new_data_source_registry(&mut uid, ctx);
- // Create a table that tracks the object IDs of price feeds and
- // attach it to the uid as a dynamic object field to minimize the
- // size of State.
- price_info::new_price_info_registry(&mut uid, ctx);
- while (!vector::is_empty(&sources)) {
- data_source::add(&mut uid, vector::pop_back(&mut sources));
- };
- let consumed_vaas = consumed_vaas::new(ctx);
- // Initialize package info. This will be used for emitting information
- // of successful migrations.
- package_utils::init_package_info(
- &mut uid,
- version_control::current_version(),
- &upgrade_cap
- );
- State {
- id: uid,
- upgrade_cap,
- governance_data_source,
- stale_price_threshold,
- fee_recipient_address: tx_context::sender(ctx),
- base_update_fee,
- consumed_vaas,
- last_executed_governance_sequence: 0
- }
- }
- ////////////////////////////////////////////////////////////////////////////
- //
- // Simple Getters
- //
- // These methods do not require `LatestOnly` for access. Anyone is free to
- // access these values.
- //
- ////////////////////////////////////////////////////////////////////////////
- public fun get_stale_price_threshold_secs(s: &State): u64 {
- s.stale_price_threshold
- }
- public fun get_base_update_fee(s: &State): u64 {
- s.base_update_fee
- }
- public fun get_fee_recipient(s: &State): address {
- s.fee_recipient_address
- }
- public fun is_valid_data_source(s: &State, data_source: DataSource): bool {
- data_source::contains(&s.id, data_source)
- }
- public fun is_valid_governance_data_source(s: &State, source: DataSource): bool {
- s.governance_data_source == source
- }
- public fun price_feed_object_exists(s: &State, p: PriceIdentifier): bool {
- price_info::contains(&s.id, p)
- }
- /// Retrieve governance chain ID, which is governance's emitter chain ID.
- public fun governance_data_source(self: &State): DataSource {
- self.governance_data_source
- }
- public fun get_last_executed_governance_sequence(self: &State): u64{
- return self.last_executed_governance_sequence
- }
- /// Retrieve governance module name.
- public fun governance_module(): Bytes32 {
- bytes32::new(
- x"0000000000000000000000000000000000000000000000000000000000000001"
- )
- }
- /// Retrieve governance chain ID, which is governance's emitter chain ID.
- public fun governance_chain(self: &State): u16 {
- let governance_data_source = governance_data_source(self);
- (data_source::emitter_chain(&governance_data_source) as u16)
- }
- /// Retrieve governance emitter address.
- public fun governance_contract(self: &State): ExternalAddress {
- let governance_data_source = governance_data_source(self);
- data_source::emitter_address(&governance_data_source)
- }
- public fun get_price_info_object_id(self: &State, price_identifier_bytes: vector<u8>): ID {
- let price_identifier = price_identifier::from_byte_vec(price_identifier_bytes);
- price_info::get_id(&self.id, price_identifier)
- }
- ////////////////////////////////////////////////////////////////////////////
- //
- // Privileged `State` Access
- //
- // This section of methods require a `LatestOnly`, which can only be created
- // within the Wormhole package. This capability allows special access to
- // the `State` object.
- //
- // 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 {}
- }
- public(friend) fun set_fee_recipient(
- _: &LatestOnly,
- self: &mut State,
- addr: address
- ) {
- self.fee_recipient_address = addr;
- }
- /// Store `VAA` hash as a way to claim a VAA. This method prevents a VAA
- /// from being replayed. For Wormhole, the only VAAs that it cares about
- /// being replayed are its governance actions.
- 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. For Wormhole, the only VAAs that it cares about
- /// being replayed are its governance actions.
- ///
- /// 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
- }
- public(friend) fun current_package(_: &LatestOnly, self: &State): ID {
- package_utils::current_package(&self.id)
- }
- public(friend) fun set_data_sources(_: &LatestOnly, s: &mut State, new_sources: vector<DataSource>) {
- // Empty the existing table of data sources registered in state.
- data_source::empty(&mut s.id);
- // Add the new data sources to the dynamic field registry.
- while (!vector::is_empty(&new_sources)) {
- data_source::add(&mut s.id, vector::pop_back(&mut new_sources));
- };
- }
- public(friend) fun register_price_info_object(_: &LatestOnly, s: &mut State, price_identifier: PriceIdentifier, id: ID) {
- price_info::add(&mut s.id, price_identifier, id);
- }
- public(friend) fun set_governance_data_source(_: &LatestOnly, s: &mut State, source: DataSource) {
- s.governance_data_source = source;
- }
- public(friend) fun set_last_executed_governance_sequence(_: &LatestOnly, s: &mut State, sequence: u64) {
- s.last_executed_governance_sequence = sequence;
- }
- // We have an unchecked version of set_last_executed_governance_sequence, because in the governance contract
- // upgrade code path, no LatestOnly is created (for example, see authorize_upgrade and commit_upgrade in
- // governance/contract_upgrade.move)
- public(friend) fun set_last_executed_governance_sequence_unchecked(s: &mut State, sequence: u64) {
- s.last_executed_governance_sequence = sequence;
- }
- public(friend) fun set_base_update_fee(_: &LatestOnly, s: &mut State, fee: u64) {
- s.base_update_fee = fee;
- }
- public(friend) fun set_stale_price_threshold_secs(_: &LatestOnly, s: &mut State, threshold_secs: u64) {
- s.stale_price_threshold = threshold_secs;
- }
- ////////////////////////////////////////////////////////////////////////////
- //
- // 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.
- //
- ////////////////////////////////////////////////////////////////////////////
- public(friend) fun migrate__v__0_1_1(self: &mut State) {
- // We need to add dynamic fields via the new package utils method. These
- // fields do not exist in the previous build (0.1.0).
- // See `state::new` above.
- // Initialize package info. This will be used for emitting information
- // of successful migrations.
- let upgrade_cap = &self.upgrade_cap;
- package_utils::init_package_info(
- &mut self.id,
- version_control::current_version(),
- upgrade_cap,
- );
- }
- #[test_only]
- /// Bloody hack.
- public fun reverse_migrate__v__0_1_0(self: &mut State) {
- package_utils::remove_package_info(&mut self.id);
- // Add back in old dynamic field(s)...
- // Add dummy hash since this is the first time the package is published.
- sui::dynamic_field::add(&mut self.id, CurrentDigest {}, bytes32::from_bytes(b"new build"));
- }
- ////////////////////////////////////////////////////////////////////////////
- //
- // Deprecated
- //
- // Dumping grounds for old structs and methods. These things should not
- // be used in future builds.
- //
- ////////////////////////////////////////////////////////////////////////////
- struct CurrentDigest has store, drop, copy {}
- }
|