From fbe3ac7e5270df0b6b8740a9ad765f07bf6cb679 Mon Sep 17 00:00:00 2001 From: Gregory Hill Date: Mon, 9 May 2022 18:11:10 +0100 Subject: [PATCH 1/2] refactor!: replace auto-register flag for future multi-collateral compat Signed-off-by: Gregory Hill --- vault/src/error.rs | 2 + vault/src/faucet.rs | 26 ++++++------- vault/src/system.rs | 93 +++++++++++++++++++++++++++------------------ 3 files changed, 72 insertions(+), 49 deletions(-) diff --git a/vault/src/error.rs b/vault/src/error.rs index 537dc57a8..1bcd77dca 100644 --- a/vault/src/error.rs +++ b/vault/src/error.rs @@ -22,6 +22,8 @@ pub enum Error { TryIntoIntError(#[from] std::num::TryFromIntError), #[error("Deadline has expired")] DeadlineExpired, + #[error("Faucet url not set")] + FaucetUrlNotSet, #[error("ServiceError: {0}")] ServiceError(#[from] ServiceError), diff --git a/vault/src/faucet.rs b/vault/src/faucet.rs index 8d6266f8b..62134f16b 100644 --- a/vault/src/faucet.rs +++ b/vault/src/faucet.rs @@ -3,9 +3,7 @@ use hex::FromHex; use jsonrpc_core::Value; use jsonrpc_core_client::{transports::http as jsonrpc_http, TypedClient}; use parity_scale_codec::{Decode, Encode}; -use runtime::{ - AccountId, BtcPublicKey, CurrencyId, CurrencyIdExt, InterBtcParachain, VaultId, VaultRegistryPallet, TX_FEES, -}; +use runtime::{AccountId, CurrencyId, CurrencyIdExt, InterBtcParachain, VaultId, VaultRegistryPallet, TX_FEES}; use serde::{Deserialize, Deserializer}; #[derive(Debug, Clone, Deserialize)] @@ -45,21 +43,26 @@ async fn get_funding(faucet_connection: TypedClient, vault_id: VaultId) -> Resul Ok(()) } -pub async fn fund_and_register( - parachain_rpc: &InterBtcParachain, - faucet_url: &str, - vault_id: &VaultId, - maybe_public_key: Option, -) -> Result<(), Error> { +pub async fn fund_account(faucet_url: &str, vault_id: &VaultId) -> Result { tracing::info!("Connecting to the faucet"); let connection = jsonrpc_http::connect::(faucet_url).await?; - let currency_id: CurrencyId = vault_id.collateral_currency(); // Receive user allowance from faucet if let Err(e) = get_funding(connection.clone(), vault_id.clone()).await { tracing::warn!("Failed to get funding from faucet: {}", e); } + Ok(connection) +} + +pub async fn fund_and_register( + parachain_rpc: &InterBtcParachain, + faucet_url: &str, + vault_id: &VaultId, +) -> Result<(), Error> { + let connection = fund_account(faucet_url, vault_id).await?; + let currency_id: CurrencyId = vault_id.collateral_currency(); + let user_allowance_in_dot: u128 = get_faucet_allowance(connection.clone(), "user_allowance").await?; let registration_collateral = user_allowance_in_dot .checked_mul(currency_id.inner().one()) @@ -68,9 +71,6 @@ pub async fn fund_and_register( .ok_or(Error::ArithmeticUnderflow)?; tracing::info!("Registering the vault"); - if let Some(public_key) = maybe_public_key { - parachain_rpc.register_public_key(public_key).await?; - } parachain_rpc.register_vault(vault_id, registration_collateral).await?; // Receive vault allowance from faucet diff --git a/vault/src/system.rs b/vault/src/system.rs index 8d81d9a8d..0bc0ada18 100644 --- a/vault/src/system.rs +++ b/vault/src/system.rs @@ -19,9 +19,9 @@ use futures::{ use git_version::git_version; use runtime::{ cli::{parse_duration_minutes, parse_duration_ms}, - parse_collateral_currency, BtcPublicKey, BtcRelayPallet, CollateralBalancesPallet, CurrencyId, - Error as RuntimeError, InterBtcParachain, PrettyPrint, RegisterVaultEvent, StoreMainChainHeaderEvent, - UpdateActiveBlockEvent, UtilFuncs, VaultCurrencyPair, VaultId, VaultRegistryPallet, + parse_collateral_currency, BtcRelayPallet, CollateralBalancesPallet, CurrencyId, Error as RuntimeError, + InterBtcParachain, PrettyPrint, RegisterVaultEvent, StoreMainChainHeaderEvent, UpdateActiveBlockEvent, UtilFuncs, + VaultCurrencyPair, VaultId, VaultRegistryPallet, }; use service::{wait_or_shutdown, Error as ServiceError, MonitoringConfig, Service, ShutdownSender}; use std::{collections::HashMap, pin::Pin, sync::Arc, time::Duration}; @@ -31,18 +31,36 @@ pub const VERSION: &str = git_version!(args = ["--tags"]); pub const AUTHORS: &str = env!("CARGO_PKG_AUTHORS"); pub const NAME: &str = env!("CARGO_PKG_NAME"); pub const ABOUT: &str = env!("CARGO_PKG_DESCRIPTION"); + const RESTART_INTERVAL: Duration = Duration::from_secs(10800); // restart every 3 hours +fn parse_collateral_and_amount( + s: &str, +) -> Result<(CurrencyId, Option), Box> { + let pos = s + .find('=') + .ok_or_else(|| format!("invalid CurrencyId=amount: no `=` found in `{}`", s))?; + + let val = &s[pos + 1..]; + Ok(( + parse_collateral_currency(&s[..pos])?, + if val.contains("faucet") { + None + } else { + Some(val.parse()?) + }, + )) +} + #[derive(Parser, Clone, Debug)] pub struct VaultServiceConfig { /// Automatically register the vault with the given amount of collateral and a newly generated address. - #[clap(long)] - pub auto_register_with_collateral: Option, + #[clap(long, parse(try_from_str = parse_collateral_and_amount))] + pub auto_register: Vec<(CurrencyId, Option)>, - /// Automatically register the vault with the collateral received from the faucet and a newly generated address. - /// The parameter is the URL of the faucet - #[clap(long, conflicts_with("auto-register-with-collateral"))] - pub auto_register_with_faucet_url: Option, + /// Pass the faucet URL for auto-registration. + #[clap(long)] + pub faucet_url: Option, /// Opt out of participation in replace requests. #[clap(long)] @@ -102,11 +120,6 @@ pub struct VaultServiceConfig { /// Don't refund overpayments. #[clap(long)] pub no_auto_refund: bool, - - /// The currency to use for the collateral, e.g. "DOT" or "KSM". - /// Defaults to the relay chain currency if not set. - #[clap(long, parse(try_from_str = parse_collateral_currency))] - pub collateral_currency_id: Option, } async fn active_block_listener( @@ -452,14 +465,8 @@ impl VaultService { } } - fn get_vault_id(&self) -> VaultId { + fn get_vault_id(&self, collateral_currency: CurrencyId) -> VaultId { let account_id = self.btc_parachain.get_account_id(); - - let collateral_currency = if let Some(currency_id) = self.config.collateral_currency_id { - currency_id - } else { - self.btc_parachain.relay_chain_currency_id - }; let wrapped_currency = self.btc_parachain.wrapped_currency_id; VaultId { @@ -474,6 +481,12 @@ impl VaultService { async fn run_service(&self) -> Result<(), Error> { let account_id = self.btc_parachain.get_account_id().clone(); + // exit if auto-register uses faucet and faucet url not set + if self.config.auto_register.iter().any(|(_, o)| o.is_none()) && self.config.faucet_url.is_none() { + // TODO: validate before bitcoin / parachain connections + return Err(Error::FaucetUrlNotSet); + } + let num_confirmations = match self.config.btc_confirmations { Some(x) => x, None => self.btc_parachain.get_bitcoin_confirmations().await?, @@ -492,7 +505,14 @@ impl VaultService { }); tokio::task::spawn(err_listener); - self.maybe_register_vault().await?; + self.maybe_register_public_key().await?; + join_all( + self.config + .auto_register + .iter() + .map(|(currency_id, amount)| self.maybe_register_vault(currency_id, amount)), + ) + .await; // purposefully _after_ maybe_register_vault and _before_ other calls self.vault_id_manager.fetch_vault_ids(false).await?; @@ -699,6 +719,11 @@ impl VaultService { } async fn maybe_register_public_key(&self) -> Result<(), Error> { + if let Some(faucet_url) = &self.config.faucet_url { + // fund the native token first to pay for tx fees + crate::faucet::fund_account(faucet_url, &self.get_vault_id(self.btc_parachain.native_currency_id)).await?; + } + if let None = self.btc_parachain.get_public_key().await? { tracing::info!("Registering bitcoin public key to the parachain..."); let new_key = self.btc_rpc_master_wallet.get_new_public_key().await?; @@ -708,8 +733,12 @@ impl VaultService { Ok(()) } - async fn maybe_register_vault(&self) -> Result<(), Error> { - let vault_id = self.get_vault_id(); + async fn maybe_register_vault( + &self, + collateral_currency: &CurrencyId, + maybe_collateral_amount: &Option, + ) -> Result<(), Error> { + let vault_id = self.get_vault_id(collateral_currency.clone()); match is_vault_registered(&self.btc_parachain, &vault_id).await { Err(Error::RuntimeError(RuntimeError::VaultLiquidated)) @@ -722,9 +751,7 @@ impl VaultService { } Ok(false) => { tracing::info!("[{}] Not registered", vault_id.pretty_print()); - - if let Some(collateral) = self.config.auto_register_with_collateral { - self.maybe_register_public_key().await?; + if let Some(collateral) = maybe_collateral_amount { tracing::info!("[{}] Automatically registering...", vault_id.pretty_print()); let free_balance = self .btc_parachain @@ -741,19 +768,13 @@ impl VaultService { ); free_balance } else { - collateral + collateral.clone() }, ) .await?; - } else if let Some(faucet_url) = &self.config.auto_register_with_faucet_url { + } else if let Some(faucet_url) = &self.config.faucet_url { tracing::info!("[{}] Automatically registering...", vault_id.pretty_print()); - let maybe_public_key = if let None = self.btc_parachain.get_public_key().await? { - tracing::info!("Created new bitcoin public key"); - Some(self.btc_rpc_master_wallet.get_new_public_key::().await?) - } else { - None - }; - faucet::fund_and_register(&self.btc_parachain, faucet_url, &vault_id, maybe_public_key).await?; + faucet::fund_and_register(&self.btc_parachain, faucet_url, &vault_id).await?; } self.vault_id_manager.add_vault_id(vault_id.clone()).await?; From a4d31d9e3fa6b9de977e392bb59c615cf0d95a57 Mon Sep 17 00:00:00 2001 From: Gregory Hill Date: Tue, 10 May 2022 09:53:05 +0100 Subject: [PATCH 2/2] refactor: remove additional add_vault_id in maybe_register_vault Signed-off-by: Gregory Hill --- vault/src/system.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/vault/src/system.rs b/vault/src/system.rs index 0bc0ada18..d0c607db3 100644 --- a/vault/src/system.rs +++ b/vault/src/system.rs @@ -776,8 +776,6 @@ impl VaultService { tracing::info!("[{}] Automatically registering...", vault_id.pretty_print()); faucet::fund_and_register(&self.btc_parachain, faucet_url, &vault_id).await?; } - - self.vault_id_manager.add_vault_id(vault_id.clone()).await?; } Err(x) => return Err(x), }