diff --git a/dlc-manager/src/channel/offered_channel.rs b/dlc-manager/src/channel/offered_channel.rs index b13a8dad..30d299a1 100644 --- a/dlc-manager/src/channel/offered_channel.rs +++ b/dlc-manager/src/channel/offered_channel.rs @@ -44,7 +44,11 @@ pub struct OfferedChannel { } impl OfferedChannel { - pub(crate) fn get_offer_channel_msg(&self, offered_contract: &OfferedContract) -> OfferChannel { + pub(crate) fn get_offer_channel_msg( + &self, + offered_contract: &OfferedContract, + cet_nsequence: u32, + ) -> OfferChannel { let party_points = &self.party_points; OfferChannel { protocol_version: crate::conversion_utils::PROTOCOL_VERSION, @@ -72,7 +76,7 @@ impl OfferedChannel { refund_locktime: offered_contract.refund_locktime, fee_rate_per_vb: offered_contract.fee_rate_per_vb, fund_output_serial_id: offered_contract.fund_output_serial_id, - cet_nsequence: crate::manager::CET_NSEQUENCE, + cet_nsequence, } } diff --git a/dlc-manager/src/channel_updater.rs b/dlc-manager/src/channel_updater.rs index dfd9cf74..c85e3545 100644 --- a/dlc-manager/src/channel_updater.rs +++ b/dlc-manager/src/channel_updater.rs @@ -19,6 +19,7 @@ use crate::{ verify_signed_contract_internal, }, error::Error, + manager::RefundDelayWindow, utils::get_new_temporary_id, Blockchain, Signer, Time, Wallet, }; @@ -72,7 +73,7 @@ pub fn offer_channel( counter_party: &PublicKey, oracle_announcements: &[Vec], cet_nsequence: u32, - refund_delay: u32, + refund_delay: RefundDelayWindow, wallet: &W, blockchain: &B, time: &T, @@ -957,7 +958,7 @@ pub fn renew_offer( contract_input: &ContractInput, oracle_announcements: Vec>, counter_payout: u64, - refund_delay: u32, + refund_delay: RefundDelayWindow, peer_timeout: u64, cet_nsequence: u32, signer: &S, @@ -1490,6 +1491,7 @@ pub fn offer_collaborative_close( counter_payout: u64, signer: &S, time: &T, + peer_timeout: u64, ) -> Result<(CollaborativeCloseOffer, Transaction), Error> where S::Target: Signer, @@ -1535,7 +1537,7 @@ where counter_payout, offer_signature: close_signature, close_tx: close_tx.clone(), - timeout: time.unix_time_now() + super::manager::PEER_TIMEOUT, + timeout: time.unix_time_now() + peer_timeout, }; std::mem::swap(&mut state, &mut signed_channel.state); signed_channel.roll_back_state = Some(state); diff --git a/dlc-manager/src/contract/offered_contract.rs b/dlc-manager/src/contract/offered_contract.rs index b9e18a31..5dc1a609 100644 --- a/dlc-manager/src/contract/offered_contract.rs +++ b/dlc-manager/src/contract/offered_contract.rs @@ -3,6 +3,7 @@ use crate::conversion_utils::{ get_contract_info_and_announcements, get_tx_input_infos, BITCOIN_CHAINHASH, PROTOCOL_VERSION, }; +use crate::manager::RefundDelayWindow; use crate::utils::get_new_serial_id; use super::contract_info::ContractInfo; @@ -80,7 +81,7 @@ impl OfferedContract { offer_params: &PartyParams, funding_inputs_info: &[FundingInputInfo], counter_party: &PublicKey, - refund_delay: u32, + refund_delay: RefundDelayWindow, cet_locktime: u32, ) -> Self { let total_collateral = contract.offer_collateral + contract.accept_collateral; @@ -111,7 +112,7 @@ impl OfferedContract { fund_output_serial_id, fee_rate_per_vb: contract.fee_rate, cet_locktime, - refund_locktime: latest_maturity + refund_delay, + refund_locktime: latest_maturity + refund_delay.min, counter_party: *counter_party, } } diff --git a/dlc-manager/src/contract_updater.rs b/dlc-manager/src/contract_updater.rs index 055d7337..46a94c43 100644 --- a/dlc-manager/src/contract_updater.rs +++ b/dlc-manager/src/contract_updater.rs @@ -20,6 +20,7 @@ use crate::{ }, conversion_utils::get_tx_input_infos, error::Error, + manager::RefundDelayWindow, Blockchain, ChannelId, Signer, Time, Wallet, }; @@ -29,7 +30,7 @@ pub fn offer_contract( secp: &Secp256k1, contract_input: &ContractInput, oracle_announcements: Vec>, - refund_delay: u32, + refund_delay: RefundDelayWindow, counter_party: &PublicKey, wallet: &W, blockchain: &B, diff --git a/dlc-manager/src/manager.rs b/dlc-manager/src/manager.rs index 8ef4bbcd..4729c55c 100644 --- a/dlc-manager/src/manager.rs +++ b/dlc-manager/src/manager.rs @@ -37,16 +37,54 @@ use std::collections::HashMap; use std::ops::Deref; use std::string::ToString; -/// The number of confirmations required before moving the the confirmed state. +/// The number of btc confirmations required before moving the DLC to the confirmed state. pub const NB_CONFIRMATIONS: u32 = 6; -/// The delay to set the refund value to. -pub const REFUND_DELAY: u32 = 86400 * 7; +/// The minimum time to trigger the refund. +pub const REFUND_DELAY_MIN: u32 = 86400 * 7; +/// The maximum time to trigger the refund. +pub const REFUND_DELAY_MAX: u32 = 86400 * 7 * 2; /// The nSequence value used for CETs in DLC channels pub const CET_NSEQUENCE: u32 = 288; /// Timeout in seconds when waiting for a peer's reply, after which a DLC channel /// is forced closed. pub const PEER_TIMEOUT: u64 = 3600; +/// The options used to configure the DLC manager. +pub struct ManagerOptions { + /// The number of btc confirmations required before moving the DLC to the confirmed state. + pub nb_confirmations: u32, + /// The refund delay window to trigger the refund. + pub refund_delay: RefundDelayWindow, + /// The nSequence value used for CETs in DLC channels + pub cet_nsequence: u32, + /// Timeout in seconds when waiting for a peer's reply, after which a DLC channel + /// is forced closed. + pub peer_timeout: u64, +} + +/// The min and max value in seconds until a DLC will be refunded. +#[derive(Clone, Copy)] +pub struct RefundDelayWindow { + /// The min delay in seconds to trigger the refund. + pub min: u32, + /// The max delay in seconds to trigger the refund. + pub max: u32, +} + +impl Default for ManagerOptions { + fn default() -> Self { + Self { + nb_confirmations: 6, + refund_delay: RefundDelayWindow { + min: 86400 * 7, + max: 86400 * 14, + }, + cet_nsequence: 288, + peer_timeout: 3600, + } + } +} + type ClosableContractInfo<'a> = Option<( &'a ContractInfo, &'a AdaptorInfo, @@ -71,6 +109,7 @@ where chain_monitor: ChainMonitor, time: T, fee_estimator: F, + options: ManagerOptions, } macro_rules! get_object_in_state { @@ -175,8 +214,10 @@ where oracles: HashMap, time: T, fee_estimator: F, + options: Option, ) -> Result { let init_height = blockchain.get_blockchain_height()?; + let options = options.unwrap_or_default(); Ok(Manager { secp: secp256k1_zkp::Secp256k1::new(), wallet, @@ -185,6 +226,7 @@ where oracles, time, fee_estimator, + options, chain_monitor: ChainMonitor::new(init_height), }) } @@ -284,7 +326,7 @@ where &self.secp, contract_input, oracle_announcements, - REFUND_DELAY, + self.options.refund_delay, &counter_party, &self.wallet, &self.blockchain, @@ -344,7 +386,11 @@ where offered_message: &OfferDlc, counter_party: PublicKey, ) -> Result<(), Error> { - offered_message.validate(&self.secp, REFUND_DELAY, REFUND_DELAY * 2)?; + offered_message.validate( + &self.secp, + self.options.refund_delay.min, + self.options.refund_delay.max, + )?; let contract: OfferedContract = OfferedContract::try_from_offer_dlc(offered_message, counter_party)?; contract.validate()?; @@ -474,7 +520,7 @@ where let confirmations = self.blockchain.get_transaction_confirmations( &contract.accepted_contract.dlc_transactions.fund.txid(), )?; - if confirmations >= NB_CONFIRMATIONS { + if confirmations >= self.options.nb_confirmations { self.store .update_contract(&Contract::Confirmed(contract.clone()))?; } @@ -604,7 +650,7 @@ where let confirmations = self .blockchain .get_transaction_confirmations(&broadcasted_txid)?; - if confirmations >= NB_CONFIRMATIONS { + if confirmations >= self.options.nb_confirmations { let closed_contract = ClosedContract { attestations: contract.attestations.clone(), signed_cet: Some(contract.signed_cet.clone()), @@ -655,7 +701,7 @@ where }; return Ok(Contract::PreClosed(preclosed_contract)); - } else if confirmations < NB_CONFIRMATIONS { + } else if confirmations < self.options.nb_confirmations { let preclosed_contract = PreClosedContract { signed_contract: contract.clone(), attestations: Some(attestations), @@ -733,14 +779,15 @@ where contract_input, &counter_party, &oracle_announcements, - CET_NSEQUENCE, - REFUND_DELAY, + self.options.cet_nsequence, + self.options.refund_delay, &self.wallet, &self.blockchain, &self.time, )?; - let msg = offered_channel.get_offer_channel_msg(&offered_contract); + let msg = + offered_channel.get_offer_channel_msg(&offered_contract, self.options.cet_nsequence); self.store.upsert_channel( Channel::Offered(offered_channel), @@ -821,7 +868,7 @@ where &self.secp, &mut signed_channel, counter_payout, - PEER_TIMEOUT, + self.options.peer_timeout, &self.wallet, &self.time, )?; @@ -846,9 +893,9 @@ where let msg = crate::channel_updater::settle_channel_accept( &self.secp, &mut signed_channel, - CET_NSEQUENCE, + self.options.cet_nsequence, 0, - PEER_TIMEOUT, + self.options.peer_timeout, &self.wallet, &self.time, )?; @@ -885,9 +932,9 @@ where contract_input, oracle_announcements, counter_payout, - REFUND_DELAY, - PEER_TIMEOUT, - CET_NSEQUENCE, + self.options.refund_delay, + self.options.peer_timeout, + self.options.cet_nsequence, &self.wallet, &self.time, )?; @@ -926,8 +973,8 @@ where &self.secp, &mut signed_channel, &offered_contract, - CET_NSEQUENCE, - PEER_TIMEOUT, + self.options.cet_nsequence, + self.options.peer_timeout, &self.wallet, &self.time, )?; @@ -1014,6 +1061,7 @@ where counter_payout, &self.wallet, &self.time, + self.options.peer_timeout, )?; self.chain_monitor.add_tx( @@ -1107,7 +1155,7 @@ where if self .blockchain .get_transaction_confirmations(&buffer_tx.txid())? - > CET_NSEQUENCE + > self.options.cet_nsequence { let confirmed_contract = get_contract_in_state!(self, &contract_id, Confirmed, None as Option)?; @@ -1131,10 +1179,10 @@ where ) -> Result<(), Error> { offer_channel.validate( &self.secp, - REFUND_DELAY, - REFUND_DELAY * 2, - CET_NSEQUENCE, - CET_NSEQUENCE * 2, + self.options.refund_delay.min, + self.options.refund_delay.max, + self.options.cet_nsequence, + self.options.cet_nsequence * 2, )?; let (channel, contract) = OfferedChannel::from_offer_channel(offer_channel, counter_party)?; @@ -1182,7 +1230,7 @@ where &offered_contract, accept_channel, //TODO(tibo): this should be parameterizable. - CET_NSEQUENCE, + self.options.cet_nsequence, &self.wallet, ); @@ -1334,9 +1382,9 @@ where &self.secp, &mut signed_channel, settle_accept, - CET_NSEQUENCE, + self.options.cet_nsequence, 0, - PEER_TIMEOUT, + self.options.peer_timeout, &self.wallet, &self.time, )?; @@ -1536,8 +1584,8 @@ where renew_accept, &mut signed_channel, &offered_contract, - CET_NSEQUENCE, - PEER_TIMEOUT, + self.options.cet_nsequence, + self.options.peer_timeout, &self.wallet, &self.time, )?; @@ -1790,7 +1838,7 @@ where crate::channel_updater::on_collaborative_close_offer( &mut signed_channel, close_offer, - PEER_TIMEOUT, + self.options.peer_timeout, &self.time, )?; @@ -2020,7 +2068,7 @@ where &counter_revocation_sk, &tx, &self.wallet.get_new_address()?, - CET_NSEQUENCE, + self.options.cet_nsequence, 0, fee_rate_per_vb, is_offer, @@ -2222,7 +2270,16 @@ mod test { mocks::mock_time::set_time(0); - Manager::new(wallet, blockchain.clone(), store, oracles, time, blockchain).unwrap() + Manager::new( + wallet, + blockchain.clone(), + store.clone(), + oracles, + time, + blockchain.clone(), + None, + ) + .unwrap() } fn pubkey() -> PublicKey { diff --git a/dlc-manager/tests/channel_execution_tests.rs b/dlc-manager/tests/channel_execution_tests.rs index 5d69bc8b..9e6d157b 100644 --- a/dlc-manager/tests/channel_execution_tests.rs +++ b/dlc-manager/tests/channel_execution_tests.rs @@ -347,6 +347,7 @@ fn channel_execution_test(test_params: TestParams, path: TestPath) { alice_oracles, Arc::clone(&mock_time), Arc::clone(&electrs), + None, ) .unwrap(), )); @@ -362,10 +363,13 @@ fn channel_execution_test(test_params: TestParams, path: TestPath) { bob_oracles, Arc::clone(&mock_time), Arc::clone(&electrs), + None, ) .unwrap(), )); + let cet_nsequence: u32 = dlc_manager::manager::CET_NSEQUENCE; + let bob_manager_loop = Arc::clone(&bob_manager); let bob_manager_send = Arc::clone(&bob_manager); let alice_send_loop = alice_send.clone(); @@ -526,7 +530,13 @@ fn channel_execution_test(test_params: TestParams, path: TestPath) { match path { TestPath::Close => { - close_established_channel(first, second, channel_id, &generate_blocks); + close_established_channel( + first, + second, + channel_id, + &generate_blocks, + cet_nsequence, + ); } TestPath::CollaborativeClose => { collaborative_close( @@ -549,6 +559,7 @@ fn channel_execution_test(test_params: TestParams, path: TestPath) { channel_id, &sync_receive, path, + dlc_manager::manager::PEER_TIMEOUT, ); } TestPath::SettleReject => { @@ -623,6 +634,7 @@ fn channel_execution_test(test_params: TestParams, path: TestPath) { &sync_receive, &test_params.contract_input, path, + dlc_manager::manager::PEER_TIMEOUT, ); } TestPath::RenewReject => { @@ -676,6 +688,7 @@ fn channel_execution_test(test_params: TestParams, path: TestPath) { second, channel_id, &generate_blocks, + cet_nsequence, ); } else if let TestPath::SettleCheat = path { cheat_punish(first, second, channel_id, &generate_blocks, false); @@ -721,6 +734,7 @@ fn close_established_channel( second: DlcParty, channel_id: ChannelId, generate_blocks: &F, + cet_nsequence: u32, ) where F: Fn(u64), { @@ -739,7 +753,7 @@ fn close_established_channel( .periodic_check() .expect("to be able to do the periodic check"); - let wait = dlc_manager::manager::CET_NSEQUENCE; + let wait = cet_nsequence; generate_blocks(10); @@ -1143,6 +1157,7 @@ fn renew_timeout( sync_receive: &Receiver<()>, contract_input: &ContractInput, path: TestPath, + peer_timeout: u64, ) { { let (renew_offer, _) = first @@ -1158,9 +1173,7 @@ fn renew_timeout( sync_receive.recv().expect("Error synchronizing"); if let TestPath::RenewOfferTimeout = path { - mocks::mock_time::set_time( - (EVENT_MATURITY as u64) + dlc_manager::manager::PEER_TIMEOUT + 2, - ); + mocks::mock_time::set_time((EVENT_MATURITY as u64) + peer_timeout + 2); first .lock() .unwrap() @@ -1183,9 +1196,7 @@ fn renew_timeout( sync_receive.recv().expect("Error synchronizing"); if let TestPath::RenewAcceptTimeout = path { - mocks::mock_time::set_time( - (EVENT_MATURITY as u64) + dlc_manager::manager::PEER_TIMEOUT + 2, - ); + mocks::mock_time::set_time((EVENT_MATURITY as u64) + peer_timeout + 2); second .lock() .unwrap() @@ -1196,9 +1207,7 @@ fn renew_timeout( } else if let TestPath::RenewConfirmTimeout = path { // Process Confirm sync_receive.recv().expect("Error synchronizing"); - mocks::mock_time::set_time( - (EVENT_MATURITY as u64) + dlc_manager::manager::PEER_TIMEOUT + 2, - ); + mocks::mock_time::set_time((EVENT_MATURITY as u64) + peer_timeout + 2); first .lock() .unwrap() @@ -1219,6 +1228,7 @@ fn settle_timeout( channel_id: ChannelId, sync_receive: &Receiver<()>, path: TestPath, + peer_timeout: u64, ) { let (settle_offer, _) = first .lock() @@ -1233,9 +1243,7 @@ fn settle_timeout( sync_receive.recv().expect("Error synchronizing"); if let TestPath::SettleOfferTimeout = path { - mocks::mock_time::set_time( - (EVENT_MATURITY as u64) + dlc_manager::manager::PEER_TIMEOUT + 2, - ); + mocks::mock_time::set_time((EVENT_MATURITY as u64) + peer_timeout + 2); first .lock() .unwrap() @@ -1258,9 +1266,7 @@ fn settle_timeout( sync_receive.recv().expect("Error synchronizing"); if let TestPath::SettleAcceptTimeout = path { - mocks::mock_time::set_time( - (EVENT_MATURITY as u64) + dlc_manager::manager::PEER_TIMEOUT + 2, - ); + mocks::mock_time::set_time((EVENT_MATURITY as u64) + peer_timeout + 2); second .lock() .unwrap() @@ -1271,9 +1277,7 @@ fn settle_timeout( } else if let TestPath::SettleConfirmTimeout = path { // Process Confirm sync_receive.recv().expect("Error synchronizing"); - mocks::mock_time::set_time( - (EVENT_MATURITY as u64) + dlc_manager::manager::PEER_TIMEOUT + 2, - ); + mocks::mock_time::set_time((EVENT_MATURITY as u64) + peer_timeout + 2); first .lock() .unwrap() diff --git a/dlc-manager/tests/manager_execution_tests.rs b/dlc-manager/tests/manager_execution_tests.rs index 5d5109f8..2dbc015e 100644 --- a/dlc-manager/tests/manager_execution_tests.rs +++ b/dlc-manager/tests/manager_execution_tests.rs @@ -502,6 +502,7 @@ fn manager_execution_test(test_params: TestParams, path: TestPath) { alice_oracles, Arc::clone(&mock_time), Arc::clone(&electrs), + None, ) .unwrap(), )); @@ -517,6 +518,7 @@ fn manager_execution_test(test_params: TestParams, path: TestPath) { bob_oracles, Arc::clone(&mock_time), Arc::clone(&electrs), + None, ) .unwrap(), )); @@ -685,7 +687,7 @@ fn manager_execution_test(test_params: TestParams, path: TestPath) { periodic_check!(second, contract_id, Confirmed); mocks::mock_time::set_time( - ((EVENT_MATURITY + dlc_manager::manager::REFUND_DELAY) as u64) + 1, + ((EVENT_MATURITY + dlc_manager::manager::REFUND_DELAY_MIN) as u64) + 1, ); generate_blocks(10); diff --git a/sample/src/main.rs b/sample/src/main.rs index cfcbc9f9..f63c51b1 100644 --- a/sample/src/main.rs +++ b/sample/src/main.rs @@ -88,6 +88,7 @@ async fn main() { oracles, Arc::new(dlc_manager::SystemTimeProvider {}), bitcoind_provider.clone(), + None, ) .expect("Could not create manager."), ));