@layerzerolabs/protocol-stellar-v2 0.2.15 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/.turbo/turbo-build.log +365 -297
  2. package/.turbo/turbo-lint.log +142 -110
  3. package/.turbo/turbo-test.log +1273 -1222
  4. package/Cargo.lock +20 -5
  5. package/Cargo.toml +4 -1
  6. package/contracts/ERROR_SPEC.md +44 -0
  7. package/contracts/common-macros/src/auth.rs +113 -0
  8. package/contracts/common-macros/src/contract_ttl.rs +84 -0
  9. package/contracts/common-macros/src/lib.rs +181 -30
  10. package/contracts/common-macros/src/lz_contract.rs +83 -0
  11. package/contracts/common-macros/src/tests/{ownable.rs → auth.rs} +48 -15
  12. package/contracts/common-macros/src/tests/contract_ttl.rs +662 -0
  13. package/contracts/common-macros/src/tests/mod.rs +2 -2
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +20 -0
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +24 -0
  16. package/contracts/common-macros/src/tests/snapshots/{common_macros__tests__ownable__snapshot_only_owner_preserves_function_signature.snap → common_macros__tests__auth__snapshot_only_auth_preserves_function_signature.snap} +4 -4
  17. package/contracts/common-macros/src/tests/snapshots/{common_macros__tests__contract_impl__snapshot_generated_contract_impl_code.snap → common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap} +3 -3
  18. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contracttrait_code.snap +69 -0
  19. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +7 -21
  20. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +2 -2
  21. package/contracts/common-macros/src/ttl_configurable.rs +19 -34
  22. package/contracts/common-macros/src/ttl_extendable.rs +36 -0
  23. package/contracts/common-macros/src/upgradeable.rs +5 -5
  24. package/contracts/common-macros/src/utils.rs +9 -0
  25. package/contracts/endpoint-v2/src/constants.rs +4 -4
  26. package/contracts/endpoint-v2/src/endpoint_v2.rs +38 -40
  27. package/contracts/endpoint-v2/src/errors.rs +4 -3
  28. package/contracts/endpoint-v2/src/events.rs +1 -1
  29. package/contracts/endpoint-v2/src/message_lib_manager.rs +18 -5
  30. package/contracts/endpoint-v2/src/messaging_channel.rs +11 -1
  31. package/contracts/endpoint-v2/src/messaging_composer.rs +11 -1
  32. package/contracts/endpoint-v2/src/storage.rs +1 -1
  33. package/contracts/endpoint-v2/src/tests/endpoint_v2/pay_messaging_fees.rs +3 -3
  34. package/contracts/endpoint-v2/src/tests/endpoint_v2/quote.rs +1 -1
  35. package/contracts/endpoint-v2/src/tests/endpoint_v2/require_oapp_auth.rs +2 -2
  36. package/contracts/endpoint-v2/src/tests/endpoint_v2/send.rs +3 -3
  37. package/contracts/endpoint-v2/src/tests/endpoint_v2/set_zro.rs +4 -4
  38. package/contracts/endpoint-v2/src/tests/message_lib_manager/require_receive_lib_for_eid.rs +3 -3
  39. package/contracts/endpoint-v2/src/tests/message_lib_manager/require_registered.rs +1 -1
  40. package/contracts/endpoint-v2/src/tests/message_lib_manager/require_send_lib_for_eid.rs +3 -3
  41. package/contracts/endpoint-v2/src/tests/message_lib_manager/require_supported_eid.rs +1 -1
  42. package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +4 -4
  43. package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +1 -1
  44. package/contracts/layerzero-views/src/layerzero_view.rs +3 -6
  45. package/contracts/macro-integration-tests/tests/runtime/ownable/mod.rs +2 -2
  46. package/contracts/macro-integration-tests/tests/runtime/ownable/{only_owner_guard.rs → only_auth_guard.rs} +1 -1
  47. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/configuration.rs +1 -1
  48. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/freeze.rs +1 -1
  49. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/mod.rs +0 -1
  50. package/contracts/macro-integration-tests/tests/ui/ownable/fail/{only_owner_missing_env.rs → only_auth_missing_env.rs} +3 -3
  51. package/contracts/macro-integration-tests/tests/ui/ownable/fail/{only_owner_missing_env.stderr → only_auth_missing_env.stderr} +4 -4
  52. package/contracts/macro-integration-tests/tests/ui/ownable/pass/namespacing_and_imports.rs +2 -3
  53. package/contracts/macro-integration-tests/tests/ui/ownable/pass/{only_owner_env_param_variants.rs → only_auth_env_param_variants.rs} +9 -9
  54. package/contracts/macro-integration-tests/tests/ui/ttl_configurable/pass/minimal_contract.rs +6 -6
  55. package/contracts/message-libs/message-lib-common/src/errors.rs +7 -2
  56. package/contracts/message-libs/message-lib-common/src/tests/packet_codec_v1/decode_packet_header.rs +3 -3
  57. package/contracts/message-libs/message-lib-common/src/tests/worker_options/append_lz_receive_option.rs +1 -2
  58. package/contracts/message-libs/message-lib-common/src/tests/worker_options/append_native_drop_option.rs +1 -2
  59. package/contracts/message-libs/message-lib-common/src/tests/worker_options/convert_legacy_options.rs +9 -9
  60. package/contracts/message-libs/message-lib-common/src/tests/worker_options/extract_type_3_options.rs +1 -1
  61. package/contracts/message-libs/message-lib-common/src/tests/worker_options/left_pad_to_bytes32.rs +1 -1
  62. package/contracts/message-libs/message-lib-common/src/tests/worker_options/split_worker_options.rs +2 -2
  63. package/contracts/message-libs/simple-message-lib/src/simple_message_lib.rs +7 -9
  64. package/contracts/message-libs/treasury/src/errors.rs +2 -2
  65. package/contracts/message-libs/treasury/src/events.rs +1 -1
  66. package/contracts/message-libs/treasury/src/interfaces/zro_fee_lib.rs +2 -2
  67. package/contracts/message-libs/treasury/src/storage.rs +1 -1
  68. package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +1 -1
  69. package/contracts/message-libs/treasury/src/treasury.rs +14 -16
  70. package/contracts/message-libs/uln-302/src/receive_uln.rs +13 -2
  71. package/contracts/message-libs/uln-302/src/send_uln.rs +24 -4
  72. package/contracts/message-libs/uln-302/src/uln302.rs +6 -24
  73. package/contracts/oapps/counter/Cargo.toml +14 -1
  74. package/contracts/oapps/counter/integration_tests/mod.rs +4 -1
  75. package/contracts/oapps/counter/integration_tests/{setup.rs → setup_sml.rs} +48 -80
  76. package/contracts/oapps/counter/integration_tests/setup_uln.rs +997 -0
  77. package/contracts/oapps/counter/integration_tests/signing.rs +62 -0
  78. package/contracts/oapps/counter/integration_tests/test_with_sml.rs +24 -55
  79. package/contracts/oapps/counter/integration_tests/test_with_uln.rs +314 -0
  80. package/contracts/oapps/counter/integration_tests/utils.rs +196 -53
  81. package/contracts/oapps/counter/src/counter.rs +67 -43
  82. package/contracts/oapps/counter/src/tests/mod.rs +0 -13
  83. package/contracts/oapps/counter/src/tests/test_counter.rs +5 -7
  84. package/contracts/oapps/oapp/src/errors.rs +5 -1
  85. package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +93 -78
  86. package/contracts/oapps/oapp/src/oapp_core.rs +36 -21
  87. package/contracts/oapps/oapp/src/oapp_options_type3.rs +48 -12
  88. package/contracts/oapps/oapp/src/oapp_receiver.rs +106 -41
  89. package/contracts/oapps/oapp/src/oapp_sender.rs +26 -34
  90. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +9 -8
  91. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +25 -17
  92. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +7 -7
  93. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +14 -15
  94. package/contracts/oapps/oapp-macros/src/generators.rs +128 -0
  95. package/contracts/oapps/oapp-macros/src/lib.rs +113 -56
  96. package/contracts/oapps/oft/Cargo.toml +10 -7
  97. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_oft_fee.rs +3 -4
  98. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_pausable.rs +2 -3
  99. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_rate_limiter.rs +1 -1
  100. package/contracts/oapps/oft/integration-tests/mod.rs +1 -1
  101. package/contracts/oapps/oft/integration-tests/setup.rs +29 -110
  102. package/contracts/oapps/oft/integration-tests/utils.rs +254 -21
  103. package/contracts/oapps/oft/src/extensions/oft_fee.rs +13 -14
  104. package/contracts/oapps/oft/src/extensions/pausable.rs +4 -4
  105. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +5 -5
  106. package/contracts/oapps/oft/src/lib.rs +11 -13
  107. package/contracts/oapps/oft/src/oft.rs +147 -225
  108. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +9 -13
  109. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +31 -14
  110. package/contracts/oapps/oft/src/oft_types/mod.rs +13 -0
  111. package/contracts/oapps/{oft-std → oft-core}/Cargo.toml +6 -4
  112. package/contracts/oapps/{oft-std → oft-core}/integration-tests/mod.rs +1 -1
  113. package/contracts/oapps/{oft-std → oft-core}/integration-tests/setup.rs +129 -30
  114. package/contracts/oapps/{oft → oft-core}/integration-tests/test_with_sml.rs +3 -3
  115. package/contracts/oapps/oft-core/integration-tests/utils.rs +201 -0
  116. package/contracts/oapps/oft-core/src/errors.rs +13 -0
  117. package/contracts/oapps/oft-core/src/lib.rs +18 -0
  118. package/contracts/oapps/oft-core/src/oft_core.rs +439 -0
  119. package/contracts/oapps/{oft → oft-core}/src/storage.rs +2 -0
  120. package/contracts/oapps/{oft → oft-core}/src/tests/mod.rs +0 -2
  121. package/contracts/oapps/{oft → oft-core}/src/tests/test_decimals.rs +2 -2
  122. package/contracts/oapps/{oft → oft-core}/src/tests/test_lz_receive.rs +7 -7
  123. package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_msg_codec.rs +4 -5
  124. package/contracts/oapps/{oft → oft-core}/src/tests/test_resolve_address.rs +3 -3
  125. package/contracts/oapps/{oft → oft-core}/src/tests/test_utils.rs +78 -37
  126. package/contracts/oapps/oft-core/src/types.rs +58 -0
  127. package/contracts/oapps/{oft → oft-core}/src/utils.rs +1 -1
  128. package/contracts/upgrader/src/lib.rs +4 -4
  129. package/contracts/utils/src/auth.rs +44 -0
  130. package/contracts/utils/src/errors.rs +31 -5
  131. package/contracts/utils/src/lib.rs +3 -0
  132. package/contracts/utils/src/multisig.rs +211 -0
  133. package/contracts/utils/src/ownable.rs +137 -13
  134. package/contracts/utils/src/tests/buffer_reader.rs +6 -6
  135. package/contracts/utils/src/tests/buffer_writer.rs +6 -6
  136. package/contracts/utils/src/tests/bytes_ext.rs +2 -4
  137. package/contracts/utils/src/tests/mod.rs +1 -0
  138. package/contracts/utils/src/tests/multisig.rs +731 -0
  139. package/contracts/utils/src/tests/option_ext.rs +2 -5
  140. package/contracts/utils/src/tests/ownable.rs +456 -7
  141. package/contracts/utils/src/tests/ttl_configurable.rs +27 -16
  142. package/contracts/utils/src/tests/upgradeable.rs +4 -2
  143. package/contracts/utils/src/ttl_configurable.rs +23 -8
  144. package/contracts/utils/src/ttl_extendable.rs +27 -0
  145. package/contracts/utils/src/upgradeable.rs +2 -0
  146. package/contracts/workers/dvn/Cargo.toml +1 -1
  147. package/contracts/workers/dvn/src/auth.rs +7 -7
  148. package/contracts/workers/dvn/src/dvn.rs +10 -38
  149. package/contracts/workers/dvn/src/errors.rs +0 -7
  150. package/contracts/workers/dvn/src/events.rs +1 -14
  151. package/contracts/workers/dvn/src/interfaces/dvn.rs +2 -2
  152. package/contracts/workers/dvn/src/interfaces/mod.rs +0 -2
  153. package/contracts/workers/dvn/src/storage.rs +3 -13
  154. package/contracts/workers/dvn/src/tests/auth.rs +4 -4
  155. package/contracts/workers/dvn/src/tests/dvn.rs +1 -2
  156. package/contracts/workers/dvn/src/tests/multisig/set_signer.rs +7 -8
  157. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +11 -8
  158. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +11 -12
  159. package/contracts/workers/dvn/src/tests/setup.rs +5 -5
  160. package/contracts/workers/dvn-fee-lib/Cargo.toml +1 -1
  161. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +3 -6
  162. package/contracts/workers/executor/src/auth.rs +80 -16
  163. package/contracts/workers/executor/src/executor.rs +5 -31
  164. package/contracts/workers/executor/src/storage.rs +2 -9
  165. package/contracts/workers/executor-fee-lib/Cargo.toml +1 -1
  166. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +3 -6
  167. package/contracts/workers/executor-helper/Cargo.toml +1 -1
  168. package/contracts/workers/executor-helper/src/executor_helper.rs +53 -73
  169. package/contracts/workers/price-feed/Cargo.toml +1 -1
  170. package/contracts/workers/price-feed/src/price_feed.rs +7 -10
  171. package/contracts/workers/worker/src/errors.rs +4 -0
  172. package/contracts/workers/worker/src/tests/worker.rs +7 -6
  173. package/contracts/workers/worker/src/worker.rs +20 -16
  174. package/package.json +7 -5
  175. package/sdk/.turbo/turbo-build.log +1 -0
  176. package/sdk/.turbo/turbo-test.log +1019 -0
  177. package/sdk/dist/generated/bml.d.ts +95 -8
  178. package/sdk/dist/generated/bml.js +95 -36
  179. package/sdk/dist/generated/counter.d.ts +289 -44
  180. package/sdk/dist/generated/counter.js +119 -49
  181. package/sdk/dist/generated/dvn.d.ts +312 -229
  182. package/sdk/dist/generated/dvn.js +144 -83
  183. package/sdk/dist/generated/dvn_fee_lib.d.ts +258 -63
  184. package/sdk/dist/generated/dvn_fee_lib.js +95 -26
  185. package/sdk/dist/generated/endpoint.d.ts +219 -24
  186. package/sdk/dist/generated/endpoint.js +108 -41
  187. package/sdk/dist/generated/executor.d.ts +239 -87
  188. package/sdk/dist/generated/executor.js +135 -63
  189. package/sdk/dist/generated/executor_fee_lib.d.ts +278 -74
  190. package/sdk/dist/generated/executor_fee_lib.js +135 -59
  191. package/sdk/dist/generated/executor_helper.d.ts +163 -21
  192. package/sdk/dist/generated/executor_helper.js +124 -52
  193. package/sdk/dist/generated/oft.d.ts +1842 -0
  194. package/sdk/dist/generated/oft.js +345 -0
  195. package/sdk/dist/generated/price_feed.d.ts +258 -63
  196. package/sdk/dist/generated/price_feed.js +95 -26
  197. package/sdk/dist/generated/sml.d.ts +235 -34
  198. package/sdk/dist/generated/sml.js +126 -53
  199. package/sdk/dist/generated/treasury.d.ts +1016 -0
  200. package/sdk/dist/generated/treasury.js +248 -0
  201. package/sdk/dist/generated/uln302.d.ts +235 -34
  202. package/sdk/dist/generated/uln302.js +126 -53
  203. package/sdk/dist/generated/upgrader.d.ts +17 -2
  204. package/sdk/dist/generated/upgrader.js +19 -1
  205. package/sdk/dist/index.d.ts +2 -1
  206. package/sdk/dist/index.js +2 -1
  207. package/sdk/package.json +6 -3
  208. package/sdk/src/index.ts +2 -1
  209. package/sdk/test/counter-sml.test.ts +376 -0
  210. package/sdk/test/counter-uln.test.ts +493 -0
  211. package/sdk/test/{oft.test.ts → oft-sml.test.ts} +196 -321
  212. package/sdk/test/suites/constants.ts +22 -2
  213. package/sdk/test/suites/globalSetup.ts +450 -0
  214. package/sdk/test/suites/localnet.ts +23 -6
  215. package/sdk/test/upgrader.test.ts +7 -16
  216. package/sdk/test/utils.ts +558 -85
  217. package/sdk/turbo.json +8 -0
  218. package/sdk/vitest.config.ts +21 -0
  219. package/tools/ts-bindings-gen/Cargo.toml +2 -0
  220. package/tools/ts-bindings-gen/src/main.rs +52 -4
  221. package/contracts/common-macros/src/contract_impl.rs +0 -52
  222. package/contracts/common-macros/src/ownable.rs +0 -41
  223. package/contracts/common-macros/src/tests/contract_impl.rs +0 -386
  224. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_generated_ownable_code.snap +0 -12
  225. package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/extend_instance_ttl.rs +0 -50
  226. package/contracts/oapps/oapp-macros/src/oapp_core.rs +0 -41
  227. package/contracts/oapps/oapp-macros/src/oapp_full.rs +0 -21
  228. package/contracts/oapps/oapp-macros/src/oapp_options_type3.rs +0 -31
  229. package/contracts/oapps/oapp-macros/src/oapp_receiver.rs +0 -48
  230. package/contracts/oapps/oapp-macros/src/oapp_sender.rs +0 -21
  231. package/contracts/oapps/oapp-macros/src/util.rs +0 -107
  232. package/contracts/oapps/oft/src/constants.rs +0 -5
  233. package/contracts/oapps/oft/src/default_oft_impl.rs +0 -152
  234. package/contracts/oapps/oft/src/errors.rs +0 -8
  235. package/contracts/oapps/oft/src/interfaces/mint_burn_token.rs +0 -23
  236. package/contracts/oapps/oft/src/interfaces/mod.rs +0 -3
  237. package/contracts/oapps/oft/src/tests/extensions/mod.rs +0 -11
  238. package/contracts/oapps/oft/src/tests/extensions/setup.rs +0 -903
  239. package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +0 -749
  240. package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +0 -432
  241. package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +0 -1078
  242. package/contracts/oapps/oft/src/types.rs +0 -38
  243. package/contracts/oapps/oft-std/integration-tests/utils.rs +0 -427
  244. package/contracts/oapps/oft-std/src/lib.rs +0 -16
  245. package/contracts/oapps/oft-std/src/oft.rs +0 -156
  246. package/contracts/workers/dvn/src/interfaces/multisig.rs +0 -56
  247. package/contracts/workers/dvn/src/multisig.rs +0 -157
  248. package/sdk/dist/generated/oft_std.d.ts +0 -1544
  249. package/sdk/dist/generated/oft_std.js +0 -271
  250. package/sdk/test/index.test.ts +0 -375
  251. /package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/mod.rs +0 -0
  252. /package/contracts/oapps/{oft → oft-core}/src/codec/mod.rs +0 -0
  253. /package/contracts/oapps/{oft → oft-core}/src/codec/oft_compose_msg_codec.rs +0 -0
  254. /package/contracts/oapps/{oft → oft-core}/src/codec/oft_msg_codec.rs +0 -0
  255. /package/contracts/oapps/{oft → oft-core}/src/events.rs +0 -0
  256. /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_compose_msg_codec.rs +0 -0
  257. /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_version.rs +0 -0
  258. /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_oft.rs +0 -0
  259. /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_send.rs +0 -0
  260. /package/contracts/oapps/{oft → oft-core}/src/tests/test_send.rs +0 -0
  261. /package/contracts/oapps/{oft → oft-core}/src/tests/test_token.rs +0 -0
  262. /package/sdk/test/suites/{testUpgradeable.ts → dummyContractClient.ts} +0 -0
@@ -0,0 +1,731 @@
1
+ use crate::{
2
+ auth::Auth,
3
+ errors::MultisigError,
4
+ multisig::{init_multisig, recover_signer, Multisig},
5
+ };
6
+ use soroban_sdk::{
7
+ contract, contractimpl,
8
+ testutils::{AuthorizedFunction, Events},
9
+ vec, Address, BytesN, Env, IntoVal, Symbol, TryIntoVal, Val, Vec,
10
+ };
11
+
12
+ // Test contract implementing Multisig + Auth (self-owning)
13
+ #[contract]
14
+ pub struct TestContract;
15
+
16
+ #[contractimpl]
17
+ impl TestContract {
18
+ pub fn __constructor(env: &Env, signers: &Vec<BytesN<20>>, threshold: u32) {
19
+ init_multisig(env, signers, threshold);
20
+ }
21
+ }
22
+
23
+ /// Self-owning contract: the contract owns itself.
24
+ /// This allows multisig quorum approval to serve as the authorizer.
25
+ #[contractimpl]
26
+ impl Auth for TestContract {
27
+ fn authorizer(env: &Env) -> Address {
28
+ env.current_contract_address()
29
+ }
30
+ }
31
+
32
+ #[contractimpl(contracttrait)]
33
+ impl Multisig for TestContract {}
34
+
35
+ fn signer(env: &Env, seed: u8) -> BytesN<20> {
36
+ let mut bytes = [0u8; 20];
37
+ bytes[0] = seed;
38
+ BytesN::from_array(env, &bytes)
39
+ }
40
+
41
+ fn zero_signer(env: &Env) -> BytesN<20> {
42
+ BytesN::from_array(env, &[0u8; 20])
43
+ }
44
+
45
+ fn assert_auth(env: &Env, contract: &Address, func: &str, expected_args: Vec<Val>) {
46
+ let auths = env.auths();
47
+ assert_eq!(auths.len(), 1);
48
+ let (addr, invocation) = &auths[0];
49
+ assert_eq!(addr, contract);
50
+ assert!(invocation.sub_invocations.is_empty());
51
+ match &invocation.function {
52
+ AuthorizedFunction::Contract((id, symbol, args)) => {
53
+ assert_eq!(id, contract);
54
+ assert_eq!(symbol, &Symbol::new(env, func));
55
+ assert_eq!(args, &expected_args);
56
+ }
57
+ _ => panic!("expected contract invocation auth"),
58
+ }
59
+ }
60
+
61
+ fn assert_signer_set_event(env: &Env, contract: &Address, signer: &BytesN<20>, active: bool) {
62
+ use soroban_sdk::Map;
63
+ let events = env.events().all();
64
+ // Find the signer_set event for this contract (snake_case event name)
65
+ let found = events.iter().any(|event| {
66
+ if &event.0 != contract {
67
+ return false;
68
+ }
69
+ // Check topics: ["signer_set", signer]
70
+ let topics = &event.1;
71
+ if topics.len() != 2 {
72
+ return false;
73
+ }
74
+ let topic_symbol: Symbol = topics.get(0).unwrap().try_into_val(env).unwrap();
75
+ let topic_signer: BytesN<20> = topics.get(1).unwrap().try_into_val(env).unwrap();
76
+ if topic_symbol != Symbol::new(env, "signer_set") || &topic_signer != signer {
77
+ return false;
78
+ }
79
+ // Check data is a map with "active" field
80
+ let data_map: Map<Symbol, bool> = event.2.try_into_val(env).unwrap();
81
+ data_map.get(Symbol::new(env, "active")) == Some(active)
82
+ });
83
+ assert!(found, "signer_set event not found");
84
+ }
85
+
86
+ fn assert_threshold_set_event(env: &Env, contract: &Address, threshold: u32) {
87
+ use soroban_sdk::Map;
88
+ let events = env.events().all();
89
+ // Find the threshold_set event for this contract (snake_case event name)
90
+ let found = events.iter().any(|event| {
91
+ if &event.0 != contract {
92
+ return false;
93
+ }
94
+ // Check topics: ["threshold_set"]
95
+ let topics = &event.1;
96
+ if topics.len() != 1 {
97
+ return false;
98
+ }
99
+ let topic_symbol: Symbol = topics.get(0).unwrap().try_into_val(env).unwrap();
100
+ if topic_symbol != Symbol::new(env, "threshold_set") {
101
+ return false;
102
+ }
103
+ // Check data is a map with "threshold" field
104
+ let data_map: Map<Symbol, u32> = event.2.try_into_val(env).unwrap();
105
+ data_map.get(Symbol::new(env, "threshold")) == Some(threshold)
106
+ });
107
+ assert!(found, "threshold_set event not found");
108
+ }
109
+
110
+ // ============================================
111
+ // init: Initialization tests
112
+ // ============================================
113
+
114
+ #[test]
115
+ fn init_sets_signers_and_threshold() {
116
+ let env = Env::default();
117
+ env.mock_all_auths();
118
+
119
+ let s1 = signer(&env, 1);
120
+ let s2 = signer(&env, 2);
121
+ let signers = vec![&env, s1.clone(), s2.clone()];
122
+
123
+ let contract = env.register(TestContract, (&signers, 2u32));
124
+ let client = TestContractClient::new(&env, &contract);
125
+
126
+ assert_eq!(client.threshold(), 2);
127
+ assert_eq!(client.total_signers(), 2);
128
+ assert!(client.is_signer(&s1));
129
+ assert!(client.is_signer(&s2));
130
+ }
131
+
132
+ #[test]
133
+ #[should_panic(expected = "Error(Contract, #1067)")] // ZeroThreshold
134
+ fn init_zero_threshold_fails() {
135
+ let env = Env::default();
136
+ let s1 = signer(&env, 1);
137
+ let signers = vec![&env, s1];
138
+
139
+ env.register(TestContract, (&signers, 0u32));
140
+ }
141
+
142
+ #[test]
143
+ #[should_panic(expected = "Error(Contract, #1065)")] // TotalSignersLessThanThreshold
144
+ fn init_threshold_exceeds_signers_fails() {
145
+ let env = Env::default();
146
+ let s1 = signer(&env, 1);
147
+ let signers = vec![&env, s1];
148
+
149
+ env.register(TestContract, (&signers, 2u32));
150
+ }
151
+
152
+ #[test]
153
+ #[should_panic(expected = "Error(Contract, #1060)")] // AlreadyInitialized
154
+ fn init_already_initialized_fails() {
155
+ let env = Env::default();
156
+ let s1 = signer(&env, 1);
157
+ let signers = vec![&env, s1.clone()];
158
+
159
+ let contract = env.register(TestContract, (&signers, 1u32));
160
+
161
+ // Try to call init_multisig again - should fail with AlreadyInitialized
162
+ env.as_contract(&contract, || {
163
+ init_multisig(&env, &signers, 1);
164
+ });
165
+ }
166
+
167
+ // ============================================
168
+ // set_signer: Add/remove signer tests
169
+ // ============================================
170
+
171
+ #[test]
172
+ fn set_signer_add() {
173
+ let env = Env::default();
174
+ env.mock_all_auths();
175
+
176
+ let s1 = signer(&env, 1);
177
+ let s2 = signer(&env, 2);
178
+ let signers = vec![&env, s1.clone()];
179
+
180
+ let contract = env.register(TestContract, (&signers, 1u32));
181
+ let client = TestContractClient::new(&env, &contract);
182
+
183
+ client.set_signer(&s2, &true);
184
+
185
+ // Verify auth was required from the contract (self-owning)
186
+ assert_auth(&env, &contract, "set_signer", (s2.clone(), true).into_val(&env));
187
+
188
+ // Verify SignerSet event was emitted
189
+ assert_signer_set_event(&env, &contract, &s2, true);
190
+
191
+ assert_eq!(client.total_signers(), 2);
192
+ assert!(client.is_signer(&s1));
193
+ assert!(client.is_signer(&s2));
194
+ }
195
+
196
+ #[test]
197
+ fn set_signer_remove() {
198
+ let env = Env::default();
199
+ env.mock_all_auths();
200
+
201
+ let s1 = signer(&env, 1);
202
+ let s2 = signer(&env, 2);
203
+ let signers = vec![&env, s1.clone(), s2.clone()];
204
+
205
+ let contract = env.register(TestContract, (&signers, 1u32));
206
+ let client = TestContractClient::new(&env, &contract);
207
+
208
+ client.set_signer(&s1, &false);
209
+
210
+ // Verify auth was required from the contract (self-owning)
211
+ assert_auth(&env, &contract, "set_signer", (s1.clone(), false).into_val(&env));
212
+
213
+ // Verify SignerSet event was emitted
214
+ assert_signer_set_event(&env, &contract, &s1, false);
215
+
216
+ assert_eq!(client.total_signers(), 1);
217
+ assert!(!client.is_signer(&s1));
218
+ assert!(client.is_signer(&s2));
219
+ }
220
+
221
+ #[test]
222
+ fn set_signer_zero_address_fails() {
223
+ let env = Env::default();
224
+ env.mock_all_auths();
225
+
226
+ let s1 = signer(&env, 1);
227
+ let signers = vec![&env, s1];
228
+
229
+ let contract = env.register(TestContract, (&signers, 1u32));
230
+ let client = TestContractClient::new(&env, &contract);
231
+
232
+ let res = client.try_set_signer(&zero_signer(&env), &true);
233
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::InvalidSigner.into());
234
+ }
235
+
236
+ #[test]
237
+ fn set_signer_duplicate_fails() {
238
+ let env = Env::default();
239
+ env.mock_all_auths();
240
+
241
+ let s1 = signer(&env, 1);
242
+ let signers = vec![&env, s1.clone()];
243
+
244
+ let contract = env.register(TestContract, (&signers, 1u32));
245
+ let client = TestContractClient::new(&env, &contract);
246
+
247
+ let res = client.try_set_signer(&s1, &true);
248
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::SignerAlreadyExists.into());
249
+ }
250
+
251
+ #[test]
252
+ fn set_signer_remove_not_found_fails() {
253
+ let env = Env::default();
254
+ env.mock_all_auths();
255
+
256
+ let s1 = signer(&env, 1);
257
+ let s2 = signer(&env, 2);
258
+ let signers = vec![&env, s1];
259
+
260
+ let contract = env.register(TestContract, (&signers, 1u32));
261
+ let client = TestContractClient::new(&env, &contract);
262
+
263
+ let res = client.try_set_signer(&s2, &false);
264
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::SignerNotFound.into());
265
+ }
266
+
267
+ #[test]
268
+ fn set_signer_remove_violates_threshold_fails() {
269
+ let env = Env::default();
270
+ env.mock_all_auths();
271
+
272
+ let s1 = signer(&env, 1);
273
+ let s2 = signer(&env, 2);
274
+ let signers = vec![&env, s1.clone(), s2];
275
+
276
+ let contract = env.register(TestContract, (&signers, 2u32));
277
+ let client = TestContractClient::new(&env, &contract);
278
+
279
+ let res = client.try_set_signer(&s1, &false);
280
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::TotalSignersLessThanThreshold.into());
281
+ }
282
+
283
+ // ============================================
284
+ // set_threshold: Threshold tests
285
+ // ============================================
286
+
287
+ #[test]
288
+ fn set_threshold_success() {
289
+ let env = Env::default();
290
+ env.mock_all_auths();
291
+
292
+ let s1 = signer(&env, 1);
293
+ let s2 = signer(&env, 2);
294
+ let signers = vec![&env, s1, s2];
295
+
296
+ let contract = env.register(TestContract, (&signers, 1u32));
297
+ let client = TestContractClient::new(&env, &contract);
298
+
299
+ client.set_threshold(&2);
300
+
301
+ // Verify auth was required from the contract (self-owning)
302
+ assert_auth(&env, &contract, "set_threshold", (2u32,).into_val(&env));
303
+
304
+ // Verify ThresholdSet event was emitted
305
+ assert_threshold_set_event(&env, &contract, 2);
306
+
307
+ assert_eq!(client.threshold(), 2);
308
+ }
309
+
310
+ #[test]
311
+ fn set_threshold_zero_fails() {
312
+ let env = Env::default();
313
+ env.mock_all_auths();
314
+
315
+ let s1 = signer(&env, 1);
316
+ let signers = vec![&env, s1];
317
+
318
+ let contract = env.register(TestContract, (&signers, 1u32));
319
+ let client = TestContractClient::new(&env, &contract);
320
+
321
+ let res = client.try_set_threshold(&0);
322
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::ZeroThreshold.into());
323
+ }
324
+
325
+ #[test]
326
+ fn set_threshold_exceeds_signers_fails() {
327
+ let env = Env::default();
328
+ env.mock_all_auths();
329
+
330
+ let s1 = signer(&env, 1);
331
+ let signers = vec![&env, s1];
332
+
333
+ let contract = env.register(TestContract, (&signers, 1u32));
334
+ let client = TestContractClient::new(&env, &contract);
335
+
336
+ let res = client.try_set_threshold(&2);
337
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::TotalSignersLessThanThreshold.into());
338
+ }
339
+
340
+ // ============================================
341
+ // get_signers: Returns all signers
342
+ // ============================================
343
+
344
+ #[test]
345
+ fn get_signers_returns_all() {
346
+ let env = Env::default();
347
+ env.mock_all_auths();
348
+
349
+ let s1 = signer(&env, 1);
350
+ let s2 = signer(&env, 2);
351
+ let s3 = signer(&env, 3);
352
+ let signers = vec![&env, s1.clone(), s2.clone(), s3.clone()];
353
+
354
+ let contract = env.register(TestContract, (&signers, 1u32));
355
+ let client = TestContractClient::new(&env, &contract);
356
+
357
+ let result = client.get_signers();
358
+ assert_eq!(result.len(), 3);
359
+ assert!(result.iter().any(|s| s == s1));
360
+ assert!(result.iter().any(|s| s == s2));
361
+ assert!(result.iter().any(|s| s == s3));
362
+ }
363
+
364
+ // ============================================
365
+ // verify: Signature verification tests
366
+ // ============================================
367
+
368
+ // Known test vectors for signature verification
369
+ fn test_digest(env: &Env) -> BytesN<32> {
370
+ BytesN::from_array(
371
+ env,
372
+ &[
373
+ 0x7d, 0x84, 0xfd, 0x50, 0x8a, 0x27, 0xac, 0x81, 0xde, 0xe4, 0xbf, 0xa9, 0x7f, 0x29, 0xbe, 0xdb, 0x88, 0x5c,
374
+ 0x1b, 0xd7, 0xfc, 0x46, 0x50, 0xf4, 0x91, 0xe6, 0x4f, 0xbd, 0xaa, 0x05, 0xcd, 0xac,
375
+ ],
376
+ )
377
+ }
378
+
379
+ // Signer 1: 0x9b6ababd080456f900ed64e74d122ff9ca40daa1
380
+ fn test_signer1(env: &Env) -> BytesN<20> {
381
+ BytesN::from_array(
382
+ env,
383
+ &[
384
+ 0x9b, 0x6a, 0xba, 0xbd, 0x08, 0x04, 0x56, 0xf9, 0x00, 0xed, 0x64, 0xe7, 0x4d, 0x12, 0x2f, 0xf9, 0xca, 0x40,
385
+ 0xda, 0xa1,
386
+ ],
387
+ )
388
+ }
389
+
390
+ // Signer 2: 0xd6869dacf9c6ce629cf042864737690641d0e2d7
391
+ fn test_signer2(env: &Env) -> BytesN<20> {
392
+ BytesN::from_array(
393
+ env,
394
+ &[
395
+ 0xd6, 0x86, 0x9d, 0xac, 0xf9, 0xc6, 0xce, 0x62, 0x9c, 0xf0, 0x42, 0x86, 0x47, 0x37, 0x69, 0x06, 0x41, 0xd0,
396
+ 0xe2, 0xd7,
397
+ ],
398
+ )
399
+ }
400
+
401
+ // Signature from signer1 for test_digest
402
+ fn test_sig1(env: &Env) -> BytesN<65> {
403
+ BytesN::from_array(
404
+ env,
405
+ &[
406
+ 0x31, 0x53, 0x7d, 0xcf, 0xe1, 0xec, 0x4d, 0x7e, 0x89, 0xf5, 0x97, 0x2c, 0xcd, 0x30, 0x65, 0xe3, 0x4e, 0x17,
407
+ 0x8e, 0x30, 0x2c, 0x1b, 0x9e, 0x8e, 0x17, 0x5d, 0x38, 0x43, 0xa2, 0x96, 0x4f, 0x28, // r
408
+ 0x63, 0xf2, 0xb6, 0x0c, 0x7e, 0xf7, 0x43, 0x63, 0xb6, 0x87, 0xf8, 0x22, 0xdb, 0x6c, 0x31, 0x3e, 0x87, 0x62,
409
+ 0xb3, 0x8d, 0x30, 0x97, 0xb6, 0xfe, 0xc9, 0xb7, 0xb7, 0x61, 0x65, 0x63, 0x0a, 0xaa, // s
410
+ 0x1b, // v = 27
411
+ ],
412
+ )
413
+ }
414
+
415
+ // Signature from signer2 for test_digest
416
+ fn test_sig2(env: &Env) -> BytesN<65> {
417
+ BytesN::from_array(
418
+ env,
419
+ &[
420
+ 0x42, 0x67, 0x4f, 0xc6, 0x30, 0xe3, 0x2f, 0x90, 0x00, 0xbc, 0x95, 0x27, 0x1e, 0xc5, 0x42, 0x83, 0xc8, 0xad,
421
+ 0x35, 0x00, 0x16, 0xbb, 0x66, 0xa1, 0x84, 0x64, 0x95, 0x89, 0x63, 0xa5, 0x1f, 0x03, // r
422
+ 0x14, 0x6e, 0x8b, 0x3f, 0xbb, 0x3a, 0x5b, 0xd0, 0xb8, 0x9d, 0x3b, 0x6c, 0x1e, 0x01, 0xdd, 0x61, 0x4e, 0x86,
423
+ 0xba, 0xe0, 0x13, 0x51, 0xdc, 0x3d, 0x1f, 0xfe, 0x35, 0x22, 0xc8, 0x3e, 0xd4, 0x50, // s
424
+ 0x1c, // v = 28
425
+ ],
426
+ )
427
+ }
428
+
429
+ #[test]
430
+ fn verify_signatures_success() {
431
+ let env = Env::default();
432
+ env.mock_all_auths();
433
+
434
+ let s1 = test_signer1(&env);
435
+ let s2 = test_signer2(&env);
436
+ let signers = vec![&env, s1, s2];
437
+
438
+ let contract = env.register(TestContract, (&signers, 2u32));
439
+ let client = TestContractClient::new(&env, &contract);
440
+
441
+ let digest = test_digest(&env);
442
+ // Signatures must be sorted by signer address (s1 < s2)
443
+ let signatures = vec![&env, test_sig1(&env), test_sig2(&env)];
444
+
445
+ // Should not panic - uses configured threshold (2)
446
+ client.verify_signatures(&digest, &signatures);
447
+ }
448
+
449
+ #[test]
450
+ fn verify_n_signatures_success() {
451
+ let env = Env::default();
452
+ env.mock_all_auths();
453
+
454
+ let s1 = test_signer1(&env);
455
+ let s2 = test_signer2(&env);
456
+ let signers = vec![&env, s1, s2];
457
+
458
+ let contract = env.register(TestContract, (&signers, 2u32));
459
+ let client = TestContractClient::new(&env, &contract);
460
+
461
+ let digest = test_digest(&env);
462
+ let signatures = vec![&env, test_sig1(&env), test_sig2(&env)];
463
+
464
+ // Should not panic - custom threshold of 1 (only need 1 sig)
465
+ client.verify_n_signatures(&digest, &signatures, &1);
466
+ }
467
+
468
+ #[test]
469
+ fn verify_n_signatures_zero_threshold_fails() {
470
+ let env = Env::default();
471
+ env.mock_all_auths();
472
+
473
+ let s1 = test_signer1(&env);
474
+ let signers = vec![&env, s1];
475
+
476
+ let contract = env.register(TestContract, (&signers, 1u32));
477
+ let client = TestContractClient::new(&env, &contract);
478
+
479
+ let digest = BytesN::from_array(&env, &[0u8; 32]);
480
+ let signatures: Vec<BytesN<65>> = vec![&env];
481
+
482
+ let res = client.try_verify_n_signatures(&digest, &signatures, &0);
483
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::ZeroThreshold.into());
484
+ }
485
+
486
+ #[test]
487
+ fn verify_n_signatures_insufficient_signatures_fails() {
488
+ let env = Env::default();
489
+ env.mock_all_auths();
490
+
491
+ let s1 = test_signer1(&env);
492
+ let signers = vec![&env, s1];
493
+
494
+ let contract = env.register(TestContract, (&signers, 1u32));
495
+ let client = TestContractClient::new(&env, &contract);
496
+
497
+ let digest = BytesN::from_array(&env, &[0u8; 32]);
498
+ let signatures: Vec<BytesN<65>> = vec![&env]; // Empty
499
+
500
+ let res = client.try_verify_n_signatures(&digest, &signatures, &1);
501
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::SignatureError.into());
502
+ }
503
+
504
+ #[test]
505
+ fn verify_n_signatures_signer_not_found_fails() {
506
+ let env = Env::default();
507
+ env.mock_all_auths();
508
+
509
+ // Register with only signer2, but provide sig1 (from signer1)
510
+ let s2 = test_signer2(&env);
511
+ let signers = vec![&env, s2];
512
+
513
+ let contract = env.register(TestContract, (&signers, 1u32));
514
+ let client = TestContractClient::new(&env, &contract);
515
+
516
+ let digest = test_digest(&env);
517
+ let signatures = vec![&env, test_sig1(&env)]; // sig1 is from signer1, not registered
518
+
519
+ let res = client.try_verify_n_signatures(&digest, &signatures, &1);
520
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::SignerNotFound.into());
521
+ }
522
+
523
+ #[test]
524
+ fn verify_n_signatures_unsorted_signers_fails() {
525
+ let env = Env::default();
526
+ env.mock_all_auths();
527
+
528
+ let s1 = test_signer1(&env);
529
+ let s2 = test_signer2(&env);
530
+ let signers = vec![&env, s1, s2];
531
+
532
+ let contract = env.register(TestContract, (&signers, 2u32));
533
+ let client = TestContractClient::new(&env, &contract);
534
+
535
+ let digest = test_digest(&env);
536
+ // Wrong order: sig2 before sig1 (signer2 > signer1)
537
+ let signatures = vec![&env, test_sig2(&env), test_sig1(&env)];
538
+
539
+ let res = client.try_verify_n_signatures(&digest, &signatures, &2);
540
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::UnsortedSigners.into());
541
+ }
542
+
543
+ // ============================================
544
+ // recover: Signature recovery tests
545
+ // ============================================
546
+
547
+ #[test]
548
+ fn recover_signer_valid_signature() {
549
+ let env = Env::default();
550
+
551
+ // Known test vector: digest + signature -> expected signer
552
+ // Using the same test data from onesig tests
553
+ let digest_bytes = [
554
+ 0x7d, 0x84, 0xfd, 0x50, 0x8a, 0x27, 0xac, 0x81, 0xde, 0xe4, 0xbf, 0xa9, 0x7f, 0x29, 0xbe, 0xdb, 0x88, 0x5c,
555
+ 0x1b, 0xd7, 0xfc, 0x46, 0x50, 0xf4, 0x91, 0xe6, 0x4f, 0xbd, 0xaa, 0x05, 0xcd, 0xac,
556
+ ];
557
+ let digest = BytesN::from_array(&env, &digest_bytes);
558
+
559
+ // Signature from signer 0x9b6ababd080456f900ed64e74d122ff9ca40daa1
560
+ let sig_bytes = [
561
+ 0x31, 0x53, 0x7d, 0xcf, 0xe1, 0xec, 0x4d, 0x7e, 0x89, 0xf5, 0x97, 0x2c, 0xcd, 0x30, 0x65, 0xe3, 0x4e, 0x17,
562
+ 0x8e, 0x30, 0x2c, 0x1b, 0x9e, 0x8e, 0x17, 0x5d, 0x38, 0x43, 0xa2, 0x96, 0x4f, 0x28, // r
563
+ 0x63, 0xf2, 0xb6, 0x0c, 0x7e, 0xf7, 0x43, 0x63, 0xb6, 0x87, 0xf8, 0x22, 0xdb, 0x6c, 0x31, 0x3e, 0x87, 0x62,
564
+ 0xb3, 0x8d, 0x30, 0x97, 0xb6, 0xfe, 0xc9, 0xb7, 0xb7, 0x61, 0x65, 0x63, 0x0a, 0xaa, // s
565
+ 0x1b, // v = 27
566
+ ];
567
+ let signature = BytesN::from_array(&env, &sig_bytes);
568
+
569
+ let expected_signer = BytesN::from_array(
570
+ &env,
571
+ &[
572
+ 0x9b, 0x6a, 0xba, 0xbd, 0x08, 0x04, 0x56, 0xf9, 0x00, 0xed, 0x64, 0xe7, 0x4d, 0x12, 0x2f, 0xf9, 0xca, 0x40,
573
+ 0xda, 0xa1,
574
+ ],
575
+ );
576
+
577
+ let recovered = recover_signer(&env, &digest, &signature);
578
+ assert_eq!(recovered, expected_signer);
579
+ }
580
+
581
+ #[test]
582
+ fn recover_signer_raw_v_path() {
583
+ let env = Env::default();
584
+
585
+ let digest_bytes = [
586
+ 0x7d, 0x84, 0xfd, 0x50, 0x8a, 0x27, 0xac, 0x81, 0xde, 0xe4, 0xbf, 0xa9, 0x7f, 0x29, 0xbe, 0xdb, 0x88, 0x5c,
587
+ 0x1b, 0xd7, 0xfc, 0x46, 0x50, 0xf4, 0x91, 0xe6, 0x4f, 0xbd, 0xaa, 0x05, 0xcd, 0xac,
588
+ ];
589
+ let digest = BytesN::from_array(&env, &digest_bytes);
590
+
591
+ // Same signature but with v=0 instead of v=27
592
+ let sig_bytes = [
593
+ 0x31, 0x53, 0x7d, 0xcf, 0xe1, 0xec, 0x4d, 0x7e, 0x89, 0xf5, 0x97, 0x2c, 0xcd, 0x30, 0x65, 0xe3, 0x4e, 0x17,
594
+ 0x8e, 0x30, 0x2c, 0x1b, 0x9e, 0x8e, 0x17, 0x5d, 0x38, 0x43, 0xa2, 0x96, 0x4f, 0x28, // r
595
+ 0x63, 0xf2, 0xb6, 0x0c, 0x7e, 0xf7, 0x43, 0x63, 0xb6, 0x87, 0xf8, 0x22, 0xdb, 0x6c, 0x31, 0x3e, 0x87, 0x62,
596
+ 0xb3, 0x8d, 0x30, 0x97, 0xb6, 0xfe, 0xc9, 0xb7, 0xb7, 0x61, 0x65, 0x63, 0x0a, 0xaa, // s
597
+ 0x00, // v = 0 (raw)
598
+ ];
599
+ let signature = BytesN::from_array(&env, &sig_bytes);
600
+
601
+ let expected_signer = BytesN::from_array(
602
+ &env,
603
+ &[
604
+ 0x9b, 0x6a, 0xba, 0xbd, 0x08, 0x04, 0x56, 0xf9, 0x00, 0xed, 0x64, 0xe7, 0x4d, 0x12, 0x2f, 0xf9, 0xca, 0x40,
605
+ 0xda, 0xa1,
606
+ ],
607
+ );
608
+
609
+ let recovered = recover_signer(&env, &digest, &signature);
610
+ assert_eq!(recovered, expected_signer);
611
+ }
612
+
613
+ #[test]
614
+ #[should_panic(expected = "Error(Crypto, InvalidInput)")]
615
+ fn recover_signer_invalid_r_zero_fails() {
616
+ let env = Env::default();
617
+
618
+ let digest = BytesN::from_array(&env, &[0u8; 32]);
619
+
620
+ // Signature with r=0 (invalid for secp256k1)
621
+ let mut sig_bytes = [0u8; 65];
622
+ // r = 0 (bytes 0-31 already 0)
623
+ // s = some non-zero value (bytes 32-63)
624
+ for i in 32..64 {
625
+ sig_bytes[i] = 1;
626
+ }
627
+ sig_bytes[64] = 27; // v
628
+ let signature = BytesN::from_array(&env, &sig_bytes);
629
+
630
+ // Should panic - r=0 is invalid for secp256k1
631
+ recover_signer(&env, &digest, &signature);
632
+ }
633
+
634
+ #[test]
635
+ #[should_panic(expected = "Error(Crypto, InvalidInput)")]
636
+ fn recover_signer_invalid_s_zero_fails() {
637
+ let env = Env::default();
638
+
639
+ let digest = BytesN::from_array(&env, &[0u8; 32]);
640
+
641
+ // Signature with s=0 (invalid for secp256k1)
642
+ let mut sig_bytes = [0u8; 65];
643
+ // r = some non-zero value (bytes 0-31)
644
+ for i in 0..32 {
645
+ sig_bytes[i] = 1;
646
+ }
647
+ // s = 0 (bytes 32-63 already 0)
648
+ sig_bytes[64] = 27; // v
649
+ let signature = BytesN::from_array(&env, &sig_bytes);
650
+
651
+ // Should panic - s=0 is invalid for secp256k1
652
+ recover_signer(&env, &digest, &signature);
653
+ }
654
+
655
+ #[test]
656
+ #[should_panic(expected = "Error(Crypto, InvalidInput)")]
657
+ fn recover_signer_invalid_v_fails() {
658
+ let env = Env::default();
659
+
660
+ let digest = BytesN::from_array(&env, &[0u8; 32]);
661
+
662
+ // Signature with invalid v value (v=29 normalizes to 2, which is invalid)
663
+ let mut sig_bytes = [1u8; 65];
664
+ sig_bytes[64] = 29; // v=29 -> normalized to 2 (invalid, must be 0 or 1)
665
+ let signature = BytesN::from_array(&env, &sig_bytes);
666
+
667
+ // Should panic - recovery_id must be 0 or 1
668
+ recover_signer(&env, &digest, &signature);
669
+ }
670
+
671
+ #[test]
672
+ #[should_panic(expected = "Error(Crypto, InvalidInput)")]
673
+ fn verify_signatures_invalid_r_zero_fails() {
674
+ let env = Env::default();
675
+ env.mock_all_auths();
676
+
677
+ let s1 = signer(&env, 1);
678
+ let signers = vec![&env, s1];
679
+
680
+ let contract = env.register(TestContract, (&signers, 1u32));
681
+ let client = TestContractClient::new(&env, &contract);
682
+
683
+ let digest = BytesN::from_array(&env, &[0u8; 32]);
684
+
685
+ // Create signature with r=0 (invalid)
686
+ // Soroban's secp256k1_recover validates signatures and rejects r=0 at the crypto level
687
+ let mut invalid_sig_bytes = [0u8; 65];
688
+ // r = 0 (bytes 0-31 are already 0)
689
+ // s = some value (bytes 32-63)
690
+ for i in 32..64 {
691
+ invalid_sig_bytes[i] = 1;
692
+ }
693
+ // v = 27 (byte 64)
694
+ invalid_sig_bytes[64] = 27;
695
+ let invalid_sig = BytesN::from_array(&env, &invalid_sig_bytes);
696
+ let signatures = vec![&env, invalid_sig];
697
+
698
+ // Soroban's crypto API rejects invalid signatures with HostError
699
+ client.verify_signatures(&digest, &signatures);
700
+ }
701
+
702
+ #[test]
703
+ #[should_panic(expected = "Error(Crypto, InvalidInput)")]
704
+ fn verify_signatures_invalid_s_zero_fails() {
705
+ let env = Env::default();
706
+ env.mock_all_auths();
707
+
708
+ let s1 = signer(&env, 1);
709
+ let signers = vec![&env, s1];
710
+
711
+ let contract = env.register(TestContract, (&signers, 1u32));
712
+ let client = TestContractClient::new(&env, &contract);
713
+
714
+ let digest = BytesN::from_array(&env, &[0u8; 32]);
715
+
716
+ // Create signature with s=0 (invalid)
717
+ // Soroban's secp256k1_recover validates signatures and rejects s=0 at the crypto level
718
+ let mut invalid_sig_bytes = [0u8; 65];
719
+ // r = some value (bytes 0-31)
720
+ for i in 0..32 {
721
+ invalid_sig_bytes[i] = 1;
722
+ }
723
+ // s = 0 (bytes 32-63 are already 0)
724
+ // v = 27 (byte 64)
725
+ invalid_sig_bytes[64] = 27;
726
+ let invalid_sig = BytesN::from_array(&env, &invalid_sig_bytes);
727
+ let signatures = vec![&env, invalid_sig];
728
+
729
+ // Soroban's crypto API rejects invalid signatures with HostError
730
+ client.verify_signatures(&digest, &signatures);
731
+ }