Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lnwire: adjusted coop close messages to comply with spec #159

Merged
merged 3 commits into from
May 23, 2017

Conversation

bryanvu
Copy link
Contributor

@bryanvu bryanvu commented Mar 8, 2017

Removed close_request and close_complete and replaced with shutdown and
closing_signed. Addresses the initial part of issue #49.

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This partially addresses the issue (the wire message portion), but both the channel closing workflow, and also the channel closure methods of the channel state machine should also be updated.

For now, we can at least send the Shutdown messages, but just immediately respond with a CloseSigned message upon receipt of the Shutdown message.

The CompleteCooperativeClose method should be modified to instead just verify the signature received, rather then generate a new signature.

"github.com/roasbeef/btcd/wire"
)

// ClosingSigned is sent by both parties to a channel once the channel is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both parties within a channel
or
both parties comprising of the channel
?

// the order of the inputs/outputs.
type Shutdown struct {
// ChannelID serves to identify which channel is to be closed.
ChannelID ChannelID
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also instead refer to the channel by it's outpoint.

// ChannelID serves to identify which channel is to be closed.
ChannelID ChannelID

// Len describes the length of the key that will be used to receive
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect, the field of the shutdown struct (other than the target channel) should be either a btcutil.Address or a plain []byte. Personally, I lean more towards using a btcutil.Address within the field as it allow us to a number of related utilities.

@bryanvu bryanvu force-pushed the channelclose branch 5 times, most recently from 94cea59 to e3dacef Compare March 31, 2017 20:39
@coveralls
Copy link

coveralls commented Mar 31, 2017

Coverage Status

Coverage decreased (-0.3%) to 68.635% when pulling e3daceface1f4279d3aebe5aafdf74e2d3cadaa9 on bryanvu:channelclose into d2b4f14 on lightningnetwork:master.

@bryanvu
Copy link
Contributor Author

bryanvu commented Mar 31, 2017

Updated with support for shutdown messages and additional coop close workflow steps.

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! I think the structure of the changes is well inline with that of the existing codebase. However, I think the new closing workflow can be consolidated and simplified a bit as I'll point out within my in-line comments.

"github.com/roasbeef/btcd/wire"
)

// ClosingSigned is sent by both the initator and responder in a channel close
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit: at a glance, this godoc comment doesn't appear to be wrapped to 80-characters.

// workflow once the channel is clear of HTLCs. Each party provides a signature
// for a transaction with a fee that they believe is fair. The process
// terminates when both sides agree on the same fee, or when one side force
// closes the channel. NOTE: The responder is able to send a signature without
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit: within the rest of the code we separate the "NOTE" portion from the rest of the comment by a line line.

//
// This is part of the lnwire.Message interface.
func (c *ClosingSigned) Decode(r io.Reader, pver uint32) error {
err := readElements(r,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This'll probably show up in the linter, but this bit of code can be simplified to:

return readElements(r, &c.ChannelID, &c.FeeSatoshis, &c.Signature)

//
// This is part of the lnwire.Message interface.
func (c *ClosingSigned) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment here about simply returning the function directly.

// funds of both parties to the final delivery addresses negotiated during the
// funding workflow.
//
// NOTE: The requester is able to only send a signature to initiate the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this NOTE: section can safely be eliminated as it was only meant to be included with the message that piggybacks the closing signature itself.

peer.go Outdated
channel := p.activeChannels[*req.chanPoint]
p.activeChanMtx.RUnlock()

err := p.sendClosingSigned(channel)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I think we can eliminate a need for this method call by adding the closing signature as a return parameter to CompleteCooperativeClose.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Atm work is duplicated a bit since on each channel closing workflow, each side will always generate their signature twice. In order to fix this, we can expand the newly added local scope map to channelManager to also hold one half of the closing signature spending on context and execution order. Next, we can modify the cooperative closure methods on the channel to have the CompleteCoopertiveClose method accept both signatures.

closingTxid)
err := p.sendShutdown(channel)
if err != nil {
req.err <- err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not strictly necessary, but in order to possibly eliminate some future bugs that arise by inserting further statements below this if, a return should be inserted here.

peer.go Outdated
return
}
doneChan := make(chan struct{})
go p.waitForCloseConfirmation(channel, closingTxid, doneChan)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you're at, this method can also be used within the rpcserver when we're handling the force closure of a channel.

peer.go Outdated
return spew.Sdump(closeTx)
}))
closingTxid := closeTx.TxHash()
// Finally, launch a goroutine which will request to be notified by the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new line should be inserted above this line.

peer.go Outdated
return err
}
channelPoint := channel.ChannelPoint()
closingSigned := lnwire.NewClosingSigned(*channelPoint, 5000, closeSig)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 5000 should be pulled out into a constant (and then later be made dynamic by your follow up work).

@bryanvu
Copy link
Contributor Author

bryanvu commented May 11, 2017

Updated with requested changes. Previous version can be found at: https://github.com/bryanvu/lnd/tree/channelclose1

// transaction is returned. It is the duty of the responding node to broadcast
// a signed+valid closure transaction to the network.
// active lightning channel. A fully signed closure transaction as well as the
// signature itself are returned.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This godoc comment should be expanded to detail the behavior of the nil-ness of either the localSig and the or remoteSig.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored so that nil sigs shouldn't be passed in.

// in as the localSig. If we're the initiator, we'll generate our
// half of the 2-of-2 multi-sig needed to redeem the funding output.
var ourSig []byte
var ourFlaggedSig []byte
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Namning suggestion: ourFlaggedSig -> sigWithSighash.

Also this comment doesn't seem quite correct. Based on my interpretation, the responder will be the one that calls this method w/o a signature in the case that they immediately accept the initiators proposal w.r.t the fee amount. The initiator will be the one that passes in a non-nil value for both signatures as they currently hold the current pending signature in volatile memory.

// inputs/outputs.
type ClosingSigned struct {
// ChannelID serves to identify which channel is to be closed.
// TODO(bvu): this should be switched to a ChannelID when the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This TODO comment can be removed. We know properly use the 32-byte channel ID's everywhere.


// ChannelID (outpoint) - 36 bytes
// TODO(bvu): Adjust when new ChannelID is added.
length += 36
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be 32-bytes as we now use the 32-byte channel ID's uniformly when de-multiplexing channel modification messages.

// on the ClosingSigned are valid.
//
// This is part of the lnwire.Message interface.
func (c *ClosingSigned) Validate() error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validate is no longer a part of the lnwire.Message interface.

lnwire/lnwire.go Outdated
}
length := binary.BigEndian.Uint16(addrLen[:])

var addrBytes [32]byte
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The max value here is actually 34 bytes. 34 bytes are needed to encode the script for a p2wsh script: OP_0 OP_PUSH_DATA_32 <32 bytes>.

// sanity. For example, signature fields must not be nil.
//
// This is part of the lnwire.Message interface.
func (s *Shutdown) Validate() error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validate is no longer a part of the lnwire.Message interface.

peer.go Outdated
// HTLCs for the specified channel.
func (p *peer) sendShutdown(channel *lnwallet.LightningChannel) error {
_, addrs, _, err :=
txscript.ExtractPkScriptAddrs(channel.LocalDeliveryScript,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be lifted into the line above.

@bryanvu bryanvu force-pushed the channelclose branch 4 times, most recently from f4b4de1 to bfe539d Compare May 13, 2017 01:58
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.09%) to 73.457% when pulling bfe539d3a82b1372a61f9661e2f160ca36ee1424 on bryanvu:channelclose into 1124b45 on lightningnetwork:master.

@bryanvu
Copy link
Contributor Author

bryanvu commented May 13, 2017

Updated. Previous version at: https://github.com/bryanvu/lnd/tree/channelclose2

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.1%) to 73.438% when pulling 131f3ad on bryanvu:channelclose into 1124b45 on lightningnetwork:master.

@bryanvu
Copy link
Contributor Author

bryanvu commented May 17, 2017

Rebased and added a commit. Previous version at: https://github.com/bryanvu/lnd/tree/channelclose3

@bryanvu bryanvu force-pushed the channelclose branch 2 times, most recently from 44b18d6 to 9f39bac Compare May 18, 2017 19:15
@coveralls
Copy link

coveralls commented May 18, 2017

Coverage Status

Coverage decreased (-0.09%) to 73.526% when pulling 9f39bac22307125472f57d40b338ec1ed13bd51e on bryanvu:channelclose into a5616a3 on lightningnetwork:master.

// fee that will be used for a cooperative channel close before generating the
// proposed channel close transaction. This is called by the responder in a
// cooperative channel close workflow.
func (lc *LightningChannel) CreateRespCloseProposal() ([]byte, uint64, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why's this needed? In the case of a responder to a cooperative closure workflow, they'd still need to be aware of the proposed fee, as using the prior (latest) fee-per-kw may be inaccurate in the case that the initiator chose a diff fee than was set initially within the channel.

@@ -2532,7 +2549,7 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) {
//
// TODO(roasbeef): caller should initiate signal to reject all incoming HTLCs,
// settle any inflight.
func (lc *LightningChannel) CreateCloseProposal(feeRate uint64) ([]byte, error) {
func (lc *LightningChannel) CreateCloseProposal(proposedFee uint64) ([]byte, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than the fee entirely, IMO the fee rate instead should be passed. This lets us keep the internal details of calculating the proper fee (based on the base weight, etc), within the state machine entirely.

@@ -2590,8 +2605,7 @@ func (lc *LightningChannel) CreateCloseProposal(feeRate uint64) ([]byte, error)
//
// NOTE: The passed local and remote sigs are expected to be fully complete
// signatures including the proper sighash byte.
func (lc *LightningChannel) CompleteCooperativeClose(localSig,
remoteSig []byte, proposedFee uint64) (*wire.MsgTx, error) {
func (lc *LightningChannel) CompleteCooperativeClose(localSig, remoteSig []byte) (*wire.MsgTx, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fee rate should still be passed in, otherwise this will sign a commitment transaction with the prior fee of the latest commitment state.

Removed close_request and close_complete and replaced with shutdown and
closing_signed.
This commit changes the cooperative channel close workflow to comply
with the latest spec. This adds steps to handle and send shutdown
messages as well as moving responsibility for sending the channel close
message from the initiator to the responder.
@coveralls
Copy link

Coverage Status

Coverage decreased (-0.09%) to 73.529% when pulling edbf0b8999338de178537cac56d83cc1c7f76939 on bryanvu:channelclose into 570c0e5 on lightningnetwork:master.

This commit switches the channel close workflow to use the lnwallet fee
estimation interface rather than the hardcoded proposedFee.
@coveralls
Copy link

coveralls commented May 23, 2017

Coverage Status

Coverage decreased (-0.08%) to 73.539% when pulling 4660ddf on bryanvu:channelclose into 570c0e5 on lightningnetwork:master.

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM ⛷

@Roasbeef Roasbeef merged commit 408be35 into lightningnetwork:master May 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants