test1.rs 13 KB

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