test1.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. use {
  2. anchor_lang::{prelude::AccountMeta, InstructionData},
  3. pyth_lazer_solana_contract::ed25519_program_args,
  4. solana_program_test::{BanksClient, BanksClientError, ProgramTest},
  5. solana_sdk::{
  6. ed25519_program,
  7. hash::Hash,
  8. instruction::{Instruction, InstructionError},
  9. pubkey::Pubkey,
  10. signature::Keypair,
  11. signer::Signer,
  12. system_instruction, system_program, sysvar,
  13. transaction::{Transaction, TransactionError},
  14. },
  15. std::env,
  16. };
  17. fn program_test() -> ProgramTest {
  18. if env::var("SBF_OUT_DIR").is_err() {
  19. env::set_var(
  20. "SBF_OUT_DIR",
  21. format!(
  22. "{}/../../../../target/sbf-solana-solana/release",
  23. env::var("CARGO_MANIFEST_DIR").unwrap()
  24. ),
  25. );
  26. }
  27. println!("if add_program fails, run `cargo build-sbf` first.");
  28. ProgramTest::new(
  29. "pyth_lazer_solana_contract",
  30. pyth_lazer_solana_contract::ID,
  31. None,
  32. )
  33. }
  34. struct Setup {
  35. banks_client: BanksClient,
  36. payer: Keypair,
  37. recent_blockhash: Hash,
  38. }
  39. impl Setup {
  40. async fn with_program_test(program_test: ProgramTest) -> Self {
  41. let (banks_client, payer, recent_blockhash) = program_test.start().await;
  42. Self {
  43. banks_client,
  44. payer,
  45. recent_blockhash,
  46. }
  47. }
  48. async fn new() -> Self {
  49. Self::with_program_test(program_test()).await
  50. }
  51. async fn create_treasury(&mut self) -> Pubkey {
  52. let treasury =
  53. Pubkey::create_with_seed(&self.payer.pubkey(), "treasury", &system_program::ID)
  54. .unwrap();
  55. let mut transaction_create_treasury = Transaction::new_with_payer(
  56. &[system_instruction::create_account_with_seed(
  57. &self.payer.pubkey(),
  58. &treasury,
  59. &self.payer.pubkey(),
  60. "treasury",
  61. 10_000_000,
  62. 0,
  63. &system_program::ID,
  64. )],
  65. Some(&self.payer.pubkey()),
  66. );
  67. transaction_create_treasury.sign(&[&self.payer], self.recent_blockhash);
  68. self.banks_client
  69. .process_transaction(transaction_create_treasury)
  70. .await
  71. .unwrap();
  72. treasury
  73. }
  74. async fn set_trusted(&mut self, verifying_key: Pubkey) {
  75. let mut transaction_set_trusted = Transaction::new_with_payer(
  76. &[Instruction::new_with_bytes(
  77. pyth_lazer_solana_contract::ID,
  78. &pyth_lazer_solana_contract::instruction::Update {
  79. trusted_signer: verifying_key,
  80. expires_at: i64::MAX,
  81. }
  82. .data(),
  83. vec![
  84. AccountMeta::new(self.payer.pubkey(), true),
  85. AccountMeta::new(pyth_lazer_solana_contract::STORAGE_ID, false),
  86. ],
  87. )],
  88. Some(&self.payer.pubkey()),
  89. );
  90. transaction_set_trusted.sign(&[&self.payer], self.recent_blockhash);
  91. self.banks_client
  92. .process_transaction(transaction_set_trusted)
  93. .await
  94. .unwrap();
  95. }
  96. async fn verify_message(&mut self, message: &[u8], treasury: Pubkey) {
  97. let treasury_starting_lamports = self
  98. .banks_client
  99. .get_account(treasury)
  100. .await
  101. .unwrap()
  102. .unwrap()
  103. .lamports;
  104. // 8 bytes for Anchor header, 4 bytes for Vec length.
  105. self.verify_message_with_offset(message, treasury, 12)
  106. .await
  107. .unwrap();
  108. assert_eq!(
  109. self.banks_client
  110. .get_account(treasury)
  111. .await
  112. .unwrap()
  113. .unwrap()
  114. .lamports,
  115. treasury_starting_lamports + 1,
  116. );
  117. }
  118. async fn verify_message_with_offset(
  119. &mut self,
  120. message: &[u8],
  121. treasury: Pubkey,
  122. message_offset: u16,
  123. ) -> Result<(), BanksClientError> {
  124. // Instruction #0 will be ed25519 instruction;
  125. // Instruction #1 will be our contract instruction.
  126. let instruction_index = 1;
  127. let ed25519_args = dbg!(pyth_lazer_solana_contract::Ed25519SignatureOffsets::new(
  128. message,
  129. instruction_index,
  130. message_offset,
  131. ));
  132. let mut transaction_verify = Transaction::new_with_payer(
  133. &[
  134. Instruction::new_with_bytes(
  135. ed25519_program::ID,
  136. &ed25519_program_args(&[ed25519_args]),
  137. vec![],
  138. ),
  139. Instruction::new_with_bytes(
  140. pyth_lazer_solana_contract::ID,
  141. &pyth_lazer_solana_contract::instruction::VerifyMessage {
  142. message_data: message.to_vec(),
  143. ed25519_instruction_index: 0,
  144. signature_index: 0,
  145. }
  146. .data(),
  147. vec![
  148. AccountMeta::new(self.payer.pubkey(), true),
  149. AccountMeta::new_readonly(pyth_lazer_solana_contract::STORAGE_ID, false),
  150. AccountMeta::new(treasury, false),
  151. AccountMeta::new_readonly(system_program::ID, false),
  152. AccountMeta::new_readonly(sysvar::instructions::ID, false),
  153. ],
  154. ),
  155. ],
  156. Some(&self.payer.pubkey()),
  157. );
  158. transaction_verify.sign(&[&self.payer], self.recent_blockhash);
  159. self.banks_client
  160. .process_transaction(transaction_verify)
  161. .await
  162. }
  163. }
  164. #[tokio::test]
  165. async fn test_basic() {
  166. let mut setup = Setup::new().await;
  167. let treasury = setup.create_treasury().await;
  168. let mut transaction_init_contract = Transaction::new_with_payer(
  169. &[Instruction::new_with_bytes(
  170. pyth_lazer_solana_contract::ID,
  171. &pyth_lazer_solana_contract::instruction::Initialize {
  172. top_authority: setup.payer.pubkey(),
  173. treasury,
  174. }
  175. .data(),
  176. vec![
  177. AccountMeta::new(setup.payer.pubkey(), true),
  178. AccountMeta::new(pyth_lazer_solana_contract::STORAGE_ID, false),
  179. AccountMeta::new_readonly(system_program::ID, false),
  180. ],
  181. )],
  182. Some(&setup.payer.pubkey()),
  183. );
  184. transaction_init_contract.sign(&[&setup.payer], setup.recent_blockhash);
  185. setup
  186. .banks_client
  187. .process_transaction(transaction_init_contract)
  188. .await
  189. .unwrap();
  190. let verifying_key =
  191. hex::decode("74313a6525edf99936aa1477e94c72bc5cc617b21745f5f03296f3154461f214").unwrap();
  192. let message = hex::decode(
  193. "b9011a82e5cddee2c1bd364c8c57e1c98a6a28d194afcad410ff412226c8b2ae931ff59a57147cb47c7307\
  194. afc2a0a1abec4dd7e835a5b7113cf5aeac13a745c6bed6c60074313a6525edf99936aa1477e94c72bc5cc61\
  195. 7b21745f5f03296f3154461f2141c0075d3c7931c9773f30a240600010102000000010000e1f50500000000",
  196. )
  197. .unwrap();
  198. setup.set_trusted(verifying_key.try_into().unwrap()).await;
  199. setup.verify_message(&message, treasury).await;
  200. }
  201. #[tokio::test]
  202. async fn test_alignment() {
  203. let mut setup = Setup::new().await;
  204. let treasury = setup.create_treasury().await;
  205. let mut transaction_init_contract = Transaction::new_with_payer(
  206. &[Instruction::new_with_bytes(
  207. pyth_lazer_solana_contract::ID,
  208. &pyth_lazer_solana_contract::instruction::Initialize {
  209. top_authority: setup.payer.pubkey(),
  210. treasury,
  211. }
  212. .data(),
  213. vec![
  214. AccountMeta::new(setup.payer.pubkey(), true),
  215. AccountMeta::new(pyth_lazer_solana_contract::STORAGE_ID, false),
  216. AccountMeta::new_readonly(system_program::ID, false),
  217. ],
  218. )],
  219. Some(&setup.payer.pubkey()),
  220. );
  221. transaction_init_contract.sign(&[&setup.payer], setup.recent_blockhash);
  222. setup
  223. .banks_client
  224. .process_transaction(transaction_init_contract)
  225. .await
  226. .unwrap();
  227. let verifying_key =
  228. hex::decode("f65210bee4fcf5b1cee1e537fabcfd95010297653b94af04d454fc473e94834f").unwrap();
  229. let message = hex::decode(
  230. "b9011a82d100f6ce88ef26fd5a74312b4bb19e18e74162ffbfe66a7204c6f7ee9085ad6b670ec96d167cfef\
  231. ad437a1c79e67c75581c5cf99e64e680a1badeb3d88733d02f65210bee4fcf5b1cee1e537fabcfd950102976\
  232. 53b94af04d454fc473e94834f770075d3c79340d609f7652c06000303010000000400305680106309000002a\
  233. 023551b6309000001a62ab3056309000004f8ff02000000040021ae69754b00000002a04b8f764b000000018\
  234. 8b24b744b00000004f8ff060000000400377180d00500000002f2c19dd0050000000162c26ad00500000004f8ff",
  235. )
  236. .unwrap();
  237. setup.set_trusted(verifying_key.try_into().unwrap()).await;
  238. setup.verify_message(&message, treasury).await;
  239. }
  240. #[tokio::test]
  241. async fn test_rejects_wrong_offset() {
  242. let mut setup = Setup::new().await;
  243. let treasury = setup.create_treasury().await;
  244. let mut transaction_init_contract = Transaction::new_with_payer(
  245. &[Instruction::new_with_bytes(
  246. pyth_lazer_solana_contract::ID,
  247. &pyth_lazer_solana_contract::instruction::Initialize {
  248. top_authority: setup.payer.pubkey(),
  249. treasury,
  250. }
  251. .data(),
  252. vec![
  253. AccountMeta::new(setup.payer.pubkey(), true),
  254. AccountMeta::new(pyth_lazer_solana_contract::STORAGE_ID, false),
  255. AccountMeta::new_readonly(system_program::ID, false),
  256. ],
  257. )],
  258. Some(&setup.payer.pubkey()),
  259. );
  260. transaction_init_contract.sign(&[&setup.payer], setup.recent_blockhash);
  261. setup
  262. .banks_client
  263. .process_transaction(transaction_init_contract)
  264. .await
  265. .unwrap();
  266. let verifying_key =
  267. hex::decode("74313a6525edf99936aa1477e94c72bc5cc617b21745f5f03296f3154461f214").unwrap();
  268. let verifying_key_2 =
  269. hex::decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap();
  270. let message = hex::decode(
  271. [
  272. // --- First copy of the message (this data is returned by the Lazer contract)
  273. // SOLANA_FORMAT_MAGIC_LE
  274. "b9011a82",
  275. // Signature
  276. "e5cddee2c1bd364c8c57e1c98a6a28d194afcad410ff412226c8b2ae931ff59a57147cb47c7307afc2a0a1abec4dd7e835a5b7113cf5aeac13a745c6bed6c600",
  277. // Pubkey
  278. "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
  279. // Payload length (could be adjusted)
  280. "1c00",
  281. // Payload
  282. "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
  283. // --- Second copy of the message (this data is used by the ed25519 program)
  284. // Unused, was SOLANA_FORMAT_MAGIC_LE, could be removed, left it for slightly easier offset adjustments
  285. "AABBCCDD",
  286. // Signature
  287. "e5cddee2c1bd364c8c57e1c98a6a28d194afcad410ff412226c8b2ae931ff59a57147cb47c7307afc2a0a1abec4dd7e835a5b7113cf5aeac13a745c6bed6c600",
  288. // Pubkey
  289. "74313a6525edf99936aa1477e94c72bc5cc617b21745f5f03296f3154461f214",
  290. // Payload length
  291. "1c00",
  292. // Payload
  293. "75d3c7931c9773f30a240600010102000000010000e1f50500000000"
  294. ].concat()
  295. )
  296. .unwrap();
  297. setup.set_trusted(verifying_key.try_into().unwrap()).await;
  298. setup.set_trusted(verifying_key_2.try_into().unwrap()).await;
  299. let err = setup
  300. .verify_message_with_offset(&message, treasury, 12 + 130)
  301. .await
  302. .unwrap_err();
  303. assert!(matches!(
  304. err,
  305. BanksClientError::TransactionError(TransactionError::InstructionError(
  306. 1,
  307. InstructionError::InvalidInstructionData
  308. ))
  309. ));
  310. }