Wormhole.fc 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. #include "imports/stdlib.fc";
  2. #include "common/errors.fc";
  3. #include "common/utils.fc";
  4. #include "common/storage.fc";
  5. ;; Signature verification function
  6. ;; ECRECOVER: Recovers the signer's address from the signature
  7. ;; It returns 1 value (0) on failure and 4 values on success
  8. ;; NULLSWAPIFNOT and NULLSWAPIFNOT2: Ensure consistent return of 4 values
  9. ;; These opcodes swap nulls onto the stack if ECRECOVER fails, maintaining the 4-value return
  10. (int, int, int, int) check_sig (int hash, int v, int r, int s) asm
  11. "ECRECOVER" ;; Attempt to recover the signer's address
  12. "NULLSWAPIFNOT" ;; If recovery failed, insert null under the top of the stack
  13. "NULLSWAPIFNOT2"; ;; If recovery failed, insert two more nulls under the top of the stack
  14. ;; Constants
  15. const int GUARDIAN_SET_EXPIRY = 86400; ;; 1 day in seconds
  16. const int UPGRADE_MODULE = 0x0000000000000000000000000000000000000000000000000000000000436f7265; ;; "Core" (left-padded to 256 bits) in hex
  17. ;; For troubleshooting purposes
  18. () dump_guardian_sets(cell keys) impure {
  19. int key = -1;
  20. do {
  21. (key, slice value, int found) = keys.udict_get_next?(32, key);
  22. if (found) {
  23. ~dump(key);
  24. ~dump(value);
  25. }
  26. } until (~ found);
  27. }
  28. ;; Internal helper methods
  29. (int, cell, int) parse_guardian_set(slice guardian_set) {
  30. slice cs = guardian_set~load_ref().begin_parse();
  31. int expiration_time = cs~load_uint(64);
  32. ;; slice keys = cs~load_ref().begin_parse();
  33. cell keys_dict = cs~load_dict();
  34. int key_count = 0;
  35. int key = -1;
  36. do {
  37. (key, slice address, int found) = keys_dict.udict_get_next?(8, key);
  38. if (found) {
  39. key_count += 1;
  40. }
  41. } until (~ found);
  42. return (expiration_time, keys_dict, key_count);
  43. }
  44. (int, cell, int) get_guardian_set_internal(int index) {
  45. (slice guardian_set, int found?) = guardian_sets.udict_get?(32, index);
  46. throw_unless(ERROR_GUARDIAN_SET_NOT_FOUND, found?);
  47. (int expiration_time, cell keys, int key_count) = parse_guardian_set(guardian_set);
  48. return (expiration_time, keys, key_count);
  49. }
  50. ;; Get methods
  51. int get_current_guardian_set_index() method_id {
  52. return current_guardian_set_index;
  53. }
  54. (int, cell, int) get_guardian_set(int index) method_id {
  55. return get_guardian_set_internal(index);
  56. }
  57. int get_chain_id() method_id {
  58. return chain_id;
  59. }
  60. int get_governance_chain_id() method_id {
  61. return governance_chain_id;
  62. }
  63. int get_governance_contract() method_id {
  64. return governance_contract;
  65. }
  66. int governance_action_is_consumed(int hash) method_id {
  67. (_, int found?) = consumed_governance_actions.udict_get?(256, hash);
  68. return found?;
  69. }
  70. () verify_signatures(int hash, slice signatures, int signers_length, cell guardian_set_keys, int guardian_set_size) impure {
  71. slice cs = signatures;
  72. int i = 0;
  73. int valid_signatures = 0;
  74. while (i < signers_length) {
  75. int guardian_index = cs~load_uint(8);
  76. (_, int found?) = guardian_sets.udict_get?(32, guardian_index);
  77. throw_unless(ERROR_GUARDIAN_SET_NOT_FOUND, found?);
  78. int r = cs~load_uint(256);
  79. int s = cs~load_uint(256);
  80. int v = cs~load_uint(8);
  81. (_, int x1, int x2, int valid) = check_sig(hash, v >= 27 ? v - 27 : v, r, s);
  82. throw_unless(ERROR_INVALID_SIGNATURES, valid);
  83. int parsed_address = pubkey_to_eth_address(x1, x2);
  84. (slice guardian_key, int found?) = guardian_set_keys.udict_get?(8, guardian_index);
  85. int guardian_address = guardian_key~load_uint(160);
  86. throw_unless(ERROR_INVALID_GUARDIAN_ADDRESS, parsed_address == guardian_address);
  87. valid_signatures += 1;
  88. i += 1;
  89. }
  90. ;; Check quorum (2/3 + 1)
  91. ;; We're using a fixed point number transformation with 1 decimal to deal with rounding.
  92. throw_unless(ERROR_NO_QUORUM, valid_signatures >= (((guardian_set_size * 10) / 3) * 2) / 10 + 1);
  93. }
  94. (int, int, int, int, int, int, int, int, slice, int) parse_and_verify_wormhole_vm(slice in_msg_body) impure {
  95. ;; Parse VM fields
  96. int version = in_msg_body~load_uint(8);
  97. throw_unless(ERROR_INVALID_VERSION, version == 1);
  98. int vm_guardian_set_index = in_msg_body~load_uint(32);
  99. ;; Verify and check if guardian set is valid
  100. (int expiration_time, cell keys, int key_count) = get_guardian_set_internal(vm_guardian_set_index);
  101. throw_if(ERROR_INVALID_GUARDIAN_SET_KEYS_LENGTH, cell_null?(keys));
  102. throw_unless(ERROR_INVALID_GUARDIAN_SET,
  103. (current_guardian_set_index == vm_guardian_set_index) &
  104. ((expiration_time == 0) | (expiration_time > now()))
  105. );
  106. int signers_length = in_msg_body~load_uint(8);
  107. ;; Calculate signatures_size in bits (66 bytes per signature: 1 (guardianIndex) + 32 (r) + 32 (s) + 1 (v))
  108. int signatures_size = signers_length * 66 * 8;
  109. ;; Load signatures
  110. (cell signatures, slice remaining_body) = read_and_store_large_data(in_msg_body, signatures_size);
  111. in_msg_body = remaining_body;
  112. ;; Calculate total body length across all references
  113. int body_length = 0;
  114. int continue? = -1; ;; -1 is true
  115. do {
  116. body_length += remaining_body.slice_bits();
  117. if (remaining_body.slice_refs_empty?()) {
  118. continue? = 0;
  119. } else {
  120. remaining_body = remaining_body~load_ref().begin_parse();
  121. }
  122. } until (~ continue?);
  123. ;; Load body
  124. (cell body_cell, _) = read_and_store_large_data(in_msg_body, body_length);
  125. int hash = hash_vm_body(body_cell.begin_parse());
  126. ;; Verify signatures
  127. verify_signatures(hash, signatures.begin_parse(), signers_length, keys, key_count);
  128. slice body_slice = body_cell.begin_parse();
  129. int timestamp = body_slice~load_uint(32);
  130. int nonce = body_slice~load_uint(32);
  131. int emitter_chain_id = body_slice~load_uint(16);
  132. int emitter_address = body_slice~load_uint(256);
  133. int sequence = body_slice~load_uint(64);
  134. int consistency_level = body_slice~load_uint(8);
  135. slice payload = body_slice;
  136. return (
  137. version,
  138. vm_guardian_set_index,
  139. timestamp,
  140. nonce,
  141. emitter_chain_id,
  142. emitter_address,
  143. sequence,
  144. consistency_level,
  145. payload,
  146. hash
  147. );
  148. }
  149. (int, int, int, cell, int) parse_encoded_upgrade(int current_guardian_set_index, slice payload) impure {
  150. int module = payload~load_uint(256);
  151. throw_unless(ERROR_INVALID_MODULE, module == UPGRADE_MODULE);
  152. int action = payload~load_uint(8);
  153. throw_unless(ERROR_INVALID_GOVERNANCE_ACTION, action == 2);
  154. int chain = payload~load_uint(16);
  155. int new_guardian_set_index = payload~load_uint(32);
  156. throw_unless(ERROR_NEW_GUARDIAN_SET_INDEX_IS_INVALID, new_guardian_set_index == (current_guardian_set_index + 1));
  157. int guardian_length = payload~load_uint(8);
  158. cell new_guardian_set_keys = new_dict();
  159. int key_count = 0;
  160. while (key_count < guardian_length) {
  161. builder key = begin_cell();
  162. int key_bits_loaded = 0;
  163. while (key_bits_loaded < 160) {
  164. int bits_to_load = min(payload.slice_bits(), 160 - key_bits_loaded);
  165. key = key.store_slice(payload~load_bits(bits_to_load));
  166. key_bits_loaded += bits_to_load;
  167. if (key_bits_loaded < 160) {
  168. throw_unless(ERROR_INVALID_GUARDIAN_SET_UPGRADE_LENGTH, ~ payload.slice_refs_empty?());
  169. payload = payload~load_ref().begin_parse();
  170. }
  171. }
  172. slice key_slice = key.end_cell().begin_parse();
  173. new_guardian_set_keys~udict_set(8, key_count, key_slice);
  174. key_count += 1;
  175. }
  176. throw_unless(ERROR_GUARDIAN_SET_KEYS_LENGTH_NOT_EQUAL, key_count == guardian_length);
  177. throw_unless(ERROR_INVALID_GUARDIAN_SET_UPGRADE_LENGTH, payload.slice_empty?());
  178. return (action, chain, module, new_guardian_set_keys, new_guardian_set_index);
  179. }
  180. () update_guardian_set(slice in_msg_body) impure {
  181. ;; Verify governance VM
  182. (int version, int vm_guardian_set_index, int timestamp, int nonce, int emitter_chain_id, int emitter_address, int sequence, int consistency_level, slice payload, int hash) = parse_and_verify_wormhole_vm(in_msg_body);
  183. ;; Verify the emitter chain and address
  184. int governance_chain_id = get_governance_chain_id();
  185. throw_unless(ERROR_INVALID_GOVERNANCE_CHAIN, emitter_chain_id == governance_chain_id);
  186. int governance_contract_address = get_governance_contract();
  187. throw_unless(ERROR_INVALID_GOVERNANCE_CONTRACT, emitter_address == governance_contract_address);
  188. ;; Check if the governance action has already been consumed
  189. throw_if(ERROR_GOVERNANCE_ACTION_ALREADY_CONSUMED, governance_action_is_consumed(hash));
  190. ;; Parse the new guardian set from the payload
  191. (int action, int chain, int module, cell new_guardian_set_keys, int new_guardian_set_index) = parse_encoded_upgrade(current_guardian_set_index, payload);
  192. ;; Set expiry if current GuardianSet exists
  193. (slice current_guardian_set, int found?) = guardian_sets.udict_get?(32, current_guardian_set_index);
  194. if (found?) {
  195. (int expiration_time, cell keys, int key_count) = parse_guardian_set(current_guardian_set);
  196. cell updated_guardian_set = begin_cell()
  197. .store_uint(now() + GUARDIAN_SET_EXPIRY, 64) ;; expiration time
  198. .store_dict(keys) ;; keys
  199. .end_cell();
  200. guardian_sets~udict_set(32, current_guardian_set_index, updated_guardian_set.begin_parse());
  201. }
  202. ;; Store the new guardian set
  203. cell new_guardian_set = begin_cell()
  204. .store_uint(0, 64) ;; expiration_time, set to 0 initially
  205. .store_ref(new_guardian_set_keys)
  206. .end_cell();
  207. guardian_sets~udict_set(32, new_guardian_set_index, new_guardian_set.begin_parse());
  208. ;; Update the current guardian set index
  209. current_guardian_set_index = new_guardian_set_index;
  210. ;; Mark the governance action as consumed
  211. consumed_governance_actions~udict_set(256, hash, begin_cell().store_int(true, 1).end_cell().begin_parse());
  212. }
  213. () execute_governance_action(slice in_msg_body) impure {
  214. ;; TODO: Implement
  215. }