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
-}