Skip to content

Commit

Permalink
feat: Let the coordinator open single-funded channels
Browse files Browse the repository at this point in the history
With the `TradeAction::OpenSingleFundedDlcChannel`, the coordinator
can choose to open a DLC channel with a trader, without using any
trader UTXOs. This is useful if the coordinator wants to onboard
trading through alternate means, such as Lightning.

`rust-dlc` now allows to configure the TX fee split between the offer
party and the accept party.

The feature has been tested locally, but is currently disabled as we
need a different flow to trigger the creation of a single-funded DLC
channel.

Also, the DLC channel is now 0-conf: the trader can keep trading as
soon as the fund transaction is found in mempool.
  • Loading branch information
luckysori committed May 16, 2024
1 parent 0e8c415 commit c9addaa
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 17 deletions.
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ resolver = "2"
# We are using our own fork of `rust-dlc` at least until we can drop all the LN-DLC features. Also,
# `p2pderivatives/rust-dlc#master` is missing certain patches that can only be found in the LN-DLC
# branch.
dlc-manager = { git = "https://github.com/get10101/rust-dlc", rev = "8d9920d" }
dlc-messages = { git = "https://github.com/get10101/rust-dlc", rev = "8d9920d" }
dlc = { git = "https://github.com/get10101/rust-dlc", rev = "8d9920d" }
p2pd-oracle-client = { git = "https://github.com/get10101/rust-dlc", rev = "8d9920d" }
dlc-trie = { git = "https://github.com/get10101/rust-dlc", rev = "8d9920d" }
dlc-manager = { git = "https://github.com/get10101/rust-dlc", rev = "2545d6e" }
dlc-messages = { git = "https://github.com/get10101/rust-dlc", rev = "2545d6e" }
dlc = { git = "https://github.com/get10101/rust-dlc", rev = "2545d6e" }
p2pd-oracle-client = { git = "https://github.com/get10101/rust-dlc", rev = "2545d6e" }
dlc-trie = { git = "https://github.com/get10101/rust-dlc", rev = "2545d6e" }

# We should usually track the `p2pderivatives/split-tx-experiment[-10101]` branch. For now we depend
# on a special fork which removes a panic in `rust-lightning`.
Expand Down
62 changes: 58 additions & 4 deletions coordinator/src/trade/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ pub mod websocket;

enum TradeAction {
OpenDlcChannel,
#[allow(dead_code)]
OpenOneSingleFundedChannel,
OpenPosition {
channel_id: DlcChannelId,
own_payout: u64,
Expand Down Expand Up @@ -98,6 +100,18 @@ pub struct TradeExecutor {
notifier: mpsc::Sender<OrderbookMessage>,
}

/// The funds the trader will need to provide to open a DLC channel with the coordinator.
///
/// We can extend this enum with a `ForTradeCost` variant to denote that the trader has to pay for
/// everything except for transaction fees.
enum TraderRequiredLiquidity {
/// Pay for margin, collateral reserve, order-matching fees and transaction fees.
ForTradeCostAndTxFees,
/// Do not pay for anything. The trader has probably paid in a different way e.g. using
/// Lightning.
None,
}

impl TradeExecutor {
pub fn new(node: Node, notifier: mpsc::Sender<OrderbookMessage>) -> Self {
Self { node, notifier }
Expand Down Expand Up @@ -212,6 +226,26 @@ impl TradeExecutor {
collateral_reserve_coordinator,
collateral_reserve_trader,
is_stable_order,
TraderRequiredLiquidity::ForTradeCostAndTxFees,
)
.await
.context("Failed to open DLC channel")?;
}
TradeAction::OpenOneSingleFundedChannel => {
let collateral_reserve_coordinator = params
.coordinator_reserve
.context("Missing coordinator collateral reserve")?;
let collateral_reserve_trader = params
.trader_reserve
.context("Missing trader collateral reserve")?;

self.open_dlc_channel(
&mut connection,
&params.trade_params,
collateral_reserve_coordinator,
collateral_reserve_trader,
is_stable_order,
TraderRequiredLiquidity::None,
)
.await
.context("Failed to open DLC channel")?;
Expand Down Expand Up @@ -270,6 +304,7 @@ impl TradeExecutor {
collateral_reserve_coordinator: Amount,
collateral_reserve_trader: Amount,
stable: bool,
trader_required_utxos: TraderRequiredLiquidity,
) -> Result<()> {
let peer_id = trade_params.pubkey;

Expand Down Expand Up @@ -337,11 +372,29 @@ impl TradeExecutor {
// coordinator.
let event_id = format!("{contract_symbol}{maturity_time}");

let (offer_collateral, accept_collateral, fee_config) = match trader_required_utxos {
TraderRequiredLiquidity::ForTradeCostAndTxFees => (
margin_coordinator + collateral_reserve_coordinator.to_sat(),
margin_trader + collateral_reserve_trader + order_matching_fee,
dlc::FeeConfig::EvenSplit,
),
TraderRequiredLiquidity::None => (
margin_coordinator
+ collateral_reserve_coordinator.to_sat()
+ margin_trader
+ collateral_reserve_trader
// If the trader doesn't bring their own UTXOs, including the order matching fee
// is not strictly necessary, but it's simpler to do so.
+ order_matching_fee,
0,
dlc::FeeConfig::AllOffer,
),
};

let contract_input = ContractInput {
offer_collateral: margin_coordinator + collateral_reserve_coordinator.to_sat(),
// The accept party has do bring additional collateral to pay for the
// `order_matching_fee`.
accept_collateral: margin_trader + collateral_reserve_trader + order_matching_fee,
offer_collateral,

accept_collateral,
fee_rate,
contract_infos: vec![ContractInputInfo {
contract_descriptor,
Expand Down Expand Up @@ -370,6 +423,7 @@ impl TradeExecutor {
contract_input,
trade_params.pubkey,
protocol_id,
fee_config,
)
.await
.context("Could not propose DLC channel")?;
Expand Down
4 changes: 4 additions & 0 deletions crates/xxi-node/src/dlc_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ impl<D: BdkStorage, S: TenTenOneStorage, N> dlc_manager::Wallet for DlcWallet<D,
base_weight_wu: u64,
lock_utxos: bool,
) -> Result<Vec<dlc_manager::Utxo>, dlc_manager::error::Error> {
if amount == 0 {
return Ok(Vec::new());
}

let network = self.on_chain_wallet.network();

let fee_rate = fee_rate.expect("always set by rust-dlc");
Expand Down
7 changes: 5 additions & 2 deletions crates/xxi-node/src/node/dlc_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ impl<D: BdkStorage, S: TenTenOneStorage + 'static, N: LnDlcStorage + Sync + Send
contract_input: ContractInput,
counterparty: PublicKey,
protocol_id: ProtocolId,
fee_config: dlc::FeeConfig,
) -> Result<(ContractId, DlcChannelId)> {
tracing::info!(
trader_id = %counterparty,
Expand Down Expand Up @@ -90,6 +91,7 @@ impl<D: BdkStorage, S: TenTenOneStorage + 'static, N: LnDlcStorage + Sync + Send
let offer_channel = dlc_manager.offer_channel(
&contract_input,
to_secp_pk_29(counterparty),
fee_config,
Some(protocol_id.into()),
)?;

Expand Down Expand Up @@ -122,8 +124,9 @@ impl<D: BdkStorage, S: TenTenOneStorage + 'static, N: LnDlcStorage + Sync + Send

tracing::info!(channel_id = %channel_id_hex, "Accepting DLC channel offer");

let (accept_channel, _channel_id, _contract_id, counter_party) =
self.dlc_manager.accept_channel(channel_id)?;
let (accept_channel, _channel_id, _contract_id, counter_party) = self
.dlc_manager
.accept_channel(channel_id, dlc::FeeConfig::EvenSplit)?;

self.event_handler.publish(NodeEvent::SendDlcMessage {
peer: to_secp_pk_30(counter_party),
Expand Down
1 change: 1 addition & 0 deletions crates/xxi-node/src/tests/dlc_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ async fn open_channel_and_position_and_settle_position(
contract_input,
app.info.pubkey,
ProtocolId::new(),
dlc::FeeConfig::EvenSplit,
)
.await
.unwrap();
Expand Down
2 changes: 1 addition & 1 deletion mobile/native/src/dlc/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ impl Node {
match self
.inner
.dlc_manager
.accept_channel(&channel_id)
.accept_channel(&channel_id, dlc::FeeConfig::AllOffer)
.map_err(anyhow::Error::new)
{
Ok((accept_channel, _, _, node_id)) => {
Expand Down

0 comments on commit c9addaa

Please sign in to comment.