workspaces.rs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146
  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::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::Vaa {
  107. emitter_chain: wormhole::Chain::Any,
  108. emitter_address: wormhole::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::Vaa {
  168. emitter_chain: wormhole::Chain::Any,
  169. emitter_address: wormhole::Address([0; 32]),
  170. payload: (),
  171. sequence: 2,
  172. ..Default::default()
  173. };
  174. let vaa = {
  175. let request_vaa = wormhole::Vaa {
  176. emitter_chain: wormhole::Chain::Solana,
  177. emitter_address: wormhole::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_async()
  225. .await
  226. .expect("Failed to submit VAA")
  227. .await
  228. .unwrap()
  229. .failures()
  230. .is_empty());
  231. // An action from the new source should now be accepted.
  232. let vaa = wormhole::Vaa {
  233. sequence: 3, // NOTE: Incremented Governance Sequence
  234. emitter_chain: wormhole::Chain::Solana,
  235. emitter_address: wormhole::Address([1; 32]),
  236. payload: (),
  237. ..Default::default()
  238. };
  239. let vaa = {
  240. let mut cur = Cursor::new(Vec::new());
  241. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  242. cur.write_all(
  243. &GovernanceInstruction {
  244. target: Chain::from(WormholeChain::Near),
  245. module: GovernanceModule::Target,
  246. action: GovernanceAction::SetDataSources {
  247. data_sources: vec![
  248. Source::default(),
  249. Source {
  250. emitter: [2; 32],
  251. chain: Chain::from(WormholeChain::Solana),
  252. },
  253. ],
  254. },
  255. }
  256. .serialize()
  257. .unwrap(),
  258. )
  259. .expect("Failed to write Payload");
  260. hex::encode(cur.into_inner())
  261. };
  262. assert!(contract
  263. .call("execute_governance_instruction")
  264. .gas(300_000_000_000_000)
  265. .deposit(300_000_000_000_000_000_000_000)
  266. .args_json(&json!({
  267. "vaa": vaa,
  268. }))
  269. .transact_async()
  270. .await
  271. .expect("Failed to submit VAA")
  272. .await
  273. .unwrap()
  274. .failures()
  275. .is_empty());
  276. // But not from the old source.
  277. let vaa = wormhole::Vaa {
  278. sequence: 4, // NOTE: Incremented Governance Sequence
  279. emitter_chain: wormhole::Chain::Any,
  280. emitter_address: wormhole::Address([0; 32]),
  281. payload: (),
  282. ..Default::default()
  283. };
  284. let vaa = {
  285. let mut cur = Cursor::new(Vec::new());
  286. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  287. cur.write_all(
  288. &GovernanceInstruction {
  289. target: Chain::from(WormholeChain::Near),
  290. module: GovernanceModule::Target,
  291. action: GovernanceAction::SetDataSources {
  292. data_sources: vec![
  293. Source::default(),
  294. Source {
  295. emitter: [2; 32],
  296. chain: Chain::from(WormholeChain::Solana),
  297. },
  298. ],
  299. },
  300. }
  301. .serialize()
  302. .unwrap(),
  303. )
  304. .expect("Failed to write Payload");
  305. hex::encode(cur.into_inner())
  306. };
  307. assert!(contract
  308. .call("execute_governance_instruction")
  309. .gas(300_000_000_000_000)
  310. .deposit(300_000_000_000_000_000_000_000)
  311. .args_json(&json!({
  312. "vaa": vaa,
  313. }))
  314. .transact_async()
  315. .await
  316. .expect("Failed to submit VAA")
  317. .await
  318. .unwrap()
  319. .outcome()
  320. .is_success());
  321. }
  322. #[tokio::test]
  323. async fn test_stale_threshold() {
  324. let (_, contract, _) = initialize_chain().await;
  325. // Submit a Price Attestation to the contract.
  326. let vaa = wormhole::Vaa {
  327. emitter_chain: wormhole::Chain::Any,
  328. emitter_address: wormhole::Address([0; 32]),
  329. payload: (),
  330. sequence: 1,
  331. ..Default::default()
  332. };
  333. // Get current UNIX timestamp and subtract a minute from it to place the price attestation in
  334. // the past. This should be accepted but untrusted.
  335. let now = std::time::SystemTime::now()
  336. .duration_since(std::time::UNIX_EPOCH)
  337. .expect("Failed to get UNIX timestamp")
  338. .as_secs()
  339. - 60;
  340. let vaa = {
  341. let mut cur = Cursor::new(Vec::new());
  342. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  343. cur.write_all(
  344. &BatchPriceAttestation {
  345. price_attestations: vec![PriceAttestation {
  346. product_id: Identifier::default(),
  347. price_id: Identifier::default(),
  348. price: 100,
  349. conf: 1,
  350. expo: 8,
  351. ema_price: 100,
  352. ema_conf: 1,
  353. status: PriceStatus::Trading,
  354. num_publishers: 8,
  355. max_num_publishers: 8,
  356. attestation_time: now.try_into().unwrap(),
  357. publish_time: now.try_into().unwrap(),
  358. prev_publish_time: now.try_into().unwrap(),
  359. prev_price: 100,
  360. prev_conf: 1,
  361. last_attested_publish_time: now.try_into().unwrap(),
  362. }],
  363. }
  364. .serialize()
  365. .unwrap(),
  366. )
  367. .expect("Failed to write Payload");
  368. hex::encode(cur.into_inner())
  369. };
  370. let update_fee = serde_json::from_slice::<U128>(
  371. &contract
  372. .view("get_update_fee_estimate")
  373. .args_json(&json!({
  374. "data": vaa,
  375. }))
  376. .await
  377. .unwrap()
  378. .result,
  379. )
  380. .unwrap();
  381. // Submit price. As there are no prices this should succeed despite being old.
  382. assert!(contract
  383. .call("update_price_feeds")
  384. .gas(300_000_000_000_000)
  385. .deposit(update_fee.into())
  386. .args_json(&json!({
  387. "data": vaa,
  388. }))
  389. .transact_async()
  390. .await
  391. .expect("Failed to submit VAA")
  392. .await
  393. .unwrap()
  394. .failures()
  395. .is_empty());
  396. // Despite succeeding, assert Price cannot be requested, 60 seconds in the past should be
  397. // considered stale. [tag:failed_price_check]
  398. assert_eq!(
  399. None,
  400. serde_json::from_slice::<Option<Price>>(
  401. &contract
  402. .view("get_price")
  403. .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
  404. .await
  405. .unwrap()
  406. .result
  407. )
  408. .unwrap(),
  409. );
  410. // Submit another Price Attestation to the contract with an even older timestamp. Which
  411. // should now fail due to the existing newer price.
  412. let vaa = wormhole::Vaa {
  413. emitter_chain: wormhole::Chain::Any,
  414. emitter_address: wormhole::Address([0; 32]),
  415. sequence: 2,
  416. payload: (),
  417. ..Default::default()
  418. };
  419. let vaa = {
  420. let mut cur = Cursor::new(Vec::new());
  421. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  422. cur.write_all(
  423. &BatchPriceAttestation {
  424. price_attestations: vec![PriceAttestation {
  425. product_id: Identifier::default(),
  426. price_id: Identifier::default(),
  427. price: 1000,
  428. conf: 1,
  429. expo: 8,
  430. ema_price: 1000,
  431. ema_conf: 1,
  432. status: PriceStatus::Trading,
  433. num_publishers: 8,
  434. max_num_publishers: 8,
  435. attestation_time: (now - 1024).try_into().unwrap(),
  436. publish_time: (now - 1024).try_into().unwrap(),
  437. prev_publish_time: (now - 1024).try_into().unwrap(),
  438. prev_price: 90,
  439. prev_conf: 1,
  440. last_attested_publish_time: (now - 1024).try_into().unwrap(),
  441. }],
  442. }
  443. .serialize()
  444. .unwrap(),
  445. )
  446. .expect("Failed to write Payload");
  447. hex::encode(cur.into_inner())
  448. };
  449. // The update handler should now succeed even if price is old, but simply not update the price.
  450. assert!(contract
  451. .call("update_price_feeds")
  452. .gas(300_000_000_000_000)
  453. .deposit(update_fee.into())
  454. .args_json(&json!({
  455. "data": vaa,
  456. }))
  457. .transact_async()
  458. .await
  459. .expect("Failed to submit VAA")
  460. .await
  461. .unwrap()
  462. .failures()
  463. .is_empty());
  464. // The price however should _not_ have updated and if we check the unsafe stored price the
  465. // timestamp and price should be unchanged.
  466. assert_eq!(
  467. Price {
  468. price: 100.into(),
  469. conf: 1.into(),
  470. expo: 8,
  471. timestamp: now.into(),
  472. },
  473. serde_json::from_slice::<Price>(
  474. &contract
  475. .view("get_price_unsafe")
  476. .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
  477. .await
  478. .unwrap()
  479. .result
  480. )
  481. .unwrap(),
  482. );
  483. // Now we extend the staleness threshold with a Governance VAA.
  484. let vaa = wormhole::Vaa {
  485. emitter_chain: wormhole::Chain::Any,
  486. emitter_address: wormhole::Address([0; 32]),
  487. sequence: 3,
  488. payload: (),
  489. ..Default::default()
  490. };
  491. let vaa = {
  492. let mut cur = Cursor::new(Vec::new());
  493. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  494. cur.write_all(
  495. &GovernanceInstruction {
  496. target: Chain::from(WormholeChain::Near),
  497. module: GovernanceModule::Target,
  498. action: GovernanceAction::SetValidPeriod { valid_seconds: 256 },
  499. }
  500. .serialize()
  501. .unwrap(),
  502. )
  503. .unwrap();
  504. hex::encode(cur.into_inner())
  505. };
  506. assert!(contract
  507. .call("execute_governance_instruction")
  508. .gas(300_000_000_000_000)
  509. .deposit(300_000_000_000_000_000_000_000)
  510. .args_json(&json!({
  511. "vaa": vaa,
  512. }))
  513. .transact_async()
  514. .await
  515. .expect("Failed to submit VAA")
  516. .await
  517. .unwrap()
  518. .failures()
  519. .is_empty());
  520. // It should now be possible to request the price that previously returned None.
  521. // [ref:failed_price_check]
  522. assert_eq!(
  523. Some(Price {
  524. price: 100.into(),
  525. conf: 1.into(),
  526. expo: 8,
  527. timestamp: now.into(),
  528. }),
  529. serde_json::from_slice::<Option<Price>>(
  530. &contract
  531. .view("get_price")
  532. .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
  533. .await
  534. .unwrap()
  535. .result
  536. )
  537. .unwrap(),
  538. );
  539. }
  540. #[tokio::test]
  541. async fn test_contract_fees() {
  542. let (_, contract, _) = initialize_chain().await;
  543. let now = std::time::SystemTime::now()
  544. .duration_since(std::time::UNIX_EPOCH)
  545. .expect("Failed to get UNIX timestamp")
  546. .as_secs();
  547. // Set a high fee for the contract needed to submit a price.
  548. let vaa = wormhole::Vaa {
  549. emitter_chain: wormhole::Chain::Any,
  550. emitter_address: wormhole::Address([0; 32]),
  551. payload: (),
  552. sequence: 1,
  553. ..Default::default()
  554. };
  555. let vaa = {
  556. let mut cur = Cursor::new(Vec::new());
  557. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  558. cur.write_all(
  559. &GovernanceInstruction {
  560. target: Chain::from(WormholeChain::Near),
  561. module: GovernanceModule::Target,
  562. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  563. }
  564. .serialize()
  565. .unwrap(),
  566. )
  567. .unwrap();
  568. hex::encode(cur.into_inner())
  569. };
  570. // Fetch Update fee before changing it.
  571. let update_fee = serde_json::from_slice::<U128>(
  572. &contract
  573. .view("get_update_fee_estimate")
  574. .args_json(&json!({
  575. "data": vaa,
  576. }))
  577. .await
  578. .unwrap()
  579. .result,
  580. )
  581. .unwrap();
  582. // Now set the update_fee so that it is too high for the deposit to cover.
  583. assert!(contract
  584. .call("execute_governance_instruction")
  585. .gas(300_000_000_000_000)
  586. .deposit(300_000_000_000_000_000_000_000)
  587. .args_json(&json!({
  588. "vaa": vaa,
  589. }))
  590. .transact_async()
  591. .await
  592. .expect("Failed to submit VAA")
  593. .await
  594. .unwrap()
  595. .failures()
  596. .is_empty());
  597. // Check the state has actually changed before we try and execute another VAA.
  598. assert_ne!(
  599. u128::from(update_fee),
  600. u128::from(
  601. serde_json::from_slice::<U128>(
  602. &contract
  603. .view("get_update_fee_estimate")
  604. .args_json(&json!({
  605. "data": vaa,
  606. }))
  607. .await
  608. .unwrap()
  609. .result,
  610. )
  611. .unwrap()
  612. )
  613. );
  614. // Attempt to update the price feed with a now too low deposit.
  615. let vaa = wormhole::Vaa {
  616. emitter_chain: wormhole::Chain::Any,
  617. emitter_address: wormhole::Address([0; 32]),
  618. sequence: 2,
  619. payload: (),
  620. ..Default::default()
  621. };
  622. let vaa = {
  623. let mut cur = Cursor::new(Vec::new());
  624. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  625. cur.write_all(
  626. &BatchPriceAttestation {
  627. price_attestations: vec![PriceAttestation {
  628. product_id: Identifier::default(),
  629. price_id: Identifier::default(),
  630. price: 1000,
  631. conf: 1,
  632. expo: 8,
  633. ema_price: 1000,
  634. ema_conf: 1,
  635. status: PriceStatus::Trading,
  636. num_publishers: 8,
  637. max_num_publishers: 8,
  638. attestation_time: (now - 1024).try_into().unwrap(),
  639. publish_time: (now - 1024).try_into().unwrap(),
  640. prev_publish_time: (now - 1024).try_into().unwrap(),
  641. prev_price: 90,
  642. prev_conf: 1,
  643. last_attested_publish_time: (now - 1024).try_into().unwrap(),
  644. }],
  645. }
  646. .serialize()
  647. .unwrap(),
  648. )
  649. .expect("Failed to write Payload");
  650. hex::encode(cur.into_inner())
  651. };
  652. assert!(contract
  653. .call("update_price_feeds")
  654. .gas(300_000_000_000_000)
  655. .deposit(update_fee.into())
  656. .args_json(&json!({
  657. "data": vaa,
  658. }))
  659. .transact_async()
  660. .await
  661. .expect("Failed to submit VAA")
  662. .await
  663. .unwrap()
  664. .failures()
  665. .is_empty());
  666. // Submitting a Price should have failed because the fee was not enough.
  667. assert_eq!(
  668. None,
  669. serde_json::from_slice::<Option<Price>>(
  670. &contract
  671. .view("get_price")
  672. .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
  673. .await
  674. .unwrap()
  675. .result
  676. )
  677. .unwrap(),
  678. );
  679. }
  680. // A test that attempts to SetFee twice with the same governance action, the first should succeed,
  681. // the second should fail.
  682. #[tokio::test]
  683. async fn test_same_governance_sequence_fails() {
  684. let (_, contract, _) = initialize_chain().await;
  685. // Set a high fee for the contract needed to submit a price.
  686. let vaa = wormhole::Vaa {
  687. emitter_chain: wormhole::Chain::Any,
  688. emitter_address: wormhole::Address([0; 32]),
  689. payload: (),
  690. sequence: 1,
  691. ..Default::default()
  692. };
  693. let vaa = {
  694. let mut cur = Cursor::new(Vec::new());
  695. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  696. cur.write_all(
  697. &GovernanceInstruction {
  698. target: Chain::from(WormholeChain::Near),
  699. module: GovernanceModule::Target,
  700. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  701. }
  702. .serialize()
  703. .unwrap(),
  704. )
  705. .unwrap();
  706. hex::encode(cur.into_inner())
  707. };
  708. // Attempt our first SetFee.
  709. assert!(contract
  710. .call("execute_governance_instruction")
  711. .gas(300_000_000_000_000)
  712. .deposit(300_000_000_000_000_000_000_000)
  713. .args_json(&json!({
  714. "vaa": vaa,
  715. }))
  716. .transact_async()
  717. .await
  718. .expect("Failed to submit VAA")
  719. .await
  720. .unwrap()
  721. .failures()
  722. .is_empty());
  723. // Attempt to run the same VAA again.
  724. assert!(!contract
  725. .call("execute_governance_instruction")
  726. .gas(300_000_000_000_000)
  727. .deposit(300_000_000_000_000_000_000_000)
  728. .args_json(&json!({
  729. "vaa": vaa,
  730. }))
  731. .transact_async()
  732. .await
  733. .expect("Failed to submit VAA")
  734. .await
  735. .unwrap()
  736. .failures()
  737. .is_empty());
  738. }
  739. // A test that attempts to SetFee twice with the same governance action, the first should succeed,
  740. // the second should fail.
  741. #[tokio::test]
  742. async fn test_out_of_order_sequences_fail() {
  743. let (_, contract, _) = initialize_chain().await;
  744. // Set a high fee for the contract needed to submit a price.
  745. let vaa = wormhole::Vaa {
  746. emitter_chain: wormhole::Chain::Any,
  747. emitter_address: wormhole::Address([0; 32]),
  748. payload: (),
  749. sequence: 1,
  750. ..Default::default()
  751. };
  752. let vaa = {
  753. let mut cur = Cursor::new(Vec::new());
  754. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  755. cur.write_all(
  756. &GovernanceInstruction {
  757. target: Chain::from(WormholeChain::Near),
  758. module: GovernanceModule::Target,
  759. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  760. }
  761. .serialize()
  762. .unwrap(),
  763. )
  764. .unwrap();
  765. hex::encode(cur.into_inner())
  766. };
  767. // Attempt our first SetFee.
  768. assert!(contract
  769. .call("execute_governance_instruction")
  770. .gas(300_000_000_000_000)
  771. .deposit(300_000_000_000_000_000_000_000)
  772. .args_json(&json!({
  773. "vaa": vaa,
  774. }))
  775. .transact_async()
  776. .await
  777. .expect("Failed to submit VAA")
  778. .await
  779. .unwrap()
  780. .failures()
  781. .is_empty());
  782. // Generate another VAA with sequence 3.
  783. let vaa = wormhole::Vaa {
  784. emitter_chain: wormhole::Chain::Any,
  785. emitter_address: wormhole::Address([0; 32]),
  786. payload: (),
  787. sequence: 3,
  788. ..Default::default()
  789. };
  790. let vaa = {
  791. let mut cur = Cursor::new(Vec::new());
  792. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  793. cur.write_all(
  794. &GovernanceInstruction {
  795. target: Chain::from(WormholeChain::Near),
  796. module: GovernanceModule::Target,
  797. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  798. }
  799. .serialize()
  800. .unwrap(),
  801. )
  802. .unwrap();
  803. hex::encode(cur.into_inner())
  804. };
  805. // This should succeed.
  806. assert!(contract
  807. .call("execute_governance_instruction")
  808. .gas(300_000_000_000_000)
  809. .deposit(300_000_000_000_000_000_000_000)
  810. .args_json(&json!({
  811. "vaa": vaa,
  812. }))
  813. .transact_async()
  814. .await
  815. .expect("Failed to submit VAA")
  816. .await
  817. .unwrap()
  818. .failures()
  819. .is_empty());
  820. // Generate another VAA with sequence 2.
  821. let vaa = wormhole::Vaa {
  822. emitter_chain: wormhole::Chain::Any,
  823. emitter_address: wormhole::Address([0; 32]),
  824. payload: (),
  825. sequence: 2,
  826. ..Default::default()
  827. };
  828. let vaa = {
  829. let mut cur = Cursor::new(Vec::new());
  830. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  831. cur.write_all(
  832. &GovernanceInstruction {
  833. target: Chain::from(WormholeChain::Near),
  834. module: GovernanceModule::Target,
  835. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  836. }
  837. .serialize()
  838. .unwrap(),
  839. )
  840. .unwrap();
  841. hex::encode(cur.into_inner())
  842. };
  843. // This should fail due to being out of order.
  844. assert!(!contract
  845. .call("execute_governance_instruction")
  846. .gas(300_000_000_000_000)
  847. .deposit(300_000_000_000_000_000_000_000)
  848. .args_json(&json!({
  849. "vaa": vaa,
  850. }))
  851. .transact_async()
  852. .await
  853. .expect("Failed to submit VAA")
  854. .await
  855. .unwrap()
  856. .failures()
  857. .is_empty());
  858. }
  859. // A test that fails if the governance action payload target is not NEAR.
  860. #[tokio::test]
  861. async fn test_governance_target_fails_if_not_near() {
  862. let (_, contract, _) = initialize_chain().await;
  863. let vaa = wormhole::Vaa {
  864. emitter_chain: wormhole::Chain::Any,
  865. emitter_address: wormhole::Address([0; 32]),
  866. payload: (),
  867. sequence: 1,
  868. ..Default::default()
  869. };
  870. let vaa = {
  871. let mut cur = Cursor::new(Vec::new());
  872. serde_wormhole::to_writer(&mut cur, &vaa).unwrap();
  873. cur.write_all(
  874. &GovernanceInstruction {
  875. target: Chain::from(WormholeChain::Solana),
  876. module: GovernanceModule::Target,
  877. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  878. }
  879. .serialize()
  880. .unwrap(),
  881. )
  882. .unwrap();
  883. hex::encode(cur.into_inner())
  884. };
  885. // This should fail as the target is Solana, when Near is expected.
  886. assert!(!contract
  887. .call("execute_governance_instruction")
  888. .gas(300_000_000_000_000)
  889. .deposit(300_000_000_000_000_000_000_000)
  890. .args_json(&json!({
  891. "vaa": vaa,
  892. }))
  893. .transact_async()
  894. .await
  895. .expect("Failed to submit VAA")
  896. .await
  897. .unwrap()
  898. .failures()
  899. .is_empty());
  900. }
  901. // A test to check accumulator style updates work as intended.
  902. #[tokio::test]
  903. async fn test_accumulator_updates() {
  904. fn create_dummy_price_feed_message(value: i64) -> Message {
  905. let mut dummy_id = [0; 32];
  906. dummy_id[0] = value as u8;
  907. let msg = PriceFeedMessage {
  908. feed_id: dummy_id,
  909. price: value,
  910. conf: value as u64,
  911. exponent: value as i32,
  912. publish_time: value,
  913. prev_publish_time: value,
  914. ema_price: value,
  915. ema_conf: value as u64,
  916. };
  917. Message::PriceFeedMessage(msg)
  918. }
  919. fn create_accumulator_message_from_updates(
  920. price_updates: Vec<MerklePriceUpdate>,
  921. tree: MerkleTree<Keccak160>,
  922. emitter_address: [u8; 32],
  923. emitter_chain: u16,
  924. ) -> Vec<u8> {
  925. let mut root_hash = [0u8; 20];
  926. root_hash.copy_from_slice(&to_vec::<_, BigEndian>(&tree.root).unwrap()[..20]);
  927. let wormhole_message = WormholeMessage::new(WormholePayload::Merkle(WormholeMerkleRoot {
  928. slot: 0,
  929. ring_size: 0,
  930. root: root_hash,
  931. }));
  932. let vaa = wormhole::Vaa {
  933. emitter_chain: emitter_chain.into(),
  934. emitter_address: wormhole::Address(emitter_address),
  935. sequence: 2,
  936. payload: (),
  937. ..Default::default()
  938. };
  939. let vaa = {
  940. let mut cur = Cursor::new(Vec::new());
  941. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  942. cur.write_all(&to_vec::<_, BigEndian>(&wormhole_message).unwrap())
  943. .expect("Failed to write Payload");
  944. cur.into_inner()
  945. };
  946. let accumulator_update_data = AccumulatorUpdateData::new(Proof::WormholeMerkle {
  947. vaa: PrefixedVec::from(vaa),
  948. updates: price_updates,
  949. });
  950. to_vec::<_, BigEndian>(&accumulator_update_data).unwrap()
  951. }
  952. fn create_accumulator_message(all_feeds: &[Message], updates: &[Message]) -> Vec<u8> {
  953. let all_feeds_bytes: Vec<_> = all_feeds
  954. .iter()
  955. .map(|f| to_vec::<_, BigEndian>(f).unwrap())
  956. .collect();
  957. let all_feeds_bytes_refs: Vec<_> = all_feeds_bytes.iter().map(|f| f.as_ref()).collect();
  958. let tree = MerkleTree::<Keccak160>::new(all_feeds_bytes_refs.as_slice()).unwrap();
  959. let mut price_updates: Vec<MerklePriceUpdate> = vec![];
  960. for update in updates {
  961. let proof = tree
  962. .prove(&to_vec::<_, BigEndian>(update).unwrap())
  963. .unwrap();
  964. price_updates.push(MerklePriceUpdate {
  965. message: PrefixedVec::from(to_vec::<_, BigEndian>(update).unwrap()),
  966. proof,
  967. });
  968. }
  969. create_accumulator_message_from_updates(
  970. price_updates,
  971. tree,
  972. [1; 32],
  973. wormhole::Chain::Any.into(),
  974. )
  975. }
  976. let (_, contract, _) = initialize_chain().await;
  977. // Submit a new Source to the contract, this will trigger a cross-contract call to wormhole
  978. let vaa = wormhole::Vaa {
  979. emitter_chain: wormhole::Chain::Any,
  980. emitter_address: wormhole::Address([0; 32]),
  981. sequence: 1,
  982. payload: (),
  983. ..Default::default()
  984. };
  985. let vaa = {
  986. let mut cur = Cursor::new(Vec::new());
  987. serde_wormhole::to_writer(&mut cur, &vaa).expect("Failed to serialize VAA");
  988. cur.write_all(
  989. &GovernanceInstruction {
  990. target: Chain::from(WormholeChain::Any),
  991. module: GovernanceModule::Target,
  992. action: GovernanceAction::SetDataSources {
  993. data_sources: vec![
  994. Source::default(),
  995. Source {
  996. emitter: [1; 32],
  997. chain: Chain::from(WormholeChain::Any),
  998. },
  999. ],
  1000. },
  1001. }
  1002. .serialize()
  1003. .unwrap(),
  1004. )
  1005. .expect("Failed to write Payload");
  1006. hex::encode(cur.into_inner())
  1007. };
  1008. assert!(contract
  1009. .call("execute_governance_instruction")
  1010. .gas(300_000_000_000_000)
  1011. .deposit(300_000_000_000_000_000_000_000)
  1012. .args_json(&json!({
  1013. "vaa": vaa,
  1014. }))
  1015. .transact_async()
  1016. .await
  1017. .expect("Failed to submit VAA")
  1018. .await
  1019. .unwrap()
  1020. .failures()
  1021. .is_empty());
  1022. // Create a couple of test feeds.
  1023. let feed_1 = create_dummy_price_feed_message(100);
  1024. let feed_2 = create_dummy_price_feed_message(200);
  1025. let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1]);
  1026. let message = hex::encode(message);
  1027. // Call the usual UpdatePriceFeed function.
  1028. assert!(contract
  1029. .call("update_price_feeds")
  1030. .gas(300_000_000_000_000)
  1031. .deposit(300_000_000_000_000_000_000_000)
  1032. .args_json(&json!({
  1033. "data": message,
  1034. }))
  1035. .transact_async()
  1036. .await
  1037. .expect("Failed to submit VAA")
  1038. .await
  1039. .unwrap()
  1040. .failures()
  1041. .is_empty());
  1042. // Check the price feed actually updated. Check both types of serialized PriceIdentifier.
  1043. let mut identifier = [0; 32];
  1044. identifier[0] = 100;
  1045. assert_eq!(
  1046. Some(Price {
  1047. price: 100.into(),
  1048. conf: 100.into(),
  1049. expo: 100,
  1050. timestamp: 100.into(),
  1051. }),
  1052. serde_json::from_slice::<Option<Price>>(
  1053. &contract
  1054. .view("get_price_unsafe")
  1055. .args_json(&json!({ "price_identifier": PriceIdentifier(identifier) }))
  1056. .await
  1057. .unwrap()
  1058. .result
  1059. )
  1060. .unwrap(),
  1061. );
  1062. }