wormhole.cairo 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. use pyth::byte_buffer::{ByteBuffer, ByteBufferImpl};
  2. use pyth::reader::ReaderImpl;
  3. use pyth::util::{array_try_into, one_shift_left_bytes_u256};
  4. use pyth::wormhole::{
  5. Event as WormholeEvent, GuardianSetAdded, IWormholeDispatcher, IWormholeDispatcherTrait,
  6. };
  7. use snforge_std::{
  8. ContractClass, ContractClassTrait, DeclareResultTrait, Event, EventSpyTrait, EventsFilterTrait,
  9. declare, spy_events,
  10. };
  11. use starknet::{ContractAddress, EthAddress};
  12. use super::wormhole_guardians::{
  13. guardian_set0, guardian_set1, guardian_set2, guardian_set3, guardian_set4,
  14. };
  15. use super::data;
  16. #[generate_trait]
  17. impl DecodeEventHelpers of DecodeEventHelpersTrait {
  18. fn pop<T, +TryInto<felt252, T>>(ref self: Array<felt252>) -> T {
  19. self.pop_front().unwrap().try_into().unwrap()
  20. }
  21. }
  22. fn decode_event(mut event: Event) -> WormholeEvent {
  23. let key0: felt252 = event.keys.pop();
  24. let output = if key0 == selector!("GuardianSetAdded") {
  25. let event = GuardianSetAdded { index: event.data.pop() };
  26. WormholeEvent::GuardianSetAdded(event)
  27. } else {
  28. panic!("unrecognized event")
  29. };
  30. assert!(event.keys.len() == 0);
  31. assert!(event.data.len() == 0);
  32. output
  33. }
  34. #[test]
  35. fn test_parse_and_verify_vm_works() {
  36. let dispatcher = deploy_with_mainnet_guardians();
  37. assert!(dispatcher.chain_id() == CHAIN_ID);
  38. assert!(dispatcher.governance_chain_id() == GOVERNANCE_CHAIN_ID);
  39. assert!(dispatcher.governance_contract() == GOVERNANCE_CONTRACT);
  40. assert!(dispatcher.get_current_guardian_set_index() == 4);
  41. let hash1 = 107301215816534416941414788869570552056251358022232518071775510605007996627157;
  42. assert!(dispatcher.governance_action_is_consumed(hash1));
  43. let hash2 = 69383087152252644362837994811527963218617951938616206795754532940371100771846;
  44. assert!(dispatcher.governance_action_is_consumed(hash2));
  45. let hash3 = 98574986520203705693876007869045870422906099682167905265431258750902697716275;
  46. assert!(dispatcher.governance_action_is_consumed(hash3));
  47. let hash4 = 108039149047034949008762260417639972218494870245424949643402483285149262098173;
  48. assert!(dispatcher.governance_action_is_consumed(hash4));
  49. let not_hash4 = 108039149047034949008762260417639972218494870245424949643402483285149262098174;
  50. assert!(!dispatcher.governance_action_is_consumed(not_hash4));
  51. let vm = dispatcher.parse_and_verify_vm(data::good_vm1());
  52. assert!(vm.version == 1);
  53. assert!(vm.guardian_set_index == 3);
  54. assert!(vm.signatures.len() == 13);
  55. assert!(*vm.signatures.at(0).guardian_index == 1);
  56. assert!(*vm.signatures.at(1).guardian_index == 2);
  57. assert!(*vm.signatures.at(12).guardian_index == 18);
  58. assert!(vm.timestamp == 1712589207);
  59. assert!(vm.nonce == 0);
  60. assert!(vm.emitter_chain_id == 26);
  61. assert!(
  62. vm.emitter_address == 0xe101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71,
  63. );
  64. assert!(vm.sequence == 0x2f03161);
  65. assert!(vm.consistency_level == 1);
  66. assert!(vm.payload.len() == 37);
  67. let mut reader = ReaderImpl::new(vm.payload);
  68. assert!(reader.read_u8() == 65);
  69. assert!(reader.read_u8() == 85);
  70. assert!(reader.read_u8() == 87);
  71. }
  72. #[test]
  73. #[fuzzer(runs: 100, seed: 0)]
  74. #[should_panic]
  75. fn test_parse_and_verify_vm_rejects_corrupted_vm(pos: usize, random1: usize, random2: usize) {
  76. let dispatcher = deploy_with_mainnet_guardians();
  77. let input = corrupted_vm(data::good_vm1(), pos, random1, random2);
  78. let vm = dispatcher.parse_and_verify_vm(input);
  79. println!("no error, output: {:?}", vm);
  80. }
  81. #[test]
  82. #[should_panic(expected: ('wrong governance contract',))]
  83. fn test_submit_guardian_set_rejects_invalid_emitter() {
  84. let dispatcher = deploy_with_test_guardian();
  85. dispatcher.submit_new_guardian_set(data::wrong_emitter_upgrade());
  86. }
  87. #[test]
  88. #[should_panic(expected: ('invalid guardian set index',))]
  89. fn test_submit_guardian_set_rejects_wrong_index_in_signer() {
  90. let dispatcher = deploy_with_mainnet_guardian_set0();
  91. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade1());
  92. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade3());
  93. }
  94. #[test]
  95. fn test_submit_guardian_set_emits_events() {
  96. let dispatcher = deploy_with_mainnet_guardian_set0();
  97. let mut spy = spy_events();
  98. assert!(dispatcher.get_current_guardian_set_index() == 0);
  99. let hash1 = 107301215816534416941414788869570552056251358022232518071775510605007996627157;
  100. assert!(!dispatcher.governance_action_is_consumed(hash1));
  101. let hash2 = 69383087152252644362837994811527963218617951938616206795754532940371100771846;
  102. assert!(!dispatcher.governance_action_is_consumed(hash2));
  103. let hash3 = 98574986520203705693876007869045870422906099682167905265431258750902697716275;
  104. assert!(!dispatcher.governance_action_is_consumed(hash3));
  105. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade1());
  106. let mut events = spy.get_events().emitted_by(dispatcher.contract_address).events;
  107. assert!(events.len() == 1);
  108. let (from, event) = events.pop_front().unwrap();
  109. assert!(from == dispatcher.contract_address);
  110. let event = decode_event(event);
  111. let expected = GuardianSetAdded { index: 1 };
  112. assert!(event == WormholeEvent::GuardianSetAdded(expected));
  113. assert!(dispatcher.get_current_guardian_set_index() == 1);
  114. assert!(dispatcher.governance_action_is_consumed(hash1));
  115. assert!(!dispatcher.governance_action_is_consumed(hash2));
  116. assert!(!dispatcher.governance_action_is_consumed(hash3));
  117. let mut spy = spy_events();
  118. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade2());
  119. let mut events = spy.get_events().emitted_by(dispatcher.contract_address).events;
  120. assert!(events.len() == 1);
  121. let (from, event) = events.pop_front().unwrap();
  122. assert!(from == dispatcher.contract_address);
  123. let event = decode_event(event);
  124. let expected = GuardianSetAdded { index: 2 };
  125. assert!(event == WormholeEvent::GuardianSetAdded(expected));
  126. assert!(dispatcher.get_current_guardian_set_index() == 2);
  127. assert!(dispatcher.governance_action_is_consumed(hash1));
  128. assert!(dispatcher.governance_action_is_consumed(hash2));
  129. assert!(!dispatcher.governance_action_is_consumed(hash3));
  130. }
  131. #[test]
  132. fn test_get_guardian_set_works() {
  133. let dispatcher = deploy_with_mainnet_guardian_set0();
  134. let set0 = dispatcher.get_guardian_set(0);
  135. assert!(set0.keys == guardian_set0());
  136. assert!(set0.expiration_time.is_none());
  137. assert!(dispatcher.get_current_guardian_set_index() == 0);
  138. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade1());
  139. let set0 = dispatcher.get_guardian_set(0);
  140. assert!(set0.keys == guardian_set0());
  141. assert!(set0.expiration_time.is_some());
  142. let set1 = dispatcher.get_guardian_set(1);
  143. assert!(set1.keys == guardian_set1());
  144. assert!(set1.expiration_time.is_none());
  145. assert!(dispatcher.get_current_guardian_set_index() == 1);
  146. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade2());
  147. let set0 = dispatcher.get_guardian_set(0);
  148. assert!(set0.keys == guardian_set0());
  149. assert!(set0.expiration_time.is_some());
  150. let set1 = dispatcher.get_guardian_set(1);
  151. assert!(set1.keys == guardian_set1());
  152. assert!(set1.expiration_time.is_some());
  153. let set2 = dispatcher.get_guardian_set(2);
  154. assert!(set2.keys == guardian_set2());
  155. assert!(set2.expiration_time.is_none());
  156. assert!(dispatcher.get_current_guardian_set_index() == 2);
  157. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade3());
  158. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade4());
  159. let set3 = dispatcher.get_guardian_set(3);
  160. assert!(set3.keys == guardian_set3());
  161. assert!(set3.expiration_time.is_some());
  162. let set4 = dispatcher.get_guardian_set(4);
  163. assert!(set4.keys == guardian_set4());
  164. assert!(set4.expiration_time.is_none());
  165. assert!(dispatcher.get_current_guardian_set_index() == 4);
  166. }
  167. #[test]
  168. #[should_panic(expected: ('invalid index',))]
  169. fn test_get_guardian_set_rejects_invalid_index() {
  170. let dispatcher = deploy_with_mainnet_guardian_set0();
  171. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade1());
  172. dispatcher.get_guardian_set(2);
  173. }
  174. #[test]
  175. #[should_panic(expected: ('invalid guardian set sequence',))]
  176. fn test_submit_guardian_set_rejects_wrong_index_in_payload() {
  177. let dispatcher = deploy_with_test_guardian();
  178. dispatcher.submit_new_guardian_set(data::wrong_index_upgrade());
  179. }
  180. #[test]
  181. #[should_panic(expected: ('no guardians specified',))]
  182. fn test_deploy_rejects_empty() {
  183. deploy(0, array![], CHAIN_ID, GOVERNANCE_CHAIN_ID, GOVERNANCE_CONTRACT);
  184. }
  185. #[test]
  186. #[should_panic(expected: ('no guardians specified',))]
  187. fn test_submit_guardian_set_rejects_empty() {
  188. let dispatcher = deploy_with_test_guardian();
  189. dispatcher.submit_new_guardian_set(data::empty_set_upgrade());
  190. }
  191. #[test]
  192. #[fuzzer(runs: 100, seed: 0)]
  193. #[should_panic]
  194. fn test_submit_guardian_set_rejects_corrupted(pos: usize, random1: usize, random2: usize) {
  195. let dispatcher = deploy_with_mainnet_guardian_set0();
  196. let vm = corrupted_vm(data::mainnet_guardian_set_upgrade1(), pos, random1, random2);
  197. dispatcher.submit_new_guardian_set(vm);
  198. }
  199. #[test]
  200. #[fuzzer(runs: 100, seed: 0)]
  201. #[should_panic(expected: ('wrong governance chain',))]
  202. fn test_submit_guardian_set_rejects_non_governance(pos: usize, random1: usize, random2: usize) {
  203. let dispatcher = deploy_with_mainnet_guardian_set0();
  204. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade1());
  205. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade2());
  206. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade3());
  207. dispatcher.submit_new_guardian_set(data::good_vm1());
  208. }
  209. // Deploys a previously declared wormhole contract class at the specified address.
  210. // If address is not specified, the default address derivation is used.
  211. pub fn deploy_declared_at(
  212. class: @ContractClass,
  213. guardian_set_index: u32,
  214. guardians: Array<EthAddress>,
  215. chain_id: u16,
  216. governance_chain_id: u16,
  217. governance_contract: u256,
  218. address: Option<ContractAddress>,
  219. ) -> IWormholeDispatcher {
  220. let mut args = array![];
  221. guardian_set_index.serialize(ref args);
  222. (guardians, chain_id, governance_chain_id, governance_contract).serialize(ref args);
  223. let result = match address {
  224. Option::Some(address) => class.deploy_at(@args, address),
  225. Option::None => class.deploy(@args),
  226. };
  227. let (contract_address, _) = match result {
  228. Result::Ok(v) => { v },
  229. Result::Err(err) => { panic(err) },
  230. };
  231. IWormholeDispatcher { contract_address }
  232. }
  233. // Declares and deploys the contract.
  234. fn deploy(
  235. guardian_set_index: u32,
  236. guardians: Array<EthAddress>,
  237. chain_id: u16,
  238. governance_chain_id: u16,
  239. governance_contract: u256,
  240. ) -> IWormholeDispatcher {
  241. let class = declare("wormhole").unwrap().contract_class().deref();
  242. deploy_declared_at(
  243. @class,
  244. guardian_set_index,
  245. guardians,
  246. chain_id,
  247. governance_chain_id,
  248. governance_contract,
  249. Option::None,
  250. )
  251. }
  252. // Declares and deploys the contract and initializes it with mainnet guardian set upgrades.
  253. pub fn deploy_with_mainnet_guardians() -> IWormholeDispatcher {
  254. let dispatcher = deploy_with_mainnet_guardian_set0();
  255. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade1());
  256. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade2());
  257. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade3());
  258. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade4());
  259. dispatcher
  260. }
  261. pub fn deploy_with_mainnet_guardian_sets_3_4() -> IWormholeDispatcher {
  262. let dispatcher = deploy(3, guardian_set3(), CHAIN_ID, GOVERNANCE_CHAIN_ID, GOVERNANCE_CONTRACT);
  263. dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade4());
  264. dispatcher
  265. }
  266. pub fn deploy_with_mainnet_guardian_set4() -> IWormholeDispatcher {
  267. deploy(4, guardian_set4(), CHAIN_ID, GOVERNANCE_CHAIN_ID, GOVERNANCE_CONTRACT)
  268. }
  269. pub fn deploy_with_mainnet_guardian_set0() -> IWormholeDispatcher {
  270. deploy(0, guardian_set0(), CHAIN_ID, GOVERNANCE_CHAIN_ID, GOVERNANCE_CONTRACT)
  271. }
  272. // Declares and deploys the contract with the test guardian address that's used to sign VAAs
  273. // generated in `test_vaas`.
  274. pub fn deploy_with_test_guardian() -> IWormholeDispatcher {
  275. deploy(
  276. 0,
  277. array_try_into(array![data::TEST_GUARDIAN_ADDRESS1]),
  278. CHAIN_ID,
  279. GOVERNANCE_CHAIN_ID,
  280. GOVERNANCE_CONTRACT,
  281. )
  282. }
  283. // Deploys a previously declared wormhole contract class
  284. // with the test guardian address that's used to sign VAAs generated in `test_vaas`.
  285. pub fn deploy_declared_with_test_guardian_at(
  286. class: @ContractClass, address: ContractAddress,
  287. ) -> IWormholeDispatcher {
  288. deploy_declared_at(
  289. class,
  290. 0,
  291. array_try_into(array![data::TEST_GUARDIAN_ADDRESS1]),
  292. CHAIN_ID,
  293. GOVERNANCE_CHAIN_ID,
  294. GOVERNANCE_CONTRACT,
  295. Option::Some(address),
  296. )
  297. }
  298. pub fn corrupted_vm(
  299. mut real_data: ByteBuffer, pos: usize, random1: usize, random2: usize,
  300. ) -> ByteBuffer {
  301. let mut new_data = array![];
  302. // Make sure we select a position not on the last item because
  303. // we didn't implement corrupting an incomplete bytes31.
  304. let pos = pos % (real_data.len() - 31);
  305. let bad_index = pos / 31;
  306. let mut num_last_bytes = 0;
  307. let mut i = 0;
  308. loop {
  309. let (real_bytes, num_bytes) = match real_data.pop_front() {
  310. Option::Some(v) => v,
  311. Option::None => { break; },
  312. };
  313. if num_bytes < 31 {
  314. new_data.append(real_bytes);
  315. num_last_bytes = num_bytes;
  316. break;
  317. }
  318. if i == bad_index {
  319. new_data.append(corrupted_bytes(real_bytes, pos % 31, random1, random2));
  320. } else {
  321. new_data.append(real_bytes);
  322. }
  323. i += 1;
  324. }
  325. ByteBufferImpl::new(new_data, num_last_bytes)
  326. }
  327. // Returns an item of ByteBuffer data with 2 bytes changed. We need to change at least 2 bytes
  328. // because a single byte can be a recovery id, where only 1 bit matters so
  329. // a modification of recovery id can result in a valid VM.
  330. fn corrupted_bytes(input: felt252, index: usize, random1: usize, random2: usize) -> felt252 {
  331. let index2 = (index + 1) % 31;
  332. let input: u256 = input.into();
  333. let mut value: u256 = 0;
  334. let mut i: usize = 0;
  335. while i < 31 {
  336. let real_byte = (input / one_shift_left_bytes_u256(30 - i.try_into().unwrap())) % 0x100;
  337. let real_byte: u8 = real_byte.try_into().unwrap();
  338. let new_byte = if i == index {
  339. corrupted_byte(real_byte, random1)
  340. } else if i == index2 {
  341. corrupted_byte(real_byte, random2)
  342. } else {
  343. real_byte
  344. };
  345. value = value * 256 + new_byte.into();
  346. i += 1;
  347. }
  348. let value: felt252 = value.try_into().expect('corrupted bytes overflow');
  349. value.try_into().expect('corrupted bytes overflow')
  350. }
  351. // Returns a byte that's not equal to `value`.
  352. fn corrupted_byte(value: u8, random: usize) -> u8 {
  353. let v: u16 = value.into() + 1 + (random % 255).try_into().unwrap();
  354. (v % 256).try_into().unwrap()
  355. }
  356. pub const CHAIN_ID: u16 = 60051;
  357. pub const GOVERNANCE_CHAIN_ID: u16 = 1;
  358. pub const GOVERNANCE_CONTRACT: u256 = 4;