diff --git a/Cargo.lock b/Cargo.lock
index ce2dd2a..0f7b18a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -26,6 +26,16 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "aead"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+dependencies = [
+ "crypto-common",
+ "generic-array",
+]
+
[[package]]
name = "aes"
version = "0.7.5"
@@ -56,7 +66,7 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc3be92e19a7ef47457b8e6f90707e12b6ac5d20c6f3866584fa3be0787d839f"
dependencies = [
- "aead",
+ "aead 0.4.3",
"aes 0.7.5",
"cipher 0.3.0",
"ctr 0.7.0",
@@ -1546,6 +1556,30 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "chacha20"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
+dependencies = [
+ "cfg-if",
+ "cipher 0.4.4",
+ "cpufeatures",
+]
+
+[[package]]
+name = "chacha20poly1305"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
+dependencies = [
+ "aead 0.5.2",
+ "chacha20",
+ "cipher 0.4.4",
+ "poly1305",
+ "zeroize",
+]
+
[[package]]
name = "chrono"
version = "0.4.38"
@@ -1578,6 +1612,7 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
+ "zeroize",
]
[[package]]
@@ -1902,6 +1937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
+ "rand_core 0.6.4",
"typenum",
]
@@ -4875,6 +4911,17 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
+[[package]]
+name = "poly1305"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
+dependencies = [
+ "cpufeatures",
+ "opaque-debug",
+ "universal-hash 0.5.1",
+]
+
[[package]]
name = "polyval"
version = "0.5.3"
@@ -4884,7 +4931,7 @@ dependencies = [
"cfg-if",
"cpufeatures",
"opaque-debug",
- "universal-hash",
+ "universal-hash 0.4.0",
]
[[package]]
@@ -8600,6 +8647,30 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+[[package]]
+name = "stealth-addresses"
+version = "0.0.0"
+dependencies = [
+ "alloy-primitives",
+ "alloy-sol-types",
+ "chacha20poly1305",
+ "clap",
+ "eyre",
+ "futures",
+ "k256",
+ "reth",
+ "reth-chainspec",
+ "reth-cli-commands",
+ "reth-cli-runner",
+ "reth-db",
+ "reth-execution-types",
+ "reth-exex",
+ "reth-node-api",
+ "reth-node-builder",
+ "reth-node-ethereum",
+ "reth-tracing",
+]
+
[[package]]
name = "strsim"
version = "0.11.1"
@@ -9438,6 +9509,16 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "universal-hash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+dependencies = [
+ "crypto-common",
+ "subtle",
+]
+
[[package]]
name = "unsigned-varint"
version = "0.7.2"
diff --git a/Cargo.toml b/Cargo.toml
index ad65603..de33a37 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,6 +6,7 @@ members = [
"op-bridge",
"remote",
"rollup",
+ "stealth-addresses"
]
resolver = "2"
@@ -20,12 +21,16 @@ publish = false
# reth
reth = { git = "https://github.com/paradigmxyz/reth" }
reth-chainspec = { git = "https://github.com/paradigmxyz/reth" }
+reth-cli-commands = { git = "https://github.com/paradigmxyz/reth" }
+reth-cli-runner = { git = "https://github.com/paradigmxyz/reth" }
+reth-db = { git = "https://github.com/paradigmxyz/reth" }
reth-discv5 = { git = "https://github.com/paradigmxyz/reth" }
reth-execution-errors = { git = "https://github.com/paradigmxyz/reth" }
reth-execution-types = { git = "https://github.com/paradigmxyz/reth" }
reth-exex = { git = "https://github.com/paradigmxyz/reth", features = ["serde"] }
reth-network-peers = { git = "https://github.com/paradigmxyz/reth" }
reth-node-api = { git = "https://github.com/paradigmxyz/reth" }
+reth-node-builder = { git = "https://github.com/paradigmxyz/reth" }
reth-node-ethereum = { git = "https://github.com/paradigmxyz/reth" }
reth-primitives = { git = "https://github.com/paradigmxyz/reth" }
reth-provider = { git = "https://github.com/paradigmxyz/reth" }
@@ -33,6 +38,7 @@ reth-revm = { git = "https://github.com/paradigmxyz/reth" }
reth-tracing = { git = "https://github.com/paradigmxyz/reth" }
# alloy
+alloy-primitives = "0.7"
alloy-sol-types = { version = "0.7", features = ["json"] }
# async
@@ -42,6 +48,7 @@ tokio = { version = "1.0", features = ["full"] }
tokio-stream = "0.1"
# misc
+clap = "4"
eyre = "0.6"
# testing
diff --git a/README.md b/README.md
index 7e6e2bc..5c1fa61 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,7 @@ for new developers.
| [OP Bridge](./op-bridge) | Decodes Optimism deposit and withdrawal receipts from L1 | `cargo run --bin op-bridge -- node` |
| [Remote](./remote) | Emits notifications using a gRPC server, and a consumer that receives them | `cargo run --bin remote-exex -- node` to start Reth node with the ExEx and a gRPC server
`cargo run --bin remote-consumer` to start a gRPC client |
| [Rollup](./rollup) | Rollup that derives the state from L1 | `cargo run --bin rollup -- node` |
+| [Stealth Addresses](./stealth-addresses) | Scans committed blocks for stealth addresses | `cargo run --bin stealthy` |
#### License
diff --git a/stealth-addresses/Cargo.toml b/stealth-addresses/Cargo.toml
new file mode 100644
index 0000000..0d8263a
--- /dev/null
+++ b/stealth-addresses/Cargo.toml
@@ -0,0 +1,33 @@
+[package]
+name = "stealth-addresses"
+version = "0.0.0"
+publish = false
+edition.workspace = true
+license.workspace = true
+
+[dependencies]
+reth.workspace = true
+reth-chainspec.workspace = true
+reth-cli-commands.workspace = true
+reth-cli-runner.workspace = true
+reth-db.workspace = true
+reth-exex.workspace = true
+reth-node-api.workspace = true
+reth-node-builder.workspace = true
+reth-node-ethereum.workspace = true
+reth-tracing.workspace = true
+reth-execution-types.workspace = true
+
+alloy-primitives.workspace = true
+alloy-sol-types = { workspace = true, features = ["json"] }
+
+k256 = { version = "0.13", default-features = false, features = ["ecdsa"] }
+chacha20poly1305 = "0.10.1"
+
+eyre.workspace = true
+futures.workspace = true
+clap.workspace = true
+
+[[bin]]
+name = "stealthy"
+path = "src/main.rs"
diff --git a/stealth-addresses/README.md b/stealth-addresses/README.md
new file mode 100644
index 0000000..69d3540
--- /dev/null
+++ b/stealth-addresses/README.md
@@ -0,0 +1,21 @@
+# Stealth addresses
+
+Scans committed blocks for stealth addresses according to [ERC5564](https://eips.ethereum.org/EIPS/eip-5564).
+
+## Node
+
+```bash
+export VIEW_KEY=0x...
+stealthy node
+```
+
+## Generation
+
+```bash
+Usage: stealthy gen [OPTIONS]
+
+Commands:
+ addr Generate a stealth address from a stealth meta address alongside an optional encrypted note
+ meta Generate a stealth meta address from view and spend private keys
+ key Generate the stealth address private key from the ephemeral public key and view & spend private keys
+```
diff --git a/stealth-addresses/src/cli.rs b/stealth-addresses/src/cli.rs
new file mode 100644
index 0000000..bf5dae1
--- /dev/null
+++ b/stealth-addresses/src/cli.rs
@@ -0,0 +1,69 @@
+//! CLI definition and entrypoint to executable
+
+use clap::{Parser, Subcommand};
+use reth::args::{
+ utils::{chain_help, chain_value_parser, SUPPORTED_CHAINS},
+ LogArgs,
+};
+use reth_chainspec::ChainSpec;
+use reth_cli_commands::node::{self, NoArgs};
+use reth_cli_runner::CliRunner;
+use reth_db::DatabaseEnv;
+use reth_node_builder::{NodeBuilder, WithLaunchContext};
+use std::{future::Future, sync::Arc};
+
+/// Entrypoint.
+#[derive(Debug, Parser)]
+pub struct Cli {
+ /// The command to run
+ #[command(subcommand)]
+ command: Commands,
+
+ /// The chain this node is running.
+ ///
+ /// Possible values are either a built-in chain or the path to a chain specification file.
+ #[arg(
+ long,
+ value_name = "CHAIN_OR_PATH",
+ long_help = chain_help(),
+ default_value = SUPPORTED_CHAINS[0],
+ value_parser = chain_value_parser,
+ global = true,
+ )]
+ chain: Arc,
+
+ #[command(flatten)]
+ logs: LogArgs,
+}
+
+impl Cli {
+ pub fn run(mut self, launcher: L) -> eyre::Result<()>
+ where
+ L: FnOnce(WithLaunchContext>>, NoArgs) -> Fut,
+ Fut: Future