-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Conversation
There was a problem hiding this 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.
lnwire/closing_signed.go
Outdated
"github.com/roasbeef/btcd/wire" | ||
) | ||
|
||
// ClosingSigned is sent by both parties to a channel once the channel is |
There was a problem hiding this comment.
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
?
lnwire/shutdown.go
Outdated
// the order of the inputs/outputs. | ||
type Shutdown struct { | ||
// ChannelID serves to identify which channel is to be closed. | ||
ChannelID ChannelID |
There was a problem hiding this comment.
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.
lnwire/shutdown.go
Outdated
// 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 |
There was a problem hiding this comment.
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.
94cea59
to
e3dacef
Compare
Coverage decreased (-0.3%) to 68.635% when pulling e3daceface1f4279d3aebe5aafdf74e2d3cadaa9 on bryanvu:channelclose into d2b4f14 on lightningnetwork:master. |
Updated with support for shutdown messages and additional coop close workflow steps. |
There was a problem hiding this 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.
lnwire/closing_signed.go
Outdated
"github.com/roasbeef/btcd/wire" | ||
) | ||
|
||
// ClosingSigned is sent by both the initator and responder in a channel close |
There was a problem hiding this comment.
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.
lnwire/closing_signed.go
Outdated
// 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 |
There was a problem hiding this comment.
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.
lnwire/closing_signed.go
Outdated
// | ||
// This is part of the lnwire.Message interface. | ||
func (c *ClosingSigned) Decode(r io.Reader, pver uint32) error { | ||
err := readElements(r, |
There was a problem hiding this comment.
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)
lnwire/closing_signed.go
Outdated
// | ||
// This is part of the lnwire.Message interface. | ||
func (c *ClosingSigned) Encode(w io.Writer, pver uint32) error { | ||
err := writeElements(w, |
There was a problem hiding this comment.
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.
lnwire/shutdown.go
Outdated
// 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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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).
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. |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
lnwallet/channel.go
Outdated
// 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 |
There was a problem hiding this comment.
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.
lnwire/closing_signed.go
Outdated
// 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 |
There was a problem hiding this comment.
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.
lnwire/closing_signed.go
Outdated
|
||
// ChannelID (outpoint) - 36 bytes | ||
// TODO(bvu): Adjust when new ChannelID is added. | ||
length += 36 |
There was a problem hiding this comment.
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.
lnwire/closing_signed.go
Outdated
// on the ClosingSigned are valid. | ||
// | ||
// This is part of the lnwire.Message interface. | ||
func (c *ClosingSigned) Validate() error { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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>
.
lnwire/shutdown.go
Outdated
// sanity. For example, signature fields must not be nil. | ||
// | ||
// This is part of the lnwire.Message interface. | ||
func (s *Shutdown) Validate() error { |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
f4b4de1
to
bfe539d
Compare
Coverage decreased (-0.09%) to 73.457% when pulling bfe539d3a82b1372a61f9661e2f160ca36ee1424 on bryanvu:channelclose into 1124b45 on lightningnetwork:master. |
Updated. Previous version at: https://github.com/bryanvu/lnd/tree/channelclose2 |
Rebased and added a commit. Previous version at: https://github.com/bryanvu/lnd/tree/channelclose3 |
44b18d6
to
9f39bac
Compare
Coverage decreased (-0.09%) to 73.526% when pulling 9f39bac22307125472f57d40b338ec1ed13bd51e on bryanvu:channelclose into a5616a3 on lightningnetwork:master. |
lnwallet/channel.go
Outdated
// 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) { |
There was a problem hiding this comment.
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.
lnwallet/channel.go
Outdated
@@ -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) { |
There was a problem hiding this comment.
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.
lnwallet/channel.go
Outdated
@@ -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) { |
There was a problem hiding this comment.
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.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM ⛷
Removed close_request and close_complete and replaced with shutdown and
closing_signed. Addresses the initial part of issue #49.