diff --git a/.changelog/unreleased/improvements/2012-sign-eth-txs.md b/.changelog/unreleased/improvements/2012-sign-eth-txs.md new file mode 100644 index 0000000000..63dfa70870 --- /dev/null +++ b/.changelog/unreleased/improvements/2012-sign-eth-txs.md @@ -0,0 +1,2 @@ +- Sign transactions originating from the Namada relayer that are sent to + Ethereum ([\#2012](https://github.com/anoma/namada/pull/2012)) \ No newline at end of file diff --git a/apps/src/lib/cli/relayer.rs b/apps/src/lib/cli/relayer.rs index 9d241f5cd0..1dee9b784e 100644 --- a/apps/src/lib/cli/relayer.rs +++ b/apps/src/lib/cli/relayer.rs @@ -1,7 +1,4 @@ -use std::sync::Arc; - use color_eyre::eyre::Result; -use namada::eth_bridge::ethers::providers::{Http, Provider}; use namada::types::io::Io; use namada_sdk::eth_bridge::{bridge_pool, validator_set}; @@ -9,6 +6,7 @@ use crate::cli; use crate::cli::api::{CliApi, CliClient}; use crate::cli::args::{CliToSdk, CliToSdkCtxless}; use crate::cli::cmds::*; +use crate::cli::utils::get_eth_rpc_client; impl CliApi { pub async fn handle_relayer_command( @@ -58,10 +56,8 @@ impl CliApi { ) }); client.wait_until_node_is_synced(io).await?; - let eth_client = Arc::new( - Provider::::try_from(&args.eth_rpc_endpoint) - .unwrap(), - ); + let eth_client = + get_eth_rpc_client(&args.eth_rpc_endpoint).await; let args = args.to_sdk_ctxless(); bridge_pool::relay_bridge_pool_proof( eth_client, &client, io, args, @@ -151,10 +147,8 @@ impl CliApi { ) }); client.wait_until_node_is_synced(io).await?; - let eth_client = Arc::new( - Provider::::try_from(&args.eth_rpc_endpoint) - .unwrap(), - ); + let eth_client = + get_eth_rpc_client(&args.eth_rpc_endpoint).await; let args = args.to_sdk_ctxless(); validator_set::relay_validator_set_update( eth_client, &client, io, args, diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index 7c8bc4100c..1a92f97770 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -3,15 +3,27 @@ use std::fmt::Debug; use std::io::Write; use std::marker::PhantomData; use std::str::FromStr; +use std::sync::Arc; use clap::{ArgAction, ArgMatches}; use color_eyre::eyre::Result; +use data_encoding::HEXLOWER_PERMISSIVE; +use namada::eth_bridge::ethers::core::k256::elliptic_curve::SecretKey as Secp256k1Sk; +use namada::eth_bridge::ethers::middleware::SignerMiddleware; +use namada::eth_bridge::ethers::providers::{Http, Middleware, Provider}; +use namada::eth_bridge::ethers::signers::{Signer, Wallet}; use super::args; use super::context::Context; use crate::cli::api::CliIo; use crate::cli::context::FromContext; +/// Environment variable where Ethereum relayer private +/// keys are stored. +// TODO: remove this in favor of getting eth keys from +// namadaw, ledger, or something more secure +const RELAYER_KEY_ENV_VAR: &str = "NAMADA_RELAYER_KEY"; + // We only use static strings pub type App = clap::Command; pub type ClapArg = clap::Arg; @@ -363,3 +375,30 @@ pub fn safe_exit(_: i32) -> ! { panic!("Test failed because the client exited unexpectedly.") } + +/// Load an Ethereum wallet from the environment. +fn get_eth_signer_from_env(chain_id: u64) -> Option { + let relayer_key = std::env::var(RELAYER_KEY_ENV_VAR).ok()?; + let relayer_key = HEXLOWER_PERMISSIVE.decode(relayer_key.as_ref()).ok()?; + let relayer_key = Secp256k1Sk::from_slice(&relayer_key).ok()?; + + let wallet: Wallet<_> = relayer_key.into(); + let wallet = wallet.with_chain_id(chain_id); + + Some(wallet) +} + +/// Return an Ethereum RPC client. +pub async fn get_eth_rpc_client(url: &str) -> Arc { + let client = Provider::::try_from(url) + .expect("Failed to instantiate Ethereum RPC client"); + let chain_id = client + .get_chainid() + .await + .expect("Failed to query chain id") + .as_u64(); + let signer = get_eth_signer_from_env(chain_id).unwrap_or_else(|| { + panic!("Failed to get Ethereum key from {RELAYER_KEY_ENV_VAR} env var") + }); + Arc::new(SignerMiddleware::new(client, signer)) +}