Skip to content

Conversation

@niftynei
Copy link
Collaborator

@niftynei niftynei commented Jan 17, 2020

Dual Funding

This PR updates the channel establishment (aka openingd's responsiblity) to allow for both the originating and the reciprocating peer to contribute UTXOs to the funding transaction. The protocol for this has been laid out in #524 in the lightning-rfc's, but for ease of review, I'll include it here as well, along with some explanation for the decisions.

Note that this PR does not encompass the RBF flows illustrated; those will be covered in a future update.

    +-------+                              +-------+
    |       |--(1)--- open_channel2  ----->|       |
    |       |<-(2)--- accept_channel2 -----|       |
    |       |                              |       |
    |       |--(3a)-  funding_add_input -->|       |
    |       |<-(3b)-  funding_add_input ---|       |
    |       |--(3c)-  funding_add_input -->|       |
    |       |--(3d)-  funding_add_output ->|       |
    |       |<-(3e)-  funding_add_output --|       |
    |       |                              |       |
    |       |--(4a)- funding_add_complete >|       |
    |       |<-(4b)- funding_add_complete -|       |
    |   A   |                              |   B   |
--->|       |--(5)--  commitment_signed -->|       |
|   |       |<-(6)--  commitment_signed ---|       |
|   |       |                              |       |
|   |       |<-(7)--  funding_signed2   ---|       |
|   |       |                              |       |
|   |       |--(a)--- init_rbf ----------->|       |
----|       |<-(b)--- ack_rbf  ------------|       |
    |       |                              |       |
    |       |--(8)--- funding_locked ----->|       |
    |       |<-(9)--  funding_locked ------|       |
    +-------+                              +-------+

There are a few notable changes. First, the funding inputs and outputs are exchanged via rounds, where either peer may submit almost any number of funding_add_input or funding_add_output messages. The round is finished when both peers have sent a funding_add_complete message, with a tally of all the inputs and outputs that they sent.

The peers construct the funding transaction from the set of inputs and outputs that have been sent and received, and update the message's channel_id accordingly, such that commitment_signed now includes both the correct funding transaction id and a valid commitment signature for that transaction.

Finally, the reciprocating peer (also known as the accepter) sends the originating peer (opener) the signatures for their inputs in the funding transaction.

Why Rounds of Inputs/Outputs

Originally, the dual funding protocol called for both peers to exchange signatures for their inputs. However, we've softened this to just the accepter side sending their signatures. The reason for this has to do with why we introduced the rounds of funding_add in the first place. The goal is to enable the accepter and the opener to both solicit and then include funding transaction outputs from other peers simultaneously. Here's a quick example of how the Accepter of a channel open might include a second open in the same transaction.

OPENER_A	ACCEPTER_A/OPENER_B		ACCEPTER_B
   -- open_channel --->
   <- accept_channel --
  -- funding_add_input->        -- open_channel --->
  <- funding_add_input -        <- accept_channel -- 
 -- funding_add_output ->       -- funding_add_input ->
     <- funding_add_output --       -- funding_add_output ->
 -- funding_add_complete->      <- funding_add_input --
 <- funding_add_input --	<- funding_add_output --
 <- funding_add_output --	<- funding_add_complete --
 <- funding_add_complete -      -- funding_add_complete ->
 -- commitment_signed ->	-- commitment_signed ->
 <- commtment_signed --         <- commitment_signed --
			        <- funding_signed2 --
 <- funding_signed2 --

In this scenario, OPENER_A will be the peer that broadcasts the funding transaction, as they're the only peer who knows the signatures to their funding transaction inputs.

Note that the commitment_signed message is broken out from the funding_signed2 message. Originally I had these as the same message, but that would delay the communication of the commitments in the case where the the first accepter (ACCEPTER_A) needs to get the funding signatures from ACCEPTER_B before sending their signatures to OPENER_A.

Note that this construction allows a small ambiguity into the origination of any input that an opener receives for a funding transaction. One downside to this is that the opener, as a result of their receiving all of the inputs and having control over the broadcast of the transaction, pays all of the fees.

This makes opens more brittle, as more peers are required to be online and participate in order to RBF an open transaction. It has no effect on the operation of channels opened in this manner.

How to Dual Fund a Channel

To dual fund a channel, we make use of the openchannel plugin hook. We've expanded the openchannel payload to include a few new fields. First, we now provide a version number of the channel open protocol that this openchannel request is using. Second, we rename funding_satoshis to opener_satoshis, as this amount is the satoshi value that the opener is contributing to the channel, not the total funding amount. Finally we also include an available_funds value, which tells the plugin the total amount of funds that the internal wallet currently has available to fund this channel.

To contribute funds to an openchannel request, the hook must respond with a continue JSON object that also includes a funding_sats member, indicating the total value of satoshi that we should add to this channel.

See tests/plugins/funder.py as a very simple example of this.

Channel Open Tracking

With the first version of channel establishment, the publishing node was the one that owned all of the outputs as well as the funding transaction. Any failure of the channel to open was within your own purview; forgetting a failed channel open as an accepting peer had no consequences.

v2, however, changes these assumptions. We now need to know if a channel open gets voided via the spend of any of its inputs. Until the channel funding transaction or an input to it is seen on chain, we must keep track and not forget any data about the channel open.

To this end, this PR also includes a series of commits which add a new table to the database, called output_tracking. This is where we keep data of every input which pertains to a channel.

If we detect a spend of an input for a channel's funding transaction (and not in that funding transaction), we move a channel to a temporary state called BORKED. It persists in the BORKED state until the offending transaction that contains the input spend reaches a depth of 6. At this point, the channel is forgotten, and a new open may be attempted with this peer. We also track these across re-orgs and restarts. If you restart a node with a rescan value of less than 6 there's a good chance you'll break this and your channel will get stuck in BORKED mode forever.

Errata

Because of how fundchannel_start and fundchannel_complete are used under the hood to make fundchannel work, we do a few hacky things to work around the fact that txprepare/txsend hold the transaction for this channel.

Dual-funded transactions expect a zero-value output from the opener that will be used/filled in with the feerate; in order to accommodate this, txprepare has an option zero_out_change, which will supply a transaction with a change output of value zero. (Likewise, hsmd has been adjusted to make sure that we never actually sign a transaction with any output of value zero, so these are unspendable, as is). Completing a funding open (fundchannel_complete) will return the updated txid for the funding transaction. This updated txid is what must be passed into txsend in order to send the transaction, as the v2 channel open pathways modify the underlying prepared transaction in c-lightning's wallet.

This is admittedly suboptimal; I've got some sketches done of how to transition this to PSBT.

Ongoing Work

  • The python tests are currently not passing (32 failed + 12 error on my machine)
  • The commits for this PR are missing CHANGELOG entries
  • Protocol tests need to be fixed up for the new SIGS() and message updates
  • Protocol tests for the opener side need to be completed

Future Work

  • RBF
  • Using PBST to handle the inputs/outputs for fundchannel_start/fundchannel_complete

@niftynei niftynei added this to the 0.8.1 milestone Jan 17, 2020
@niftynei niftynei requested a review from cdecker January 17, 2020 03:07
@ZmnSCPxj
Copy link
Contributor

@niftynei added 30 commits ... 47 hidden items Load More ... @niftynei added 25 commits. Monstrous indeed.

Note that the funding_signed2 message is broken out from the funding_signed2 message.

Maybe you mean "Note that the commitment_signed message is broken out from the funding_signed2 message"?

Completing a funding open (fundchannel_complete) will return the updated txid for the funding transaction. This updated txid is what must be passed into txsend in order to send the transaction, as the v2 channel open pathways modify the underlying prepared transaction in c-lightning's wallet.

That seems a bit too magical. In principle the funding process should not interact with the wallet, otherwise you tie yourself in the following manner:

  • No longer able to use a truly external wallet for funding.
  • Opening to multiple peers is done how now?

Maybe some modification to #3415 can be done instead, where we provide some way to "edit" a txprepared transaction? This probably implies more splitting of the funding process and more fundchannel_* sub-commands.

@niftynei
Copy link
Collaborator Author

niftynei commented Jan 17, 2020 via email

@niftynei niftynei force-pushed the nifty/df branch 2 times, most recently from da1c130 to 48f6d1c Compare January 17, 2020 18:09
@niftynei niftynei force-pushed the nifty/df branch 3 times, most recently from 18f0ddc to 069353a Compare February 7, 2020 01:54
@rustyrussell rustyrussell modified the milestones: 0.8.1, 0.8.2 Feb 9, 2020
@niftynei niftynei force-pushed the nifty/df branch 5 times, most recently from 58ca254 to 26f4a1f Compare February 13, 2020 18:17
@niftynei niftynei modified the milestones: 0.8.2, Next Release Apr 7, 2020
Previously we've used the term 'funder' to refer to the peer
paying the fees for a transaction; v2 of openchannel will make
this no longer true. Instead we rename this to 'opener', or the
peer sending the 'open_channel' message, since this will be universally
true in a dual-funding world.
we need a way to tell openingd to use v2 for channel open.
We've now got two amounts we need to keep track of: the
opener's funding and the accpeter's. We add a utlity to help
keep track of the full funding.

Also wires in the calls for open/accept v2 for when we're the
opener.
so we can refer to it later when doing 'complete' things
for v2 we need to know the inputs/outputs of the transaction
so we look them up. there's more problems here that we need to unwind
still: figuring out which the funding output is plus how to handle
change.
A slight misunderstanding in how negotiation_failed handles control
flow; now we return from failures, and clean up state before we go in
the funder case.
Needs to be updated/re-enabled when we support external wallet funding
for v2 opens
Previously we had effectively skipped tests without marking them as
such; now we mark them as such to make it more obvious what's going on
We don't have a way to introspect if a node will be using v2 before the
node has been created, so for now we use the 'crude' "are
EXPERIMENTAL_FEATURES turned on" switch to decide what messages to add
to the disconnects.
Change up how the 'inputs/outputs' are exchanged, removes ack_rbf and
adds a new gossip message `blacklist_podle` for gossiping podle
commitments.

Using sha 3f9f65d3ad5a21835994f9d9226ed9e0e4066662, but this will
likely change, not be stable in the final
routine for calculating the locktime for a transaction, based on the
wallet.cpp method in bitcoind

9 times out of 10, will use the current tip height, 10% of the time will
use a randomly chosen height within 100 of tip
niftynei added 14 commits April 30, 2020 18:15
sizeof doesn't give the correct length, instead use tal_hex which will
measure the script length correctly
We were using a 'quiet' flag, but that broke on the busybox
implementation of `patch`, since they don't support it. Instead, we use
the universally supported "pipe to /dev/null" approach

Suggested-By: @rustyrussell
There's no counts for the interactive protocol, so we remove them.
Since we're not limiting the number of inputs to a funding transaction,
we now report the entire wallet's balance to the openchannel hook for
the available_funds
This needs more thought, but for now is a hack that allows to
c-lightning nodes to successfully communicate their inputs/outputs.

Since we send everything and then start rolling through the stack
of things that they sent us, the 'completes' aren't exactly
"chronologically" ordered between nodes.

In fact, they can/could send us a 'complete' from the past. If we then
read off the complete, which is followed by more adds, with this
construction we'll miss them and the whole thing will fall apart.

There's a better policy here, tbd.
@cdecker cdecker removed this from the v0.9.0 milestone Jun 29, 2020
@niftynei
Copy link
Collaborator Author

retiring this, new PR incoming soon ™️

@niftynei niftynei closed this Jul 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants