diff --git a/Cargo.toml b/Cargo.toml index 35d0d82..d5a205a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ tokio = { version = "1.43.0", default-features = false, features = ["signal", "m tracing = { version = "0.1.41", default-features = false, features = ["attributes"]} tracing-subscriber = { version = "0.3.19", default-features = false, features = ["env-filter", "fmt", "json", "time", "ansi"] } url = { version = "2.5.4", default-features = false } -lru = {version = "0.16.0", default-features = false } +lru = { version = "0.16.0", default-features = false } [dev-dependencies] mockito = { version = "1.7.0", default-features = false } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5851fa6..037bbfe 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.88.0" +channel = "1.94.1" components = ["rustfmt", "clippy"] targets = ["x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"] profile = "minimal" diff --git a/src/config/default.toml b/src/config/default.toml index eefa7eb..19da99e 100644 --- a/src/config/default.toml +++ b/src/config/default.toml @@ -27,7 +27,8 @@ bitcoin_rpc_endpoint = "http://devnet:devnet@127.0.0.1:18443" signers_xonly = "0000000000000000000000000000000000000000000000000000000000000001" recipient = "ST3497E9JFQ7KB9VEHAZRWYKF3296WQZEXBPXG193" max_fee = 20000 -lock_time = 10 +lock_time = 950 +# This MUST be configured correctly in non-test environments reclaim_script = "" # !! =========================================================================== diff --git a/src/deposit_monitor.rs b/src/deposit_monitor.rs index a3085b2..3335254 100644 --- a/src/deposit_monitor.rs +++ b/src/deposit_monitor.rs @@ -1,7 +1,8 @@ //! Module to monitor for pending deposits use std::collections::HashMap; -use std::num::NonZero; +use std::num::NonZeroUsize; +use std::str::FromStr as _; use bitcoin::{BlockHash, ScriptBuf, Txid}; use emily_client::models::CreateDepositRequestBody; @@ -59,12 +60,18 @@ pub struct DepositMonitor { context: Context, monitored: HashMap, tx_hex_cache: LruCache<(Txid, BlockHash), String>, + created_deposits: LruCache<(Txid, u32), ()>, } // TODO: make cache size configurable // As for now numbers are chosen to keep cache size around 4MB -const TX_HEX_CACHE_SIZE: NonZero = - NonZero::new(8_000_usize).expect("Cache size must be non-zero"); +const TX_HEX_CACHE_SIZE: NonZeroUsize = + NonZeroUsize::new(8_000).expect("Cache size must be non-zero"); + +/// How many created deposits to keep track of. This should keep the max memory +/// usage below 10MB. +const CREATED_DEPOSITS_CACHE_SIZE: NonZeroUsize = + NonZeroUsize::new(100_000).expect("Cache size must be non-zero"); impl DepositMonitor { /// Creates a new `DepositMonitor` @@ -78,6 +85,7 @@ impl DepositMonitor { context, monitored, tx_hex_cache: LruCache::new(TX_HEX_CACHE_SIZE), + created_deposits: LruCache::new(CREATED_DEPOSITS_CACHE_SIZE), } } @@ -136,7 +144,13 @@ impl DepositMonitor { let create_deposits = utxos .iter() - .flat_map(|utxo| { + .filter_map(|utxo| { + // Emily will nop for duplicates, still we try avoiding wasting + // time for deposits we already created in this session. + if self.created_deposits.get(&(utxo.txid, utxo.vout)).is_some() { + return None; + } + self.get_deposit_from_utxo(utxo, chain_tip) .inspect_err(|error| match error { Error::DepositExpired => tracing::info!( @@ -160,4 +174,22 @@ impl DepositMonitor { Ok(create_deposits) } + + /// Mark a deposit as (locally) created + pub fn deposit_created(&mut self, bitcoin_txid: &str, bitcoin_tx_output_index: u32) { + match Txid::from_str(bitcoin_txid) { + Ok(txid) => { + self.created_deposits + .put((txid, bitcoin_tx_output_index), ()); + } + Err(error) => { + tracing::warn!( + %error, + txid = %bitcoin_txid, + vout = %bitcoin_tx_output_index, + "failed to parse transaction id" + ); + } + }; + } } diff --git a/src/main.rs b/src/main.rs index f606989..bc47473 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,7 +60,6 @@ async fn fetch_and_create_deposits( } for deposit in deposits { - // TODO: emily will nop for duplicates, but we shouldn't send them if let Err(error) = deposit_api::create_deposit(emily_config, deposit.clone()).await { tracing::warn!( %error, @@ -74,6 +73,7 @@ async fn fetch_and_create_deposits( vout = %deposit.bitcoin_tx_output_index, "created deposit in emily" ); + deposit_monitor.deposit_created(&deposit.bitcoin_txid, deposit.bitcoin_tx_output_index); } }