workspaces.rs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206
  1. use {
  2. byteorder::BigEndian,
  3. near_sdk::json_types::U128,
  4. pyth::{
  5. governance::{
  6. GovernanceAction,
  7. GovernanceInstruction,
  8. GovernanceModule,
  9. },
  10. state::{
  11. Chain,
  12. Price,
  13. PriceIdentifier,
  14. Source,
  15. },
  16. },
  17. pyth_wormhole_attester_sdk::{
  18. BatchPriceAttestation,
  19. Identifier,
  20. PriceAttestation,
  21. PriceStatus,
  22. },
  23. pythnet_sdk::{
  24. accumulators::{
  25. merkle::MerkleTree,
  26. Accumulator,
  27. },
  28. hashers::keccak256_160::Keccak160,
  29. messages::{
  30. Message,
  31. PriceFeedMessage,
  32. },
  33. wire::{
  34. to_vec,
  35. v1::{
  36. AccumulatorUpdateData,
  37. MerklePriceUpdate,
  38. Proof,
  39. WormholeMerkleRoot,
  40. WormholeMessage,
  41. WormholePayload,
  42. },
  43. PrefixedVec,
  44. },
  45. },
  46. serde_json::json,
  47. std::io::{
  48. Cursor,
  49. Write,
  50. },
  51. wormhole_sdk::Chain as WormholeChain,
  52. };
  53. async fn initialize_chain() -> (
  54. workspaces::Worker<workspaces::network::Sandbox>,
  55. workspaces::Contract,
  56. workspaces::Contract,
  57. ) {
  58. let worker = workspaces::sandbox().await.expect("Workspaces Failed");
  59. // Deploy Pyth
  60. let contract = worker
  61. .dev_deploy(&std::fs::read("pyth.wasm").expect("Failed to find pyth.wasm"))
  62. .await
  63. .expect("Failed to deploy pyth.wasm");
  64. // Deploy Wormhole Stub, this is a dummy contract that always verifies VAA's correctly so we
  65. // can test the ext_wormhole API.
  66. let wormhole = worker
  67. .dev_deploy(
  68. &std::fs::read("wormhole_stub.wasm").expect("Failed to find wormhole_stub.wasm"),
  69. )
  70. .await
  71. .expect("Failed to deploy wormhole_stub.wasm");
  72. // Initialize Wormhole.
  73. let _ = wormhole
  74. .call("new")
  75. .args_json(&json!({}))
  76. .gas(300_000_000_000_000)
  77. .transact_async()
  78. .await
  79. .expect("Failed to initialize Wormhole")
  80. .await
  81. .unwrap();
  82. // Initialize Pyth, one time operation that sets the Wormhole contract address.
  83. let codehash = [0u8; 32];
  84. let _ = contract
  85. .call("new")
  86. .args_json(&json!({
  87. "wormhole": wormhole.id(),
  88. "codehash": codehash,
  89. "initial_source": Source::default(),
  90. "gov_source": Source::default(),
  91. "update_fee": U128::from(1u128),
  92. "stale_threshold": 32,
  93. }))
  94. .gas(300_000_000_000_000)
  95. .transact_async()
  96. .await
  97. .expect("Failed to initialize Pyth")
  98. .await
  99. .unwrap();
  100. (worker, contract, wormhole)
  101. }
  102. #[tokio::test]
  103. async fn test_set_sources() {
  104. let (_, contract, _) = initialize_chain().await;
  105. // Submit a new Source to the contract, this will trigger a cross-contract call to wormhole
  106. let vaa = wormhole_sdk::Vaa {
  107. emitter_chain: wormhole_sdk::Chain::Any,
  108. emitter_address: wormhole_sdk::Address([0; 32]),
  109. sequence: 1,
  110. payload: (),
  111. ..Default::default()
  112. };
  113. let vaa = {
  114. let mut cur = Cursor::new(Vec::new());
  115. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  116. cur.write_all(
  117. &GovernanceInstruction {
  118. target: Chain::from(WormholeChain::Any),
  119. module: GovernanceModule::Target,
  120. action: GovernanceAction::SetDataSources {
  121. data_sources: vec![
  122. Source::default(),
  123. Source {
  124. emitter: [1; 32],
  125. chain: Chain::from(WormholeChain::Solana),
  126. },
  127. ],
  128. },
  129. }
  130. .serialize()
  131. .unwrap(),
  132. )
  133. .expect("Failed to write Payload");
  134. hex::encode(cur.into_inner())
  135. };
  136. assert!(contract
  137. .call("execute_governance_instruction")
  138. .gas(300_000_000_000_000)
  139. .deposit(300_000_000_000_000_000_000_000)
  140. .args_json(&json!({
  141. "vaa": vaa,
  142. }))
  143. .transact_async()
  144. .await
  145. .expect("Failed to submit VAA")
  146. .await
  147. .unwrap()
  148. .failures()
  149. .is_empty());
  150. // There should now be a two sources in the contract state.
  151. assert_eq!(
  152. serde_json::from_slice::<Vec<Source>>(&contract.view("get_sources").await.unwrap().result)
  153. .unwrap(),
  154. &[
  155. Source::default(),
  156. Source {
  157. emitter: [1; 32],
  158. chain: Chain::from(WormholeChain::Solana),
  159. },
  160. ]
  161. );
  162. }
  163. #[tokio::test]
  164. async fn test_set_governance_source() {
  165. let (_, contract, _) = initialize_chain().await;
  166. // Submit a new Source to the contract, this will trigger a cross-contract call to wormhole
  167. let vaa = wormhole_sdk::Vaa {
  168. emitter_chain: wormhole_sdk::Chain::Any,
  169. emitter_address: wormhole_sdk::Address([0; 32]),
  170. payload: (),
  171. sequence: 2,
  172. ..Default::default()
  173. };
  174. let vaa = {
  175. let request_vaa = wormhole_sdk::Vaa {
  176. emitter_chain: wormhole_sdk::Chain::Solana,
  177. emitter_address: wormhole_sdk::Address([1; 32]),
  178. payload: (),
  179. sequence: 1,
  180. ..Default::default()
  181. };
  182. // Data Source Upgrades are submitted with an embedded VAA, generate that one here first
  183. // before we embed it.
  184. let request_vaa = {
  185. let mut cur = Cursor::new(Vec::new());
  186. serde_wormhole::to_writer(&mut cur, &request_vaa).expect("Failed to serialize VAA");
  187. cur.write_all(
  188. &GovernanceInstruction {
  189. target: Chain::from(WormholeChain::Near),
  190. module: GovernanceModule::Target,
  191. action: GovernanceAction::RequestGovernanceDataSourceTransfer {
  192. governance_data_source_index: 1,
  193. },
  194. }
  195. .serialize()
  196. .unwrap(),
  197. )
  198. .expect("Failed to write Payload");
  199. cur.into_inner()
  200. };
  201. let mut cur = Cursor::new(Vec::new());
  202. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  203. cur.write_all(
  204. &GovernanceInstruction {
  205. target: Chain::from(WormholeChain::Near),
  206. module: GovernanceModule::Target,
  207. action: GovernanceAction::AuthorizeGovernanceDataSourceTransfer {
  208. claim_vaa: request_vaa,
  209. },
  210. }
  211. .serialize()
  212. .unwrap(),
  213. )
  214. .expect("Failed to write Payload");
  215. hex::encode(cur.into_inner())
  216. };
  217. assert!(contract
  218. .call("execute_governance_instruction")
  219. .gas(300_000_000_000_000)
  220. .deposit(300_000_000_000_000_000_000_000)
  221. .args_json(&json!({
  222. "vaa": vaa,
  223. }))
  224. .transact()
  225. .await
  226. .expect("Failed to submit VAA")
  227. .unwrap()
  228. .failures()
  229. .is_empty());
  230. // An action from the new source should now be accepted.
  231. let vaa = wormhole_sdk::Vaa {
  232. sequence: 3, // NOTE: Incremented Governance Sequence
  233. emitter_chain: wormhole_sdk::Chain::Solana,
  234. emitter_address: wormhole_sdk::Address([1; 32]),
  235. payload: (),
  236. ..Default::default()
  237. };
  238. let vaa = {
  239. let mut cur = Cursor::new(Vec::new());
  240. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  241. cur.write_all(
  242. &GovernanceInstruction {
  243. target: Chain::from(WormholeChain::Near),
  244. module: GovernanceModule::Target,
  245. action: GovernanceAction::SetDataSources {
  246. data_sources: vec![
  247. Source::default(),
  248. Source {
  249. emitter: [2; 32],
  250. chain: Chain::from(WormholeChain::Solana),
  251. },
  252. ],
  253. },
  254. }
  255. .serialize()
  256. .unwrap(),
  257. )
  258. .expect("Failed to write Payload");
  259. hex::encode(cur.into_inner())
  260. };
  261. assert!(contract
  262. .call("execute_governance_instruction")
  263. .gas(300_000_000_000_000)
  264. .deposit(300_000_000_000_000_000_000_000)
  265. .args_json(&json!({
  266. "vaa": vaa,
  267. }))
  268. .transact_async()
  269. .await
  270. .expect("Failed to submit VAA")
  271. .await
  272. .unwrap()
  273. .failures()
  274. .is_empty());
  275. // But not from the old source.
  276. let vaa = wormhole_sdk::Vaa {
  277. sequence: 4, // NOTE: Incremented Governance Sequence
  278. emitter_chain: wormhole_sdk::Chain::Any,
  279. emitter_address: wormhole_sdk::Address([0; 32]),
  280. payload: (),
  281. ..Default::default()
  282. };
  283. let vaa = {
  284. let mut cur = Cursor::new(Vec::new());
  285. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  286. cur.write_all(
  287. &GovernanceInstruction {
  288. target: Chain::from(WormholeChain::Near),
  289. module: GovernanceModule::Target,
  290. action: GovernanceAction::SetDataSources {
  291. data_sources: vec![
  292. Source::default(),
  293. Source {
  294. emitter: [2; 32],
  295. chain: Chain::from(WormholeChain::Solana),
  296. },
  297. ],
  298. },
  299. }
  300. .serialize()
  301. .unwrap(),
  302. )
  303. .expect("Failed to write Payload");
  304. hex::encode(cur.into_inner())
  305. };
  306. assert!(contract
  307. .call("execute_governance_instruction")
  308. .gas(300_000_000_000_000)
  309. .deposit(300_000_000_000_000_000_000_000)
  310. .args_json(&json!({
  311. "vaa": vaa,
  312. }))
  313. .transact_async()
  314. .await
  315. .expect("Failed to submit VAA")
  316. .await
  317. .unwrap()
  318. .outcome()
  319. .is_success());
  320. }
  321. #[tokio::test]
  322. async fn test_stale_threshold() {
  323. let (_, contract, _) = initialize_chain().await;
  324. // Submit a Price Attestation to the contract.
  325. let vaa = wormhole_sdk::Vaa {
  326. emitter_chain: wormhole_sdk::Chain::Any,
  327. emitter_address: wormhole_sdk::Address([0; 32]),
  328. payload: (),
  329. sequence: 1,
  330. ..Default::default()
  331. };
  332. // Get current UNIX timestamp and subtract a minute from it to place the price attestation in
  333. // the past. This should be accepted but untrusted.
  334. let now = std::time::SystemTime::now()
  335. .duration_since(std::time::UNIX_EPOCH)
  336. .expect("Failed to get UNIX timestamp")
  337. .as_secs()
  338. - 60;
  339. let vaa = {
  340. let mut cur = Cursor::new(Vec::new());
  341. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  342. cur.write_all(
  343. &BatchPriceAttestation {
  344. price_attestations: vec![PriceAttestation {
  345. product_id: Identifier::default(),
  346. price_id: Identifier::default(),
  347. price: 100,
  348. conf: 1,
  349. expo: 8,
  350. ema_price: 100,
  351. ema_conf: 1,
  352. status: PriceStatus::Trading,
  353. num_publishers: 8,
  354. max_num_publishers: 8,
  355. attestation_time: now.try_into().unwrap(),
  356. publish_time: now.try_into().unwrap(),
  357. prev_publish_time: now.try_into().unwrap(),
  358. prev_price: 100,
  359. prev_conf: 1,
  360. last_attested_publish_time: now.try_into().unwrap(),
  361. }],
  362. }
  363. .serialize()
  364. .unwrap(),
  365. )
  366. .expect("Failed to write Payload");
  367. hex::encode(cur.into_inner())
  368. };
  369. let update_fee = serde_json::from_slice::<U128>(
  370. &contract
  371. .view("get_update_fee_estimate")
  372. .args_json(&json!({
  373. "data": vaa,
  374. }))
  375. .await
  376. .unwrap()
  377. .result,
  378. )
  379. .unwrap();
  380. // Submit price. As there are no prices this should succeed despite being old.
  381. assert!(contract
  382. .call("update_price_feeds")
  383. .gas(300_000_000_000_000)
  384. .deposit(update_fee.into())
  385. .args_json(&json!({
  386. "data": vaa,
  387. }))
  388. .transact_async()
  389. .await
  390. .expect("Failed to submit VAA")
  391. .await
  392. .unwrap()
  393. .failures()
  394. .is_empty());
  395. // Despite succeeding, assert Price cannot be requested, 60 seconds in the past should be
  396. // considered stale. [tag:failed_price_check]
  397. assert_eq!(
  398. None,
  399. serde_json::from_slice::<Option<Price>>(
  400. &contract
  401. .view("get_price")
  402. .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
  403. .await
  404. .unwrap()
  405. .result
  406. )
  407. .unwrap(),
  408. );
  409. // Submit another Price Attestation to the contract with an even older timestamp. Which
  410. // should now fail due to the existing newer price.
  411. let vaa = wormhole_sdk::Vaa {
  412. emitter_chain: wormhole_sdk::Chain::Any,
  413. emitter_address: wormhole_sdk::Address([0; 32]),
  414. sequence: 2,
  415. payload: (),
  416. ..Default::default()
  417. };
  418. let vaa = {
  419. let mut cur = Cursor::new(Vec::new());
  420. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  421. cur.write_all(
  422. &BatchPriceAttestation {
  423. price_attestations: vec![PriceAttestation {
  424. product_id: Identifier::default(),
  425. price_id: Identifier::default(),
  426. price: 1000,
  427. conf: 1,
  428. expo: 8,
  429. ema_price: 1000,
  430. ema_conf: 1,
  431. status: PriceStatus::Trading,
  432. num_publishers: 8,
  433. max_num_publishers: 8,
  434. attestation_time: (now - 1024).try_into().unwrap(),
  435. publish_time: (now - 1024).try_into().unwrap(),
  436. prev_publish_time: (now - 1024).try_into().unwrap(),
  437. prev_price: 90,
  438. prev_conf: 1,
  439. last_attested_publish_time: (now - 1024).try_into().unwrap(),
  440. }],
  441. }
  442. .serialize()
  443. .unwrap(),
  444. )
  445. .expect("Failed to write Payload");
  446. hex::encode(cur.into_inner())
  447. };
  448. // The update handler should now succeed even if price is old, but simply not update the price.
  449. assert!(contract
  450. .call("update_price_feeds")
  451. .gas(300_000_000_000_000)
  452. .deposit(update_fee.into())
  453. .args_json(&json!({
  454. "data": vaa,
  455. }))
  456. .transact_async()
  457. .await
  458. .expect("Failed to submit VAA")
  459. .await
  460. .unwrap()
  461. .failures()
  462. .is_empty());
  463. // The price however should _not_ have updated and if we check the unsafe stored price the
  464. // timestamp and price should be unchanged.
  465. assert_eq!(
  466. Price {
  467. price: 100.into(),
  468. conf: 1.into(),
  469. expo: 8,
  470. publish_time: now as i64,
  471. },
  472. serde_json::from_slice::<Price>(
  473. &contract
  474. .view("get_price_unsafe")
  475. .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
  476. .await
  477. .unwrap()
  478. .result
  479. )
  480. .unwrap(),
  481. );
  482. // Now we extend the staleness threshold with a Governance VAA.
  483. let vaa = wormhole_sdk::Vaa {
  484. emitter_chain: wormhole_sdk::Chain::Any,
  485. emitter_address: wormhole_sdk::Address([0; 32]),
  486. sequence: 3,
  487. payload: (),
  488. ..Default::default()
  489. };
  490. let vaa = {
  491. let mut cur = Cursor::new(Vec::new());
  492. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  493. cur.write_all(
  494. &GovernanceInstruction {
  495. target: Chain::from(WormholeChain::Near),
  496. module: GovernanceModule::Target,
  497. action: GovernanceAction::SetValidPeriod { valid_seconds: 256 },
  498. }
  499. .serialize()
  500. .unwrap(),
  501. )
  502. .unwrap();
  503. hex::encode(cur.into_inner())
  504. };
  505. assert!(contract
  506. .call("execute_governance_instruction")
  507. .gas(300_000_000_000_000)
  508. .deposit(300_000_000_000_000_000_000_000)
  509. .args_json(&json!({
  510. "vaa": vaa,
  511. }))
  512. .transact_async()
  513. .await
  514. .expect("Failed to submit VAA")
  515. .await
  516. .unwrap()
  517. .failures()
  518. .is_empty());
  519. // It should now be possible to request the price that previously returned None.
  520. // [ref:failed_price_check]
  521. assert_eq!(
  522. Some(Price {
  523. price: 100.into(),
  524. conf: 1.into(),
  525. expo: 8,
  526. publish_time: now as i64,
  527. }),
  528. serde_json::from_slice::<Option<Price>>(
  529. &contract
  530. .view("get_price")
  531. .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
  532. .await
  533. .unwrap()
  534. .result
  535. )
  536. .unwrap(),
  537. );
  538. }
  539. #[tokio::test]
  540. async fn test_contract_fees() {
  541. let (_, contract, _) = initialize_chain().await;
  542. let now = std::time::SystemTime::now()
  543. .duration_since(std::time::UNIX_EPOCH)
  544. .expect("Failed to get UNIX timestamp")
  545. .as_secs();
  546. // Set a high fee for the contract needed to submit a price.
  547. let vaa = wormhole_sdk::Vaa {
  548. emitter_chain: wormhole_sdk::Chain::Any,
  549. emitter_address: wormhole_sdk::Address([0; 32]),
  550. payload: (),
  551. sequence: 1,
  552. ..Default::default()
  553. };
  554. let vaa = {
  555. let mut cur = Cursor::new(Vec::new());
  556. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  557. cur.write_all(
  558. &GovernanceInstruction {
  559. target: Chain::from(WormholeChain::Near),
  560. module: GovernanceModule::Target,
  561. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  562. }
  563. .serialize()
  564. .unwrap(),
  565. )
  566. .unwrap();
  567. hex::encode(cur.into_inner())
  568. };
  569. // Fetch Update fee before changing it.
  570. let update_fee = serde_json::from_slice::<U128>(
  571. &contract
  572. .view("get_update_fee_estimate")
  573. .args_json(&json!({
  574. "data": vaa,
  575. }))
  576. .await
  577. .unwrap()
  578. .result,
  579. )
  580. .unwrap();
  581. // Now set the update_fee so that it is too high for the deposit to cover.
  582. assert!(contract
  583. .call("execute_governance_instruction")
  584. .gas(300_000_000_000_000)
  585. .deposit(300_000_000_000_000_000_000_000)
  586. .args_json(&json!({
  587. "vaa": vaa,
  588. }))
  589. .transact_async()
  590. .await
  591. .expect("Failed to submit VAA")
  592. .await
  593. .unwrap()
  594. .failures()
  595. .is_empty());
  596. // Check the state has actually changed before we try and execute another VAA.
  597. assert_ne!(
  598. u128::from(update_fee),
  599. u128::from(
  600. serde_json::from_slice::<U128>(
  601. &contract
  602. .view("get_update_fee_estimate")
  603. .args_json(&json!({
  604. "data": vaa,
  605. }))
  606. .await
  607. .unwrap()
  608. .result,
  609. )
  610. .unwrap()
  611. )
  612. );
  613. // Attempt to update the price feed with a now too low deposit.
  614. let vaa = wormhole_sdk::Vaa {
  615. emitter_chain: wormhole_sdk::Chain::Any,
  616. emitter_address: wormhole_sdk::Address([0; 32]),
  617. sequence: 2,
  618. payload: (),
  619. ..Default::default()
  620. };
  621. let vaa = {
  622. let mut cur = Cursor::new(Vec::new());
  623. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  624. cur.write_all(
  625. &BatchPriceAttestation {
  626. price_attestations: vec![PriceAttestation {
  627. product_id: Identifier::default(),
  628. price_id: Identifier::default(),
  629. price: 1000,
  630. conf: 1,
  631. expo: 8,
  632. ema_price: 1000,
  633. ema_conf: 1,
  634. status: PriceStatus::Trading,
  635. num_publishers: 8,
  636. max_num_publishers: 8,
  637. attestation_time: (now - 1024).try_into().unwrap(),
  638. publish_time: (now - 1024).try_into().unwrap(),
  639. prev_publish_time: (now - 1024).try_into().unwrap(),
  640. prev_price: 90,
  641. prev_conf: 1,
  642. last_attested_publish_time: (now - 1024).try_into().unwrap(),
  643. }],
  644. }
  645. .serialize()
  646. .unwrap(),
  647. )
  648. .expect("Failed to write Payload");
  649. hex::encode(cur.into_inner())
  650. };
  651. assert!(contract
  652. .call("update_price_feeds")
  653. .gas(300_000_000_000_000)
  654. .deposit(update_fee.into())
  655. .args_json(&json!({
  656. "data": vaa,
  657. }))
  658. .transact_async()
  659. .await
  660. .expect("Failed to submit VAA")
  661. .await
  662. .unwrap()
  663. .failures()
  664. .is_empty());
  665. // Submitting a Price should have failed because the fee was not enough.
  666. assert_eq!(
  667. None,
  668. serde_json::from_slice::<Option<Price>>(
  669. &contract
  670. .view("get_price")
  671. .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
  672. .await
  673. .unwrap()
  674. .result
  675. )
  676. .unwrap(),
  677. );
  678. }
  679. // A test that attempts to SetFee twice with the same governance action, the first should succeed,
  680. // the second should fail.
  681. #[tokio::test]
  682. async fn test_same_governance_sequence_fails() {
  683. let (_, contract, _) = initialize_chain().await;
  684. // Set a high fee for the contract needed to submit a price.
  685. let vaa = wormhole_sdk::Vaa {
  686. emitter_chain: wormhole_sdk::Chain::Any,
  687. emitter_address: wormhole_sdk::Address([0; 32]),
  688. payload: (),
  689. sequence: 1,
  690. ..Default::default()
  691. };
  692. let vaa = {
  693. let mut cur = Cursor::new(Vec::new());
  694. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  695. cur.write_all(
  696. &GovernanceInstruction {
  697. target: Chain::from(WormholeChain::Near),
  698. module: GovernanceModule::Target,
  699. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  700. }
  701. .serialize()
  702. .unwrap(),
  703. )
  704. .unwrap();
  705. hex::encode(cur.into_inner())
  706. };
  707. // Attempt our first SetFee.
  708. assert!(contract
  709. .call("execute_governance_instruction")
  710. .gas(300_000_000_000_000)
  711. .deposit(300_000_000_000_000_000_000_000)
  712. .args_json(&json!({
  713. "vaa": vaa,
  714. }))
  715. .transact_async()
  716. .await
  717. .expect("Failed to submit VAA")
  718. .await
  719. .unwrap()
  720. .failures()
  721. .is_empty());
  722. // Attempt to run the same VAA again.
  723. assert!(!contract
  724. .call("execute_governance_instruction")
  725. .gas(300_000_000_000_000)
  726. .deposit(300_000_000_000_000_000_000_000)
  727. .args_json(&json!({
  728. "vaa": vaa,
  729. }))
  730. .transact_async()
  731. .await
  732. .expect("Failed to submit VAA")
  733. .await
  734. .unwrap()
  735. .failures()
  736. .is_empty());
  737. }
  738. // A test that attempts to SetFee twice with the same governance action, the first should succeed,
  739. // the second should fail.
  740. #[tokio::test]
  741. async fn test_out_of_order_sequences_fail() {
  742. let (_, contract, _) = initialize_chain().await;
  743. // Set a high fee for the contract needed to submit a price.
  744. let vaa = wormhole_sdk::Vaa {
  745. emitter_chain: wormhole_sdk::Chain::Any,
  746. emitter_address: wormhole_sdk::Address([0; 32]),
  747. payload: (),
  748. sequence: 1,
  749. ..Default::default()
  750. };
  751. let vaa = {
  752. let mut cur = Cursor::new(Vec::new());
  753. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  754. cur.write_all(
  755. &GovernanceInstruction {
  756. target: Chain::from(WormholeChain::Near),
  757. module: GovernanceModule::Target,
  758. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  759. }
  760. .serialize()
  761. .unwrap(),
  762. )
  763. .unwrap();
  764. hex::encode(cur.into_inner())
  765. };
  766. // Attempt our first SetFee.
  767. assert!(contract
  768. .call("execute_governance_instruction")
  769. .gas(300_000_000_000_000)
  770. .deposit(300_000_000_000_000_000_000_000)
  771. .args_json(&json!({
  772. "vaa": vaa,
  773. }))
  774. .transact_async()
  775. .await
  776. .expect("Failed to submit VAA")
  777. .await
  778. .unwrap()
  779. .failures()
  780. .is_empty());
  781. // Generate another VAA with sequence 3.
  782. let vaa = wormhole_sdk::Vaa {
  783. emitter_chain: wormhole_sdk::Chain::Any,
  784. emitter_address: wormhole_sdk::Address([0; 32]),
  785. payload: (),
  786. sequence: 3,
  787. ..Default::default()
  788. };
  789. let vaa = {
  790. let mut cur = Cursor::new(Vec::new());
  791. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  792. cur.write_all(
  793. &GovernanceInstruction {
  794. target: Chain::from(WormholeChain::Near),
  795. module: GovernanceModule::Target,
  796. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  797. }
  798. .serialize()
  799. .unwrap(),
  800. )
  801. .unwrap();
  802. hex::encode(cur.into_inner())
  803. };
  804. // This should succeed.
  805. assert!(contract
  806. .call("execute_governance_instruction")
  807. .gas(300_000_000_000_000)
  808. .deposit(300_000_000_000_000_000_000_000)
  809. .args_json(&json!({
  810. "vaa": vaa,
  811. }))
  812. .transact_async()
  813. .await
  814. .expect("Failed to submit VAA")
  815. .await
  816. .unwrap()
  817. .failures()
  818. .is_empty());
  819. // Generate another VAA with sequence 2.
  820. let vaa = wormhole_sdk::Vaa {
  821. emitter_chain: wormhole_sdk::Chain::Any,
  822. emitter_address: wormhole_sdk::Address([0; 32]),
  823. payload: (),
  824. sequence: 2,
  825. ..Default::default()
  826. };
  827. let vaa = {
  828. let mut cur = Cursor::new(Vec::new());
  829. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  830. cur.write_all(
  831. &GovernanceInstruction {
  832. target: Chain::from(WormholeChain::Near),
  833. module: GovernanceModule::Target,
  834. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  835. }
  836. .serialize()
  837. .unwrap(),
  838. )
  839. .unwrap();
  840. hex::encode(cur.into_inner())
  841. };
  842. // This should fail due to being out of order.
  843. assert!(!contract
  844. .call("execute_governance_instruction")
  845. .gas(300_000_000_000_000)
  846. .deposit(300_000_000_000_000_000_000_000)
  847. .args_json(&json!({
  848. "vaa": vaa,
  849. }))
  850. .transact_async()
  851. .await
  852. .expect("Failed to submit VAA")
  853. .await
  854. .unwrap()
  855. .failures()
  856. .is_empty());
  857. }
  858. // A test that fails if the governance action payload target is not NEAR.
  859. #[tokio::test]
  860. async fn test_governance_target_fails_if_not_near() {
  861. let (_, contract, _) = initialize_chain().await;
  862. let vaa = wormhole_sdk::Vaa {
  863. emitter_chain: wormhole_sdk::Chain::Any,
  864. emitter_address: wormhole_sdk::Address([0; 32]),
  865. payload: (),
  866. sequence: 1,
  867. ..Default::default()
  868. };
  869. let vaa = {
  870. let mut cur = Cursor::new(Vec::new());
  871. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  872. cur.write_all(
  873. &GovernanceInstruction {
  874. target: Chain::from(WormholeChain::Solana),
  875. module: GovernanceModule::Target,
  876. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  877. }
  878. .serialize()
  879. .unwrap(),
  880. )
  881. .unwrap();
  882. hex::encode(cur.into_inner())
  883. };
  884. // This should fail as the target is Solana, when Near is expected.
  885. assert!(!contract
  886. .call("execute_governance_instruction")
  887. .gas(300_000_000_000_000)
  888. .deposit(300_000_000_000_000_000_000_000)
  889. .args_json(&json!({
  890. "vaa": vaa,
  891. }))
  892. .transact_async()
  893. .await
  894. .expect("Failed to submit VAA")
  895. .await
  896. .unwrap()
  897. .failures()
  898. .is_empty());
  899. }
  900. // A test to check accumulator style updates work as intended.
  901. #[tokio::test]
  902. async fn test_accumulator_updates() {
  903. fn create_dummy_price_feed_message(value: i64) -> Message {
  904. let mut dummy_id = [0; 32];
  905. dummy_id[0] = value as u8;
  906. let msg = PriceFeedMessage {
  907. feed_id: dummy_id,
  908. price: value,
  909. conf: value as u64,
  910. exponent: value as i32,
  911. publish_time: value,
  912. prev_publish_time: value,
  913. ema_price: value,
  914. ema_conf: value as u64,
  915. };
  916. Message::PriceFeedMessage(msg)
  917. }
  918. fn create_accumulator_message_from_updates(
  919. price_updates: Vec<MerklePriceUpdate>,
  920. tree: MerkleTree<Keccak160>,
  921. emitter_address: [u8; 32],
  922. emitter_chain: u16,
  923. ) -> Vec<u8> {
  924. let mut root_hash = [0u8; 20];
  925. root_hash.copy_from_slice(&to_vec::<_, BigEndian>(&tree.root).unwrap()[..20]);
  926. let wormhole_message = WormholeMessage::new(WormholePayload::Merkle(WormholeMerkleRoot {
  927. slot: 0,
  928. ring_size: 0,
  929. root: root_hash,
  930. }));
  931. let vaa = wormhole_sdk::Vaa {
  932. emitter_chain: emitter_chain.into(),
  933. emitter_address: wormhole_sdk::Address(emitter_address),
  934. sequence: 2,
  935. payload: (),
  936. ..Default::default()
  937. };
  938. let vaa = {
  939. let mut cur = Cursor::new(Vec::new());
  940. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  941. cur.write_all(&to_vec::<_, BigEndian>(&wormhole_message).unwrap())
  942. .expect("Failed to write Payload");
  943. cur.into_inner()
  944. };
  945. let accumulator_update_data = AccumulatorUpdateData::new(Proof::WormholeMerkle {
  946. vaa: PrefixedVec::from(vaa),
  947. updates: price_updates,
  948. });
  949. to_vec::<_, BigEndian>(&accumulator_update_data).unwrap()
  950. }
  951. fn create_accumulator_message(all_feeds: &[Message], updates: &[Message]) -> Vec<u8> {
  952. let all_feeds_bytes: Vec<_> = all_feeds
  953. .iter()
  954. .map(|f| to_vec::<_, BigEndian>(f).unwrap())
  955. .collect();
  956. let all_feeds_bytes_refs: Vec<_> = all_feeds_bytes.iter().map(|f| f.as_ref()).collect();
  957. let tree = MerkleTree::<Keccak160>::new(all_feeds_bytes_refs.as_slice()).unwrap();
  958. let mut price_updates: Vec<MerklePriceUpdate> = vec![];
  959. for update in updates {
  960. let proof = tree
  961. .prove(&to_vec::<_, BigEndian>(update).unwrap())
  962. .unwrap();
  963. price_updates.push(MerklePriceUpdate {
  964. message: PrefixedVec::from(to_vec::<_, BigEndian>(update).unwrap()),
  965. proof,
  966. });
  967. }
  968. create_accumulator_message_from_updates(
  969. price_updates,
  970. tree,
  971. [1; 32],
  972. wormhole_sdk::Chain::Any.into(),
  973. )
  974. }
  975. let (_, contract, _) = initialize_chain().await;
  976. // Submit a new Source to the contract, this will trigger a cross-contract call to wormhole
  977. let vaa = wormhole_sdk::Vaa {
  978. emitter_chain: wormhole_sdk::Chain::Any,
  979. emitter_address: wormhole_sdk::Address([0; 32]),
  980. sequence: 1,
  981. payload: (),
  982. ..Default::default()
  983. };
  984. let vaa = {
  985. let mut cur = Cursor::new(Vec::new());
  986. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  987. cur.write_all(
  988. &GovernanceInstruction {
  989. target: Chain::from(WormholeChain::Any),
  990. module: GovernanceModule::Target,
  991. action: GovernanceAction::SetDataSources {
  992. data_sources: vec![
  993. Source::default(),
  994. Source {
  995. emitter: [1; 32],
  996. chain: Chain::from(WormholeChain::Any),
  997. },
  998. ],
  999. },
  1000. }
  1001. .serialize()
  1002. .unwrap(),
  1003. )
  1004. .expect("Failed to write Payload");
  1005. hex::encode(cur.into_inner())
  1006. };
  1007. assert!(contract
  1008. .call("execute_governance_instruction")
  1009. .gas(300_000_000_000_000)
  1010. .deposit(300_000_000_000_000_000_000_000)
  1011. .args_json(&json!({
  1012. "vaa": vaa,
  1013. }))
  1014. .transact_async()
  1015. .await
  1016. .expect("Failed to submit VAA")
  1017. .await
  1018. .unwrap()
  1019. .failures()
  1020. .is_empty());
  1021. // Create a couple of test feeds.
  1022. let feed_1 = create_dummy_price_feed_message(100);
  1023. let feed_2 = create_dummy_price_feed_message(200);
  1024. let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1]);
  1025. let message = hex::encode(message);
  1026. // Call the usual UpdatePriceFeed function.
  1027. assert!(contract
  1028. .call("update_price_feeds")
  1029. .gas(300_000_000_000_000)
  1030. .deposit(300_000_000_000_000_000_000_000)
  1031. .args_json(&json!({
  1032. "data": message,
  1033. }))
  1034. .transact_async()
  1035. .await
  1036. .expect("Failed to submit VAA")
  1037. .await
  1038. .unwrap()
  1039. .failures()
  1040. .is_empty());
  1041. // Check the price feed actually updated. Check both types of serialized PriceIdentifier.
  1042. let mut identifier = [0; 32];
  1043. identifier[0] = 100;
  1044. assert_eq!(
  1045. Some(Price {
  1046. price: 100.into(),
  1047. conf: 100.into(),
  1048. expo: 100,
  1049. publish_time: 100,
  1050. }),
  1051. serde_json::from_slice::<Option<Price>>(
  1052. &contract
  1053. .view("get_price_unsafe")
  1054. .args_json(&json!({ "price_identifier": PriceIdentifier(identifier) }))
  1055. .await
  1056. .unwrap()
  1057. .result
  1058. )
  1059. .unwrap(),
  1060. );
  1061. }
  1062. #[tokio::test]
  1063. async fn test_sdk_compat() {
  1064. let price = pyth_sdk::Price {
  1065. price: i64::MAX,
  1066. conf: u64::MAX,
  1067. expo: 100,
  1068. publish_time: 100,
  1069. };
  1070. let encoded = serde_json::to_string(&price).unwrap();
  1071. let decoded_price: Price = serde_json::from_str(&encoded).unwrap();
  1072. assert_eq!(
  1073. decoded_price,
  1074. Price {
  1075. price: i64::MAX.into(),
  1076. conf: u64::MAX.into(),
  1077. expo: 100,
  1078. publish_time: 100,
  1079. }
  1080. );
  1081. }
  1082. #[tokio::test]
  1083. async fn test_borsh_field_cmopat() {
  1084. use near_sdk::borsh::{
  1085. self,
  1086. BorshDeserialize,
  1087. BorshSerialize,
  1088. };
  1089. let price = pyth_sdk::Price {
  1090. price: i64::MAX,
  1091. conf: u64::MAX,
  1092. expo: 100,
  1093. publish_time: 100,
  1094. };
  1095. // Verify that we can still BorshDeserialize a struct with a different field name. Confirms
  1096. // we don't have to migrate the state.
  1097. #[derive(Eq, PartialEq, Debug, BorshSerialize, BorshDeserialize)]
  1098. struct PriceTester {
  1099. price: i64,
  1100. conf: u64,
  1101. expo: u32,
  1102. bad_field_name: u64,
  1103. }
  1104. let encoded = near_sdk::borsh::BorshSerialize::try_to_vec(&price).unwrap();
  1105. let decoded_price: PriceTester =
  1106. near_sdk::borsh::BorshDeserialize::try_from_slice(&encoded).unwrap();
  1107. assert_eq!(
  1108. decoded_price,
  1109. PriceTester {
  1110. price: i64::MAX.into(),
  1111. conf: u64::MAX.into(),
  1112. expo: 100,
  1113. bad_field_name: 100,
  1114. }
  1115. );
  1116. }