wormhole.cairo 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. use pyth::reader::ByteArray;
  2. use core::starknet::secp256_trait::Signature;
  3. use pyth::util::UnwrapWithFelt252;
  4. #[starknet::interface]
  5. pub trait IWormhole<T> {
  6. fn submit_new_guardian_set(
  7. ref self: T, set_index: u32, guardians: Array<felt252>
  8. ) -> Result<(), SubmitNewGuardianSetError>;
  9. fn parse_and_verify_vm(ref self: T, encoded_vm: ByteArray) -> Result<VM, ParseAndVerifyVmError>;
  10. }
  11. #[derive(Drop, Debug, Clone, Serde)]
  12. pub struct GuardianSignature {
  13. pub guardian_index: u8,
  14. pub signature: Signature,
  15. }
  16. #[derive(Drop, Debug, Clone, Serde)]
  17. pub struct VM {
  18. pub version: u8,
  19. pub guardian_set_index: u32,
  20. pub signatures: Array<GuardianSignature>,
  21. pub timestamp: u32,
  22. pub nonce: u32,
  23. pub emitter_chain_id: u16,
  24. pub emitter_address: u256,
  25. pub sequence: u64,
  26. pub consistency_level: u8,
  27. pub payload: ByteArray,
  28. }
  29. #[derive(Copy, Drop, Debug, Serde, PartialEq)]
  30. pub enum SubmitNewGuardianSetError {
  31. NoGuardiansSpecified,
  32. TooManyGuardians,
  33. InvalidGuardianKey,
  34. // guardian set index must increase in steps of 1
  35. InvalidGuardianSetSequence,
  36. AccessDenied,
  37. }
  38. pub impl SubmitNewGuardianSetErrorUnwrapWithFelt252<
  39. T
  40. > of UnwrapWithFelt252<T, SubmitNewGuardianSetError> {
  41. fn unwrap_with_felt252(self: Result<T, SubmitNewGuardianSetError>) -> T {
  42. match self {
  43. Result::Ok(v) => v,
  44. Result::Err(err) => core::panic_with_felt252(err.into()),
  45. }
  46. }
  47. }
  48. impl SubmitNewGuardianSetErrorIntoFelt252 of Into<SubmitNewGuardianSetError, felt252> {
  49. fn into(self: SubmitNewGuardianSetError) -> felt252 {
  50. match self {
  51. SubmitNewGuardianSetError::NoGuardiansSpecified => 'no guardians specified',
  52. SubmitNewGuardianSetError::TooManyGuardians => 'too many guardians',
  53. SubmitNewGuardianSetError::InvalidGuardianKey => 'invalid guardian key',
  54. SubmitNewGuardianSetError::InvalidGuardianSetSequence => 'invalid guardian set sequence',
  55. SubmitNewGuardianSetError::AccessDenied => 'access denied',
  56. }
  57. }
  58. }
  59. #[derive(Copy, Drop, Debug, Serde, PartialEq)]
  60. pub enum ParseAndVerifyVmError {
  61. Reader: pyth::reader::Error,
  62. VmVersionIncompatible,
  63. InvalidGuardianSetIndex,
  64. InvalidSignature,
  65. GuardianSetExpired,
  66. NoQuorum,
  67. InvalidSignatureOrder,
  68. InvalidGuardianIndex,
  69. }
  70. pub impl ParseAndVerifyVmErrorUnwrapWithFelt252<T> of UnwrapWithFelt252<T, ParseAndVerifyVmError> {
  71. fn unwrap_with_felt252(self: Result<T, ParseAndVerifyVmError>) -> T {
  72. match self {
  73. Result::Ok(v) => v,
  74. Result::Err(err) => core::panic_with_felt252(err.into()),
  75. }
  76. }
  77. }
  78. impl ErrorIntoFelt252 of Into<ParseAndVerifyVmError, felt252> {
  79. fn into(self: ParseAndVerifyVmError) -> felt252 {
  80. match self {
  81. ParseAndVerifyVmError::Reader(err) => err.into(),
  82. ParseAndVerifyVmError::VmVersionIncompatible => 'VM version incompatible',
  83. ParseAndVerifyVmError::InvalidGuardianSetIndex => 'invalid guardian set index',
  84. ParseAndVerifyVmError::InvalidSignature => 'invalid signature',
  85. ParseAndVerifyVmError::GuardianSetExpired => 'guardian set expired',
  86. ParseAndVerifyVmError::NoQuorum => 'no quorum',
  87. ParseAndVerifyVmError::InvalidSignatureOrder => 'invalid signature order',
  88. ParseAndVerifyVmError::InvalidGuardianIndex => 'invalid guardian index',
  89. }
  90. }
  91. }
  92. pub fn quorum(num_guardians: usize) -> usize {
  93. assert(num_guardians < 256, SubmitNewGuardianSetError::TooManyGuardians.into());
  94. ((num_guardians * 2) / 3) + 1
  95. }
  96. #[starknet::contract]
  97. mod wormhole {
  98. use pyth::util::UnwrapWithFelt252;
  99. use core::box::BoxTrait;
  100. use core::array::ArrayTrait;
  101. use super::{
  102. VM, IWormhole, GuardianSignature, quorum, ParseAndVerifyVmError, SubmitNewGuardianSetError
  103. };
  104. use pyth::reader::{Reader, ReaderImpl, ByteArray};
  105. use core::starknet::secp256_trait::{Signature, recover_public_key, Secp256PointTrait};
  106. use core::starknet::secp256k1::Secp256k1Point;
  107. use core::starknet::{
  108. ContractAddress, get_execution_info, get_caller_address, get_block_timestamp
  109. };
  110. use core::keccak::cairo_keccak;
  111. use core::integer::u128_byte_reverse;
  112. use core::panic_with_felt252;
  113. use pyth::hash::{Hasher, HasherImpl};
  114. use pyth::util::{ONE_SHIFT_160, UNEXPECTED_OVERFLOW};
  115. #[generate_trait]
  116. impl ResultReaderToWormhole<T> of ResultReaderToWormholeTrait<T> {
  117. fn map_err(self: Result<T, pyth::reader::Error>) -> Result<T, ParseAndVerifyVmError> {
  118. match self {
  119. Result::Ok(v) => Result::Ok(v),
  120. Result::Err(err) => Result::Err(ParseAndVerifyVmError::Reader(err)),
  121. }
  122. }
  123. }
  124. #[derive(Drop, Debug, Clone, Serde, starknet::Store)]
  125. struct GuardianSet {
  126. num_guardians: usize,
  127. // XXX: storage doesn't work if we use Option here.
  128. expiration_time: u64,
  129. }
  130. #[storage]
  131. struct Storage {
  132. owner: ContractAddress,
  133. current_guardian_set_index: u32,
  134. guardian_sets: LegacyMap<u32, GuardianSet>,
  135. // (guardian_set_index, guardian_index) => guardian_address
  136. guardian_keys: LegacyMap<(u32, u8), u256>,
  137. }
  138. #[constructor]
  139. fn constructor(
  140. ref self: ContractState, owner: ContractAddress, initial_guardians: Array<felt252>
  141. ) {
  142. self.owner.write(owner);
  143. let set_index = 0;
  144. store_guardian_set(ref self, set_index, initial_guardians).unwrap_with_felt252();
  145. }
  146. fn store_guardian_set(
  147. ref self: ContractState, set_index: u32, guardians: Array<felt252>
  148. ) -> Result<(), SubmitNewGuardianSetError> {
  149. if guardians.len() == 0 {
  150. return Result::Err(SubmitNewGuardianSetError::NoGuardiansSpecified.into());
  151. }
  152. if guardians.len() >= 256 {
  153. return Result::Err(SubmitNewGuardianSetError::TooManyGuardians.into());
  154. }
  155. let set = GuardianSet { num_guardians: guardians.len(), expiration_time: 0 };
  156. self.guardian_sets.write(set_index, set);
  157. let mut i = 0;
  158. let mut result = Result::Ok(());
  159. while i < guardians.len() {
  160. let key = *guardians.at(i);
  161. if key == 0 {
  162. result = Result::Err(SubmitNewGuardianSetError::InvalidGuardianKey.into());
  163. break;
  164. }
  165. // i < 256
  166. self
  167. .guardian_keys
  168. .write((set_index, i.try_into().expect(UNEXPECTED_OVERFLOW)), key.into());
  169. i += 1;
  170. };
  171. result?;
  172. self.current_guardian_set_index.write(set_index);
  173. Result::Ok(())
  174. }
  175. fn expire_guardian_set(ref self: ContractState, set_index: u32, now: u64) {
  176. let mut set = self.guardian_sets.read(set_index);
  177. set.expiration_time = now + 86400;
  178. self.guardian_sets.write(set_index, set);
  179. }
  180. #[abi(embed_v0)]
  181. impl WormholeImpl of IWormhole<ContractState> {
  182. fn submit_new_guardian_set(
  183. ref self: ContractState, set_index: u32, guardians: Array<felt252>
  184. ) -> Result<(), SubmitNewGuardianSetError> {
  185. let execution_info = get_execution_info().unbox();
  186. if self.owner.read() != execution_info.caller_address {
  187. return Result::Err(SubmitNewGuardianSetError::AccessDenied);
  188. }
  189. let current_set_index = self.current_guardian_set_index.read();
  190. if set_index != current_set_index + 1 {
  191. return Result::Err(SubmitNewGuardianSetError::InvalidGuardianSetSequence.into());
  192. }
  193. expire_guardian_set(
  194. ref self, current_set_index, execution_info.block_info.unbox().block_timestamp
  195. );
  196. store_guardian_set(ref self, set_index, guardians)
  197. }
  198. fn parse_and_verify_vm(
  199. ref self: ContractState, encoded_vm: ByteArray
  200. ) -> Result<VM, ParseAndVerifyVmError> {
  201. let (vm, body_hash) = parse_vm(encoded_vm)?;
  202. let guardian_set = self.guardian_sets.read(vm.guardian_set_index);
  203. if guardian_set.num_guardians == 0 {
  204. return Result::Err(ParseAndVerifyVmError::InvalidGuardianSetIndex);
  205. }
  206. if vm.guardian_set_index != self.current_guardian_set_index.read()
  207. && guardian_set.expiration_time < get_block_timestamp() {
  208. return Result::Err(ParseAndVerifyVmError::GuardianSetExpired);
  209. }
  210. if vm.signatures.len() < quorum(guardian_set.num_guardians) {
  211. return Result::Err(ParseAndVerifyVmError::NoQuorum);
  212. }
  213. let mut signatures_clone = vm.signatures.clone();
  214. let mut last_index = Option::None;
  215. let mut result = Result::Ok(());
  216. loop {
  217. let signature = match signatures_clone.pop_front() {
  218. Option::Some(v) => { v },
  219. Option::None => { break; },
  220. };
  221. match last_index {
  222. Option::Some(last_index) => {
  223. if *(@signature).guardian_index <= last_index {
  224. result = Result::Err(ParseAndVerifyVmError::InvalidSignatureOrder);
  225. break;
  226. }
  227. },
  228. Option::None => {},
  229. };
  230. last_index = Option::Some(*(@signature).guardian_index);
  231. if signature.guardian_index.into() >= guardian_set.num_guardians {
  232. result = Result::Err(ParseAndVerifyVmError::InvalidGuardianIndex);
  233. break;
  234. }
  235. let guardian_key = self
  236. .guardian_keys
  237. .read((vm.guardian_set_index, signature.guardian_index));
  238. let r = verify_signature(body_hash, signature.signature, guardian_key);
  239. if r.is_err() {
  240. result = r;
  241. break;
  242. }
  243. };
  244. result?;
  245. Result::Ok(vm)
  246. }
  247. }
  248. fn parse_signature(ref reader: Reader) -> Result<GuardianSignature, ParseAndVerifyVmError> {
  249. let guardian_index = reader.read_u8().map_err()?;
  250. let r = reader.read_u256().map_err()?;
  251. let s = reader.read_u256().map_err()?;
  252. let recovery_id = reader.read_u8().map_err()?;
  253. let y_parity = (recovery_id % 2) > 0;
  254. let signature = GuardianSignature {
  255. guardian_index, signature: Signature { r, s, y_parity }
  256. };
  257. Result::Ok(signature)
  258. }
  259. fn parse_vm(encoded_vm: ByteArray) -> Result<(VM, u256), ParseAndVerifyVmError> {
  260. let mut reader = ReaderImpl::new(encoded_vm);
  261. let version = reader.read_u8().map_err()?;
  262. if version != 1 {
  263. return Result::Err(ParseAndVerifyVmError::VmVersionIncompatible);
  264. }
  265. let guardian_set_index = reader.read_u32().map_err()?;
  266. let sig_count = reader.read_u8().map_err()?;
  267. let mut i = 0;
  268. let mut signatures = array![];
  269. let mut result = Result::Ok(());
  270. while i < sig_count {
  271. match parse_signature(ref reader) {
  272. Result::Ok(signature) => { signatures.append(signature); },
  273. Result::Err(err) => {
  274. result = Result::Err(err);
  275. break;
  276. },
  277. }
  278. i += 1;
  279. };
  280. result?;
  281. let mut reader_for_hash = reader.clone();
  282. let mut hasher = HasherImpl::new();
  283. hasher.push_reader(ref reader_for_hash).map_err()?;
  284. let body_hash1 = hasher.finalize();
  285. let mut hasher2 = HasherImpl::new();
  286. hasher2.push_u256(body_hash1);
  287. let body_hash2 = hasher2.finalize();
  288. let timestamp = reader.read_u32().map_err()?;
  289. let nonce = reader.read_u32().map_err()?;
  290. let emitter_chain_id = reader.read_u16().map_err()?;
  291. let emitter_address = reader.read_u256().map_err()?;
  292. let sequence = reader.read_u64().map_err()?;
  293. let consistency_level = reader.read_u8().map_err()?;
  294. let payload_len = reader.len();
  295. let payload = reader.read_byte_array(payload_len).map_err()?;
  296. let vm = VM {
  297. version,
  298. guardian_set_index,
  299. signatures,
  300. timestamp,
  301. nonce,
  302. emitter_chain_id,
  303. emitter_address,
  304. sequence,
  305. consistency_level,
  306. payload,
  307. };
  308. Result::Ok((vm, body_hash2))
  309. }
  310. fn verify_signature(
  311. body_hash: u256, signature: Signature, guardian_key: u256,
  312. ) -> Result<(), ParseAndVerifyVmError> {
  313. let point: Secp256k1Point = recover_public_key(body_hash, signature)
  314. .ok_or(ParseAndVerifyVmError::InvalidSignature)?;
  315. let address = eth_address(point)?;
  316. assert(guardian_key != 0, SubmitNewGuardianSetError::InvalidGuardianKey.into());
  317. if address != guardian_key {
  318. return Result::Err(ParseAndVerifyVmError::InvalidSignature);
  319. }
  320. Result::Ok(())
  321. }
  322. fn eth_address(point: Secp256k1Point) -> Result<u256, ParseAndVerifyVmError> {
  323. let (x, y) = match point.get_coordinates() {
  324. Result::Ok(v) => { v },
  325. Result::Err(_) => { return Result::Err(ParseAndVerifyVmError::InvalidSignature); },
  326. };
  327. let mut hasher = HasherImpl::new();
  328. hasher.push_u256(x);
  329. hasher.push_u256(y);
  330. let address = hasher.finalize() % ONE_SHIFT_160;
  331. Result::Ok(address)
  332. }
  333. }