diff --git a/funds-manager/funds-manager-api/src/lib.rs b/funds-manager/funds-manager-api/src/lib.rs index 295f8b2..5b43139 100644 --- a/funds-manager/funds-manager-api/src/lib.rs +++ b/funds-manager/funds-manager-api/src/lib.rs @@ -24,6 +24,8 @@ pub const WITHDRAW_CUSTODY_ROUTE: &str = "withdraw"; /// The route to withdraw gas from custody pub const WITHDRAW_GAS_ROUTE: &str = "withdraw-gas"; +/// The route to register a gas wallet for a peer +pub const REGISTER_GAS_WALLET_ROUTE: &str = "register-gas-wallet"; /// The route to get fee wallets pub const GET_FEE_WALLETS_ROUTE: &str = "get-fee-wallets"; @@ -95,6 +97,23 @@ pub struct CreateGasWalletResponse { pub address: String, } +/// A request to allocate a gas wallet for a peer +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RegisterGasWalletRequest { + /// The peer ID of the peer to allocate a gas wallet for + pub peer_id: String, +} + +/// The response containing an newly active gas wallet's key +/// +/// Clients will hit the corresponding endpoint to register a gas wallet with +/// the funds manager when they spin up +#[derive(Debug, Serialize, Deserialize)] +pub struct RegisterGasWalletResponse { + /// The key of the active gas wallet + pub key: String, +} + // --- Hot Wallets --- // /// The request body for creating a hot wallet diff --git a/funds-manager/funds-manager-server/src/custody_client/gas_wallets.rs b/funds-manager/funds-manager-server/src/custody_client/gas_wallets.rs index e5b13a9..7b26e0c 100644 --- a/funds-manager/funds-manager-server/src/custody_client/gas_wallets.rs +++ b/funds-manager/funds-manager-server/src/custody_client/gas_wallets.rs @@ -7,11 +7,18 @@ use ethers::{ use rand::thread_rng; use tracing::info; -use crate::{error::FundsManagerError, helpers::create_secrets_manager_entry_with_description}; +use crate::{ + error::FundsManagerError, + helpers::{create_secrets_manager_entry_with_description, get_secret}, +}; use super::CustodyClient; impl CustodyClient { + // ------------ + // | Handlers | + // ------------ + /// Create a new gas wallet pub(crate) async fn create_gas_wallet(&self) -> Result { // Sample a new ethereum keypair @@ -38,6 +45,26 @@ impl CustodyClient { Ok(address) } + /// Register a gas wallet for a peer + /// + /// Returns the private key the client should use for gas + pub(crate) async fn register_gas_wallet( + &self, + peer_id: &str, + ) -> Result { + let gas_wallet = self.find_inactive_gas_wallet().await?; + let secret_name = Self::gas_wallet_secret_name(&gas_wallet.address); + let secret_value = get_secret(&secret_name, &self.aws_config).await?; + + // Update the gas wallet to be active and return the keypair + self.mark_gas_wallet_active(&gas_wallet.address, peer_id).await?; + Ok(secret_value) + } + + // ----------- + // | Helpers | + // ----------- + /// Get the secret name for a gas wallet's private key fn gas_wallet_secret_name(address: &str) -> String { format!("gas-wallet-{}", address) diff --git a/funds-manager/funds-manager-server/src/custody_client/queries.rs b/funds-manager/funds-manager-server/src/custody_client/queries.rs index 907037c..09cda82 100644 --- a/funds-manager/funds-manager-server/src/custody_client/queries.rs +++ b/funds-manager/funds-manager-server/src/custody_client/queries.rs @@ -27,6 +27,33 @@ impl CustodyClient { Ok(()) } + /// Find an inactive gas wallet + pub async fn find_inactive_gas_wallet(&self) -> Result { + let mut conn = self.get_db_conn().await?; + gas_wallets::table + .filter(gas_wallets::active.eq(false)) + .first::(&mut conn) + .await + .map_err(err_str!(FundsManagerError::Db)) + } + + /// Mark a gas wallet as active + pub async fn mark_gas_wallet_active( + &self, + address: &str, + peer_id: &str, + ) -> Result<(), FundsManagerError> { + let mut conn = self.get_db_conn().await?; + let updates = (gas_wallets::active.eq(true), gas_wallets::peer_id.eq(peer_id)); + diesel::update(gas_wallets::table.filter(gas_wallets::address.eq(address))) + .set(updates) + .execute(&mut conn) + .await + .map_err(err_str!(FundsManagerError::Db))?; + + Ok(()) + } + // --- Hot Wallets --- // /// Get all hot wallets diff --git a/funds-manager/funds-manager-server/src/db/models.rs b/funds-manager/funds-manager-server/src/db/models.rs index ac28488..e2b6a7c 100644 --- a/funds-manager/funds-manager-server/src/db/models.rs +++ b/funds-manager/funds-manager-server/src/db/models.rs @@ -119,6 +119,6 @@ impl GasWallet { /// Construct a new gas wallet pub fn new(address: String) -> Self { let id = Uuid::new_v4(); - GasWallet { id, address, peer_id: None, active: true, created_at: SystemTime::now() } + GasWallet { id, address, peer_id: None, active: false, created_at: SystemTime::now() } } } diff --git a/funds-manager/funds-manager-server/src/handlers.rs b/funds-manager/funds-manager-server/src/handlers.rs index 6607f09..06b50c8 100644 --- a/funds-manager/funds-manager-server/src/handlers.rs +++ b/funds-manager/funds-manager-server/src/handlers.rs @@ -6,7 +6,8 @@ use crate::Server; use bytes::Bytes; use funds_manager_api::{ CreateGasWalletResponse, CreateHotWalletRequest, CreateHotWalletResponse, - DepositAddressResponse, FeeWalletsResponse, HotWalletBalancesResponse, TransferToVaultRequest, + DepositAddressResponse, FeeWalletsResponse, HotWalletBalancesResponse, + RegisterGasWalletRequest, RegisterGasWalletResponse, TransferToVaultRequest, WithdrawFeeBalanceRequest, WithdrawFundsRequest, WithdrawGasRequest, WithdrawToHotWalletRequest, }; @@ -134,11 +135,29 @@ pub(crate) async fn create_gas_wallet_handler( _body: Bytes, // no body server: Arc, ) -> Result { - let address = server.custody_client.create_gas_wallet().await?; + let address = server + .custody_client + .create_gas_wallet() + .await + .map_err(|e| warp::reject::custom(ApiError::InternalError(e.to_string())))?; let resp = CreateGasWalletResponse { address }; Ok(warp::reply::json(&resp)) } +/// Handler for registering a gas wallet for a peer +pub(crate) async fn register_gas_wallet_handler( + req: RegisterGasWalletRequest, + server: Arc, +) -> Result { + let key = server + .custody_client + .register_gas_wallet(&req.peer_id) + .await + .map_err(|e| warp::reject::custom(ApiError::InternalError(e.to_string())))?; + let resp = RegisterGasWalletResponse { key }; + Ok(warp::reply::json(&resp)) +} + // --- Hot Wallets --- // /// Handler for creating a hot wallet diff --git a/funds-manager/funds-manager-server/src/main.rs b/funds-manager/funds-manager-server/src/main.rs index 184ed90..ca25989 100644 --- a/funds-manager/funds-manager-server/src/main.rs +++ b/funds-manager/funds-manager-server/src/main.rs @@ -21,16 +21,18 @@ use error::FundsManagerError; use ethers::signers::LocalWallet; use fee_indexer::Indexer; use funds_manager_api::{ - CreateHotWalletRequest, TransferToVaultRequest, WithdrawFeeBalanceRequest, WithdrawGasRequest, - WithdrawToHotWalletRequest, GET_DEPOSIT_ADDRESS_ROUTE, GET_FEE_WALLETS_ROUTE, INDEX_FEES_ROUTE, - PING_ROUTE, REDEEM_FEES_ROUTE, TRANSFER_TO_VAULT_ROUTE, WITHDRAW_CUSTODY_ROUTE, + CreateHotWalletRequest, RegisterGasWalletRequest, TransferToVaultRequest, + WithdrawFeeBalanceRequest, WithdrawGasRequest, WithdrawToHotWalletRequest, + GET_DEPOSIT_ADDRESS_ROUTE, GET_FEE_WALLETS_ROUTE, INDEX_FEES_ROUTE, PING_ROUTE, + REDEEM_FEES_ROUTE, REGISTER_GAS_WALLET_ROUTE, TRANSFER_TO_VAULT_ROUTE, WITHDRAW_CUSTODY_ROUTE, WITHDRAW_FEE_BALANCE_ROUTE, WITHDRAW_GAS_ROUTE, WITHDRAW_TO_HOT_WALLET_ROUTE, }; use handlers::{ create_gas_wallet_handler, create_hot_wallet_handler, get_deposit_address_handler, get_fee_wallets_handler, get_hot_wallet_balances_handler, index_fees_handler, - quoter_withdraw_handler, redeem_fees_handler, transfer_to_vault_handler, - withdraw_fee_balance_handler, withdraw_from_vault_handler, withdraw_gas_handler, + quoter_withdraw_handler, redeem_fees_handler, register_gas_wallet_handler, + transfer_to_vault_handler, withdraw_fee_balance_handler, withdraw_from_vault_handler, + withdraw_gas_handler, }; use middleware::{identity, with_hmac_auth, with_json_body}; use relayer_client::RelayerClient; @@ -347,6 +349,16 @@ async fn main() -> Result<(), Box> { .and(with_server(server.clone())) .and_then(create_gas_wallet_handler); + let register_gas_wallet = warp::post() + .and(warp::path("custody")) + .and(warp::path("gas-wallets")) + .and(warp::path(REGISTER_GAS_WALLET_ROUTE)) + .and(with_hmac_auth(server.clone())) + .map(with_json_body::) + .and_then(identity) + .and(with_server(server.clone())) + .and_then(register_gas_wallet_handler); + // --- Hot Wallets --- // let create_hot_wallet = warp::post() @@ -392,6 +404,7 @@ async fn main() -> Result<(), Box> { .or(withdraw_custody) .or(get_deposit_address) .or(withdraw_gas) + .or(register_gas_wallet) .or(add_gas_wallet) .or(get_balances) .or(withdraw_fee_balance)