From 17503ed0f883de9bc9a4273f4809b6250883157b Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Sun, 14 Jul 2024 23:00:59 +0200 Subject: [PATCH 1/2] add stealthy --- stealth-addresses/Cargo.toml | 33 +++++ stealth-addresses/README.md | 21 ++++ stealth-addresses/src/cli.rs | 69 ++++++++++ stealth-addresses/src/crypto.rs | 160 ++++++++++++++++++++++++ stealth-addresses/src/generator/addr.rs | 68 ++++++++++ stealth-addresses/src/generator/key.rs | 41 ++++++ stealth-addresses/src/generator/meta.rs | 28 +++++ stealth-addresses/src/generator/mod.rs | 39 ++++++ stealth-addresses/src/main.rs | 43 +++++++ stealth-addresses/src/parser.rs | 75 +++++++++++ 10 files changed, 577 insertions(+) create mode 100644 stealth-addresses/Cargo.toml create mode 100644 stealth-addresses/README.md create mode 100644 stealth-addresses/src/cli.rs create mode 100644 stealth-addresses/src/crypto.rs create mode 100644 stealth-addresses/src/generator/addr.rs create mode 100644 stealth-addresses/src/generator/key.rs create mode 100644 stealth-addresses/src/generator/meta.rs create mode 100644 stealth-addresses/src/generator/mod.rs create mode 100644 stealth-addresses/src/main.rs create mode 100644 stealth-addresses/src/parser.rs 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>, + { + // add network name to logs dir + self.logs.log_file_directory = + self.logs.log_file_directory.join(self.chain.chain.to_string()); + + let _guard = self.logs.init_tracing()?; + let runner = CliRunner::default(); + match self.command { + Commands::Node(command) => { + runner.run_command_until_exit(|ctx| command.execute(ctx, launcher)) + } + Commands::Generator(command) => runner.run_command_until_exit(|_| command.execute()), + } + } +} + +/// Commands to be executed +#[derive(Debug, Subcommand)] +pub enum Commands { + /// Start the node + #[command(name = "node")] + Node(node::NodeCommand), + /// Generate stealth addresses, meta addresses and stealth address private keys. + #[command(name = "gen")] + Generator(Box), +} diff --git a/stealth-addresses/src/crypto.rs b/stealth-addresses/src/crypto.rs new file mode 100644 index 0000000..8652339 --- /dev/null +++ b/stealth-addresses/src/crypto.rs @@ -0,0 +1,160 @@ +#![allow(dead_code)] + +use chacha20poly1305::{ + aead::{Aead, OsRng}, + ChaCha20Poly1305, KeyInit, Nonce, +}; +use k256::{ + ecdsa::{SigningKey, VerifyingKey}, + elliptic_curve::{group::GroupEncoding, rand_core::RngCore, sec1::ToEncodedPoint}, + EncodedPoint, Secp256k1, +}; +use reth::primitives::{keccak256, Address, B256}; +use std::ops::Mul; + +const NONCE_LEN: usize = 12; + +type ProjectivePoint = k256::elliptic_curve::ProjectivePoint; + +fn shared_secret(pk: &SigningKey, pb: &VerifyingKey) -> B256 { + keccak256( + ProjectivePoint::from(pb.as_affine()) + .mul(pk.as_nonzero_scalar().as_ref()) + .to_affine() + .to_bytes(), + ) +} + +/// Converts a raw public key to [`VerifyingKey`]. +pub fn to_verifying_key(key: &[u8]) -> Option { + // Ensure the key is compressed or uncompressed + if !(key.len() == 33 || key.len() == 65) { + return None; + } + let encoded_point = EncodedPoint::from_bytes(key).ok()?; + + VerifyingKey::from_encoded_point(&encoded_point).ok() +} + +/// Checks if given this view public key and ephemeral public key, the view tag matches. +pub fn is_ours(view: &SigningKey, eph_pub: &VerifyingKey, expected_view_tag: u8) -> bool { + let shared_bytes = shared_secret(view, eph_pub); + // first byte is the msb: big endian + expected_view_tag == shared_bytes[0] +} + +/// Generates the corresponding stealth address private key. +/// +/// It will check the view tag first if passed. +pub fn stealth_key( + spend: &SigningKey, + view: &SigningKey, + eph_pub: &VerifyingKey, + expected_view_tag: Option, +) -> Option { + let shared_bytes = shared_secret(view, eph_pub); + + if expected_view_tag.is_none() || (expected_view_tag.is_some_and(|v| v == shared_bytes[0])) { + let shared = SigningKey::from_slice(shared_bytes.as_slice()).ok()?; + + let combined_scalar = + spend.as_nonzero_scalar().as_ref() + shared.as_nonzero_scalar().as_ref(); + return SigningKey::from_bytes(&combined_scalar.to_bytes()).ok(); + } + + None +} + +/// Generates a stealth address from the given keys, alongside a view tag. +pub fn stealth_address( + spend_pub: &VerifyingKey, + view_pub: &VerifyingKey, + eph: &SigningKey, +) -> (Address, u8) { + let shared_bytes = shared_secret(eph, view_pub); + let shared = SigningKey::from_slice(shared_bytes.as_slice()).unwrap(); + let stealth_pub = ProjectivePoint::from(shared.verifying_key().as_affine()) + + ProjectivePoint::from(spend_pub.as_affine()); + + // first byte is the msb: big endian + let tag = shared_bytes[0]; + (Address::from_raw_public_key(&stealth_pub.to_encoded_point(false).to_bytes()[1..]), tag) +} + +/// Tries to decrypt a secure note by using the view private key and the ephemeral public one. +/// +/// ```text +/// ┌────────────┬────────────────────────┐ +/// data: │ nonce (12B)│ ciphertext (remaining) │ +/// └────────────┴────────────────────────┘ +/// ``` +pub fn try_decrypt_node(view: &SigningKey, eph_pub: &VerifyingKey, data: &[u8]) -> Option { + if data.len() <= NONCE_LEN { + return None; + } + + let nonce = Nonce::from_slice(&data[..NONCE_LEN]); + let key = keccak256(shared_secret(view, eph_pub)); + let cipher = ChaCha20Poly1305::new(key.as_slice().into()); + + let decrypted_plaintext = cipher.decrypt(nonce, &data[NONCE_LEN..]).ok()?; + String::from_utf8(decrypted_plaintext).ok() +} + +/// Encrypts a secure note by using the ephemeral private key and the view public one. +/// +/// Returns: +/// ```text +/// ┌────────────┬────────────────────────┐ +/// │ nonce (12B)│ ciphertext (remaining) │ +/// └────────────┴────────────────────────┘ +/// ``` +pub fn try_encrypt_note( + eph: &SigningKey, + view_pub: &VerifyingKey, + plaintext: &str, +) -> Option> { + let mut buf = vec![0u8; NONCE_LEN]; + OsRng.fill_bytes(&mut buf); + + let nonce = Nonce::from_slice(&buf); + let key = keccak256(shared_secret(eph, view_pub)); + let cipher = ChaCha20Poly1305::new(key.as_slice().into()); + + buf.extend(cipher.encrypt(nonce, plaintext.as_bytes()).ok()?); + Some(buf) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_note_roundtrip() { + let view = SigningKey::from_slice(B256::random().as_slice()).unwrap(); + let eph = SigningKey::from_slice(B256::random().as_slice()).unwrap(); + let plaintext = "Stealthy".to_string(); + + let cipher_text = try_encrypt_note(&eph, view.verifying_key(), &plaintext).unwrap(); + + assert_eq!(Some(plaintext), try_decrypt_node(&view, eph.verifying_key(), &cipher_text)); + } + + #[test] + fn test_stealth_roundtrip() { + let spend = SigningKey::from_slice(B256::random().as_slice()).unwrap(); + let view = SigningKey::from_slice(B256::random().as_slice()).unwrap(); + let eph = SigningKey::from_slice(B256::random().as_slice()).unwrap(); + + let (sth_addr, tag) = stealth_address(spend.verifying_key(), view.verifying_key(), &eph); + + let key = stealth_key(&spend, &view, eph.verifying_key(), Some(tag)).unwrap(); + + assert_eq!( + sth_addr, + Address::from_raw_public_key( + &key.verifying_key().to_encoded_point(false).to_bytes()[1..] + ) + ); + } +} diff --git a/stealth-addresses/src/generator/addr.rs b/stealth-addresses/src/generator/addr.rs new file mode 100644 index 0000000..bc3cb61 --- /dev/null +++ b/stealth-addresses/src/generator/addr.rs @@ -0,0 +1,68 @@ +use crate::crypto; +use alloy_primitives::{hex, B256}; +use clap::Parser; +use k256::ecdsa::{SigningKey, VerifyingKey}; +use reth_tracing::tracing::info; + +#[derive(Debug, Clone)] +pub struct StealthMeta { + /// View public key + pub view_pub: VerifyingKey, + /// Spend public key + pub spend_pub: VerifyingKey, +} + +#[derive(Debug, Parser)] +pub struct Command { + #[arg(value_parser = parse_stealth_meta_address)] + recipient: StealthMeta, + note: Option, +} + +impl Command { + pub async fn execute(self) -> eyre::Result<()> { + info!(target: "reth::cli", "Generating random stealth address"); + + let ephemeral = SigningKey::from_slice(B256::random().as_slice())?; + + let (stealth_address, view_tag) = crypto::stealth_address( + &self.recipient.spend_pub, + &self.recipient.view_pub, + &ephemeral, + ); + + let eph_pub = + hex::encode_prefixed(ephemeral.verifying_key().to_encoded_point(true).as_bytes()); + + info!("🔐 🔐 🔐"); + info!( + ephemeral_public_key = ?eph_pub, + ?view_tag, + ?stealth_address, + ); + + if let Some(note) = self.note { + let mut payload = vec![view_tag]; + payload.extend( + crypto::try_encrypt_note(&ephemeral, &self.recipient.view_pub, ¬e).unwrap(), + ); + info!("encrypted note with view tag prefix:\"{}\"", hex::encode_prefixed(payload)) + } + info!("🔐 🔐 🔐"); + + Ok(()) + } +} + +fn parse_stealth_meta_address(value: &str) -> eyre::Result { + let expect_str = "right meta format."; + + let public_keys = value.split(':').last().expect(expect_str); + let public_keys = hex::decode(public_keys).expect(expect_str); + assert!(public_keys.len() == 66, "{expect_str}"); + + Ok(StealthMeta { + view_pub: crypto::to_verifying_key(&public_keys[..33]).unwrap(), + spend_pub: crypto::to_verifying_key(&public_keys[33..]).unwrap(), + }) +} diff --git a/stealth-addresses/src/generator/key.rs b/stealth-addresses/src/generator/key.rs new file mode 100644 index 0000000..7f8c0f1 --- /dev/null +++ b/stealth-addresses/src/generator/key.rs @@ -0,0 +1,41 @@ +use alloy_primitives::{hex, Address, B256}; +use clap::Parser; +use k256::ecdsa::{SigningKey, VerifyingKey}; +use reth_tracing::tracing::info; + +use crate::crypto; + +#[derive(Debug, Parser)] +pub struct Command { + #[arg(value_parser = parse_ephemeral_public_key)] + ephemeral: VerifyingKey, + view: B256, + spend: B256, +} + +impl Command { + pub async fn execute(self) -> eyre::Result<()> { + info!(target: "reth::cli", "Generating stealth address private key"); + + let view = SigningKey::from_slice(self.view.as_slice())?; + let spend = SigningKey::from_slice(self.spend.as_slice())?; + + let stealth_key = crypto::stealth_key(&spend, &view, &self.ephemeral, None).unwrap(); + + let stealth_address = Address::from_private_key(&stealth_key); + let stealth_key = hex::encode_prefixed(stealth_key.to_bytes()); + + info!(?stealth_address, stealth_key); + + Ok(()) + } +} + +fn parse_ephemeral_public_key(value: &str) -> eyre::Result { + let expect_str = "to be 33 bytes."; + + let eph_pub = hex::decode(value).expect(expect_str); + assert!(eph_pub.len() == 33, "{expect_str}"); + + Ok(crypto::to_verifying_key(&eph_pub).unwrap()) +} diff --git a/stealth-addresses/src/generator/meta.rs b/stealth-addresses/src/generator/meta.rs new file mode 100644 index 0000000..7edeb8f --- /dev/null +++ b/stealth-addresses/src/generator/meta.rs @@ -0,0 +1,28 @@ +use alloy_primitives::{hex, B256}; +use clap::Parser; +use k256::ecdsa::SigningKey; +use reth_tracing::tracing::info; + +#[derive(Debug, Parser)] +pub struct Command { + view: B256, + spend: B256, +} + +impl Command { + pub async fn execute(self) -> eyre::Result<()> { + info!(target: "reth::cli", "Generating stealth meta address"); + + let view = SigningKey::from_slice(self.view.as_slice())?; + let view = hex::encode(view.verifying_key().to_encoded_point(true).as_bytes()); + + let spend = SigningKey::from_slice(self.spend.as_slice())?; + let spend = hex::encode(spend.verifying_key().to_encoded_point(true).as_bytes()); + + let meta = format!("sth:eth:{view}{spend}"); + + info!(meta); + + Ok(()) + } +} diff --git a/stealth-addresses/src/generator/mod.rs b/stealth-addresses/src/generator/mod.rs new file mode 100644 index 0000000..b2e9ded --- /dev/null +++ b/stealth-addresses/src/generator/mod.rs @@ -0,0 +1,39 @@ +//! Command that initializes the node from a genesis file. +use clap::{command, Parser, Subcommand}; + +mod addr; +mod key; +mod meta; + +/// `stealthy gen` command +#[derive(Debug, Parser)] +pub struct Command { + #[command(subcommand)] + command: Generator, +} + +/// Commands to be executed +#[derive(Debug, Subcommand)] +pub enum Generator { + /// Generate a stealth address from a stealth meta address alongside an optional encrypted + /// note. + #[command(name = "addr")] + Address(addr::Command), + /// Generate a stealth meta address from view and spend private keys. + #[command(name = "meta")] + Meta(meta::Command), + /// Generate the stealth address private key from the ephemeral public key and view & spend + /// private keys. + #[command(name = "key")] + Key(key::Command), +} + +impl Command { + pub async fn execute(self) -> eyre::Result<()> { + match self.command { + Generator::Address(command) => command.execute().await, + Generator::Meta(command) => command.execute().await, + Generator::Key(command) => command.execute().await, + } + } +} diff --git a/stealth-addresses/src/main.rs b/stealth-addresses/src/main.rs new file mode 100644 index 0000000..b87cbf4 --- /dev/null +++ b/stealth-addresses/src/main.rs @@ -0,0 +1,43 @@ +use clap::Parser; +use futures::Future; +use reth_exex::{ExExContext, ExExEvent}; +use reth_node_api::FullNodeComponents; +use reth_node_ethereum::EthereumNode; + +mod cli; +mod crypto; +mod generator; +mod parser; + +async fn init_stealthy( + ctx: ExExContext, +) -> eyre::Result>> { + Ok(stealthy(ctx)) +} + +/// Checks every committed chain of blocks for stealth addresses belonging to us according to [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564). +/// +/// Assumes that `VIEW_KEY` has been set as an environment variable. +async fn stealthy(mut ctx: ExExContext) -> eyre::Result<()> { + while let Some(notification) = ctx.notifications.recv().await { + if let Some(committed_chain) = notification.committed_chain() { + parser::peek(&committed_chain); + + ctx.events.send(ExExEvent::FinishedHeight(committed_chain.tip().number))?; + } + } + + Ok(()) +} + +fn main() -> eyre::Result<()> { + cli::Cli::parse().run(|builder, _| async move { + let handle = builder + .node(EthereumNode::default()) + .install_exex("Stealthy", init_stealthy) + .launch() + .await?; + + handle.wait_for_node_exit().await + }) +} diff --git a/stealth-addresses/src/parser.rs b/stealth-addresses/src/parser.rs new file mode 100644 index 0000000..84d3926 --- /dev/null +++ b/stealth-addresses/src/parser.rs @@ -0,0 +1,75 @@ +#![allow(dead_code)] + +use crate::crypto; +use alloy_primitives::hex; +use alloy_sol_types::{sol, SolEvent}; +use k256::ecdsa::SigningKey; +use reth::primitives::{address, format_ether, Address, B256, U256}; +use reth_execution_types::Chain; +use reth_tracing::tracing::info; +use std::str::FromStr; + +const ANNOUNCER: Address = address!("55649E01B5Df198D18D95b5cc5051630cfD45564"); + +sol!( + event ERC5564Announcement( + uint256 indexed schemeId, + address indexed stealthAddress, + address indexed caller, + bytes ephemeralPubKey, + bytes metadata + ); +); + +fn access_keystore() -> SigningKey { + let view = B256::from_str(&std::env::var("VIEW_KEY").expect("exists")).expect("valid"); + SigningKey::from_slice(view.as_slice()).expect("valid") +} + +/// Checks the blocks for any stealth address announcements according to [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564). +pub(crate) fn peek(chain: &Chain) { + let view = access_keystore(); + for announcement in get_announcements(chain) { + if let Some(ephemeral_pub) = crypto::to_verifying_key(&announcement.ephemeralPubKey) { + let view_tag = announcement.metadata[0]; + if crypto::is_ours(&view, &ephemeral_pub, view_tag) { + let hex_ephemeral_pub = + hex::encode_prefixed(ephemeral_pub.to_encoded_point(true).as_bytes()); + + let balance = chain + .execution_outcome() + .bundle + .account(&announcement.stealthAddress) + .and_then(|acc| acc.info.as_ref().map(|i| format_ether(i.balance))); + + info!("🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸"); + info!("🎉 One of us! One of us! 🎉"); + info!( + ephemeral_public_key = ?hex_ephemeral_pub, + stealth_address = ?announcement.stealthAddress, + ?view_tag, + balance, + ); + if let Some(note) = + crypto::try_decrypt_node(&view, &ephemeral_pub, &announcement.metadata[1..]) + { + info!("🔐 Found a secure note! 🔐"); + info!("🔐 {note} 🔐"); + } + info!("🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸"); + } + } + } +} + +/// Gets all [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564) announcements with `secp256k1` scheme. +fn get_announcements(chain: &Chain) -> Vec { + chain + .block_receipts_iter() + .flat_map(|receipts| receipts.iter().flatten()) + .flat_map(|r| r.logs.iter()) + .filter(|l| l.address == ANNOUNCER) + .filter_map(|l| ERC5564Announcement::decode_log(l, false).ok().map(|e| e.data)) + .filter(|ev| ev.schemeId == U256::from(1) && !ev.metadata.is_empty()) + .collect() +} From 16f2d23e3a919e2507c19118d6771bb34a226887 Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Sun, 14 Jul 2024 23:01:24 +0200 Subject: [PATCH 2/2] update README.md --- Cargo.lock | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 7 +++++ README.md | 1 + 3 files changed, 91 insertions(+), 2 deletions(-) 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