diff --git a/Cargo.lock b/Cargo.lock index 7eaf65d6..a23aac9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,6 +92,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "arrayvec" version = "0.7.6" @@ -107,6 +113,17 @@ dependencies = [ "term", ] +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-trait" version = "0.1.83" @@ -216,6 +233,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "backoff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +dependencies = [ + "getrandom", + "instant", + "rand", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -267,6 +295,29 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "binread" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16598dfc8e6578e9b597d9910ba2e73618385dc9f4b1d43dd92c349d6be6418f" +dependencies = [ + "binread_derive", + "lazy_static", + "rustversion", +] + +[[package]] +name = "binread_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9672209df1714ee804b1f4d4f68c8eb2a90b1f7a07acf472f88ce198ef1fed" +dependencies = [ + "either", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -315,7 +366,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -325,10 +376,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ "arrayref", - "arrayvec", + "arrayvec 0.7.6", "constant_time_eq 0.3.1", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -344,7 +404,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "sha2", + "sha2 0.10.8", "tinyvec", ] @@ -396,6 +456,19 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cached" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8466736fe5dbcaf8b8ee24f9bbefe43c884dc3e9ff7178da70f55bffca1133c" +dependencies = [ + "ahash", + "hashbrown 0.14.5", + "instant", + "once_cell", + "thiserror", +] + [[package]] name = "camino" version = "1.1.9" @@ -405,6 +478,41 @@ dependencies = [ "serde", ] +[[package]] +name = "candid" +version = "0.10.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c30ee7f886f296b6422c0ff017e89dd4f831521dfdcc76f3f71aae1ce817222" +dependencies = [ + "anyhow", + "binread", + "byteorder", + "candid_derive", + "hex", + "ic_principal", + "leb128", + "num-bigint", + "num-traits", + "paste", + "pretty", + "serde", + "serde_bytes", + "stacker", + "thiserror", +] + +[[package]] +name = "candid_derive" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3de398570c386726e7a59d9887b68763c481477f9a043fb998a2e09d428df1a9" +dependencies = [ + "lazy_static", + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "cargo-platform" version = "0.1.8" @@ -459,7 +567,7 @@ dependencies = [ "rsa", "serde_json", "sha-1", - "sha2", + "sha2 0.10.8", "slog", "trust-dns-resolver", "wasm-bindgen", @@ -514,11 +622,11 @@ checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" dependencies = [ "bs58", "coins-core", - "digest", + "digest 0.10.7", "hmac", "k256", "serde", - "sha2", + "sha2 0.10.8", "thiserror", ] @@ -534,7 +642,7 @@ dependencies = [ "once_cell", "pbkdf2 0.12.2", "rand", - "sha2", + "sha2 0.10.8", "thiserror", ] @@ -547,13 +655,13 @@ dependencies = [ "base64 0.21.7", "bech32", "bs58", - "digest", + "digest 0.10.7", "generic-array", "hex", "ripemd", "serde", "serde_derive", - "sha2", + "sha2 0.10.8", "sha3", "thiserror", ] @@ -746,7 +854,7 @@ dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", - "digest", + "digest 0.10.7", "fiat-crypto", "rustc_version", "subtle", @@ -764,6 +872,19 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core", + "subtle-ng", + "zeroize", +] + [[package]] name = "data-encoding" version = "2.6.0" @@ -801,13 +922,22 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle", @@ -874,7 +1004,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature", @@ -891,6 +1021,21 @@ dependencies = [ "signature", ] +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core", + "serde", + "sha2 0.9.9", + "thiserror", + "zeroize", +] + [[package]] name = "ed25519-dalek" version = "2.1.1" @@ -900,7 +1045,7 @@ dependencies = [ "curve25519-dalek", "ed25519", "serde", - "sha2", + "sha2 0.10.8", "subtle", "zeroize", ] @@ -922,10 +1067,11 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", + "pem-rfc7468", "pkcs8", "rand_core", "sec1", @@ -1016,7 +1162,7 @@ checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ "aes", "ctr", - "digest", + "digest 0.10.7", "hex", "hmac", "pbkdf2 0.11.0", @@ -1024,7 +1170,7 @@ dependencies = [ "scrypt", "serde", "serde_json", - "sha2", + "sha2 0.10.8", "sha3", "thiserror", "uuid 0.8.2", @@ -1171,7 +1317,7 @@ version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82d80cc6ad30b14a48ab786523af33b37f28a8623fc06afd55324816ef18fb1f" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "bytes", "cargo_metadata", "chrono", @@ -1289,7 +1435,7 @@ dependencies = [ "eth-keystore", "ethers-core", "rand", - "sha2", + "sha2 0.10.8", "thiserror", "tracing", ] @@ -1337,6 +1483,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "eyre" version = "0.6.12" @@ -1702,13 +1858,19 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + [[package]] name = "halo2curves" version = "0.7.0" source = "git+https://github.com/privacy-scaling-explorations/halo2curves.git#8771fe5a5d54fc03e74dbc8915db5dad3ab46a83" dependencies = [ "blake2", - "digest", + "digest 0.10.7", "ff", "group", "halo2derive", @@ -1722,7 +1884,7 @@ dependencies = [ "rand", "rand_core", "rayon", - "sha2", + "sha2 0.10.8", "static_assertions", "subtle", "unroll", @@ -1840,7 +2002,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -2010,6 +2172,7 @@ dependencies = [ "tokio", "tokio-rustls 0.26.0", "tower-service", + "webpki-roots 0.26.6", ] [[package]] @@ -2083,6 +2246,139 @@ dependencies = [ "cc", ] +[[package]] +name = "ic-agent" +version = "0.37.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd3fdf5e5c4f4a9fe5ca612f0febd22dcb161d2f2b75b0142326732be5e4978" +dependencies = [ + "async-lock", + "backoff", + "cached", + "candid", + "ed25519-consensus", + "futures-util", + "hex", + "http 1.1.0", + "http-body 1.0.1", + "ic-certification", + "ic-transport-types", + "ic-verify-bls-signature", + "k256", + "leb128", + "p256", + "pem 3.0.4", + "pkcs8", + "rand", + "rangemap", + "reqwest 0.12.8", + "ring 0.17.8", + "rustls-webpki 0.102.8", + "sec1", + "serde", + "serde_bytes", + "serde_cbor", + "serde_repr", + "sha2 0.10.8", + "simple_asn1", + "thiserror", + "time", + "tokio", + "url", +] + +[[package]] +name = "ic-certification" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64ee3d8b6e81b51f245716d3e0badb63c283c00f3c9fb5d5219afc30b5bf821" +dependencies = [ + "hex", + "serde", + "serde_bytes", + "sha2 0.10.8", +] + +[[package]] +name = "ic-transport-types" +version = "0.37.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875dc4704780383112e8e8b5063a1b98de114321d0c7d3e7f635dcf360a57fba" +dependencies = [ + "candid", + "hex", + "ic-certification", + "leb128", + "serde", + "serde_bytes", + "serde_repr", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "ic-utils" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fa832296800758c9c921dd1704985ded6b3e6fbc3aee409727eb1f00d69a595" +dependencies = [ + "async-trait", + "candid", + "futures-util", + "ic-agent", + "once_cell", + "semver 1.0.23", + "serde", + "serde_bytes", + "sha2 0.10.8", + "strum", + "strum_macros", + "thiserror", + "time", + "tokio", +] + +[[package]] +name = "ic-verify-bls-signature" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d420b25c0091059f6c3c23a21427a81915e6e0aca3b79e0d403ed767f286a3b9" +dependencies = [ + "hex", + "ic_bls12_381", + "lazy_static", + "pairing", + "rand", + "sha2 0.10.8", +] + +[[package]] +name = "ic_bls12_381" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22c65787944f32af084dffd0c68c1e544237b76e215654ddea8cd9f527dd8b69" +dependencies = [ + "digest 0.10.7", + "ff", + "group", + "pairing", + "rand_core", + "subtle", +] + +[[package]] +name = "ic_principal" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1762deb6f7c8d8c2bdee4b6c5a47b60195b74e9b5280faa5ba29692f8e17429c" +dependencies = [ + "crc32fast", + "data-encoding", + "serde", + "sha2 0.10.8", + "thiserror", +] + [[package]] name = "idna" version = "0.2.3" @@ -2273,7 +2569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ "base64 0.21.7", - "pem", + "pem 1.1.1", "ring 0.16.20", "serde", "serde_json", @@ -2290,7 +2586,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2", + "sha2 0.10.8", "signature", ] @@ -2342,6 +2638,12 @@ dependencies = [ "spin 0.9.8", ] +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + [[package]] name = "libc" version = "0.2.159" @@ -2458,7 +2760,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest", + "digest 0.10.7", ] [[package]] @@ -2592,6 +2894,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", + "serde", ] [[package]] @@ -2694,13 +2997,19 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "open-fastrlp" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "auto_impl", "bytes", "ethereum-types", @@ -2769,6 +3078,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + [[package]] name = "pairing" version = "0.23.0" @@ -2784,7 +3105,7 @@ version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "bitvec", "byte-slice-cast", "impl-trait-for-tuples", @@ -2877,10 +3198,10 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest", + "digest 0.10.7", "hmac", "password-hash", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -2889,7 +3210,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest", + "digest 0.10.7", "hmac", ] @@ -2902,6 +3223,16 @@ dependencies = [ "base64 0.13.1", ] +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -2959,7 +3290,7 @@ checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" dependencies = [ "once_cell", "pest", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -3125,6 +3456,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "pretty" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55c4d17d994b637e2f4daf6e5dc5d660d209d5642377d675d7a1c3ab69fa579" +dependencies = [ + "arrayvec 0.5.2", + "typed-arena", + "unicode-width", +] + [[package]] name = "prettyplease" version = "0.2.22" @@ -3135,6 +3477,15 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "primitive-types" version = "0.12.2" @@ -3183,6 +3534,15 @@ dependencies = [ "unarray", ] +[[package]] +name = "psm" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +dependencies = [ + "cc", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -3195,6 +3555,54 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +[[package]] +name = "quinn" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.14", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +dependencies = [ + "bytes", + "rand", + "ring 0.17.8", + "rustc-hash", + "rustls 0.23.14", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.37" @@ -3255,6 +3663,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "rangemap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" + [[package]] name = "rayon" version = "1.10.0" @@ -3330,9 +3744,13 @@ version = "0.1.0" dependencies = [ "anyhow", "axum", + "candid", "chrono", "ethers", "handlebars", + "ic-agent", + "ic-utils", + "lazy_static", "regex", "relayer-utils", "reqwest 0.12.8", @@ -3419,7 +3837,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.25.4", "winreg", ] @@ -3432,6 +3850,7 @@ dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2 0.4.6", @@ -3450,7 +3869,10 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "quinn", + "rustls 0.23.14", "rustls-pemfile 2.2.0", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", @@ -3458,11 +3880,15 @@ dependencies = [ "system-configuration 0.6.1", "tokio", "tokio-native-tls", + "tokio-rustls 0.26.0", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", + "webpki-roots 0.26.6", "windows-registry", ] @@ -3522,7 +3948,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -3554,7 +3980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" dependencies = [ "const-oid", - "digest", + "digest 0.10.7", "num-bigint-dig", "num-integer", "num-traits", @@ -3562,7 +3988,7 @@ dependencies = [ "pkcs8", "rand_core", "serde", - "sha2", + "sha2 0.10.8", "signature", "spki", "subtle", @@ -3575,6 +4001,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -3622,6 +4054,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "once_cell", + "ring 0.17.8", "rustls-pki-types", "rustls-webpki 0.102.8", "subtle", @@ -3757,7 +4190,7 @@ dependencies = [ "hmac", "pbkdf2 0.11.0", "salsa20", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -3863,6 +4296,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.210" @@ -3896,6 +4348,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "serde_spanned" version = "0.6.8" @@ -3925,7 +4388,7 @@ checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -3936,7 +4399,20 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", ] [[package]] @@ -3947,7 +4423,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -3956,7 +4432,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest", + "digest 0.10.7", "keccak", ] @@ -3981,7 +4457,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core", ] @@ -4145,6 +4621,7 @@ dependencies = [ "atoi", "byteorder", "bytes", + "chrono", "crc", "crossbeam-queue", "either", @@ -4165,7 +4642,7 @@ dependencies = [ "percent-encoding", "serde", "serde_json", - "sha2", + "sha2 0.10.8", "smallvec", "sqlformat", "thiserror", @@ -4205,7 +4682,7 @@ dependencies = [ "quote", "serde", "serde_json", - "sha2", + "sha2 0.10.8", "sqlx-core", "sqlx-mysql", "sqlx-postgres", @@ -4227,8 +4704,9 @@ dependencies = [ "bitflags 2.6.0", "byteorder", "bytes", + "chrono", "crc", - "digest", + "digest 0.10.7", "dotenvy", "either", "futures-channel", @@ -4249,7 +4727,7 @@ dependencies = [ "rsa", "serde", "sha1", - "sha2", + "sha2 0.10.8", "smallvec", "sqlx-core", "stringprep", @@ -4270,6 +4748,7 @@ dependencies = [ "base64 0.22.1", "bitflags 2.6.0", "byteorder", + "chrono", "crc", "dotenvy", "etcetera", @@ -4289,7 +4768,7 @@ dependencies = [ "rand", "serde", "serde_json", - "sha2", + "sha2 0.10.8", "smallvec", "sqlx-core", "stringprep", @@ -4307,6 +4786,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" dependencies = [ "atoi", + "chrono", "flume", "futures-channel", "futures-core", @@ -4325,6 +4805,19 @@ dependencies = [ "uuid 1.10.0", ] +[[package]] +name = "stacker" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -4383,6 +4876,12 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "svm-rs" version = "0.3.5" @@ -4397,7 +4896,7 @@ dependencies = [ "semver 1.0.23", "serde", "serde_json", - "sha2", + "sha2 0.10.8", "thiserror", "url", "zip", @@ -4697,7 +5196,7 @@ dependencies = [ "tokio", "tokio-rustls 0.24.1", "tungstenite", - "webpki-roots", + "webpki-roots 0.25.4", ] [[package]] @@ -4902,6 +5401,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typenum" version = "1.17.0" @@ -4959,6 +5464,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -5168,6 +5679,19 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "wasm-streams" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.70" @@ -5184,6 +5708,15 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "webpki-roots" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "whoami" version = "1.5.2" diff --git a/packages/relayer/Cargo.toml b/packages/relayer/Cargo.toml index 2675f1bd..a9fa0964 100644 --- a/packages/relayer/Cargo.toml +++ b/packages/relayer/Cargo.toml @@ -8,7 +8,7 @@ anyhow = "1.0.89" axum = "0.7.7" serde = { version = "1.0.210", features = ["derive"] } serde_json = "1.0.128" -sqlx = { version = "0.8.2", features = ["postgres", "runtime-tokio", "migrate", "uuid", "time"] } +sqlx = { version = "0.8.2", features = ["postgres", "runtime-tokio", "migrate", "uuid", "time", "chrono"] } tokio = { version = "1.40.0", features = ["full"] } tower-http = { version = "0.6.1", features = ["cors"] } # relayer-utils = { git = "https://github.com/zkemail/relayer-utils.git", branch = "main" } @@ -23,3 +23,10 @@ ethers = "2.0.14" reqwest = { version = "0.12.8", features = ["json"] } handlebars = "6.1.0" regex = "1.11.0" +ic-agent = { version = "0.37.1", features = ["pem", "reqwest"] } +ic-utils = "0.37.0" +candid = "0.10.10" +lazy_static = "1.5.0" + +[build-dependencies] +ethers = "2.0.14" diff --git a/packages/relayer/build.rs b/packages/relayer/build.rs new file mode 100644 index 00000000..0da3c7f8 --- /dev/null +++ b/packages/relayer/build.rs @@ -0,0 +1,23 @@ +use ethers::contract::Abigen; + +fn main() { + Abigen::new( + "EmailAuth", + "../contracts/artifacts/EmailAuth.sol/EmailAuth.json", + ) + .unwrap() + .generate() + .unwrap() + .write_to_file("./src/abis/email_auth.rs") + .unwrap(); + + Abigen::new( + "ECDSAOwnedDKIMRegistry", + "../contracts/artifacts/ECDSAOwnedDKIMRegistry.sol/ECDSAOwnedDKIMRegistry.json", + ) + .unwrap() + .generate() + .unwrap() + .write_to_file("./src/abis/ecdsa_owned_dkim_registry.rs") + .unwrap(); +} diff --git a/packages/relayer/email_templates/acknowledgement_template.html b/packages/relayer/email_templates/acknowledgement_template.html index 6c720aa7..729f7a13 100644 --- a/packages/relayer/email_templates/acknowledgement_template.html +++ b/packages/relayer/email_templates/acknowledgement_template.html @@ -117,7 +117,7 @@ > - Hi, {{userEmailAddr}}! + Hi, diff --git a/packages/relayer/email_templates/command_template.html b/packages/relayer/email_templates/command_template.html index 91ad7ce2..04053475 100644 --- a/packages/relayer/email_templates/command_template.html +++ b/packages/relayer/email_templates/command_template.html @@ -117,7 +117,7 @@ > - Hi, {{userEmailAddr}}! + Hi, diff --git a/packages/relayer/email_templates/completion_template.html b/packages/relayer/email_templates/completion_template.html index 3a4b2e97..cc39f73e 100644 --- a/packages/relayer/email_templates/completion_template.html +++ b/packages/relayer/email_templates/completion_template.html @@ -117,14 +117,12 @@ > - Hi, {{userEmailAddr}}! + Hi, - {{body}} -

- Your request ID is #{{requestId}} is now complete. + Your request ID is {{requestId}} is now complete. diff --git a/packages/relayer/migrations/20241008135456_init.up.sql b/packages/relayer/migrations/20241008135456_init.up.sql index bf18f5b3..7b183059 100644 --- a/packages/relayer/migrations/20241008135456_init.up.sql +++ b/packages/relayer/migrations/20241008135456_init.up.sql @@ -2,12 +2,18 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -CREATE TYPE status_enum AS ENUM ('Request received', 'Email sent', 'Email response received', 'Proving', 'Performing on chain transaction', 'Finished'); +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'status_enum') THEN + CREATE TYPE status_enum AS ENUM ('Request received', 'Processing', 'Completed', 'Failed'); + END IF; +END $$; CREATE TABLE IF NOT EXISTS requests ( id UUID PRIMARY KEY NOT NULL DEFAULT (uuid_generate_v4()), status status_enum NOT NULL DEFAULT 'Request received', - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + email_tx_auth JSONB NOT NULL ); CREATE TABLE IF NOT EXISTS expected_replies ( diff --git a/packages/relayer/src/chain.rs b/packages/relayer/src/chain.rs new file mode 100644 index 00000000..3d2fa6b8 --- /dev/null +++ b/packages/relayer/src/chain.rs @@ -0,0 +1,207 @@ +use std::collections::HashMap; + +use crate::*; +use abi::{Abi, Token}; +use abis::{ECDSAOwnedDKIMRegistry, EmailAuth, EmailAuthMsg, EmailProof}; +use anyhow::anyhow; +use config::ChainConfig; +use ethers::prelude::*; +use ethers::signers::Signer; +use ethers::utils::hex; +use model::RequestModel; +use statics::SHARED_MUTEX; + +const CONFIRMATIONS: usize = 1; + +type SignerM = SignerMiddleware, LocalWallet>; + +/// Represents a client for interacting with the blockchain. +#[derive(Debug, Clone)] +pub struct ChainClient { + pub client: Arc, +} + +impl ChainClient { + /// Sets up a new ChainClient. + /// + /// # Returns + /// + /// A `Result` containing the new `ChainClient` if successful, or an error if not. + pub async fn setup(chain: String, chains: HashMap) -> Result { + let chain_config = chains + .get(&chain) + .ok_or_else(|| anyhow!("Chain configuration not found"))?; + let wallet: LocalWallet = chain_config.private_key.parse()?; + let provider = Provider::::try_from(chain_config.rpc_url.clone())?; + + // Create a new SignerMiddleware with the provider and wallet + let client = Arc::new(SignerMiddleware::new( + provider, + wallet.with_chain_id(chain_config.chain_id), + )); + + Ok(Self { client }) + } + + /// Sets the DKIM public key hash. + /// + /// # Arguments + /// + /// * `selector` - The selector string. + /// * `domain_name` - The domain name. + /// * `public_key_hash` - The public key hash as a 32-byte array. + /// * `signature` - The signature as Bytes. + /// * `dkim` - The ECDSA Owned DKIM Registry. + /// + /// # Returns + /// + /// A `Result` containing the transaction hash as a String if successful, or an error if not. + pub async fn set_dkim_public_key_hash( + &self, + selector: String, + domain_name: String, + public_key_hash: [u8; 32], + signature: Bytes, + dkim: ECDSAOwnedDKIMRegistry, + ) -> Result { + // Mutex is used to prevent nonce conflicts. + let mut mutex = SHARED_MUTEX + .lock() + .map_err(|e| anyhow::anyhow!("Mutex poisoned: {}", e))?; + *mutex += 1; + + // Call the contract method + let call = dkim.set_dkim_public_key_hash(selector, domain_name, public_key_hash, signature); + let tx = call.send().await?; + + // Wait for the transaction to be confirmed + let receipt = tx + .log() + .confirmations(CONFIRMATIONS) + .await? + .ok_or(anyhow!("No receipt"))?; + + // Format the transaction hash + let tx_hash = receipt.transaction_hash; + let tx_hash = format!("0x{}", hex::encode(tx_hash.as_bytes())); + Ok(tx_hash) + } + + /// Checks if a DKIM public key hash is valid. + /// + /// # Arguments + /// + /// * `domain_name` - The domain name. + /// * `public_key_hash` - The public key hash as a 32-byte array. + /// * `dkim` - The ECDSA Owned DKIM Registry. + /// + /// # Returns + /// + /// A `Result` containing a boolean indicating if the hash is valid. + pub async fn check_if_dkim_public_key_hash_valid( + &self, + domain_name: ::std::string::String, + public_key_hash: [u8; 32], + dkim: ECDSAOwnedDKIMRegistry, + ) -> Result { + // Call the contract method to check if the hash is valid + let is_valid = dkim + .is_dkim_public_key_hash_valid(domain_name, public_key_hash) + .call() + .await?; + Ok(is_valid) + } + + /// Gets the DKIM from an email auth address. + /// + /// # Arguments + /// + /// * `email_auth_addr` - The email auth address as a string. + /// + /// # Returns + /// + /// A `Result` containing the ECDSA Owned DKIM Registry if successful, or an error if not. + pub async fn get_dkim_from_email_auth( + &self, + email_auth_address: Address, + ) -> Result, anyhow::Error> { + // Create a new EmailAuth contract instance + let contract = EmailAuth::new(email_auth_address, self.client.clone()); + + // Call the dkim_registry_addr method to get the DKIM registry address + let dkim = contract.dkim_registry_addr().call().await?; + + // Create and return a new ECDSAOwnedDKIMRegistry instance + Ok(ECDSAOwnedDKIMRegistry::new(dkim, self.client.clone())) + } + + pub async fn call(&self, request: RequestModel, email_auth_msg: EmailAuthMsg) -> Result<()> { + let abi = Abi { + functions: vec![request.email_tx_auth.function_abi.clone()] + .into_iter() + .map(|f| (f.name.clone(), vec![f])) + .collect(), + events: Default::default(), + constructor: None, + receive: false, + fallback: false, + errors: Default::default(), + }; + + let contract = Contract::new( + request.email_tx_auth.contract_address, + abi, + self.client.clone(), + ); + let function = request.email_tx_auth.function_abi; + let remaining_args = request.email_tx_auth.remaining_args; + + // Assuming remaining_args is a Vec of some type that can be converted to tokens + let mut tokens = email_auth_msg.to_tokens(); + + // Convert remaining_args to tokens and add them to the tokens vector + for arg in remaining_args { + // Convert each arg to a Token. This conversion depends on the type of arg. + // For example, if arg is a string, you might use Token::String(arg). + // Adjust the conversion based on the actual type of arg. + let token = Token::from(arg); // Replace with appropriate conversion + tokens.push(token); + } + + // Now you can use the tokens vector to call the contract function + let call = contract.method::<_, ()>(&function.name, tokens)?; + let _result = call.send().await?; + Ok(()) + } +} + +impl EmailAuthMsg { + pub fn to_tokens(&self) -> Vec { + vec![ + Token::Uint(self.template_id), + Token::Array( + self.command_params + .iter() + .map(|param| Token::Bytes(param.clone().to_vec())) + .collect(), + ), + Token::Uint(self.skipped_command_prefix), + Token::Tuple(self.proof.to_tokens()), + ] + } +} + +impl EmailProof { + pub fn to_tokens(&self) -> Vec { + vec![ + Token::String(self.domain_name.clone()), + Token::FixedBytes(self.public_key_hash.to_vec()), + Token::Uint(self.timestamp), + Token::String(self.masked_command.clone()), + Token::FixedBytes(self.email_nullifier.to_vec()), + Token::FixedBytes(self.account_salt.to_vec()), + Token::Bool(self.is_code_exist), + Token::Bytes(self.proof.clone().to_vec()), + ] + } +} diff --git a/packages/relayer/src/command.rs b/packages/relayer/src/command.rs new file mode 100644 index 00000000..5171e407 --- /dev/null +++ b/packages/relayer/src/command.rs @@ -0,0 +1,80 @@ +use anyhow::{anyhow, Result}; +use ethers::types::{Bytes, U256}; +use relayer_utils::{ + command_templates, extract_template_vals_from_command, u256_to_bytes32_little, +}; + +use crate::{constants::COMMAND_FIELDS, model::RequestModel}; + +pub fn parse_command_template(template: &str, params: Vec) -> String { + let mut parsed_string = template.to_string(); + let mut param_iter = params.iter(); + + while let Some(value) = param_iter.next() { + if let Some(start) = parsed_string.find('{') { + if let Some(end) = parsed_string[start..].find('}') { + parsed_string.replace_range(start..start + end + 1, value); + } + } + } + + parsed_string +} + +/// Retrieves and encodes the command parameters for the email authentication request. +/// +/// # Arguments +/// +/// * `params` - The `EmailRequestContext` containing request details. +/// +/// # Returns +/// +/// A `Result` containing a vector of encoded command parameters or an `EmailError`. +pub async fn get_encoded_command_params(email: &str, request: RequestModel) -> Result> { + let command_template = request + .email_tx_auth + .command_template + .split_whitespace() + .map(String::from) + .collect(); + + let command_params = extract_template_vals_from_command(email, command_template)?; + + let command_params_encoded = command_params + .iter() + .map(|param| { + param + .abi_encode(None) + .map_err(|e| anyhow::anyhow!(e.to_string())) + }) + .collect::>>()?; + + Ok(command_params_encoded) +} + +/// Extracts the masked command from public signals. +/// +/// # Arguments +/// +/// * `public_signals` - The vector of public signals. +/// * `start_idx` - The starting index for command extraction. +/// +/// # Returns +/// +/// A `Result` containing the masked command as a `String` or an error. +pub fn get_masked_command(public_signals: Vec, start_idx: usize) -> Result { + // Gather signals from start_idx to start_idx + COMMAND_FIELDS + let command_bytes: Vec = public_signals + .iter() + .skip(start_idx) + .take(COMMAND_FIELDS) + .take_while(|&signal| *signal != U256::zero()) + .flat_map(u256_to_bytes32_little) + .collect(); + + // Bytes to string, removing null bytes + let command = String::from_utf8(command_bytes.into_iter().filter(|&b| b != 0u8).collect()) + .map_err(|e| anyhow!("Failed to convert bytes to string: {}", e))?; + + Ok(command) +} diff --git a/packages/relayer/src/constants.rs b/packages/relayer/src/constants.rs new file mode 100644 index 00000000..4dd6a3b2 --- /dev/null +++ b/packages/relayer/src/constants.rs @@ -0,0 +1,4 @@ +pub const SHA_PRECOMPUTE_SELECTOR: &str = r#"(<(=\r\n)?d(=\r\n)?i(=\r\n)?v(=\r\n)? (=\r\n)?i(=\r\n)?d(=\r\n)?=3D(=\r\n)?\"(=\r\n)?[^\"]*(=\r\n)?z(=\r\n)?k(=\r\n)?e(=\r\n)?m(=\r\n)?a(=\r\n)?i(=\r\n)?l(=\r\n)?[^\"]*(=\r\n)?\"(=\r\n)?[^>]*(=\r\n)?>(=\r\n)?)(=\r\n)?([^<>\/]+)(<(=\r\n)?\/(=\r\n)?d(=\r\n)?i(=\r\n)?v(=\r\n)?>(=\r\n)?)"#; + +pub const DOMAIN_FIELDS: usize = 9; +pub const COMMAND_FIELDS: usize = 20; diff --git a/packages/relayer/src/dkim.rs b/packages/relayer/src/dkim.rs new file mode 100644 index 00000000..6b62d21a --- /dev/null +++ b/packages/relayer/src/dkim.rs @@ -0,0 +1,215 @@ +use std::env; + +use anyhow::anyhow; +use chain::ChainClient; +use ethers::types::Address; +use ethers::utils::hex; +use relayer_utils::fr_to_bytes32; +use relayer_utils::public_key_hash; +use relayer_utils::ParsedEmail; +use relayer_utils::LOG; + +use crate::*; +use candid::CandidType; +use ethers::types::Bytes; +use ethers::utils::hex::FromHex; +use ic_agent::agent::http_transport::ReqwestTransport; +use ic_agent::agent::*; +use ic_agent::identity::*; +use ic_utils::canister::*; + +use serde::Deserialize; + +/// Represents a client for interacting with the DKIM Oracle. +#[derive(Debug, Clone)] +pub struct DkimOracleClient<'a> { + /// The canister used for communication + pub canister: Canister<'a>, +} + +/// Represents a signed DKIM public key. +#[derive(Default, CandidType, Deserialize, Debug, Clone)] +pub struct SignedDkimPublicKey { + /// The selector for the DKIM key + pub selector: String, + /// The domain for the DKIM key + pub domain: String, + /// The signature of the DKIM key + pub signature: String, + /// The public key + pub public_key: String, + /// The hash of the public key + pub public_key_hash: String, +} + +impl<'a> DkimOracleClient<'a> { + /// Generates an agent for the DKIM Oracle Client. + /// + /// # Arguments + /// + /// * `pem_path` - The path to the PEM file. + /// * `replica_url` - The URL of the replica. + /// + /// # Returns + /// + /// An `anyhow::Result`. + pub fn gen_agent(pem_path: &str, replica_url: &str) -> anyhow::Result { + // Create identity from PEM file + let identity = Secp256k1Identity::from_pem_file(pem_path)?; + + // Create transport using the replica URL + let transport = ReqwestTransport::create(replica_url)?; + + // Build and return the agent + let agent = AgentBuilder::default() + .with_identity(identity) + .with_transport(transport) + .build()?; + Ok(agent) + } + + /// Creates a new DkimOracleClient. + /// + /// # Arguments + /// + /// * `canister_id` - The ID of the canister. + /// * `agent` - The agent to use for communication. + /// + /// # Returns + /// + /// An `anyhow::Result`. + pub fn new(canister_id: &str, agent: &'a Agent) -> anyhow::Result { + // Build the canister using the provided ID and agent + let canister = CanisterBuilder::new() + .with_canister_id(canister_id) + .with_agent(agent) + .build()?; + Ok(Self { canister }) + } + + /// Requests a signature for a DKIM public key. + /// + /// # Arguments + /// + /// * `selector` - The selector for the DKIM key. + /// * `domain` - The domain for the DKIM key. + /// + /// # Returns + /// + /// An `anyhow::Result`. + pub async fn request_signature( + &self, + selector: &str, + domain: &str, + ) -> anyhow::Result { + // Build the request to sign the DKIM public key + let request = self + .canister + .update("sign_dkim_public_key") + .with_args((selector, domain)) + .build::<(Result,)>(); + + // Call the canister and wait for the response + let response = request + .call_and_wait_one::>() + .await? + .map_err(|e| anyhow!(format!("Error from canister: {:?}", e)))?; + + Ok(response) + } +} + +/// Checks and updates the DKIM for a given email. +/// +/// # Arguments +/// +/// * `email` - The email address. +/// * `parsed_email` - The parsed email data. +/// * `controller_eth_addr` - The Ethereum address of the controller. +/// * `wallet_addr` - The address of the wallet. +/// * `account_salt` - The salt for the account. +/// +/// # Returns +/// +/// A `Result<()>`. +pub async fn check_and_update_dkim( + parsed_email: &ParsedEmail, + email_auth_addr: Address, + chain_client: ChainClient, + relayer_state: RelayerState, +) -> Result<()> { + // Generate public key hash + let mut public_key_n = parsed_email.public_key.clone(); + public_key_n.reverse(); + let public_key_hash = public_key_hash(&public_key_n)?; + info!(LOG, "public_key_hash {:?}", public_key_hash); + + // Get email domain + let domain = parsed_email.get_email_domain()?; + info!(LOG, "domain {:?}", domain); + + // Get DKIM + let dkim = chain_client + .get_dkim_from_email_auth(email_auth_addr) + .await?; + + info!(LOG, "dkim {:?}", dkim); + + // Check if DKIM public key hash is valid + if chain_client + .check_if_dkim_public_key_hash_valid( + domain.clone(), + fr_to_bytes32(&public_key_hash)?, + dkim.clone(), + ) + .await? + { + info!(LOG, "public key registered"); + return Ok(()); + } + + // Get selector using regex + let regex_pattern = r"((\r\n)|^)dkim-signature:([a-z]+=[^;]+; )+s=([0-9a-z_-]+);"; + let re = regex::Regex::new(regex_pattern).map_err(|e| anyhow!("Invalid regex: {}", e))?; + + let selector = re + .captures(&parsed_email.canonicalized_header) + .and_then(|caps| caps.get(4)) + .map(|m| m.as_str().to_string()) + .ok_or_else(|| anyhow!("Failed to extract selector using regex"))?; + + info!(LOG, "selector {}", selector); + + // Generate IC agent and create oracle client + let ic_agent = DkimOracleClient::gen_agent( + &env::var(relayer_state.config.path.pem).unwrap(), + &env::var(relayer_state.config.icp.ic_replica_url).unwrap(), + )?; + let oracle_client = DkimOracleClient::new( + &env::var(relayer_state.config.icp.canister_id).unwrap(), + &ic_agent, + )?; + + // Request signature from oracle + let oracle_result = oracle_client.request_signature(&selector, &domain).await?; + info!(LOG, "DKIM oracle result {:?}", oracle_result); + + // Process oracle response + let public_key_hash = hex::decode(&oracle_result.public_key_hash[2..])?; + info!(LOG, "public_key_hash from oracle {:?}", public_key_hash); + let signature = Bytes::from_hex(&oracle_result.signature[2..])?; + info!(LOG, "signature {:?}", signature); + + // Set DKIM public key hash + let tx_hash = chain_client + .set_dkim_public_key_hash( + selector, + domain, + TryInto::<[u8; 32]>::try_into(public_key_hash).unwrap(), + signature, + dkim, + ) + .await?; + info!(LOG, "DKIM registry updated {:?}", tx_hash); + Ok(()) +} diff --git a/packages/relayer/src/handler.rs b/packages/relayer/src/handler.rs index 53002d87..90ff686a 100644 --- a/packages/relayer/src/handler.rs +++ b/packages/relayer/src/handler.rs @@ -1,17 +1,23 @@ use std::sync::Arc; -use axum::{extract::State, http::StatusCode, response::IntoResponse, Json}; +use axum::{ + body::Body, + extract::State, + http::{request, StatusCode}, + response::IntoResponse, + Json, +}; use regex::Regex; use relayer_utils::{field_to_hex, ParsedEmail, LOG}; use serde_json::{json, Value}; -use slog::info; +use slog::{error, info, trace}; use uuid::Uuid; use crate::{ - mail::handle_email_event, - model::{create_request, update_request, RequestStatus}, + command::parse_command_template, + mail::{handle_email, handle_email_event, EmailEvent}, + model::{create_request, get_request, update_request, RequestStatus}, schema::EmailTxAuthSchema, - utils::parse_command_template, RelayerState, }; @@ -32,14 +38,16 @@ pub async fn submit_handler( ) -> Result)> { info!(LOG, "Payload: {:?}", body); - let uuid = create_request(&relayer_state.db).await.map_err(|e| { - ( - axum::http::StatusCode::INTERNAL_SERVER_ERROR, - axum::Json(json!({"error": e.to_string()})), - ) - })?; + let uuid = create_request(&relayer_state.db, &body) + .await + .map_err(|e| { + ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + axum::Json(json!({"error": e.to_string()})), + ) + })?; - let command = parse_command_template(&body.command_template, &body.command_params); + let command = parse_command_template(&body.command_template, body.command_params); let account_code = if body.code_exists_in_email { let hex_code = field_to_hex(&body.account_code.clone().0); @@ -49,7 +57,7 @@ pub async fn submit_handler( }; handle_email_event( - crate::mail::EmailEvent::Command { + EmailEvent::Command { request_id: uuid, email_address: body.email_address.clone(), command, @@ -79,8 +87,25 @@ pub async fn submit_handler( pub async fn receive_email_handler( State(relayer_state): State>, - body: String, + body: axum::body::Body, ) -> Result)> { + // Convert the body into a string + let bytes = axum::body::to_bytes(body, usize::MAX) + .await + .map_err(|err| { + ( + StatusCode::BAD_REQUEST, + axum::Json(json!({"error": format!("Failed to read request body: {}", err)})), + ) + })?; + + let email = String::from_utf8(bytes.to_vec()).map_err(|err| { + ( + StatusCode::BAD_REQUEST, + axum::Json(json!({"error": format!("Invalid UTF-8 sequence: {}", err)})), + ) + })?; + // Define the regex pattern for UUID let uuid_regex = Regex::new( r"(Your request ID is )([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})", @@ -88,7 +113,7 @@ pub async fn receive_email_handler( .unwrap(); // Attempt to find a UUID in the body - let captures = uuid_regex.captures(&body); + let captures = uuid_regex.captures(&email); let request_id = captures .and_then(|caps| caps.get(2).map(|m| m.as_str())) @@ -124,18 +149,95 @@ pub async fn receive_email_handler( })?; // Log the received body - info!(LOG, "Received email body: {:?}", body); + info!(LOG, "Received email: {:?}", email); - let parsed_email = ParsedEmail::new_from_raw_email(&body).await.map_err(|e| { + let parsed_email = ParsedEmail::new_from_raw_email(&email).await.map_err(|e| { // Convert the error to the expected type ( reqwest::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(json!({"error": e.to_string()})), ) })?; + let from_addr = match parsed_email.get_from_addr() { + Ok(addr) => addr, + Err(e) => { + return Err(( + reqwest::StatusCode::INTERNAL_SERVER_ERROR, + axum::Json(json!({"error": e.to_string()})), + )) + } + }; + let original_subject = match parsed_email.get_subject_all() { + Ok(subject) => subject, + Err(e) => { + return Err(( + reqwest::StatusCode::INTERNAL_SERVER_ERROR, + axum::Json(json!({"error": e.to_string()})), + )) + } + }; + + // Send acknowledgment email + match handle_email_event( + EmailEvent::Ack { + email_addr: from_addr.clone(), + command: parsed_email.get_command(false).unwrap_or_default(), + original_message_id: parsed_email.get_message_id().ok(), + original_subject, + }, + (*relayer_state).clone(), + ) + .await + { + Ok(_) => { + trace!(LOG, "Ack email event sent"); + } + Err(e) => { + error!(LOG, "Error handling email event: {:?}", e); + } + } + + let request = get_request(&relayer_state.db, request_id) + .await + .map_err(|e| { + // Convert the error to the expected type + ( + reqwest::StatusCode::INTERNAL_SERVER_ERROR, + axum::Json(json!({"error": e.to_string()})), + ) + })?; - // Process the body as needed - // For example, you might want to parse it or pass it to another function + // Process the email + match handle_email(email, request, (*relayer_state).clone()).await { + Ok(event) => match handle_email_event(event, (*relayer_state).clone()).await { + Ok(_) => {} + Err(e) => { + error!(LOG, "Error handling email event: {:?}", e); + } + }, + Err(e) => { + error!(LOG, "Error handling email: {:?}", e); + let original_subject = parsed_email + .get_subject_all() + .unwrap_or("Unknown Error".to_string()); + match handle_email_event( + EmailEvent::Error { + email_addr: from_addr, + error: e.to_string(), + original_subject, + original_message_id: parsed_email.get_message_id().ok(), + }, + (*relayer_state).clone(), + ) + .await + { + Ok(_) => {} + Err(e) => { + error!(LOG, "Error handling email event: {:?}", e); + } + } + } + } let response = json!({ "status": "success", diff --git a/packages/relayer/src/mail.rs b/packages/relayer/src/mail.rs index 4ca859a1..17b41d3d 100644 --- a/packages/relayer/src/mail.rs +++ b/packages/relayer/src/mail.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; use anyhow::Result; +use ethers::types::U256; use handlebars::Handlebars; use relayer_utils::ParsedEmail; use serde::{Deserialize, Serialize}; @@ -10,8 +11,12 @@ use tokio::fs::read_to_string; use uuid::Uuid; use crate::{ - config::PathConfig, - model::{insert_expected_reply, is_valid_reply, update_request, RequestStatus}, + abis::{EmailAuthMsg, EmailProof}, + chain::ChainClient, + command::get_encoded_command_params, + dkim::check_and_update_dkim, + model::{insert_expected_reply, is_valid_reply, update_request, RequestModel, RequestStatus}, + prove::generate_email_proof, RelayerState, }; @@ -52,7 +57,12 @@ pub enum EmailEvent { original_message_id: Option, original_subject: String, }, - Completion {}, + Completion { + email_addr: String, + request_id: Uuid, + original_subject: String, + original_message_id: Option, + }, Error { email_addr: String, error: String, @@ -96,7 +106,6 @@ pub async fn handle_email_event(event: EmailEvent, relayer_state: RelayerState) // Prepare data for HTML rendering let render_data = serde_json::json!({ - "userEmailAddr": email_address, "body": body, "requestId": request_id, "command": command, @@ -124,79 +133,104 @@ pub async fn handle_email_event(event: EmailEvent, relayer_state: RelayerState) update_request(&relayer_state.db, request_id, RequestStatus::EmailSent).await?; } + EmailEvent::Completion { + email_addr, + request_id, + original_subject, + original_message_id, + } => { + let subject = format!("Re: {}", original_subject); + let body_plain = format!("Your request ID is #{} is now complete.", request_id); + + // Prepare data for HTML rendering + let render_data = serde_json::json!({ + "requestId": request_id, + }); + let body_html = render_html( + "acceptance_success.html", + render_data, + relayer_state.clone(), + ) + .await?; + + // Create and send the email + let email = EmailMessage { + to: email_addr, + subject: subject.to_string(), + reference: original_message_id.clone(), + reply_to: original_message_id, + body_plain, + body_html, + body_attachments: None, + }; + + send_email(email, None, relayer_state).await?; + } EmailEvent::Ack { email_addr, command, original_message_id, original_subject, - } => todo!(), - EmailEvent::Completion {} => todo!(), + } => { + let body_plain = format!( + "Hi {}!\nYour email with the command {} is received.", + email_addr, command + ); + // Prepare data for HTML rendering + let render_data = serde_json::json!({"request": command}); + let body_html = render_html( + "acknowledgement_template.html", + render_data, + relayer_state.clone(), + ) + .await?; + let subject = format!("Re: {}", original_subject); + // Create and send the email + let email = EmailMessage { + to: email_addr, + subject, + body_plain, + body_html, + reference: original_message_id.clone(), + reply_to: original_message_id, + body_attachments: None, + }; + send_email(email, None, relayer_state).await?; + } EmailEvent::Error { email_addr, error, original_subject, original_message_id, - } => todo!(), // EmailEvent::Ack { - // email_addr, - // command, - // original_message_id, - // original_subject, - // } => { - // let body_plain = format!( - // "Hi {}!\nYour email with the command {} is received.", - // email_addr, command - // ); - // // Prepare data for HTML rendering - // let render_data = serde_json::json!({"userEmailAddr": email_addr, "request": command}); - // let body_html = render_html("acknowledgement.html", render_data).await?; - // let subject = format!("Re: {}", original_subject); - // // Create and send the email - // let email = EmailMessage { - // to: email_addr, - // subject, - // body_plain, - // body_html, - // reference: original_message_id.clone(), - // reply_to: original_message_id, - // body_attachments: None, - // }; - // send_email(email, None).await?; - // } - // EmailEvent::Completion {} => {} - // EmailEvent::Error { - // email_addr, - // error, - // original_subject, - // original_message_id, - // } => { - // let subject = format!("Re: {}", original_subject); - - // let body_plain = format!( - // "An error occurred while processing your request. \ - // Error: {}", - // error - // ); - - // // Prepare data for HTML rendering - // let render_data = serde_json::json!({ - // "error": error, - // "userEmailAddr": email_addr, - // }); - // let body_html = render_html("error.html", render_data).await?; - - // // Create and send the email - // let email = EmailMessage { - // to: email_addr, - // subject, - // reference: original_message_id.clone(), - // reply_to: original_message_id, - // body_plain, - // body_html, - // body_attachments: None, - // }; - - // send_email(email, None).await?; - // } + } => { + let subject = format!("Re: {}", original_subject); + + let body_plain = format!( + "An error occurred while processing your request. \ + Error: {}", + error + ); + + // Prepare data for HTML rendering + let render_data = serde_json::json!({ + "error": error, + "userEmailAddr": email_addr, + }); + let body_html = render_html("error.html", render_data, relayer_state.clone()).await?; + + // Create and send the email + let email = EmailMessage { + to: email_addr, + subject, + reference: original_message_id.clone(), + reply_to: original_message_id, + body_plain, + body_html, + body_attachments: None, + }; + + send_email(email, None, relayer_state).await?; + } } Ok(()) @@ -332,3 +366,61 @@ pub async fn check_is_valid_request(email: &ParsedEmail, pool: &PgPool) -> Resul let is_valid = is_valid_reply(pool, &reply_message_id).await?; Ok(is_valid) } + +pub async fn handle_email( + email: String, + request: RequestModel, + relayer_state: RelayerState, +) -> Result { + let parsed_email = ParsedEmail::new_from_raw_email(&email).await?; + + let chain_client = ChainClient::setup( + request.clone().email_tx_auth.chain, + relayer_state.clone().config.chains, + ) + .await?; + + check_and_update_dkim( + &parsed_email, + request.email_tx_auth.email_auth_contract_address, + chain_client.clone(), + relayer_state.clone(), + ) + .await?; + + let email_auth_msg = get_email_auth_msg(&email, request.clone(), relayer_state).await?; + + chain_client.call(request.clone(), email_auth_msg).await?; + + Ok(EmailEvent::Completion { + email_addr: parsed_email.get_from_addr()?, + request_id: request.id, + original_subject: parsed_email.get_subject_all()?, + original_message_id: parsed_email.get_message_id().ok(), + }) +} + +/// Generates the email authentication message. +/// +/// # Arguments +/// +/// * `params` - The `EmailRequestContext` containing request details. +/// +/// # Returns +/// +/// A `Result` containing the `EmailAuthMsg`, `EmailProof`, and account salt, or an `EmailError`. +async fn get_email_auth_msg( + email: &str, + request: RequestModel, + relayer_state: RelayerState, +) -> Result { + let command_params_encoded = get_encoded_command_params(email, request.clone()).await?; + let email_proof = generate_email_proof(email, request.clone(), relayer_state).await?; + let email_auth_msg = EmailAuthMsg { + template_id: request.email_tx_auth.template_id.into(), + command_params: command_params_encoded, + skipped_command_prefix: U256::zero(), + proof: email_proof, + }; + Ok(email_auth_msg) +} diff --git a/packages/relayer/src/main.rs b/packages/relayer/src/main.rs index ca490f21..e931a39d 100644 --- a/packages/relayer/src/main.rs +++ b/packages/relayer/src/main.rs @@ -1,10 +1,16 @@ +mod abis; +mod chain; +mod command; mod config; +mod constants; +mod dkim; mod handler; mod mail; mod model; +mod prove; mod route; mod schema; -mod utils; +mod statics; use std::sync::Arc; diff --git a/packages/relayer/src/model.rs b/packages/relayer/src/model.rs index 3070c8a9..b2f27f64 100644 --- a/packages/relayer/src/model.rs +++ b/packages/relayer/src/model.rs @@ -1,15 +1,23 @@ +use std::fmt::Display; + use anyhow::{Error, Ok, Result}; +use chrono::{DateTime, NaiveDateTime, Utc}; use serde::{Deserialize, Serialize}; -use sqlx::{FromRow, PgPool, Row}; +use sqlx::types::Json; +use sqlx::{FromRow, PgPool}; use uuid::Uuid; -#[derive(Debug, FromRow, Deserialize, Serialize)] +use crate::schema::EmailTxAuthSchema; + +#[derive(Debug, FromRow, Deserialize, Serialize, Clone)] #[allow(non_snake_case)] pub struct RequestModel { pub id: Uuid, pub status: String, #[serde(rename = "updatedAt")] - pub updated_at: Option>, + pub updated_at: Option, + #[serde(rename = "emailTxAuth")] + pub email_tx_auth: EmailTxAuthSchema, } #[derive(Debug, FromRow, Deserialize, Serialize)] @@ -39,10 +47,32 @@ pub enum RequestStatus { Finished, } -pub async fn create_request(pool: &PgPool) -> Result { - let query_result = sqlx::query!("INSERT INTO requests DEFAULT VALUES RETURNING id") - .fetch_one(pool) - .await?; +impl std::fmt::Display for RequestStatus { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for String { + fn from(status: RequestStatus) -> Self { + status.to_string() + } +} + +impl From> for EmailTxAuthSchema { + fn from(json: sqlx::types::Json) -> Self { + json.0 + } +} + +pub async fn create_request(pool: &PgPool, email_tx_auth: &EmailTxAuthSchema) -> Result { + // Assuming the database column is of type JSONB and can directly accept the struct + let query_result = sqlx::query!( + "INSERT INTO requests (email_tx_auth) VALUES ($1) RETURNING id", + serde_json::to_value(email_tx_auth)? // Convert struct to JSON for insertion + ) + .fetch_one(pool) + .await?; Ok(query_result.id) } @@ -60,6 +90,27 @@ pub async fn update_request(pool: &PgPool, request_id: Uuid, status: RequestStat Ok(()) } +pub async fn get_request(pool: &PgPool, request_id: Uuid) -> Result { + let query_result = sqlx::query_as!( + RequestModel, + r#" + SELECT + id, + status as "status: RequestStatus", + updated_at::timestamp as "updated_at: NaiveDateTime", + email_tx_auth as "email_tx_auth: Json" + FROM requests + WHERE id = $1 + "#, + request_id + ) + .fetch_optional(pool) + .await?; + + // If query_result is None, it means no row was found for the given request_id + query_result.ok_or_else(|| sqlx::Error::RowNotFound) +} + pub async fn insert_expected_reply( pool: &PgPool, message_id: &str, diff --git a/packages/relayer/src/prove.rs b/packages/relayer/src/prove.rs new file mode 100644 index 00000000..d04ff32e --- /dev/null +++ b/packages/relayer/src/prove.rs @@ -0,0 +1,69 @@ +use anyhow::{anyhow, Result}; +use ethers::types::U256; +use relayer_utils::{ + generate_email_circuit_input, generate_proof, hex_to_field, u256_to_bytes32, + u256_to_bytes32_little, AccountCode, AccountSalt, EmailCircuitParams, ParsedEmail, LOG, +}; +use slog::info; + +use crate::{ + abis::EmailProof, + command::get_masked_command, + constants::{COMMAND_FIELDS, DOMAIN_FIELDS, SHA_PRECOMPUTE_SELECTOR}, + model::RequestModel, + RelayerState, +}; + +/// Generates the email proof for authentication. +/// +/// # Arguments +/// +/// * `params` - The `EmailRequestContext` containing request details. +/// +/// # Returns +/// +/// A `Result` containing the `EmailProof` and account salt, or an `EmailError`. +pub async fn generate_email_proof( + email: &str, + request: RequestModel, + relayer_state: RelayerState, +) -> Result { + let parsed_email = ParsedEmail::new_from_raw_email(&email).await?; + let circuit_input = generate_email_circuit_input( + &email, + &request.email_tx_auth.account_code, + Some(EmailCircuitParams { + max_header_length: Some(1024), + max_body_length: Some(1024), + sha_precompute_selector: Some(SHA_PRECOMPUTE_SELECTOR.to_string()), + ignore_body_hash_check: Some(false), + }), + ) + .await?; + + let (proof, public_signals) = generate_proof( + &circuit_input, + "email_auth", + &relayer_state.config.prover_url, + ) + .await?; + + info!(LOG, "Public signals: {:?}", public_signals); + + let account_salt = u256_to_bytes32(&public_signals[COMMAND_FIELDS + DOMAIN_FIELDS + 3]); + let is_code_exist = public_signals[COMMAND_FIELDS + DOMAIN_FIELDS + 4] == 1u8.into(); + let masked_command = get_masked_command(public_signals.clone(), DOMAIN_FIELDS + 3)?; + + let email_proof = EmailProof { + proof, + domain_name: parsed_email.get_email_domain()?, + public_key_hash: u256_to_bytes32(&public_signals[DOMAIN_FIELDS + 0]), + timestamp: u256_to_bytes32(&public_signals[DOMAIN_FIELDS + 2]).into(), + masked_command, + email_nullifier: u256_to_bytes32(&public_signals[DOMAIN_FIELDS + 1]), + account_salt, + is_code_exist, + }; + + Ok((email_proof)) +} diff --git a/packages/relayer/src/schema.rs b/packages/relayer/src/schema.rs index bb396c1d..c562fb6c 100644 --- a/packages/relayer/src/schema.rs +++ b/packages/relayer/src/schema.rs @@ -1,27 +1,31 @@ use std::collections::HashMap; -use ethers::{abi::Item, types::Address}; +use ethers::{ + abi::{Abi, Function, Token}, + types::Address, +}; use relayer_utils::AccountCode; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct EmailTxAuthSchema { pub contract_address: Address, pub email_auth_contract_address: Address, pub account_code: AccountCode, pub code_exists_in_email: bool, - pub function_abi: Item, + pub function_abi: Function, pub command_template: String, - pub command_params: HashMap, - pub remaining_args: HashMap, + pub command_params: Vec, + pub template_id: usize, + pub remaining_args: Vec, pub email_address: String, pub subject: String, pub body: String, pub chain: String, } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct DKIMSchema { dkim_contract_address: Address, diff --git a/packages/relayer/src/statics.rs b/packages/relayer/src/statics.rs new file mode 100644 index 00000000..46a7cec7 --- /dev/null +++ b/packages/relayer/src/statics.rs @@ -0,0 +1,7 @@ +use std::sync::{Arc, Mutex}; + +use lazy_static::lazy_static; + +lazy_static! { + pub static ref SHARED_MUTEX: Arc> = Arc::new(Mutex::new(0)); +} diff --git a/packages/relayer/src/utils.rs b/packages/relayer/src/utils.rs deleted file mode 100644 index 8ddadd18..00000000 --- a/packages/relayer/src/utils.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::collections::HashMap; - -pub fn parse_command_template(template: &str, params: &HashMap) -> String { - let mut parsed_string = template.to_string(); - - for (key, value) in params { - let placeholder = format!("${{{}}}", key); - parsed_string = parsed_string.replace(&placeholder, value); - } - - parsed_string -}