From 9873a7295572bf865b90cce635c393ff2a27555e Mon Sep 17 00:00:00 2001 From: Mateo Rico <89949621+ricomateo@users.noreply.github.com> Date: Wed, 31 Jul 2024 11:23:55 -0300 Subject: [PATCH] Web3 signer functionality (#56) This PR branches from [lambdaclass:signer](https://github.com/lambdaclass/eigen-rs/tree/signer) branch This PR adds web3 signer functionality. At the moment we have implemented: * A function to retrieve a remote signer from AWS Key Management Service * A web3 signer that gets a transaction signature by sending an RPC request --------- Co-authored-by: tomasarrachea --- Cargo.lock | 980 ++++++++++++++++++++++++++++++- Cargo.toml | 16 +- README.md | 16 +- crates/signer/Cargo.toml | 14 +- crates/signer/src/lib.rs | 5 +- crates/signer/src/signer.rs | 171 +++++- crates/signer/src/web3_signer.rs | 73 +++ 7 files changed, 1234 insertions(+), 41 deletions(-) create mode 100644 crates/signer/src/web3_signer.rs diff --git a/Cargo.lock b/Cargo.lock index 526cd0bb..5325fe55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,6 +140,17 @@ dependencies = [ "sha2", ] +[[package]] +name = "alloy-genesis" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca15afde1b6d15e3fc1c97421262b1bbb37aee45752e3c8b6d6f13f776554ff" +dependencies = [ + "alloy-primitives", + "alloy-serde", + "serde", +] + [[package]] name = "alloy-json-abi" version = "0.7.7" @@ -185,6 +196,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "alloy-node-bindings" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b2fb0276a78ec13791446a417c2517eee5c8e8a8c520ae0681975b8056e5c" +dependencies = [ + "alloy-genesis", + "alloy-primitives", + "k256", + "serde_json", + "tempfile", + "thiserror", + "tracing", + "url", +] + [[package]] name = "alloy-primitives" version = "0.7.7" @@ -359,6 +386,24 @@ dependencies = [ "thiserror", ] +[[package]] +name = "alloy-signer-aws" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63ce17bfd5aa38e14b9c83b553d93c76e1bd5641a2db45f381f81619fd3e54ca" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "aws-sdk-kms", + "k256", + "spki", + "thiserror", + "tracing", +] + [[package]] name = "alloy-signer-local" version = "0.1.2" @@ -398,7 +443,7 @@ dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", "const-hex", - "heck", + "heck 0.5.0", "indexmap 2.2.6", "proc-macro-error", "proc-macro2", @@ -417,7 +462,7 @@ dependencies = [ "alloy-json-abi", "const-hex", "dunce", - "heck", + "heck 0.5.0", "proc-macro2", "quote", "serde_json", @@ -499,6 +544,21 @@ dependencies = [ "ws_stream_wasm", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.86" @@ -721,9 +781,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", @@ -764,6 +824,327 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "aws-config" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf6cfe2881cb1fcbba9ae946fb9a6480d3b7a714ca84c74925014a89ef3387a" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http 0.2.12", + "hyper 0.14.29", + "ring 0.17.8", + "time", + "tokio", + "tracing", + "url", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16838e6c9e12125face1c1eff1343c75e3ff540de98ff7ebd61874a89bcfeb9" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-runtime" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c5f920ffd1e0526ec9e70e50bf444db50b204395a0fa7016bbf9e31ea1698f" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid 1.10.0", +] + +[[package]] +name = "aws-sdk-kms" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91f43512620f4b0d9e67ccf7d768fab5ed310ac2229ebb9422177abe99c36ba" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6acca681c53374bf1d9af0e317a41d12a44902ca0f2d1e10e5cb5bb98ed74f35" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79c6bdfe612503a526059c05c9ccccbf6bd9530b003673cb863e547fd7c0c9a" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e6ecdb2bd756f3b2383e6f0588dc10a4e65f5d551e70a56e0bfe0c884673ce" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df1b0fa6be58efe9d4ccc257df0a53b89cd8909e86591a13ca54817c87517be" +dependencies = [ + "aws-credential-types", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.1.0", + "once_cell", + "percent-encoding", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9cd0ae3d97daa0a2bf377a4d8e8e1362cae590c4a1aad0d40058ebca18eb91e" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce87155eba55e11768b8c1afa607f3e864ae82f03caf63258b37455b0ad02537" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "http-body 1.0.0", + "httparse", + "hyper 0.14.29", + "hyper-rustls 0.24.2", + "once_cell", + "pin-project-lite", + "pin-utils", + "rustls 0.21.12", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30819352ed0a04ecf6a2f3477e344d2d1ba33d43e0f09ad9047c12e0d923616f" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.1.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe321a6b21f5d8eabd0ade9c55d3d0335f3c3157fc2b3e87f05f34b539e4df5" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.1.0", + "http-body 0.4.6", + "http-body 1.0.0", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d123fbc2a4adc3c301652ba8e149bf4bc1d1725affb9784eb20c953ace06bf55" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5221b91b3e441e6675310829fd8984801b772cb1546ef6c0e54dec9f1ac13fef" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version 0.4.0", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -803,6 +1184,16 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "base64ct" version = "1.6.0" @@ -881,6 +1272,56 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bollard" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aed08d3adb6ebe0eff737115056652670ae290f177759aac19c30456135f94c" +dependencies = [ + "base64 0.22.1", + "bollard-stubs", + "bytes", + "futures-core", + "futures-util", + "hex", + "home", + "http 1.1.0", + "http-body-util", + "hyper 1.3.1", + "hyper-named-pipe", + "hyper-rustls 0.26.0", + "hyper-util", + "hyperlocal-next", + "log", + "pin-project-lite", + "rustls 0.22.4", + "rustls-native-certs 0.7.1", + "rustls-pemfile 2.1.2", + "rustls-pki-types", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "serde_urlencoded", + "thiserror", + "tokio", + "tokio-util", + "tower-service", + "url", + "winapi", +] + +[[package]] +name = "bollard-stubs" +version = "1.44.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709d9aa1c37abb89d40f19f5d0ad6f0d88cb1581264e571c9350fc5bb89cf1c5" +dependencies = [ + "serde", + "serde_repr", + "serde_with", +] + [[package]] name = "bs58" version = "0.5.1" @@ -918,6 +1359,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "bzip2" version = "0.4.4" @@ -1008,7 +1459,11 @@ version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ + "android-tzdata", + "iana-time-zone", "num-traits", + "serde", + "windows-targets 0.52.5", ] [[package]] @@ -1200,6 +1655,41 @@ dependencies = [ "cipher", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.67", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.67", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -1236,6 +1726,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -1325,6 +1816,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "docker_credential" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31951f49556e34d90ed28342e1df7e1cb7a229c4cab0aecc627b5d91edd41d07" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + [[package]] name = "dunce" version = "1.0.4" @@ -1565,13 +2067,21 @@ version = "0.0.1-alpha" dependencies = [ "alloy-consensus", "alloy-network", + "alloy-node-bindings", "alloy-primitives", + "alloy-rpc-client", "alloy-signer", + "alloy-signer-aws", "alloy-signer-local", + "async-trait", + "aws-config", + "aws-sdk-kms", "eth-keystore", - "hex-literal", - "k256", + "serde", + "testcontainers", "thiserror", + "tokio", + "url", ] [[package]] @@ -1671,6 +2181,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "enum-as-inner" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.67", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1706,7 +2228,7 @@ dependencies = [ "sha2", "sha3", "thiserror", - "uuid", + "uuid 0.8.2", ] [[package]] @@ -2411,6 +2933,12 @@ dependencies = [ "fxhash", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" @@ -2438,6 +2966,51 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hickory-proto" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "hmac" version = "0.12.1" @@ -2456,6 +3029,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "http" version = "0.2.12" @@ -2565,21 +3149,57 @@ dependencies = [ "pin-project-lite", "smallvec", "tokio", - "want", + "want", +] + +[[package]] +name = "hyper-named-pipe" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" +dependencies = [ + "hex", + "hyper 1.3.1", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", + "winapi", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.29", + "log", + "rustls 0.21.12", + "rustls-native-certs 0.6.3", + "tokio", + "tokio-rustls 0.24.1", ] [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", - "http 0.2.12", - "hyper 0.14.29", - "rustls 0.21.12", + "http 1.1.0", + "hyper 1.3.1", + "hyper-util", + "log", + "rustls 0.22.4", + "rustls-native-certs 0.7.1", + "rustls-pki-types", "tokio", - "tokio-rustls 0.24.1", + "tokio-rustls 0.25.0", + "tower-service", ] [[package]] @@ -2593,6 +3213,7 @@ dependencies = [ "hyper 1.3.1", "hyper-util", "rustls 0.23.10", + "rustls-native-certs 0.7.1", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -2636,6 +3257,60 @@ dependencies = [ "tracing", ] +[[package]] +name = "hyperlocal-next" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf569d43fa9848e510358c07b80f4adf34084ddc28c6a4a651ee8474c070dcc" +dependencies = [ + "hex", + "http-body-util", + "hyper 1.3.1", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.5.0" @@ -2698,6 +3373,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -2708,6 +3384,7 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.5", + "serde", ] [[package]] @@ -2739,6 +3416,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2", + "widestring", + "windows-sys 0.48.0", + "winreg 0.50.0", +] + [[package]] name = "ipnet" version = "2.9.0" @@ -2901,6 +3590,12 @@ dependencies = [ "libc", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -2932,6 +3627,15 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "mach2" version = "0.4.2" @@ -2941,6 +3645,12 @@ dependencies = [ "libc", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "md-5" version = "0.10.6" @@ -3227,6 +3937,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + [[package]] name = "overload" version = "0.1.1" @@ -3282,6 +3998,31 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "parse-display" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" +dependencies = [ + "parse-display-derive", + "regex", + "regex-syntax", +] + +[[package]] +name = "parse-display-derive" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "regex-syntax", + "structmeta", + "syn 2.0.67", +] + [[package]] name = "password-hash" version = "0.4.2" @@ -3777,6 +4518,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.8.4" @@ -3836,6 +4583,7 @@ dependencies = [ "futures-core", "futures-util", "h2 0.4.5", + "hickory-resolver", "http 1.1.0", "http-body 1.0.0", "http-body-util", @@ -3853,6 +4601,7 @@ dependencies = [ "pin-project-lite", "quinn", "rustls 0.23.10", + "rustls-native-certs 0.7.1", "rustls-pemfile 2.1.2", "rustls-pki-types", "serde", @@ -3872,6 +4621,16 @@ dependencies = [ "winreg 0.52.0", ] +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -4034,6 +4793,20 @@ dependencies = [ "sct", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring 0.17.8", + "rustls-pki-types", + "rustls-webpki 0.102.4", + "subtle", + "zeroize", +] + [[package]] name = "rustls" version = "0.23.10" @@ -4048,6 +4821,31 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.1.2", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -4305,6 +5103,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.67", +] + [[package]] name = "serde_spanned" version = "0.6.6" @@ -4326,6 +5135,36 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.67", +] + [[package]] name = "sha1" version = "0.10.6" @@ -4500,6 +5339,35 @@ dependencies = [ "precomputed-hash", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "structmeta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329" +dependencies = [ + "proc-macro2", + "quote", + "structmeta-derive", + "syn 2.0.67", +] + +[[package]] +name = "structmeta-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.67", +] + [[package]] name = "strum" version = "0.26.2" @@ -4515,7 +5383,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", @@ -4653,6 +5521,35 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "testcontainers" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "725cbe485aafddfd8b2d01665937c95498d894c07fabd9c4e06a53c7da4ccc56" +dependencies = [ + "async-trait", + "bollard", + "bollard-stubs", + "bytes", + "dirs", + "docker_credential", + "either", + "futures", + "log", + "memchr", + "parse-display", + "pin-project-lite", + "reqwest 0.12.5", + "serde", + "serde_json", + "serde_with", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "url", +] + [[package]] name = "thiserror" version = "1.0.61" @@ -4797,6 +5694,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.0" @@ -5154,10 +6062,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", + "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" @@ -5174,6 +6089,12 @@ dependencies = [ "serde", ] +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" + [[package]] name = "valuable" version = "0.1.0" @@ -5192,6 +6113,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "wait-timeout" version = "0.2.0" @@ -5317,6 +6244,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" @@ -5348,6 +6281,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -5553,6 +6495,12 @@ dependencies = [ "tap", ] +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 1ceb2fd6..1be257bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,11 +21,12 @@ members = [ "crates/types/", "crates/signer/", "crates/logging/", + "crates/signer/", "examples/info-operator-service/", "testing/testing-utils/", "examples/avsregistry-read", "examples/avsregistry-write", - "examples/anvil-utils" + "examples/anvil-utils", ] resolver = "2" @@ -89,9 +90,14 @@ thiserror = "1.0" tracing = "0.1.40" tracing-subscriber = { version = "0.3", features = ["json"] } hyper = "0.14.25" -eth-keystore = "0.5.0" hex-literal = "0.4.1" -k256 = "0.13.3" +ethers-signers = "2.0.14" +eth-keystore = "0.5.0" +aws-config = "1.5.4" +aws-sdk-kms = "1.37.0" +url = "2.5.2" +testcontainers = "0.20.1" +async-trait = "0.1.81" #misc parking_lot = "0.12" @@ -132,8 +138,10 @@ alloy-transport-ws = { version = "0.1", default-features = false } alloy-transport-ipc = { version = "0.1", default-features = false } alloy-pubsub = { version = "0.1", default-features = false } alloy-json-rpc = { version = "0.1", default-features = false } -alloy-rpc-client = { version = "0.1", default-features = false } +alloy-rpc-client = "0.1" alloy-contract = { version = "0.1", default-features = false } +alloy-signer-aws = "0.1" + # examples avsregistry-read = { path = "examples/avsregistry-read" } diff --git a/README.md b/README.md index 0baece2e..f06d66fa 100644 --- a/README.md +++ b/README.md @@ -14,17 +14,18 @@ List of crates in the repository :- - [eigen-contracts-bindings](https://github.com/supernovahs/eigen-rs/tree/main/crates/contracts/bindings) - Generate ethers bindings for Eigen Layer. - [eigen-crypto-bls](https://github.com/supernovahs/eigen-rs/tree/main/crates/crypto/bls) - bls utilities - [eigen-crypto-bn254](https://github.com/supernovahs/eigen-rs/tree/main/crates/crypto/bn254) - bn254 utilities -- [eigen-metrics](https://github.com/supernovahs/eigen-rs/tree/main/crates/metrics) - performance , rpc and economic metrics +- [eigen-metrics](https://github.com/supernovahs/eigen-rs/tree/main/crates/metrics) - performance , rpc and economic metrics - [eigen-services](https://github.com/supernovahs/eigen-rs/tree/main/crates/services) - Spawn tokio services for operators info , bls aggregation - [eigen-types](https://github.com/supernovahs/eigen-rs/tree/main/crates/types) - Common types -- [eigen-utils](https://github.com/supernovahs/eigen-rs/tree/main/crates/utils) - Publicly exportable `m2-mainnet` compatible alloy bindings. -- [eigen-testing-utils](https://github.com/supernovahs/eigen-rs/tree/main/testing/testing-utils) - Contains publicly exportable anvil , holesky , mainnet addresses for eigen contracts . +- [eigen-utils](https://github.com/supernovahs/eigen-rs/tree/main/crates/utils) - Publicly exportable `m2-mainnet` compatible alloy bindings. +- [eigen-testing-utils](https://github.com/supernovahs/eigen-rs/tree/main/testing/testing-utils) - Contains publicly exportable anvil , holesky , mainnet addresses for eigen contracts . ## Examples You can run any [example](https://github.com/supernovahs/eigen-rs/tree/main/examples) using the command cargo run --example -Example : +Example : + ``` cargo run --example get_quorum_count ``` @@ -33,11 +34,12 @@ cargo run --example get_quorum_count We are actively looking for contributors. Thank you for your interest. We have strict ci checks in place. In case of any questions and support , feel free to raise an issue. -To test locally and raise a PR :- +To test locally and raise a PR :- + +You need `foundry` , `docker` and `make` and `nightly rust` to successfully run it. Also, the `docker` engine has to be running for all the tests to execute correctly. -You need `foundry` , `docker` and `make` and `nightly rust` to successfully run it. ``` -make pr +make pr ``` ## Supported Rust Version diff --git a/crates/signer/Cargo.toml b/crates/signer/Cargo.toml index 14ec785f..29c5b0b3 100644 --- a/crates/signer/Cargo.toml +++ b/crates/signer/Cargo.toml @@ -10,8 +10,18 @@ alloy-consensus.workspace = true alloy-network.workspace = true alloy-primitives.workspace = true alloy-signer.workspace = true +alloy-signer-aws.workspace = true alloy-signer-local.workspace = true +alloy-rpc-client.workspace = true +async-trait.workspace = true +aws-sdk-kms.workspace = true eth-keystore.workspace = true -hex-literal.workspace = true -k256.workspace = true +serde.workspace = true thiserror.workspace = true +url.workspace = true + +[dev-dependencies] +alloy-node-bindings.workspace = true +aws-config.workspace = true +testcontainers.workspace = true +tokio.workspace = true diff --git a/crates/signer/src/lib.rs b/crates/signer/src/lib.rs index e78f58cd..ac1cd13c 100644 --- a/crates/signer/src/lib.rs +++ b/crates/signer/src/lib.rs @@ -1,6 +1,7 @@ #![doc( - html_logo_url = "https://github.com/supernovahs/eigensdk-rs/assets/91280922/bd13caec-3c00-4afc-839a-b83d2890beb5", - issue_tracker_base_url = "https://github.com/supernovahs/eigen-rs/issues/" + html_logo_url = "https://github.com/Layr-Labs/eigensdk-rs/assets/91280922/bd13caec-3c00-4afc-839a-b83d2890beb5", + issue_tracker_base_url = "https://github.com/Layr-Labs/eigensdk-rs/issues/" )] pub mod signer; +pub mod web3_signer; diff --git a/crates/signer/src/signer.rs b/crates/signer/src/signer.rs index 0c44d353..7cbc2714 100644 --- a/crates/signer/src/signer.rs +++ b/crates/signer/src/signer.rs @@ -1,7 +1,11 @@ +use crate::web3_signer::Web3Signer; +use alloy_primitives::Address; +use alloy_signer_aws::{AwsSigner, AwsSignerError}; use alloy_signer_local::PrivateKeySigner; use eth_keystore::decrypt_key; use std::path::Path; use thiserror::Error; +use url::Url; /// Represents the input params to create a signer #[derive(Debug)] @@ -10,17 +14,19 @@ pub enum Config { PrivateKey(String), /// Keystore path and password Keystore(String, String), - /// Web3 endpoint and address - Web3(String, String), } -#[derive(Error, Debug)] /// Possible errors raised in signer creation +#[derive(Error, Debug)] pub enum SignerError { #[error("invalid private key")] InvalidPrivateKey, #[error("invalid keystore password")] InvalidPassword, + #[error("invalid address")] + InvalidAddress, + #[error("invalid url")] + InvalidUrl, } impl Config { @@ -38,23 +44,47 @@ impl Config { PrivateKeySigner::from_slice(&private_key) .map_err(|_| SignerError::InvalidPrivateKey) } - Config::Web3(_endpoint, _address) => { - todo!() // We are implementing this in a following PR - } } } + + /// Creates a signer from a key ID in AWS Key Management Service + pub async fn aws_signer( + key_id: String, + chain_id: Option, + client: aws_sdk_kms::Client, + ) -> Result { + AwsSigner::new(client, key_id, chain_id).await + } + + /// Creates a signer that uses the Web3Signer JSON-RPC API + pub fn web3_signer(endpoint: String, address: Address) -> Result { + let url: Url = endpoint.parse().map_err(|_| SignerError::InvalidUrl)?; + Ok(Web3Signer::new(address, url)) + } } #[cfg(test)] mod test { use super::Config; - use alloy_consensus::TxLegacy; - use alloy_network::TxSignerSync; - use alloy_primitives::{bytes, Address, U256}; + use alloy_consensus::{SignableTransaction, TxLegacy}; + use alloy_network::{TxSigner, TxSignerSync}; + use alloy_node_bindings::Anvil; + use alloy_primitives::{address, bytes, hex_literal::hex, keccak256, Address, U256}; use alloy_signer::Signature; use alloy_signer_local::PrivateKeySigner; - use hex_literal::hex; + use aws_config::{BehaviorVersion, Region, SdkConfig}; + use aws_sdk_kms::{ + self, + config::{Credentials, SharedCredentialsProvider}, + types::KeyMetadata, + }; use std::str::FromStr; + use testcontainers::{ + core::{IntoContainerPort, WaitFor}, + runners::AsyncRunner, + ContainerAsync, GenericImage, ImageExt, + }; + use tokio; const PRIVATE_KEY: &str = "dcf2cbdd171a21c480aa7f53d77f31bb102282b3ff099c78e3118b37348c72f7"; const ADDRESS: [u8; 20] = hex!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); @@ -65,6 +95,10 @@ mod test { const SIGNATURE_Y_PARITY: u64 = 37; const KEYSTORE_PATH: &str = "mockdata/dummy.key.json"; const KEYSTORE_PASSWORD: &str = "testpassword"; + const LOCALSTACK_PORT: u16 = 4566; + const AWS_US_WEST_REGION: &str = "us-west-1"; + const LOCALSTACK_IMAGE_NAME: &str = "localstack/localstack"; + const LOCALSTACK_IMAGE_TAG: &str = "latest"; #[test] fn sign_transaction_with_private_key() { @@ -113,4 +147,121 @@ mod test { assert_eq!(signature, expected_signature); } + + #[tokio::test] + async fn sign_transaction_with_aws_signer() { + // Start the container running Localstack + let _container = start_localstack_container().await; + + let localstack_endpoint = format!("http://localhost:{}", LOCALSTACK_PORT); + let config = get_aws_config( + "localstack".into(), + "localstack".into(), + Region::from_static(&AWS_US_WEST_REGION), + localstack_endpoint, + ) + .await; + + // Create an AWS KMS Client + let client = aws_sdk_kms::Client::new(&config); + + // Create a key + let key_metadata = create_kms_key(&client).await; + + // Create a signer for the given key + let key_id = key_metadata.key_id(); + let chain_id = Some(1); + let signer = Config::aws_signer(key_id.into(), chain_id, client.clone()) + .await + .unwrap(); + + let mut tx = TxLegacy { + to: address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into(), + value: U256::from(1_000_000_000), + gas_limit: 2_000_000, + nonce: 0, + gas_price: 21_000_000_000, + input: bytes!(), + chain_id: Some(1), + }; + + // Sign the transaction + let signature = signer.sign_transaction(&mut tx).await.unwrap(); + + // Recover the address + let mut encoded_tx = Vec::new(); + tx.encode_for_signing(&mut encoded_tx); + let prehash = keccak256(encoded_tx); + let recovered_address = signature.recover_address_from_prehash(&prehash).unwrap(); + + // Check that the recovered addresses are the same + assert_eq!(signer.address(), recovered_address); + } + + #[tokio::test] + async fn sign_legacy_transaction_with_web3_signer() { + let anvil = Anvil::default().spawn(); + + let endpoint = anvil.endpoint(); + let address = anvil.addresses()[0]; + let signer = Config::web3_signer(endpoint, address).unwrap(); + let mut tx = TxLegacy { + to: anvil.addresses()[1].into(), + value: U256::from(1_000_000_000), + gas_limit: 0x76c0, + gas_price: 21_000_000_000, + nonce: 0, + input: bytes!(), + chain_id: Some(anvil.chain_id()), + }; + + let signature = signer.sign_transaction(&mut tx).await.unwrap(); + + let private_key = anvil.keys()[0].clone(); + let expected_signer = PrivateKeySigner::from_field_bytes(&private_key.to_bytes()).unwrap(); + let expected_signature = expected_signer.sign_transaction_sync(&mut tx).unwrap(); + + assert_eq!(signature, expected_signature); + } + + async fn start_localstack_container() -> ContainerAsync { + let container = GenericImage::new(LOCALSTACK_IMAGE_NAME, LOCALSTACK_IMAGE_TAG) + .with_exposed_port(LOCALSTACK_PORT.tcp()) + .with_wait_for(WaitFor::message_on_stdout("Ready.")) + .with_mapped_port(LOCALSTACK_PORT, LOCALSTACK_PORT.tcp()) + .start() + .await + .expect("Error starting localstack container"); + container + } + + async fn create_kms_key(client: &aws_sdk_kms::Client) -> KeyMetadata { + client + .create_key() + .key_spec(aws_sdk_kms::types::KeySpec::EccSecgP256K1) + .key_usage(aws_sdk_kms::types::KeyUsageType::SignVerify) + .send() + .await + .unwrap() + .key_metadata() + .unwrap() + .clone() + } + + async fn get_aws_config( + access_key: String, + secret_access_key: String, + region: Region, + endpoint_url: String, + ) -> SdkConfig { + let creds = Credentials::new(access_key, secret_access_key, None, None, "Static"); + let config = aws_config::load_defaults(BehaviorVersion::latest()) + .await + .to_builder() + .credentials_provider(SharedCredentialsProvider::new(creds)) + .endpoint_url(endpoint_url) + .region(Some(region.clone())) + .build(); + config + } } diff --git a/crates/signer/src/web3_signer.rs b/crates/signer/src/web3_signer.rs new file mode 100644 index 00000000..ee82ff08 --- /dev/null +++ b/crates/signer/src/web3_signer.rs @@ -0,0 +1,73 @@ +use alloy_consensus::{SignableTransaction, TxLegacy}; +use alloy_network::TxSigner; +use alloy_primitives::{Address, Bytes, TxKind, U256}; +use alloy_rpc_client::{ClientBuilder, ReqwestClient, RpcCall}; +use alloy_signer::Signature; +use async_trait::async_trait; +use serde::Serialize; +use url::Url; + +/// A signer that sends an RPC request to sign a transaction remotely +/// Implements `eth_signTransaction` method of Consensys Web3 Signer +/// Reference: https://docs.web3signer.consensys.io/reference/api/json-rpc#eth_signtransaction +#[derive(Debug)] +pub struct Web3Signer { + /// Client used to send an RPC request + pub client: ReqwestClient, + /// Address of the account that intends to sign a transaction. + /// It must match the `from` field in the transaction. + pub address: Address, +} + +#[derive(Serialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +struct SignTransactionParams { + from: String, + to: TxKind, + value: U256, + gas: String, + #[serde(skip_serializing_if = "Option::is_none")] + gas_price: Option, + nonce: String, + data: String, +} + +impl Web3Signer { + pub fn new(address: Address, url: Url) -> Self { + Web3Signer { + client: ClientBuilder::default().http(url), + address, + } + } +} + +#[async_trait] +impl TxSigner for Web3Signer { + fn address(&self) -> Address { + self.address + } + + async fn sign_transaction( + &self, + tx: &mut dyn SignableTransaction, + ) -> alloy_signer::Result { + let params = SignTransactionParams { + from: self.address.to_string(), + to: tx.to(), + value: tx.value(), + gas: format!("0x{:x}", tx.gas_limit()), + gas_price: tx.gas_price().map(|price| format!("0x{:x}", price)), + nonce: format!("0x{:x}", tx.nonce()), + data: Bytes::copy_from_slice(tx.input()).to_string(), + }; + + let request: RpcCall<_, Vec, Bytes> = + self.client.request("eth_signTransaction", vec![params]); + let rlp_encoded_signed_tx = request.await.map_err(alloy_signer::Error::other)?; + + let signed_tx = TxLegacy::decode_signed_fields(&mut rlp_encoded_signed_tx.as_ref()) + .map_err(alloy_signer::Error::other)?; + + Ok(*signed_tx.signature()) + } +}