diff --git a/Cargo.lock b/Cargo.lock index 8e87368f..aba7308c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -22,9 +31,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anyhow" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "ark-bls12-381" @@ -189,6 +198,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + [[package]] name = "block-buffer" version = "0.10.4" @@ -212,18 +227,27 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" dependencies = [ "serde", ] [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "num-traits", +] [[package]] name = "const-oid" @@ -231,6 +255,19 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "cosmos-sdk-proto" +version = "0.23.0-pre" +source = "git+https://github.com/burnt-labs/cosmos-rust?branch=feat%2Fxion-zk#b7c2358d00fd51f258ba66c9d753d4cf71da2e41" +dependencies = [ + "pbjson", + "pbjson-types", + "prost", + "prost-types", + "serde", + "tendermint-proto", +] + [[package]] name = "cosmwasm-core" version = "2.2.2" @@ -269,7 +306,7 @@ checksum = "a782b93fae93e57ca8ad3e9e994e784583f5933aeaaa5c80a545c4b437be2047" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -293,7 +330,7 @@ checksum = "e01c9214319017f6ebd8e299036e1f717fa9bb6724e758f7d6fb2477599d1a29" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -406,7 +443,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -448,9 +485,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] @@ -483,7 +520,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", "unicode-xid", ] @@ -507,7 +544,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -537,21 +574,25 @@ version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ + "pkcs8", + "serde", "signature", ] [[package]] name = "ed25519-zebra" -version = "4.0.3" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d9ce6874da5d4415896cd45ffbc4d1cfc0c4f9c079427bd870742c30f2f65a9" +checksum = "0017d969298eec91e3db7a2985a8cab4df6341d86e6f3a6f5878b13fb7846bc9" dependencies = [ "curve25519-dalek", "ed25519", - "hashbrown 0.14.5", - "hex", + "hashbrown 0.15.5", + "pkcs8", "rand_core", + "serde", "sha2", + "subtle", "zeroize", ] @@ -582,6 +623,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "ff" version = "0.13.1" @@ -598,6 +661,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flex-error" version = "0.4.4" @@ -607,6 +676,12 @@ dependencies = [ "paste", ] +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -618,9 +693,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -638,6 +713,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + [[package]] name = "group" version = "0.13.0" @@ -660,14 +747,27 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "ahash", "allocator-api2", + "equivalent", + "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hex" version = "0.4.3" @@ -685,9 +785,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -698,9 +798,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -711,11 +811,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -726,42 +825,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -791,13 +886,13 @@ dependencies = [ ] [[package]] -name = "informalsystems-pbjson" -version = "0.7.0" +name = "indexmap" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa4a0980c8379295100d70854354e78df2ee1c6ca0f96ffe89afeb3140e3a3d" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ - "base64 0.21.7", - "serde", + "equivalent", + "hashbrown 0.16.0", ] [[package]] @@ -809,6 +904,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -847,9 +951,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.175" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libm" @@ -857,17 +961,35 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "num-bigint" @@ -881,11 +1003,10 @@ dependencies = [ [[package]] name = "num-bigint-dig" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" dependencies = [ - "byteorder", "lazy_static", "libm", "num-integer", @@ -957,6 +1078,43 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pbjson" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e6349fa080353f4a597daffd05cb81572a9c031a6d4fff7e504947496fcc68" +dependencies = [ + "base64 0.21.7", + "serde", +] + +[[package]] +name = "pbjson-build" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eea3058763d6e656105d1403cb04e0a41b7bbac6362d413e7c33be0c32279c9" +dependencies = [ + "heck", + "itertools 0.13.0", + "prost", + "prost-types", +] + +[[package]] +name = "pbjson-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54e5e7bfb1652f95bc361d76f3c780d8e526b134b85417e774166ee941f0887" +dependencies = [ + "bytes", + "chrono", + "pbjson", + "pbjson-build", + "prost", + "prost-build", + "serde", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -972,6 +1130,16 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "pkcs1" version = "0.7.5" @@ -995,9 +1163,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -1017,6 +1185,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.110", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -1029,9 +1207,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -1046,6 +1224,26 @@ dependencies = [ "prost-derive", ] +[[package]] +name = "prost-build" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck", + "itertools 0.14.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.110", + "tempfile", +] + [[package]] name = "prost-derive" version = "0.13.5" @@ -1056,18 +1254,33 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" version = "0.8.5" @@ -1094,7 +1307,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.16", ] [[package]] @@ -1117,6 +1330,35 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + [[package]] name = "rfc6979" version = "0.4.0" @@ -1160,9 +1402,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" dependencies = [ "const-oid", "digest", @@ -1187,6 +1429,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.20" @@ -1214,7 +1469,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -1240,9 +1495,9 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -1269,22 +1524,22 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.225" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -1295,7 +1550,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -1366,9 +1621,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -1404,9 +1659,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", @@ -1421,18 +1676,32 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", +] + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys", ] [[package]] name = "tendermint-proto" -version = "0.40.4" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c40e13d39ca19082d8a7ed22de7595979350319833698f8b1080f29620a094" +checksum = "8ed14abe3b0502a3afe21ca74ca5cdd6c7e8d326d982c26f98a394445eb31d6e" dependencies = [ "bytes", "flex-error", "prost", + "prost-types", "serde", "serde_bytes", "subtle-encoding", @@ -1456,14 +1725,14 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "time" -version = "0.3.43" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "num-conv", @@ -1499,9 +1768,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -1511,29 +1780,26 @@ dependencies = [ name = "treasury" version = "0.1.0" dependencies = [ + "cosmos-sdk-proto", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "cw2", - "schemars", - "serde", - "serde_json", "thiserror", "url", - "xion-cosmos-sdk-proto", ] [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-xid" @@ -1583,11 +1849,41 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "xion-account" @@ -1595,11 +1891,13 @@ version = "0.1.1" dependencies = [ "base64 0.21.7", "bech32 0.9.1", + "cosmos-sdk-proto", "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "cw2", - "getrandom", + "ed25519-zebra", + "getrandom 0.2.16", "hex", "p256", "ripemd", @@ -1610,28 +1908,14 @@ dependencies = [ "sha2", "thiserror", "tiny-keccak", - "xion-cosmos-sdk-proto", -] - -[[package]] -name = "xion-cosmos-sdk-proto" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5950da92cdb6e0fdebe4513a1defd73b6c4af7d1fa72ae5f14780451c535bc2" -dependencies = [ - "informalsystems-pbjson", - "prost", - "serde", - "tendermint-proto", ] [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -1639,13 +1923,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", "synstructure", ] @@ -1666,7 +1950,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] @@ -1686,15 +1970,15 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] @@ -1707,14 +1991,14 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -1723,9 +2007,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -1734,11 +2018,11 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.110", ] diff --git a/Cargo.toml b/Cargo.toml index a1ea9e35..06e5c609 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,5 +28,6 @@ phf = { version = "0.11.2", features = ["macros"] } rsa = { version = "0.9.2" } getrandom = { version = "0.2.10", features = ["custom"] } p256 = {version = "0.13.2", features = ["ecdsa-core", "arithmetic", "serde"]} -cosmos-sdk-proto = {package = "xion-cosmos-sdk-proto", version = "0.26.1", default-features = false, features = ["std", "cosmwasm", "xion", "serde"]} +ed25519-zebra = { version = "4.1.0", features = ["alloc"] } +cosmos-sdk-proto = {package = "cosmos-sdk-proto", git = "https://github.com/burnt-labs/cosmos-rust", branch = "feat/xion-zk", default-features = false, features = ["cosmwasm", "xion"]} url = "2.5.2" diff --git a/contracts/account/Cargo.toml b/contracts/account/Cargo.toml index f1b9a0f8..b88a13fc 100644 --- a/contracts/account/Cargo.toml +++ b/contracts/account/Cargo.toml @@ -31,4 +31,5 @@ base64 = { workspace = true } rsa = { workspace = true } getrandom = { workspace = true } p256 = { workspace = true } -cosmos-sdk-proto = { workspace = true } +ed25519-zebra = { workspace = true } +cosmos-sdk-proto = { workspace = true } \ No newline at end of file diff --git a/contracts/account/src/auth.rs b/contracts/account/src/auth.rs index 75682ed6..d7418f6e 100644 --- a/contracts/account/src/auth.rs +++ b/contracts/account/src/auth.rs @@ -10,6 +10,7 @@ pub mod passkey; mod secp256r1; mod sign_arb; pub mod util; +pub mod zkemail; pub mod testing { pub use super::sign_arb::wrap_message; @@ -48,6 +49,12 @@ pub enum AddAuthenticator { url: String, credential: Binary, }, + ZKEmail { + id: u8, + email_salt: String, + allowed_email_hosts: Vec, + signature: Binary, + }, } impl AddAuthenticator { @@ -59,18 +66,37 @@ impl AddAuthenticator { AddAuthenticator::Jwt { id, .. } => *id, AddAuthenticator::Secp256R1 { id, .. } => *id, AddAuthenticator::Passkey { id, .. } => *id, + AddAuthenticator::ZKEmail { id, .. } => *id, } } } #[derive(Serialize, Deserialize, Clone, JsonSchema, PartialEq, Debug)] pub enum Authenticator { - Secp256K1 { pubkey: Binary }, - Ed25519 { pubkey: Binary }, - EthWallet { address: String }, - Jwt { aud: String, sub: String }, - Secp256R1 { pubkey: Binary }, - Passkey { url: String, passkey: Binary }, + Secp256K1 { + pubkey: Binary, + }, + Ed25519 { + pubkey: Binary, + }, + EthWallet { + address: String, + }, + Jwt { + aud: String, + sub: String, + }, + Secp256R1 { + pubkey: Binary, + }, + Passkey { + url: String, + passkey: Binary, + }, + ZKEmail { + email_salt: String, + allowed_email_hosts: Vec, + }, } impl Authenticator { @@ -144,6 +170,14 @@ impl Authenticator { Ok(true) } + Authenticator::ZKEmail { + email_salt, + allowed_email_hosts, + } => { + let tx_bytes_hash = util::base64url_encode(tx_bytes); + let verification = zkemail::verify(deps, tx_bytes_hash.as_bytes(), sig_bytes, email_salt, allowed_email_hosts)?; + Ok(verification) + } } } } diff --git a/contracts/account/src/auth/util.rs b/contracts/account/src/auth/util.rs index c4bfe3cc..3774a7bb 100644 --- a/contracts/account/src/auth/util.rs +++ b/contracts/account/src/auth/util.rs @@ -2,6 +2,7 @@ use crate::error::ContractError; use bech32::{ToBase32, Variant}; use ripemd::Ripemd160; use sha2::{Digest, Sha256}; +use base64::{Engine as _, engine::general_purpose}; pub fn sha256(msg: &[u8]) -> Vec { let mut hasher = Sha256::new(); @@ -9,6 +10,10 @@ pub fn sha256(msg: &[u8]) -> Vec { hasher.finalize().to_vec() } +pub fn base64url_encode(msg: &[u8]) -> String { + general_purpose::URL_SAFE_NO_PAD.encode(msg) +} + fn ripemd160(bytes: &[u8]) -> Vec { let mut hasher = Ripemd160::new(); hasher.update(bytes); @@ -25,3 +30,35 @@ pub fn derive_addr(prefix: &str, pubkey_bytes: &[u8]) -> Result Err(err.into()), } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_base64url_encode() { + // Test empty input + assert_eq!(base64url_encode(b""), ""); + + // Test simple string + assert_eq!(base64url_encode(b"hello"), "aGVsbG8"); + + // Test with special characters + assert_eq!(base64url_encode(b"hello world!"), "aGVsbG8gd29ybGQh"); + + // Test binary data + let binary_data = [0x00, 0x01, 0x02, 0x03, 0xFF]; + assert_eq!(base64url_encode(&binary_data), "AAECA_8"); + + // Test longer binary data + let longer_binary = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64]; + assert_eq!(base64url_encode(&longer_binary), "SGVsbG8gV29ybGQ"); + + // Test that it produces URL-safe output (no + or / characters, no padding) + let test_data = b"test data with special chars: +/="; + let encoded = base64url_encode(test_data); + assert!(!encoded.contains('+')); + assert!(!encoded.contains('/')); + assert!(!encoded.ends_with('=')); + } +} diff --git a/contracts/account/src/auth/zkemail.rs b/contracts/account/src/auth/zkemail.rs new file mode 100644 index 00000000..b944cf22 --- /dev/null +++ b/contracts/account/src/auth/zkemail.rs @@ -0,0 +1,496 @@ +use crate::error::ContractResult; +use cosmos_sdk_proto::{ + prost::Message, + traits::MessageExt, + xion::v1::dkim::{AuthenticateResponse, QueryAuthenticateRequest}, +}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{from_json, Binary, Deps}; + +#[cw_serde] +pub struct SnarkJsProof { + #[serde(rename = "pi_a")] + pi_a: [String; 3], + #[serde(rename = "pi_b")] + pi_b: [[String; 2]; 3], + #[serde(rename = "pi_c")] + pi_c: [String; 3], + protocol: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + curve: Option, +} + +#[cw_serde] +pub struct ZKEmailSignature { + proof: SnarkJsProof, + #[serde(rename = "publicInputs")] + public_inputs: Vec, +} + +pub fn verify( + deps: Deps, + tx_bytes: &[u8], + sig_bytes: &[u8], + email_salt: &str, + allowed_email_hosts: &[String], +) -> ContractResult { + // split the sig_bytes into 2 parts proof and publicOutputs + let sig: ZKEmailSignature = from_json(sig_bytes.to_vec())?; + let proof = sig.proof; + let public_inputs = sig.public_inputs; + + let verification_request = QueryAuthenticateRequest { + tx_bytes: tx_bytes.to_vec(), + proof: serde_json::to_vec(&proof)?, + public_inputs: public_inputs.clone(), + email_hash: email_salt.to_string(), + allowed_email_hosts: allowed_email_hosts.to_vec(), + }; + + let verification_request_bytes = verification_request.to_bytes()?; + let verification_response: Binary = deps.querier.query_grpc( + "/xion.dkim.v1.Query/Authenticate".to_string(), + Binary::from(verification_request_bytes), + )?; + + let res: AuthenticateResponse = AuthenticateResponse::decode(verification_response.as_slice())?; + + Ok(res.verified) +} + +#[cfg(test)] +mod tests { + use super::*; + + // Sample test data based on the provided signature + fn sample_zkemail_signature() -> ZKEmailSignature { + ZKEmailSignature { + proof: SnarkJsProof { + pi_a: [ + "13359235437905510146488545267580847868768563960781729194939527523243795688772".to_string(), + "16255212479465089639502013432936572417100794023004408906770080834142123006135".to_string(), + "1".to_string(), + ], + pi_b: [ + [ + "19284413907248568809076802931471620471530787252392478315569414028536127540332".to_string(), + "3391348177043200450451461793330092888088268452280878870378654788048816463108".to_string(), + ], + [ + "19852853133236466964633006998998630882202598701108272747914380336016310877725".to_string(), + "1320566082262176804917574208663865769527718771716928098903701681357146586169".to_string(), + ], + [ + "1".to_string(), + "0".to_string(), + ], + ], + pi_c: [ + "15683269302985443708971822532209957645618630393306369984958148167283539586821".to_string(), + "6442476935792224156511907661500477129513526142139554915043685301572568416380".to_string(), + "1".to_string(), + ], + protocol: "groth16".to_string(), + curve: None, + }, + public_inputs: vec![ + "2018721414038404820327".to_string(), + "0".to_string(), + "0".to_string(), + "0".to_string(), + "0".to_string(), + "0".to_string(), + "0".to_string(), + "0".to_string(), + "0".to_string(), + "6632353713085157925504008443078919716322386156160602218536961028046468237192".to_string(), + "19544515484294133365621150860798248908781994760432589784803858418698789050087".to_string(), + "1759147291".to_string(), + "124413588010935573100449456468959839270027757215138439816955024736271298883".to_string(), + "125987718504881168702817372751405511311626515399128115957683055706162879081".to_string(), + "138174294419566073638917398478480233783462655482283489778477032129860416308".to_string(), + "87164429935183530231106524238772469083021376536857547601286350511895957042".to_string(), + "159508995554830235422881220221659222882416701537684367907262541081181107041".to_string(), + "216177859633033993616607456010987870980723214832657304250929052054387451251".to_string(), + "136870293077760051536514689814528040652982158268238924211443105143315312977".to_string(), + "209027647271941540634260128227139143305212625530130988286308577451934433604".to_string(), + "216041037480816501846348705353738079775803623607373665378499876478757721956".to_string(), + "184099808892606061942559141059081527262834859629181581270585908529014000483".to_string(), + "173926821082308056829441773860483849128404996084932919505946802488367989070".to_string(), + "136498083332900321215526260868562056670892412932671519510981704427905430578".to_string(), + "0".to_string(), + "0".to_string(), + "0".to_string(), + "0".to_string(), + "0".to_string(), + "0".to_string(), + "0".to_string(), + "0".to_string(), + "8106355043968901587346579634598098765933160394002251948170420219958523220425".to_string(), + "1".to_string(), + ], + } + } + + fn sample_signature_json() -> String { + r#"{ + "proof": { + "pi_a": [ + "13359235437905510146488545267580847868768563960781729194939527523243795688772", + "16255212479465089639502013432936572417100794023004408906770080834142123006135", + "1" + ], + "pi_b": [ + [ + "19284413907248568809076802931471620471530787252392478315569414028536127540332", + "3391348177043200450451461793330092888088268452280878870378654788048816463108" + ], + [ + "19852853133236466964633006998998630882202598701108272747914380336016310877725", + "1320566082262176804917574208663865769527718771716928098903701681357146586169" + ], + [ + "1", + "0" + ] + ], + "pi_c": [ + "15683269302985443708971822532209957645618630393306369984958148167283539586821", + "6442476935792224156511907661500477129513526142139554915043685301572568416380", + "1" + ], + "protocol": "groth16" + }, + "publicInputs": [ + "2018721414038404820327", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "6632353713085157925504008443078919716322386156160602218536961028046468237192", + "19544515484294133365621150860798248908781994760432589784803858418698789050087", + "1759147291", + "124413588010935573100449456468959839270027757215138439816955024736271298883", + "125987718504881168702817372751405511311626515399128115957683055706162879081", + "138174294419566073638917398478480233783462655482283489778477032129860416308", + "87164429935183530231106524238772469083021376536857547601286350511895957042", + "159508995554830235422881220221659222882416701537684367907262541081181107041", + "216177859633033993616607456010987870980723214832657304250929052054387451251", + "136870293077760051536514689814528040652982158268238924211443105143315312977", + "209027647271941540634260128227139143305212625530130988286308577451934433604", + "216041037480816501846348705353738079775803623607373665378499876478757721956", + "184099808892606061942559141059081527262834859629181581270585908529014000483", + "173926821082308056829441773860483849128404996084932919505946802488367989070", + "136498083332900321215526260868562056670892412932671519510981704427905430578", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "8106355043968901587346579634598098765933160394002251948170420219958523220425", + "1" + ] + }"#.to_string() + } + + #[test] + fn test_snarkjs_proof_serialization() { + let proof = SnarkJsProof { + pi_a: ["1".to_string(), "2".to_string(), "3".to_string()], + pi_b: [ + ["4".to_string(), "5".to_string()], + ["6".to_string(), "7".to_string()], + ["8".to_string(), "9".to_string()], + ], + pi_c: ["10".to_string(), "11".to_string(), "12".to_string()], + protocol: "groth16".to_string(), + curve: None, + }; + + let serialized = serde_json::to_string(&proof).unwrap(); + let deserialized: SnarkJsProof = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(proof.pi_a, deserialized.pi_a); + assert_eq!(proof.pi_b, deserialized.pi_b); + assert_eq!(proof.pi_c, deserialized.pi_c); + assert_eq!(proof.protocol, deserialized.protocol); + } + + #[test] + fn test_zkemail_signature_serialization() { + let signature = sample_zkemail_signature(); + let json_str = sample_signature_json(); + + // Test deserialization from JSON string + let deserialized: ZKEmailSignature = serde_json::from_str(&json_str).unwrap(); + assert_eq!(signature.proof.pi_a, deserialized.proof.pi_a); + assert_eq!(signature.proof.pi_b, deserialized.proof.pi_b); + assert_eq!(signature.proof.pi_c, deserialized.proof.pi_c); + assert_eq!(signature.proof.protocol, deserialized.proof.protocol); + assert_eq!(signature.public_inputs, deserialized.public_inputs); + + // Test round-trip serialization + let serialized = serde_json::to_string(&signature).unwrap(); + let round_trip: ZKEmailSignature = serde_json::from_str(&serialized).unwrap(); + assert_eq!(signature.public_inputs, round_trip.public_inputs); + } + + #[test] + fn test_zkemail_signature_field_names() { + // Test that the JSON field names match exactly (camelCase vs snake_case) + let json_str = sample_signature_json(); + let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap(); + + // Verify the JSON structure has the expected field names + assert!(parsed.get("proof").is_some()); + assert!(parsed.get("publicInputs").is_some()); + let proof = parsed.get("proof").unwrap(); + assert!(proof.get("pi_a").is_some()); + assert!(proof.get("pi_b").is_some()); + assert!(proof.get("pi_c").is_some()); + assert!(proof.get("protocol").is_some()); + } + + #[test] + fn test_proof_structure_validation() { + let signature = sample_zkemail_signature(); + + // Verify array sizes + assert_eq!(signature.proof.pi_a.len(), 3); + assert_eq!(signature.proof.pi_b.len(), 3); + assert_eq!(signature.proof.pi_c.len(), 3); + assert_eq!(signature.public_inputs.len(), 34); + + // Verify nested array structure + for row in &signature.proof.pi_b { + assert_eq!(row.len(), 2); + } + + // Verify protocol + assert_eq!(signature.proof.protocol, "groth16"); + } + + #[test] + fn test_public_outputs_boundary_cases() { + // Test access to first element + let signature = sample_zkemail_signature(); + + assert_eq!(signature.public_inputs[0], "2018721414038404820327"); + + // Test access to last element (index 33) + assert_eq!(signature.public_inputs[33], "1"); + + // Test access to email salt element (index 32) + assert_eq!(signature.public_inputs[32], "8106355043968901587346579634598098765933160394002251948170420219958523220425"); + } + + #[test] + fn test_snarkjs_proof_with_empty_fields() { + let proof = SnarkJsProof { + pi_a: ["".to_string(), "".to_string(), "".to_string()], + pi_b: [ + ["".to_string(), "".to_string()], + ["".to_string(), "".to_string()], + ["".to_string(), "".to_string()], + ], + pi_c: ["".to_string(), "".to_string(), "".to_string()], + protocol: "".to_string(), + curve: None, + }; + + let serialized = serde_json::to_string(&proof).unwrap(); + let deserialized: SnarkJsProof = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(proof.pi_a, deserialized.pi_a); + assert_eq!(proof.pi_b, deserialized.pi_b); + assert_eq!(proof.pi_c, deserialized.pi_c); + assert_eq!(proof.protocol, deserialized.protocol); + } + + #[test] + fn test_snarkjs_proof_with_special_characters() { + let proof = SnarkJsProof { + pi_a: [ + "123!@#".to_string(), + "456$%^".to_string(), + "789&*()".to_string(), + ], + pi_b: [ + ["test\\n".to_string(), "test\"quote".to_string()], + ["test'single".to_string(), "test\ttab".to_string()], + ["test/slash".to_string(), "test\\backslash".to_string()], + ], + pi_c: [ + "unicode🚀".to_string(), + "unicode💯".to_string(), + "unicode✨".to_string(), + ], + protocol: "groth16-custom".to_string(), + curve: None, + }; + + let serialized = serde_json::to_string(&proof).unwrap(); + let deserialized: SnarkJsProof = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(proof.pi_a, deserialized.pi_a); + assert_eq!(proof.pi_b, deserialized.pi_b); + assert_eq!(proof.pi_c, deserialized.pi_c); + assert_eq!(proof.protocol, deserialized.protocol); + } + + #[test] + fn test_zkemail_signature_with_empty_public_outputs() { + let signature = ZKEmailSignature { + proof: SnarkJsProof { + pi_a: ["1".to_string(), "2".to_string(), "3".to_string()], + pi_b: [ + ["4".to_string(), "5".to_string()], + ["6".to_string(), "7".to_string()], + ["8".to_string(), "9".to_string()], + ], + pi_c: ["10".to_string(), "11".to_string(), "12".to_string()], + protocol: "groth16".to_string(), + curve: None, + }, + public_inputs: vec![], + }; + + let serialized = serde_json::to_string(&signature).unwrap(); + let deserialized: ZKEmailSignature = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(signature.public_inputs, deserialized.public_inputs); + assert!(signature.public_inputs.is_empty()); + } + + #[test] + fn test_zkemail_signature_with_large_public_outputs() { + let mut large_outputs = Vec::new(); + for i in 0..100 { + large_outputs.push(format!("output_{}", i)); + } + + let signature = ZKEmailSignature { + proof: SnarkJsProof { + pi_a: ["1".to_string(), "2".to_string(), "3".to_string()], + pi_b: [ + ["4".to_string(), "5".to_string()], + ["6".to_string(), "7".to_string()], + ["8".to_string(), "9".to_string()], + ], + pi_c: ["10".to_string(), "11".to_string(), "12".to_string()], + protocol: "groth16".to_string(), + curve: None, + }, + public_inputs: large_outputs.clone(), + }; + + let serialized = serde_json::to_string(&signature).unwrap(); + let deserialized: ZKEmailSignature = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(signature.public_inputs.len(), 100); + assert_eq!(signature.public_inputs, deserialized.public_inputs); + assert_eq!(deserialized.public_inputs[0], "output_0"); + assert_eq!(deserialized.public_inputs[99], "output_99"); + } + + #[test] + fn test_multiple_proof_protocols() { + let protocols = vec!["groth16", "plonk", "stark", "custom_protocol"]; + + for protocol in protocols { + let proof = SnarkJsProof { + pi_a: ["1".to_string(), "2".to_string(), "3".to_string()], + pi_b: [ + ["4".to_string(), "5".to_string()], + ["6".to_string(), "7".to_string()], + ["8".to_string(), "9".to_string()], + ], + pi_c: ["10".to_string(), "11".to_string(), "12".to_string()], + protocol: protocol.to_string(), + curve: None, + }; + + let serialized = serde_json::to_string(&proof).unwrap(); + let deserialized: SnarkJsProof = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(proof.protocol, deserialized.protocol); + assert_eq!(proof.protocol, protocol); + } + } + + #[test] + fn test_serde_field_name_mapping() { + // Test that serde correctly maps camelCase JSON to snake_case Rust fields + let json_with_camel_case = r#"{ + "proof": { + "pi_a": ["1", "2", "3"], + "pi_b": [["4", "5"], ["6", "7"], ["8", "9"]], + "pi_c": ["10", "11", "12"], + "protocol": "groth16" + }, + "publicInputs": ["test_value"] + }"#; + + let signature: ZKEmailSignature = serde_json::from_str(json_with_camel_case).unwrap(); + + // Verify the struct fields are populated correctly + assert_eq!(signature.proof.pi_a, ["1", "2", "3"]); + assert_eq!(signature.proof.protocol, "groth16"); + assert_eq!(signature.public_inputs, vec!["test_value"]); + + // Verify serialization produces camelCase JSON + let serialized = serde_json::to_string(&signature).unwrap(); + let parsed_back: serde_json::Value = serde_json::from_str(&serialized).unwrap(); + assert!(parsed_back.get("publicInputs").is_some()); + assert!(parsed_back.get("public_inputs").is_none()); // Should not exist + } + + #[test] + fn test_signature_parsing_from_bytes() { + let json_str = sample_signature_json(); + let sig_bytes = json_str.as_bytes(); + + // Test parsing signature from bytes using from_json + let sig: ZKEmailSignature = from_json(sig_bytes.to_vec()).unwrap(); + + // Verify the parsed signature matches our sample + assert_eq!(sig.proof.protocol, "groth16"); + assert_eq!(sig.public_inputs.len(), 34); + assert_eq!(sig.public_inputs[0], "2018721414038404820327"); + assert_eq!(sig.public_inputs[33], "1"); + } + + #[test] + fn test_query_verify_request_creation() { + let signature = sample_zkemail_signature(); + let tx_bytes = "test_transaction"; + let email_salt = "test_salt"; + let allowed_email_hosts = vec!["example.com".to_string(), "test.com".to_string()]; + + // Test creating QueryVerifyRequest from signature components + let verification_request = QueryAuthenticateRequest { + tx_bytes: tx_bytes.as_bytes().to_vec(), + proof: serde_json::to_vec(&signature.proof).unwrap(), + public_inputs: signature.public_inputs.clone(), + email_hash: email_salt.to_string(), + allowed_email_hosts: allowed_email_hosts.clone(), + }; + + // Verify the request is properly constructed + assert_eq!(verification_request.tx_bytes, tx_bytes.as_bytes()); + assert_eq!(verification_request.email_hash, email_salt.to_string()); + assert_eq!(verification_request.public_inputs, signature.public_inputs); + assert_eq!(verification_request.allowed_email_hosts, allowed_email_hosts); + + // Verify proof serialization + let proof_bytes = serde_json::to_vec(&signature.proof).unwrap(); + assert_eq!(verification_request.proof, proof_bytes); + } +} diff --git a/contracts/account/src/contract.rs b/contracts/account/src/contract.rs index 0957147c..84fc912b 100644 --- a/contracts/account/src/contract.rs +++ b/contracts/account/src/contract.rs @@ -4,7 +4,10 @@ use cosmwasm_std::{ }; use crate::error::ContractError; -use crate::execute::{add_auth_method, assert_self, emit, remove_auth_method}; +use crate::execute::{ + add_auth_method, add_allowed_email_host, assert_self, emit, remove_allowed_email_host, + remove_auth_method, update_allowed_email_hosts, +}; use crate::msg::{ExecuteMsg, MigrateMsg}; use crate::{ error::ContractResult, @@ -91,6 +94,16 @@ pub fn execute( } ExecuteMsg::RemoveAuthMethod { id } => remove_auth_method(deps, env, *id), ExecuteMsg::Emit { data } => emit(env, data.to_string()), + ExecuteMsg::UpdateAllowedEmailHosts { + id, + allowed_email_hosts, + } => update_allowed_email_hosts(deps, env, *id, allowed_email_hosts.clone()), + ExecuteMsg::AddAllowedEmailHost { id, email_host } => { + add_allowed_email_host(deps, env, *id, email_host.clone()) + } + ExecuteMsg::RemoveAllowedEmailHost { id, email_host } => { + remove_allowed_email_host(deps, env, *id, email_host.clone()) + } } } diff --git a/contracts/account/src/error.rs b/contracts/account/src/error.rs index ea4e0ac9..4920f67d 100644 --- a/contracts/account/src/error.rs +++ b/contracts/account/src/error.rs @@ -91,8 +91,14 @@ pub enum ContractError { #[error("invalid ethereum address")] InvalidEthAddress, - #[error("authenticator {index} not found")] - AuthenticatorNotFound { index: u8 }, + #[error("at least one allowed email host must be present")] + NoAllowedEmailHosts, + + #[error("authenticator not found")] + AuthenticatorNotFound, + + #[error("operation not supported for this authenticator type")] + UnsupportedAuthenticatorOperation, } pub type ContractResult = Result; diff --git a/contracts/account/src/execute.rs b/contracts/account/src/execute.rs index 1ff57dbc..6b6738de 100644 --- a/contracts/account/src/execute.rs +++ b/contracts/account/src/execute.rs @@ -8,6 +8,7 @@ use crate::{ state::AUTHENTICATORS, }; + pub fn init( deps: DepsMut, env: Env, @@ -70,6 +71,12 @@ pub fn before_tx( Authenticator::Passkey { .. } => { // todo: figure out if there are minimum checks for passkeys } + Authenticator::ZKEmail { .. } => { + // todo: verify that this minimum is as high as possible + if sig_bytes.len() < 700 { + return Err(ContractError::ShortSignature); + } + } } return match authenticator.verify(deps, env, tx_bytes, sig_bytes)? { @@ -220,6 +227,33 @@ pub fn add_auth_method( *(credential) = passkey; Ok(()) } + AddAuthenticator::ZKEmail { + id, + email_salt, + allowed_email_hosts, + signature, + } => { + // Validate that at least one email host is provided + if allowed_email_hosts.is_empty() { + return Err(ContractError::NoAllowedEmailHosts); + } + + let auth = Authenticator::ZKEmail { + email_salt: (*email_salt).clone(), + allowed_email_hosts: allowed_email_hosts.clone(), + }; + if !auth.verify( + deps.as_ref(), + env, + &Binary::from(env.contract.address.as_bytes()), + signature, + )? { + Err(ContractError::InvalidSignature) + } else { + save_authenticator(deps, *id, &auth)?; + Ok(()) + } + } }?; Ok( Response::new().add_event(Event::new("add_auth_method").add_attributes(vec![ @@ -252,11 +286,6 @@ pub fn remove_auth_method(deps: DepsMut, env: Env, id: u8) -> ContractResult ContractResult<()> { Ok(()) } +pub fn update_allowed_email_hosts( + deps: DepsMut, + env: Env, + id: u8, + allowed_email_hosts: Vec, +) -> ContractResult { + // Validate that at least one email host is provided + if allowed_email_hosts.is_empty() { + return Err(ContractError::NoAllowedEmailHosts); + } + + // Load the authenticator + let authenticator = AUTHENTICATORS.load(deps.storage, id)?; + + // Ensure it's a ZKEmail authenticator + match authenticator { + Authenticator::ZKEmail { email_salt, .. } => { + // Update the authenticator with new allowed_email_hosts + let updated_auth = Authenticator::ZKEmail { + email_salt, + allowed_email_hosts: allowed_email_hosts.clone(), + }; + + AUTHENTICATORS.save(deps.storage, id, &updated_auth)?; + + Ok(Response::new().add_event( + Event::new("update_allowed_email_hosts").add_attributes(vec![ + ("contract_address", env.contract.address.to_string()), + ("authenticator_id", id.to_string()), + ("allowed_email_hosts", serde_json::to_string(&allowed_email_hosts)?), + ]), + )) + } + _ => Err(ContractError::UnsupportedAuthenticatorOperation), + } +} + +pub fn add_allowed_email_host( + deps: DepsMut, + env: Env, + id: u8, + email_host: String, +) -> ContractResult { + // Load the authenticator + let authenticator = AUTHENTICATORS.load(deps.storage, id)?; + + // Ensure it's a ZKEmail authenticator + match authenticator { + Authenticator::ZKEmail { + email_salt, + mut allowed_email_hosts, + } => { + // Check if the email host already exists + if allowed_email_hosts.contains(&email_host) { + return Ok(Response::new().add_event( + Event::new("add_allowed_email_host").add_attributes(vec![ + ("contract_address", env.contract.address.to_string()), + ("authenticator_id", id.to_string()), + ("email_host", email_host), + ("status", "already_exists".to_string()), + ]), + )); + } + + // Add the new email host + allowed_email_hosts.push(email_host.clone()); + + // Update the authenticator + let updated_auth = Authenticator::ZKEmail { + email_salt, + allowed_email_hosts, + }; + + AUTHENTICATORS.save(deps.storage, id, &updated_auth)?; + + Ok(Response::new().add_event( + Event::new("add_allowed_email_host").add_attributes(vec![ + ("contract_address", env.contract.address.to_string()), + ("authenticator_id", id.to_string()), + ("email_host", email_host), + ("status", "added".to_string()), + ]), + )) + } + _ => Err(ContractError::UnsupportedAuthenticatorOperation), + } +} + +pub fn remove_allowed_email_host( + deps: DepsMut, + env: Env, + id: u8, + email_host: String, +) -> ContractResult { + // Load the authenticator + let authenticator = AUTHENTICATORS.load(deps.storage, id)?; + + // Ensure it's a ZKEmail authenticator + match authenticator { + Authenticator::ZKEmail { + email_salt, + mut allowed_email_hosts, + } => { + // Ensure at least one email host remains after removal + if allowed_email_hosts.len() <= 1 { + return Err(ContractError::NoAllowedEmailHosts); + } + + // Remove the email host + allowed_email_hosts.retain(|host| host != &email_host); + + // Update the authenticator + let updated_auth = Authenticator::ZKEmail { + email_salt, + allowed_email_hosts, + }; + + AUTHENTICATORS.save(deps.storage, id, &updated_auth)?; + + Ok(Response::new().add_event( + Event::new("remove_allowed_email_host").add_attributes(vec![ + ("contract_address", env.contract.address.to_string()), + ("authenticator_id", id.to_string()), + ("email_host", email_host), + ("status", "removed".to_string()), + ]), + )) + } + _ => Err(ContractError::UnsupportedAuthenticatorOperation), + } +} + #[cfg(test)] pub mod tests { use base64::{engine::general_purpose, Engine as _}; @@ -395,4 +556,330 @@ pub mod tests { Binary::from("true".as_bytes()) ); } + + #[test] + fn test_add_zkemail_authenticator_invalid_signature() { + use crate::auth::AddAuthenticator; + use crate::execute::add_auth_method; + + let mut deps = OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default().with_prefix("xion"), + querier: MockQuerier::::new(&[]), + custom_query_type: std::marker::PhantomData, + }; + let env = mock_env(); + + // Create an invalid signature (insufficient public outputs) + let invalid_signature_json = r#"{ + "proof": { + "pi_a": ["1", "2", "3"], + "pi_b": [["4", "5"], ["6", "7"], ["8", "9"]], + "pi_c": ["10", "11", "12"], + "protocol": "groth16" + }, + "publicInputs": ["1", "2", "3"] + }"#; + + let signature_binary = Binary::from(invalid_signature_json.as_bytes()); + + let mut add_authenticator = AddAuthenticator::ZKEmail { + id: 1, + email_salt: "test_email_salt".to_string(), + allowed_email_hosts: vec!["example.com".to_string()], + signature: signature_binary, + }; + + // Call add_auth_method - should fail due to insufficient public outputs + let result = add_auth_method(deps.as_mut(), &env, &mut add_authenticator); + + // Verify the result is an error + assert!(result.is_err()); + + // Verify the authenticator was not saved + assert!(!AUTHENTICATORS.has(deps.as_ref().storage, 1)); + } + + #[test] + fn test_allowed_email_hosts_operations() { + use crate::auth::Authenticator; + use crate::error::ContractError; + use crate::execute::{add_allowed_email_host, remove_allowed_email_host, update_allowed_email_hosts}; + + let mut deps = OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default().with_prefix("xion"), + querier: MockQuerier::::new(&[]), + custom_query_type: std::marker::PhantomData, + }; + let env = mock_env(); + let auth_id = 1u8; + + // Setup: Create a ZKEmail authenticator with initial email hosts + let initial_authenticator = Authenticator::ZKEmail { + email_salt: "test_salt".to_string(), + allowed_email_hosts: vec!["example.com".to_string(), "test.com".to_string()], + }; + AUTHENTICATORS + .save(deps.as_mut().storage, auth_id, &initial_authenticator) + .unwrap(); + + // Test 1: Add a new email host + let result = add_allowed_email_host( + deps.as_mut(), + env.clone(), + auth_id, + "newhost.com".to_string(), + ); + assert!(result.is_ok()); + + // Verify the host was added + let updated_auth = AUTHENTICATORS.load(deps.as_ref().storage, auth_id).unwrap(); + match updated_auth { + Authenticator::ZKEmail { allowed_email_hosts, .. } => { + assert_eq!(allowed_email_hosts.len(), 3); + assert!(allowed_email_hosts.contains(&"newhost.com".to_string())); + } + _ => panic!("Expected ZKEmail authenticator"), + } + + // Test 2: Try to add a duplicate email host + let result = add_allowed_email_host( + deps.as_mut(), + env.clone(), + auth_id, + "newhost.com".to_string(), + ); + assert!(result.is_ok()); // Should succeed but not add duplicate + + // Verify no duplicate was added + let updated_auth = AUTHENTICATORS.load(deps.as_ref().storage, auth_id).unwrap(); + match updated_auth { + Authenticator::ZKEmail { allowed_email_hosts, .. } => { + assert_eq!(allowed_email_hosts.len(), 3); // Still 3, not 4 + assert_eq!( + allowed_email_hosts.iter().filter(|h| *h == "newhost.com").count(), + 1 + ); + } + _ => panic!("Expected ZKEmail authenticator"), + } + + // Test 3: Remove an email host + let result = remove_allowed_email_host( + deps.as_mut(), + env.clone(), + auth_id, + "newhost.com".to_string(), + ); + assert!(result.is_ok()); + + // Verify the host was removed + let updated_auth = AUTHENTICATORS.load(deps.as_ref().storage, auth_id).unwrap(); + match updated_auth { + Authenticator::ZKEmail { allowed_email_hosts, .. } => { + assert_eq!(allowed_email_hosts.len(), 2); + assert!(!allowed_email_hosts.contains(&"newhost.com".to_string())); + } + _ => panic!("Expected ZKEmail authenticator"), + } + + // Test 4: Try to remove the second-to-last email host (should succeed) + let result = remove_allowed_email_host( + deps.as_mut(), + env.clone(), + auth_id, + "example.com".to_string(), + ); + assert!(result.is_ok()); + + // Verify only one host remains + let updated_auth = AUTHENTICATORS.load(deps.as_ref().storage, auth_id).unwrap(); + match updated_auth { + Authenticator::ZKEmail { allowed_email_hosts, .. } => { + assert_eq!(allowed_email_hosts.len(), 1); + assert_eq!(allowed_email_hosts[0], "test.com"); + } + _ => panic!("Expected ZKEmail authenticator"), + } + + // Test 5: Try to remove the last email host (should fail) + let result = remove_allowed_email_host( + deps.as_mut(), + env.clone(), + auth_id, + "test.com".to_string(), + ); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), ContractError::NoAllowedEmailHosts); + + // Verify the host was not removed + let updated_auth = AUTHENTICATORS.load(deps.as_ref().storage, auth_id).unwrap(); + match updated_auth { + Authenticator::ZKEmail { allowed_email_hosts, .. } => { + assert_eq!(allowed_email_hosts.len(), 1); + } + _ => panic!("Expected ZKEmail authenticator"), + } + + // Test 6: Update allowed email hosts with a new list + let new_hosts = vec![ + "updated1.com".to_string(), + "updated2.com".to_string(), + "updated3.com".to_string(), + ]; + let result = update_allowed_email_hosts( + deps.as_mut(), + env.clone(), + auth_id, + new_hosts.clone(), + ); + assert!(result.is_ok()); + + // Verify the hosts were updated + let updated_auth = AUTHENTICATORS.load(deps.as_ref().storage, auth_id).unwrap(); + match updated_auth { + Authenticator::ZKEmail { allowed_email_hosts, .. } => { + assert_eq!(allowed_email_hosts, new_hosts); + } + _ => panic!("Expected ZKEmail authenticator"), + } + + // Test 7: Try to update with an empty list (should fail) + let result = update_allowed_email_hosts( + deps.as_mut(), + env.clone(), + auth_id, + vec![], + ); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), ContractError::NoAllowedEmailHosts); + + // Verify hosts were not changed + let updated_auth = AUTHENTICATORS.load(deps.as_ref().storage, auth_id).unwrap(); + match updated_auth { + Authenticator::ZKEmail { allowed_email_hosts, .. } => { + assert_eq!(allowed_email_hosts, new_hosts); + } + _ => panic!("Expected ZKEmail authenticator"), + } + + // Test 8: Try operations on a non-existent authenticator + let result = add_allowed_email_host( + deps.as_mut(), + env.clone(), + 99u8, // Non-existent ID + "test.com".to_string(), + ); + assert!(result.is_err()); + + // Test 9: Try operations on a non-ZKEmail authenticator + let secp_auth = Authenticator::Secp256K1 { + pubkey: Binary::from(vec![1, 2, 3]), + }; + AUTHENTICATORS + .save(deps.as_mut().storage, 2u8, &secp_auth) + .unwrap(); + + let result = add_allowed_email_host( + deps.as_mut(), + env.clone(), + 2u8, + "test.com".to_string(), + ); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), ContractError::UnsupportedAuthenticatorOperation); + + let result = remove_allowed_email_host( + deps.as_mut(), + env.clone(), + 2u8, + "test.com".to_string(), + ); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), ContractError::UnsupportedAuthenticatorOperation); + + let result = update_allowed_email_hosts( + deps.as_mut(), + env.clone(), + 2u8, + vec!["test.com".to_string()], + ); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), ContractError::UnsupportedAuthenticatorOperation); + + // Test 10: Try to remove a host that doesn't exist + let result = remove_allowed_email_host( + deps.as_mut(), + env.clone(), + auth_id, + "nonexistent.com".to_string(), + ); + // This should succeed but not change anything + assert!(result.is_ok()); + + // Verify the hosts remain unchanged + let updated_auth = AUTHENTICATORS.load(deps.as_ref().storage, auth_id).unwrap(); + match updated_auth { + Authenticator::ZKEmail { allowed_email_hosts, .. } => { + assert_eq!(allowed_email_hosts, new_hosts); + } + _ => panic!("Expected ZKEmail authenticator"), + } + + // Test 11: Update with a single host (edge case - minimum valid) + let result = update_allowed_email_hosts( + deps.as_mut(), + env.clone(), + auth_id, + vec!["single.com".to_string()], + ); + assert!(result.is_ok()); + + let updated_auth = AUTHENTICATORS.load(deps.as_ref().storage, auth_id).unwrap(); + match updated_auth { + Authenticator::ZKEmail { allowed_email_hosts, .. } => { + assert_eq!(allowed_email_hosts.len(), 1); + assert_eq!(allowed_email_hosts[0], "single.com"); + } + _ => panic!("Expected ZKEmail authenticator"), + } + + // Test 12: Add multiple hosts one by one + let result1 = add_allowed_email_host( + deps.as_mut(), + env.clone(), + auth_id, + "multi1.com".to_string(), + ); + assert!(result1.is_ok()); + + let result2 = add_allowed_email_host( + deps.as_mut(), + env.clone(), + auth_id, + "multi2.com".to_string(), + ); + assert!(result2.is_ok()); + + let updated_auth = AUTHENTICATORS.load(deps.as_ref().storage, auth_id).unwrap(); + match updated_auth { + Authenticator::ZKEmail { allowed_email_hosts, .. } => { + assert_eq!(allowed_email_hosts.len(), 3); + assert!(allowed_email_hosts.contains(&"single.com".to_string())); + assert!(allowed_email_hosts.contains(&"multi1.com".to_string())); + assert!(allowed_email_hosts.contains(&"multi2.com".to_string())); + } + _ => panic!("Expected ZKEmail authenticator"), + } + + // Test 13: Verify email_salt is preserved through updates + let updated_auth = AUTHENTICATORS.load(deps.as_ref().storage, auth_id).unwrap(); + match updated_auth { + Authenticator::ZKEmail { email_salt, .. } => { + assert_eq!(email_salt, "test_salt"); + } + _ => panic!("Expected ZKEmail authenticator"), + } + } } diff --git a/contracts/account/src/msg.rs b/contracts/account/src/msg.rs index f61a410d..3601ea1f 100644 --- a/contracts/account/src/msg.rs +++ b/contracts/account/src/msg.rs @@ -12,6 +12,18 @@ pub enum ExecuteMsg { AddAuthMethod { add_authenticator: AddAuthenticator }, RemoveAuthMethod { id: u8 }, Emit { data: String }, + UpdateAllowedEmailHosts { + id: u8, + allowed_email_hosts: Vec + }, + AddAllowedEmailHost { + id: u8, + email_host: String + }, + RemoveAllowedEmailHost { + id: u8, + email_host: String + }, } #[cw_serde] diff --git a/contracts/treasury/Cargo.toml b/contracts/treasury/Cargo.toml index 12bfa79e..5e857845 100644 --- a/contracts/treasury/Cargo.toml +++ b/contracts/treasury/Cargo.toml @@ -17,8 +17,5 @@ cosmwasm-std = { workspace = true } cw2 = { workspace = true } cw-storage-plus = { workspace = true } thiserror = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -schemars = { workspace = true } cosmos-sdk-proto = { workspace = true } url = { workspace = true } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..9dc8f2ac --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,53 @@ +services: + # Build the custom optimizer image + optimizer-build: + build: + context: . + dockerfile: docker/custom_coswam_optimiser.docker + platforms: + - linux/amd64 + platform: linux/amd64 + image: cosmwasm/optimizer-ssh:latest + command: echo "Image built successfully" + + # Service for optimizing the account contract + optimize-account: + platform: linux/amd64 + image: cosmwasm/optimizer-ssh:latest + volumes: + - ./:/code + - ~/.ssh:/root/.ssh:ro + working_dir: /code + command: contracts/account + environment: + - SSH_AUTH_SOCK=/ssh-agent + depends_on: + - optimizer-build + + # Service for optimizing the treasury contract + optimize-treasury: + platform: linux/amd64 + image: cosmwasm/optimizer-ssh:latest + volumes: + - ./:/code + - ~/.ssh:/root/.ssh:ro + working_dir: /code + command: contracts/treasury + environment: + - SSH_AUTH_SOCK=/ssh-agent + depends_on: + - optimizer-build + + # Service for optimizing the user_map contract + optimize-user-map: + platform: linux/amd64 + image: cosmwasm/optimizer-ssh:latest + volumes: + - ./:/code + - ~/.ssh:/root/.ssh:ro + working_dir: /code + command: contracts/user_map + environment: + - SSH_AUTH_SOCK=/ssh-agent + depends_on: + - optimizer-build \ No newline at end of file diff --git a/docker/custom_coswam_optimiser.docker b/docker/custom_coswam_optimiser.docker new file mode 100644 index 00000000..593a1a67 --- /dev/null +++ b/docker/custom_coswam_optimiser.docker @@ -0,0 +1,18 @@ +FROM cosmwasm/optimizer:0.17.0 AS base + +RUN echo "https://dl-cdn.alpinelinux.org/alpine/v3.21/main" > /etc/apk/repositories && \ + echo "https://dl-cdn.alpinelinux.org/alpine/v3.21/community" >> /etc/apk/repositories && \ + apk add --no-cache git openssh-client + +RUN mkdir -p /root/.ssh && chmod 700 /root/.ssh && \ + ssh-keyscan github.com >> /root/.ssh/known_hosts + +# Ensure cargo always uses CLI git for submodules and dependencies +ENV CARGO_NET_GIT_FETCH_WITH_CLI=true +RUN echo 'export CARGO_NET_GIT_FETCH_WITH_CLI=true' >> /etc/profile \ + && echo 'export CARGO_NET_GIT_FETCH_WITH_CLI=true' >> /root/.bashrc + +# Optional: set git config to use SSH instead of HTTPS (for submodules) +RUN git config --global url."ssh://git@github.com/".insteadOf "https://github.com/" + +ENTRYPOINT ["/usr/local/bin/optimize.sh"] \ No newline at end of file