state.move 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. module pyth::state {
  2. use std::vector;
  3. use sui::object::{Self, UID, ID};
  4. use sui::tx_context::{Self, TxContext};
  5. use sui::package::{UpgradeCap, UpgradeTicket, UpgradeReceipt};
  6. use pyth::data_source::{Self, DataSource};
  7. use pyth::price_info::{Self};
  8. use pyth::price_identifier::{Self, PriceIdentifier};
  9. use pyth::version_control::{Self};
  10. use wormhole::consumed_vaas::{Self, ConsumedVAAs};
  11. use wormhole::bytes32::{Self, Bytes32};
  12. use wormhole::package_utils::{Self};
  13. use wormhole::external_address::{ExternalAddress};
  14. friend pyth::pyth;
  15. #[test_only]
  16. friend pyth::pyth_tests;
  17. friend pyth::governance_action;
  18. friend pyth::set_update_fee;
  19. friend pyth::set_stale_price_threshold;
  20. friend pyth::set_data_sources;
  21. friend pyth::governance;
  22. friend pyth::set_governance_data_source;
  23. friend pyth::migrate;
  24. friend pyth::contract_upgrade;
  25. friend pyth::set_fee_recipient;
  26. friend pyth::setup;
  27. /// Build digest does not agree with current implementation.
  28. const E_INVALID_BUILD_DIGEST: u64 = 0;
  29. /// Specified version does not match this build's version.
  30. const E_VERSION_MISMATCH: u64 = 1;
  31. /// Sui's chain ID is hard-coded to one value.
  32. const CHAIN_ID: u16 = 21;
  33. /// Capability reflecting that the current build version is used to invoke
  34. /// state methods.
  35. struct LatestOnly has drop {}
  36. #[test_only]
  37. public fun create_latest_only_for_test():LatestOnly {
  38. LatestOnly{}
  39. }
  40. struct State has key, store {
  41. id: UID,
  42. governance_data_source: DataSource,
  43. stale_price_threshold: u64,
  44. base_update_fee: u64,
  45. fee_recipient_address: address,
  46. last_executed_governance_sequence: u64,
  47. consumed_vaas: ConsumedVAAs,
  48. // Upgrade capability.
  49. upgrade_cap: UpgradeCap
  50. }
  51. public(friend) fun new(
  52. upgrade_cap: UpgradeCap,
  53. sources: vector<DataSource>,
  54. governance_data_source: DataSource,
  55. stale_price_threshold: u64,
  56. base_update_fee: u64,
  57. ctx: &mut TxContext
  58. ): State {
  59. let uid = object::new(ctx);
  60. // Create a set that contains all registered data sources and
  61. // attach it to uid as a dynamic field to minimize the
  62. // size of State.
  63. data_source::new_data_source_registry(&mut uid, ctx);
  64. // Create a table that tracks the object IDs of price feeds and
  65. // attach it to the uid as a dynamic object field to minimize the
  66. // size of State.
  67. price_info::new_price_info_registry(&mut uid, ctx);
  68. while (!vector::is_empty(&sources)) {
  69. data_source::add(&mut uid, vector::pop_back(&mut sources));
  70. };
  71. let consumed_vaas = consumed_vaas::new(ctx);
  72. // Initialize package info. This will be used for emitting information
  73. // of successful migrations.
  74. package_utils::init_package_info(
  75. &mut uid,
  76. version_control::current_version(),
  77. &upgrade_cap
  78. );
  79. State {
  80. id: uid,
  81. upgrade_cap,
  82. governance_data_source,
  83. stale_price_threshold,
  84. fee_recipient_address: tx_context::sender(ctx),
  85. base_update_fee,
  86. consumed_vaas,
  87. last_executed_governance_sequence: 0
  88. }
  89. }
  90. ////////////////////////////////////////////////////////////////////////////
  91. //
  92. // Simple Getters
  93. //
  94. // These methods do not require `LatestOnly` for access. Anyone is free to
  95. // access these values.
  96. //
  97. ////////////////////////////////////////////////////////////////////////////
  98. public fun get_stale_price_threshold_secs(s: &State): u64 {
  99. s.stale_price_threshold
  100. }
  101. public fun get_base_update_fee(s: &State): u64 {
  102. s.base_update_fee
  103. }
  104. public fun get_fee_recipient(s: &State): address {
  105. s.fee_recipient_address
  106. }
  107. public fun is_valid_data_source(s: &State, data_source: DataSource): bool {
  108. data_source::contains(&s.id, data_source)
  109. }
  110. public fun is_valid_governance_data_source(s: &State, source: DataSource): bool {
  111. s.governance_data_source == source
  112. }
  113. public fun price_feed_object_exists(s: &State, p: PriceIdentifier): bool {
  114. price_info::contains(&s.id, p)
  115. }
  116. /// Retrieve governance chain ID, which is governance's emitter chain ID.
  117. public fun governance_data_source(self: &State): DataSource {
  118. self.governance_data_source
  119. }
  120. public fun get_last_executed_governance_sequence(self: &State): u64{
  121. return self.last_executed_governance_sequence
  122. }
  123. /// Retrieve governance module name.
  124. public fun governance_module(): Bytes32 {
  125. bytes32::new(
  126. x"0000000000000000000000000000000000000000000000000000000000000001"
  127. )
  128. }
  129. /// Retrieve governance chain ID, which is governance's emitter chain ID.
  130. public fun governance_chain(self: &State): u16 {
  131. let governance_data_source = governance_data_source(self);
  132. (data_source::emitter_chain(&governance_data_source) as u16)
  133. }
  134. /// Retrieve governance emitter address.
  135. public fun governance_contract(self: &State): ExternalAddress {
  136. let governance_data_source = governance_data_source(self);
  137. data_source::emitter_address(&governance_data_source)
  138. }
  139. public fun get_price_info_object_id(self: &State, price_identifier_bytes: vector<u8>): ID {
  140. let price_identifier = price_identifier::from_byte_vec(price_identifier_bytes);
  141. price_info::get_id(&self.id, price_identifier)
  142. }
  143. ////////////////////////////////////////////////////////////////////////////
  144. //
  145. // Privileged `State` Access
  146. //
  147. // This section of methods require a `LatestOnly`, which can only be created
  148. // within the Wormhole package. This capability allows special access to
  149. // the `State` object.
  150. //
  151. // NOTE: A lot of these methods are still marked as `(friend)` as a safety
  152. // precaution. When a package is upgraded, friend modifiers can be
  153. // removed.
  154. //
  155. ////////////////////////////////////////////////////////////////////////////
  156. /// Obtain a capability to interact with `State` methods. This method checks
  157. /// that we are running the current build.
  158. ///
  159. /// NOTE: This method allows caching the current version check so we avoid
  160. /// multiple checks to dynamic fields.
  161. public(friend) fun assert_latest_only(self: &State): LatestOnly {
  162. package_utils::assert_version(
  163. &self.id,
  164. version_control::current_version()
  165. );
  166. LatestOnly {}
  167. }
  168. public(friend) fun set_fee_recipient(
  169. _: &LatestOnly,
  170. self: &mut State,
  171. addr: address
  172. ) {
  173. self.fee_recipient_address = addr;
  174. }
  175. /// Store `VAA` hash as a way to claim a VAA. This method prevents a VAA
  176. /// from being replayed. For Wormhole, the only VAAs that it cares about
  177. /// being replayed are its governance actions.
  178. public(friend) fun borrow_mut_consumed_vaas(
  179. _: &LatestOnly,
  180. self: &mut State
  181. ): &mut ConsumedVAAs {
  182. borrow_mut_consumed_vaas_unchecked(self)
  183. }
  184. /// Store `VAA` hash as a way to claim a VAA. This method prevents a VAA
  185. /// from being replayed. For Wormhole, the only VAAs that it cares about
  186. /// being replayed are its governance actions.
  187. ///
  188. /// NOTE: This method does not require `LatestOnly`. Only methods in the
  189. /// `upgrade_contract` module requires this to be unprotected to prevent
  190. /// a corrupted upgraded contract from bricking upgradability.
  191. public(friend) fun borrow_mut_consumed_vaas_unchecked(
  192. self: &mut State
  193. ): &mut ConsumedVAAs {
  194. &mut self.consumed_vaas
  195. }
  196. public(friend) fun current_package(_: &LatestOnly, self: &State): ID {
  197. package_utils::current_package(&self.id)
  198. }
  199. public(friend) fun set_data_sources(_: &LatestOnly, s: &mut State, new_sources: vector<DataSource>) {
  200. // Empty the existing table of data sources registered in state.
  201. data_source::empty(&mut s.id);
  202. // Add the new data sources to the dynamic field registry.
  203. while (!vector::is_empty(&new_sources)) {
  204. data_source::add(&mut s.id, vector::pop_back(&mut new_sources));
  205. };
  206. }
  207. public(friend) fun register_price_info_object(_: &LatestOnly, s: &mut State, price_identifier: PriceIdentifier, id: ID) {
  208. price_info::add(&mut s.id, price_identifier, id);
  209. }
  210. public(friend) fun set_governance_data_source(_: &LatestOnly, s: &mut State, source: DataSource) {
  211. s.governance_data_source = source;
  212. }
  213. public(friend) fun set_last_executed_governance_sequence(_: &LatestOnly, s: &mut State, sequence: u64) {
  214. s.last_executed_governance_sequence = sequence;
  215. }
  216. public(friend) fun set_base_update_fee(_: &LatestOnly, s: &mut State, fee: u64) {
  217. s.base_update_fee = fee;
  218. }
  219. public(friend) fun set_stale_price_threshold_secs(_: &LatestOnly, s: &mut State, threshold_secs: u64) {
  220. s.stale_price_threshold = threshold_secs;
  221. }
  222. ////////////////////////////////////////////////////////////////////////////
  223. //
  224. // Upgradability
  225. //
  226. // A special space that controls upgrade logic. These methods are invoked
  227. // via the `upgrade_contract` module.
  228. //
  229. // Also in this section is managing contract migrations, which uses the
  230. // `migrate` module to officially roll state access to the latest build.
  231. // Only those methods that require `LatestOnly` will be affected by an
  232. // upgrade.
  233. //
  234. ////////////////////////////////////////////////////////////////////////////
  235. /// Issue an `UpgradeTicket` for the upgrade.
  236. ///
  237. /// NOTE: The Sui VM performs a check that this method is executed from the
  238. /// latest published package. If someone were to try to execute this using
  239. /// a stale build, the transaction will revert with `PackageUpgradeError`,
  240. /// specifically `PackageIDDoesNotMatch`.
  241. public(friend) fun authorize_upgrade(
  242. self: &mut State,
  243. package_digest: Bytes32
  244. ): UpgradeTicket {
  245. let cap = &mut self.upgrade_cap;
  246. package_utils::authorize_upgrade(&mut self.id, cap, package_digest)
  247. }
  248. /// Finalize the upgrade that ran to produce the given `receipt`.
  249. ///
  250. /// NOTE: The Sui VM performs a check that this method is executed from the
  251. /// latest published package. If someone were to try to execute this using
  252. /// a stale build, the transaction will revert with `PackageUpgradeError`,
  253. /// specifically `PackageIDDoesNotMatch`.
  254. public(friend) fun commit_upgrade(
  255. self: &mut State,
  256. receipt: UpgradeReceipt
  257. ): (ID, ID) {
  258. let cap = &mut self.upgrade_cap;
  259. package_utils::commit_upgrade(&mut self.id, cap, receipt)
  260. }
  261. /// Method executed by the `migrate` module to roll access from one package
  262. /// to another. This method will be called from the upgraded package.
  263. public(friend) fun migrate_version(self: &mut State) {
  264. package_utils::migrate_version(
  265. &mut self.id,
  266. version_control::previous_version(),
  267. version_control::current_version()
  268. );
  269. }
  270. /// As a part of the migration, we verify that the upgrade contract VAA's
  271. /// encoded package digest used in `migrate` equals the one used to conduct
  272. /// the upgrade.
  273. public(friend) fun assert_authorized_digest(
  274. _: &LatestOnly,
  275. self: &State,
  276. digest: Bytes32
  277. ) {
  278. let authorized = package_utils::authorized_digest(&self.id);
  279. assert!(digest == authorized, E_INVALID_BUILD_DIGEST);
  280. }
  281. ////////////////////////////////////////////////////////////////////////////
  282. //
  283. // Special State Interaction via Migrate
  284. //
  285. // A VERY special space that manipulates `State` via calling `migrate`.
  286. //
  287. // PLEASE KEEP ANY METHODS HERE AS FRIENDS. We want the ability to remove
  288. // these for future builds.
  289. //
  290. ////////////////////////////////////////////////////////////////////////////
  291. public(friend) fun migrate__v__0_1_1(self: &mut State) {
  292. // We need to add dynamic fields via the new package utils method. These
  293. // fields do not exist in the previous build (0.1.0).
  294. // See `state::new` above.
  295. // Initialize package info. This will be used for emitting information
  296. // of successful migrations.
  297. let upgrade_cap = &self.upgrade_cap;
  298. package_utils::init_package_info(
  299. &mut self.id,
  300. version_control::current_version(),
  301. upgrade_cap,
  302. );
  303. }
  304. #[test_only]
  305. /// Bloody hack.
  306. public fun reverse_migrate__v__0_1_0(self: &mut State) {
  307. package_utils::remove_package_info(&mut self.id);
  308. // Add back in old dynamic field(s)...
  309. // Add dummy hash since this is the first time the package is published.
  310. sui::dynamic_field::add(&mut self.id, CurrentDigest {}, bytes32::from_bytes(b"new build"));
  311. }
  312. ////////////////////////////////////////////////////////////////////////////
  313. //
  314. // Deprecated
  315. //
  316. // Dumping grounds for old structs and methods. These things should not
  317. // be used in future builds.
  318. //
  319. ////////////////////////////////////////////////////////////////////////////
  320. struct CurrentDigest has store, drop, copy {}
  321. }