Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relayer: permissible ERC20 tokens #99

Merged
merged 5 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/shielder-cli/src/shielder_ops/withdraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use shielder_account::{
use shielder_contract::{
events::get_event, merkle_path::get_current_merkle_path, ShielderContract::WithdrawNative,
};
use shielder_relayer::{QuoteFeeResponse, RelayQuery, RelayResponse};
use shielder_relayer::{FeeToken, QuoteFeeResponse, RelayQuery, RelayResponse};
use shielder_setup::version::contract_version;
use tokio::time::sleep;
use tracing::{debug, info};
Expand Down Expand Up @@ -147,5 +147,6 @@ async fn prepare_relayer_query(
nullifier_hash: calldata.oldNullifierHash,
new_note: calldata.newNote,
proof: calldata.proof,
fee_token: FeeToken::Native,
})
}
8 changes: 8 additions & 0 deletions crates/shielder-relayer/src/config/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,12 @@ pub struct CLIConfig {
the default value is `{DEFAULT_RELAY_GAS:?}`.")
)]
pub relay_gas: Option<u64>,

#[clap(
long,
help = "Addresses of the ERC20 tokens that are qualified as a fee token.",
long_help = "Addresses of the ERC20 tokens that are qualified as a fee token. If not provided, the value from the \
environment variable `{FEE_TOKEN_ENV}` will be used. If that is not set, assumed to be empty."
)]
pub fee_tokens: Option<Vec<String>>,
kroist marked this conversation as resolved.
Show resolved Hide resolved
}
17 changes: 15 additions & 2 deletions crates/shielder-relayer/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use defaults::{
pub use enums::{DryRunning, LoggingFormat, NoncePolicy};
use shielder_contract::alloy_primitives::{Address, U256};
use shielder_relayer::{
BALANCE_MONITOR_INTERVAL_SECS_ENV, DRY_RUNNING_ENV, FEE_DESTINATION_KEY_ENV,
BALANCE_MONITOR_INTERVAL_SECS_ENV, DRY_RUNNING_ENV, FEE_DESTINATION_KEY_ENV, FEE_TOKENS_ENV,
LOGGING_FORMAT_ENV, NODE_RPC_URL_ENV, NONCE_POLICY_ENV, RELAYER_HOST_ENV,
RELAYER_METRICS_PORT_ENV, RELAYER_PORT_ENV, RELAYER_SIGNING_KEYS_ENV,
RELAY_COUNT_FOR_RECHARGE_ENV, RELAY_GAS_ENV, SHIELDER_CONTRACT_ADDRESS_ENV, TOTAL_FEE_ENV,
Expand Down Expand Up @@ -52,12 +52,13 @@ pub struct ChainConfig {
pub relay_gas: u64,
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct OperationalConfig {
pub balance_monitor_interval_secs: u64,
pub nonce_policy: NoncePolicy,
pub dry_running: DryRunning,
pub relay_count_for_recharge: u32,
pub fee_tokens: Vec<Address>,
}

/// Resolved configuration for the Shielder relayer. Order of precedence is:
Expand Down Expand Up @@ -96,6 +97,7 @@ fn resolve_config_from_cli_config(
relay_count_for_recharge,
total_fee,
relay_gas,
fee_tokens,
}: CLIConfig,
) -> ServerConfig {
let to_address = |s: &str| Address::from_str(s).expect("Invalid address");
Expand Down Expand Up @@ -136,6 +138,16 @@ fn resolve_config_from_cli_config(
relay_gas: resolve_value(relay_gas, RELAY_GAS_ENV, Some(DEFAULT_RELAY_GAS)),
};

let fee_tokens = fee_tokens
.unwrap_or_else(|| {
std::env::var(FEE_TOKENS_ENV)
.map(|env| env.split(',').map(|s| s.to_string()).collect())
.unwrap_or_default()
})
.iter()
.map(|a| to_address(a))
.collect();

let operational_config = OperationalConfig {
balance_monitor_interval_secs: resolve_value(
balance_monitor_interval_secs,
Expand All @@ -149,6 +161,7 @@ fn resolve_config_from_cli_config(
RELAY_COUNT_FOR_RECHARGE_ENV,
Some(DEFAULT_RELAY_COUNT_FOR_RECHARGE),
),
fee_tokens,
};

ServerConfig {
Expand Down
4 changes: 4 additions & 0 deletions crates/shielder-relayer/src/config/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fn config_resolution() {
let relay_count_for_recharge = DEFAULT_RELAY_COUNT_FOR_RECHARGE;
let total_fee = DEFAULT_TOTAL_FEE.to_string();
let relay_gas: u64 = DEFAULT_RELAY_GAS + 1;
let fee_tokens = vec![address!("1111111111111111111111111111111111111111")];

let expected_config = ServerConfig {
logging_format, // from CLI
Expand All @@ -48,6 +49,7 @@ fn config_resolution() {
nonce_policy, // default
dry_running, // from CLI
relay_count_for_recharge, // default
fee_tokens, // from env
},
};

Expand All @@ -67,6 +69,7 @@ fn config_resolution() {
relay_count_for_recharge: None,
total_fee: Some(total_fee),
relay_gas: None,
fee_tokens: None,
};

// ---- Environment variables. -----------------------------------------------------------
Expand All @@ -79,6 +82,7 @@ fn config_resolution() {
std::env::set_var(FEE_DESTINATION_KEY_ENV, fee_destination_key);
std::env::set_var(RELAYER_SIGNING_KEYS_ENV, format!("{key1},{key2}"));
std::env::set_var(RELAY_GAS_ENV, relay_gas.to_string());
std::env::set_var(FEE_TOKENS_ENV, "1111111111111111111111111111111111111111");
}

// ---- Test. ------------------------------------------------------------------------------
Expand Down
8 changes: 8 additions & 0 deletions crates/shielder-relayer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub const DRY_RUNNING_ENV: &str = "DRY_RUNNING";
pub const RELAY_COUNT_FOR_RECHARGE_ENV: &str = "RELAY_COUNT_FOR_RECHARGE";
pub const TOTAL_FEE_ENV: &str = "TOTAL_FEE";
pub const RELAY_GAS_ENV: &str = "RELAY_GAS";
pub const FEE_TOKENS_ENV: &str = "FEE_TOKENS";

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(transparent)]
Expand Down Expand Up @@ -76,6 +77,13 @@ pub struct RelayQuery {
pub nullifier_hash: U256,
pub new_note: U256,
pub proof: Bytes,
pub fee_token: FeeToken,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum FeeToken {
Native,
ERC20(Address),
}

pub fn server_error(msg: &str) -> Response {
Expand Down
2 changes: 2 additions & 0 deletions crates/shielder-relayer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub struct AppState {
pub signer_addresses: Vec<Address>,
pub taskmaster: Taskmaster,
pub balances: Balances,
pub fee_tokens: Vec<Address>,
}

struct Signers {
Expand Down Expand Up @@ -136,6 +137,7 @@ async fn start_main_server(config: &ServerConfig, signers: Signers) -> Result<()
config.operations.dry_running,
report_for_recharge,
),
fee_tokens: config.operations.fee_tokens.clone(),
};

let app = Router::new()
Expand Down
21 changes: 20 additions & 1 deletion crates/shielder-relayer/src/relay/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use shielder_contract::{
alloy_primitives::{Address, U256},
ShielderContract::withdrawNativeCall,
};
use shielder_relayer::{server_error, RelayQuery, RelayResponse, SimpleServiceResponse};
use shielder_relayer::{server_error, FeeToken, RelayQuery, RelayResponse, SimpleServiceResponse};
use shielder_setup::version::{contract_version, ContractVersion};
use tracing::{debug, error};

Expand All @@ -33,6 +33,9 @@ pub async fn relay(app_state: State<AppState>, Json(query): Json<RelayQuery>) ->
if let Err(response) = check_expected_version(&query, &mut request_trace) {
return response;
}
if let Err(response) = check_fee_token(&app_state.fee_tokens, &query, &mut request_trace) {
return response;
}

let withdraw_call = create_call(query, app_state.fee_destination, app_state.total_fee);
let Ok(rx) = app_state
Expand Down Expand Up @@ -103,3 +106,19 @@ fn check_expected_version(
}
Ok(())
}

fn check_fee_token(
permissible_tokens: &[Address],
query: &RelayQuery,
request_trace: &mut RequestTrace,
) -> Result<(), Response> {
match &query.fee_token {
FeeToken::ERC20(erc20) if !permissible_tokens.contains(erc20) => {
request_trace.record_incorrect_token_fee(erc20);
Err(bad_request(&format!(
"Fee token {erc20} is not allowed. Only native and {permissible_tokens:?} tokens are supported."
)))
}
_ => Ok(()),
}
}
6 changes: 6 additions & 0 deletions crates/shielder-relayer/src/relay/request_trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ impl RequestTrace {
self.finish("❌ VERSION FAILURE");
}

pub fn record_incorrect_token_fee(&mut self, requested_token: &Address) {
metrics::counter!(WITHDRAW_FAILURE).increment(1);
error!("Requested token fee is not supported: {requested_token}");
self.finish("❌ FEE TOKEN FAILURE");
}

pub fn record_failure(&mut self, err: ShielderContractError) {
metrics::counter!(WITHDRAW_FAILURE).increment(1);
error!("Relay failed: {err}");
Expand Down
3 changes: 2 additions & 1 deletion crates/shielder-relayer/tests/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use alloy_primitives::{Address, Bytes, U256};
use rand::Rng;
use reqwest::Response;
use serde::{Deserialize, Serialize};
use shielder_relayer::RelayQuery;
use shielder_relayer::{FeeToken, RelayQuery};
use shielder_setup::version::contract_version;
use testcontainers::{
core::IntoContainerPort, runners::AsyncRunner, ContainerAsync, ContainerRequest, Image,
Expand Down Expand Up @@ -72,6 +72,7 @@ impl TestContext {
nullifier_hash: U256::ZERO,
new_note: U256::ZERO,
proof: Bytes::new(),
fee_token: FeeToken::Native,
})
.send()
.await
Expand Down
3 changes: 2 additions & 1 deletion crates/stress-testing/src/party.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use shielder_circuits::{
withdraw::WithdrawCircuit,
};
use shielder_contract::{alloy_primitives::U256, merkle_path::get_current_merkle_path};
use shielder_relayer::{QuoteFeeResponse, RelayQuery};
use shielder_relayer::{FeeToken, QuoteFeeResponse, RelayQuery};
use shielder_setup::version::contract_version;

use crate::{actor::Actor, config::Config, util::proving_keys, WITHDRAW_AMOUNT};
Expand Down Expand Up @@ -127,6 +127,7 @@ async fn prepare_relay_query(
nullifier_hash: calldata.oldNullifierHash,
new_note: calldata.newNote,
proof: calldata.proof,
fee_token: FeeToken::Native,
};
println!(" ✅ Prepared relay query for actor {}", actor.id);
Ok(query)
Expand Down