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 3322e84e2f..f74ae1b5bf 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::{eyre, Report, Result}; -use namada::eth_bridge::ethers::providers::{Http, Provider}; use namada::ledger::eth_bridge::{bridge_pool, validator_set}; use namada::types::control_flow::ProceedOrElse; use namada::types::io::Io; @@ -10,6 +7,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; fn error() -> Report { eyre!("Fatal error") @@ -74,10 +72,8 @@ impl CliApi { .wait_until_node_is_synced::() .await .proceed_or_else(error)?; - 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::<_, _, IO>( eth_client, &client, args, @@ -191,10 +187,8 @@ impl CliApi { .wait_until_node_is_synced::() .await .proceed_or_else(error)?; - 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::<_, _, IO>( eth_client, &client, args, diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index 26cc38ff7f..9a44add694 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -3,14 +3,26 @@ 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, FromContext}; use crate::cli::api::CliIo; +/// 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; @@ -362,3 +374,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)) +}