discreetlogcontracts / dlcspecs

Specification for Discreet Log Contracts
Creative Commons Attribution 4.0 International
239 stars 36 forks source link

Free option problem during Funding Phase #155

Open matthewjablack opened 3 years ago

matthewjablack commented 3 years ago

During the Funding Phase, the Non-Interactive-Protocol specifies that if all counter-party CET and funding signatures are received then the node MUST finalize the funding transaction with a local SIGHASH_ALL signatures and MUST broadcast the funding transaction

However, if a node does not perform these actions, it puts the counter-party in an awkward situation. If they do nothing, then the node can monitor changes in the market, and broadcast when it is optimal for them.

In this case, the counter-party should move their UTXOs, either to a new address in their wallet or into a new DLC, they should blacklist the node, and they should also monitor the chain to ensure the node doesn't broadcast the funding transaction at a later point.

This should be specified in the spec, and there should probably also be a timeout value in the offer message / accept message to specify how long a party will wait for a transaction to be broadcast.

Tibo-lg commented 3 years ago

I think this is covered by

if funding_security_point is reached:
MUST spend a funding input with a higher-fee-rate transaction/absolute fee than any previously propagated package fee-rate
MUST start a fee-bumping timer of length security_bump_frequency
MUST disable congestion_bump_frequency timer

Though the wording could maybe be made more explicit.

This should be specified in the spec, and there should probably also be a timeout value in the offer message / accept message to specify how long a party will wait for a transaction to be broadcast.

I'm not sure about having it as a parameter in the protocol. I think it makes more sense to carefully check that the fee rate is adequate with respect to current mempool conditions and then have a global timeout. Open for discussions though.

matthewjablack commented 3 years ago

I think this is covered by ...

So a security_point is a time/block-stamp after which the confirmation of a corresponding transaction is a matter of contract safety. So it has to do specifically with confirmation of the funding tx.

My understanding for funding_security_point was that it referred to the safety of the contracts once it had been broadcast. I.e. if not confirmed within 24 hours. Whereas this issue refers to the situation where it has not been broadcast to the mempool at all, since the user would obviously not be able to fee bump in that scenario.

I'm not sure about having it as a parameter in the protocol. I think it makes more sense to carefully check that the fee rate is adequate with respect to current mempool conditions and then have a global timeout. Open for discussions though.

Perhaps there needs to be a finalize_security_point which refers to the point at which the funding transaction not being present in the mempool is considered a matter of contract security.

Tibo-lg commented 3 years ago

My understanding for funding_security_point was that it referred to the safety of the contracts once it had been broadcast. I.e. if not confirmed within 24 hours. Whereas this issue refers to the situation where it has not been broadcast to the mempool at all, since the user would obviously not be able to fee bump in that scenario.

My understanding was that it referred to both situations. But if we are having this discussion and both of us aren't sure, it probably means that it's not clear enough.

@ariard can you confirm which one it is?

ariard commented 3 years ago

Currently, funding transactions signatures are only known by the DLC initiator, which as you described open 2 issues : a) it offers to the initiator a call option in function of mempool feerate fluctuation b) it puts at risk the acceptor if funding transaction isn't confirmed before the security_point is reached

W.r.t to a), the spec currently suggests both DLC counterparties to fee-bump the funding transaction in case it doesn't confirm. To remove the option we might either restrain the fee-bumping burden on the initiator only or send back the initiator funding transaction signatures to the acceptor ?

W.r.t to b) the current spec says

"if a funding signature has been sent or a funding transaction broadcast and there is no confirmation: MUST spend a funding input with a higher-fee-rate transaction/absolute fee than any previously propagated package fee-rate"

The first part of the sentence ("if a funding signature has been sent") targets the acceptor, but I confess the second sentence isn't really clear. The acceptor might not be able to observe the propagated package fee-rate in case of a malicious counterparty and left with a scortched earth approach of slowly burning its collateral input value as a fee. If a package fee-rate can be observed the starter feerate for such collateral input should be the funding transaction one. I should modify the spec saying this.

A 3rd issue raised in this issue is when it's lawful for both counterparties to try a double-spend and not be reputation-penalized in consequence. Likely we need a global timeout announced in some message.

matthewjablack commented 3 years ago

W.r.t a) I think suggesting that both DLC counterparties fee-bump the funding tx in case it doesn't confirm is reasonable. I have concerns around making fee-bumping one-sided since this puts the DLC Initiator in an awkward position where their only recourse in the case of a low-fee funding tx that isn't getting confirmed is to wait until refund_security_point. It also gives the Acceptor the ability to just not fee bump if market conditions aren't favorable.

W.r.t b) Isn't it the DLC Acceptor that is the only one who knows the funding transaction signatures? https://github.com/discreetlogcontracts/dlcspecs/blob/master/Protocol.md#contract-negotiation

Once the accepter receives the sign_dlc message, it must broadcast the funding transaction to the Bitcoin network.

So in this case:

The first part of the sentence ("if a funding signature has been sent") targets the acceptor, but I confess the second sentence isn't really clear. The acceptor might not be able to observe the propagated package fee-rate in case of a malicious counterparty

I guess acceptor should be changed to initiator. If the initiator isn't able to observe the propagated package fee-rate in case of a malicious counterparty, their only recourse is to move their funding inputs using this scorched earth approach as you mentioned.

A 3rd issue raised in this issue is when it's lawful for both counterparties to try a double-spend and not be reputation-penalized in consequence. Likely we need a global timeout announced in some message.

Agreed. Are you thinking of a security_point for this global timeout as well as a TLV? In practical terms, this should probably be a very short global timeout for UX purposes.

Tibo-lg commented 3 years ago

Agreed. Are you thinking of a security_point for this global timeout as well as a TLV? In practical terms, this should probably be a very short global timeout for UX purposes.

I think it will be difficult to have a "very short" timeout as it is difficult to rely on transaction propagation. Maybe a solution would be to add a step to the protocol where the acceptor also shares his signatures for the fund tx, in which case it would be possible and meaningful to have a short timeout on this message which would make the UX smooth.

matthewjablack commented 3 years ago

Maybe a solution would be to add a step to the protocol where the acceptor also shares his signatures for the fund tx, in which case it would be possible and meaningful to have a short timeout on this message which would make the UX smooth.

I think this is a great idea! In that case, the full set of messages could be something like Offer, Accept, Sign, Finalize, with specification for timeout between each of these messages.

Thoughts @nkohen @ariard?

benthecarman commented 3 years ago

I think it will be difficult to have a "very short" timeout as it is difficult to rely on transaction propagation. Maybe a solution would be to add a step to the protocol where the acceptor also shares his signatures for the fund tx, in which case it would be possible and meaningful to have a short timeout on this message which would make the UX smooth.

The only problem here is that the Accepter doesn't need to ever send that final message

matthewjablack commented 3 years ago

The only problem here is that the Accepter doesn't need to ever send that final message

Right, and I guess the assumption is if Finalize isn't received by the Initiator, and the Initiator also doesn't detect anything in mempool, then Initiator assumes malicious intent and starts moving funding UTXOs.

nkohen commented 3 years ago

Just read the whole thread, here are my thoughts:

  1. funding_security_point + x should be used as a time to double-spend funding inputs in the case that you are an offerer who has not received confirmation of the funding transaction on-chain. I don't think there's any issue with this as the offerer should be black-listing the accepter in this case so what should they care if the accepter is black-listing them in return?
  2. I don't see what utility adding a timeout to any communication message would bring seeing as all timeouts are client-configurable and don't really affect counter-parties.
  3. I have no strong opinions about one-sided vs. two-sided fee bumping because it really doesn't matter all that much. If you are using a blockchain you better be doing transaction activity that isn't right above dust-level because then you'd essentially be burning money. For example, the dual-funded lightning channel proposal has the initiator pay all fees for simplicity's sake (not that I am saying we need to do that here, just that it really doesn't matter as much as one might think).
  4. I don't really have any strong opinions about adding a Finalize message so long as if it is added it is explicitly marked as optional (likely with a description of how it leads to better UX and allows one's counter-party to participate in fee-bumping without worrying about whether the funding tx has been broadcast, which is good for the sender of this optional message).
  5. Lastly, one point that hasn't been discussed is the possibility of adding a dlc_cancel message to make cooperative-case double-spending of a single funding input by one party not result in black-listing.

EDIT: To be clear, the dlc_cancel requires an ACK response

nkohen commented 3 years ago

On further discussion in slack with Tibo, I've decided that I don't actually have any opinions on the optional vs. mandatory nature of points 4 and 5

ariard commented 3 years ago

W.r.t b) Isn't it the DLC Acceptor that is the only one who knows the funding transaction signatures? https://github.com/discreetlogcontracts/dlcspecs/blob/master/Protocol.md#contract-negotiation

You're right, the initiator doesn't learn the acceptor signature. I guess I've been confused with the dual-funding_tx proposal where the initiator is the one collecting all the signatures and responsible of the fee-bumping.

Right, and I guess the assumption is if Finalize isn't received by the Initiator, and the Initiator also doesn't detect anything in mempool, then Initiator assumes malicious intent and starts moving funding UTXOs.

I would rather shun away from any mempool-detection as lightclient don't have a mempool and it's gameable by broadcasting the funding_tx in your local mempool and a double-spend/pinning tx to the rest of the network.

Let's consider the following framework of actions and see the margin of actions we have to solve the call_option. Those are the actions counterparties can take with the current transactions/signatures exchanged, not exactly what the spec is requiring to do.

An Acceptor:

  1. if the funding_tx is broadcasted and mempools spikes,
    1. it can fee-bump the funding transaction
      1. if Initiator is jamming propagation of funding-tx, Acceptor is at risk of inflation of its bumping feerate 2.if mempools spikes higher, Acceptor is at risk of inflation of its bumping feerate
    2. it can double-spend the funding_tx
      1. if Initiator is pinning the funding_tx and Acceptor fee-bump its double-spend, Acceptor is at risk of inflation of its bumping feerate
      2. if Initiator is pinning the funding_tx and Acceptor doesn't fee-bump its double-spend, Acceptor is at risk of infinite timevalue DoS on its collateral input
  2. if the funding_tx is never broadcasted,
    1. it can double-spend the funding_tx,
      1. Initiator cannot pin the double-spend, Acceptor can fee-bump, no risk of bumping feerate inflation

An Initiator:

  1. if the funding_tx is broadcasted and mempools spikes,
    1. it can fee-bump the funding transaction
      1. if Acceptor is pinning the funding_tx and Initiator fee-bump the funding_tx, Initiator is at risk of inflation of its bumping feerate
    2. it can double-spend the funding transaction
      1. if Acceptor is pinning the funding_tx and Initiator fee-bump its double-spend, Acceptor is at risk of inflation of its bumping feerate
      2. if Acceptor is pinning the funding_tx and Initiator doesn't fee-bump its double-spend, Acceptor is at risk of infinite timevalue DoS on its collateral input

As you can observe by running the optional paths, in case of malicious counterparties, we have the choice between overpaying bumping feerate OR infinite timevalue DoS. The current spec is recommending the first option as it forces the attacker to engage in a scorched earth game and to bid competing fees in the mempool to maintain the pin. Note, this last point benefits the Acceptor only if it does engage in funding input double-spend once reaching its funding_security_point. Otherwise a malicious Initiator has a free pinning trick by stucking a rbf-disabled doube-spend of its input in network mempools.

With this context in mind, I think solving the call_option by either two-sided fee-bumping or complete signatures exchange will only complexify further this part of the spec. And introduce new issues like asynchronous fee-bumping overshooting the package feerate far above what is required for block inclusion.

What we might consider in the feature would be a premium fee paid by the initiator to the acceptor to cover mempool spikes. Such fee could be zero or even negative if the mempool might dry-up in near-feature. It could be added on any change output.

I would shun away from black-listing from now, failing to confirm the funding_tx might have a lot of acceptable reasons like a network outage or black mempool spikes. We might introduce service-level-agreement negotiation feature in the future.

Lastly, I think a timeout announced by the Initiator to the Acceptor is pretty nice, at it lets the Acceptor stops fee-bumping and frees out any bumping utxo especially locked by the operation. You might a small-value change output and uses a 2 input CPFP.

I'm pretty sure I need to correct the current spec to make it clearer, but for v0 I'm of the opinion we shouldn't increase its scope. Note also, we won't reuse most of the work here once we piggyback on payment channels and if get package-relay, a lot of those issues should be flattened.

Tibo-lg commented 3 years ago

I think solving the call_option by either two-sided fee-bumping or complete signatures exchange will only complexify further this part of the spec.

The main goal of complete signature exchange (or Finalize message) would be to prevent the acceptor from not broadcasting the fund_tx for the purpose of getting a free option, or at least for the offerer to see that the acceptor might not be cooperating. The way I see it from the acceptor POV is:

An Initiator:

  1. if the funding_tx is not broadcast and didn't receive Finalize:
    • try double spending UTXOs after short timeout
  2. if the funding_tx not broadcast and has Finalize message:
    • broadcast funding_tx and use longer timeout before try double spend
  3. if the funding_tx is broadcasted and mempools spikes [...] <- what you have in your description

Tbh I'm not trying to convince anybody that Finalize is something we should have, I agree it might make thing a bit more complicated.

nkohen commented 3 years ago

The more I think about this the more it seems to me that messages such as Finalize and Cancel should not be included in the protocol specification but rather should be viewed as second layer "features" which parties can opt-in to using. I think we should keep a clean separation between the information needed to facilitate the construction of a DLC and additional messages which make mitigation of problems like these and UX easier/nicer. Users can of course choose not to interact with other users who do not support these additional messages but I don't think this warrants complicating the core protocol when it can instead be added as a second layer interaction.

matthewjablack commented 3 years ago

I do think Finalize and Cancel should be laid out somewhere in the specification for clients to opt-in implementing, but agree with @nkohen that it doesn't make as much sense to be included in the core protocol specification. A second layer interaction section makes sense to me, and there are additional "features" that I think would be useful to have laid out as well (such as #161).

ACK adding second layer interaction section