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
Add fee negotiation on channel cooperative shutdown. #225
Add fee negotiation on channel cooperative shutdown. #225
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.
Really nice work on this @halseth! I've added a few comments, questions and a bunch of formatting nitpicks. Let me know if you have any questions!
lnwallet/channel.go
Outdated
@@ -2614,22 +2614,21 @@ 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, uint64, | |||
func (lc *LightningChannel) CreateCloseProposal(proposedFee uint64) ([]byte, uint64, |
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.
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 reason I did this was because the closing_signed
message contains a proposed fee (not fee-per-kw), and I would have to convert that back to a feeRate
before creating a close proposal in response. This would lead to a slightly different fee in some cases due to rounding. I am happy to make it use feeRate if this can be resolved.
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.
If the computation is performed correctly, both side should always arrive at the same fee rate.
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.
Let 666
be an example fee rate. The old CreateCloseProposal
would calculate a proposed fee of
666 * 724 / 1000 = 482,184 => 482 as uint64
If the receiver of this proposal agreed on this fee, he would need to create his own close proposal with the same fee rate, calculated as
482 * 1000 / 724 = 665,7458563536 => 665 as uint64
which leads to incompatible proposed fees.
We could make this work by using a more granular feeRate, but I found it easier to just use the fee directly. Any better way to do this, and what are the advantages of using the fee rate?
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.
Upon reflection, the absolute fee value is the proper way to go here.
lnwallet/channel.go
Outdated
// closed. | ||
channelClosing | ||
ChannelClosing |
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 seems a little strange to me that some of the possible states would be visible outside the package, but that others wouldn't. Is there a way to make them all one way or the other?
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.
Yeah, this was me trying to do make as few visible as possible, but I agree that if one one is visible then they should all be. These are just constants in any case, so shouldn't hurt to be visible.
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 do these states need to be visible outside the package at all? FWIW, most of them aren't even being used within the package. Still getting through the PR, so there may be a reason that becomes apparent for me later on.
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 initially had to make some of these public to move the "is closing" check to peer.go
(see below comment). I deleted this check by instead using the state in channelManager
to determine of a shutdown is already in progress, but the peer tests are still using these states to check that the channel is ChannelClosing/ChannelClosed
when we expect it. Should these checks not be done by the peer tests (since the states really are internal to channel.go
), or should I use another way of querying the state of the channel from the peer tests?
lnwallet/channel.go
Outdated
} | ||
|
||
// GetChannelStatus returns the channelState of this channel. | ||
func (lc *LightningChannel) GetChannelStatus() channelState { |
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 believe the use of "Get" on these methods is more Java-ish than Go-ish. Could this just be ChannelStatus()?
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.
Yeah, sounds better :)
lnwallet/channel.go
Outdated
@@ -2851,3 +2849,13 @@ func CreateCooperativeCloseTx(fundingTxIn *wire.TxIn, | |||
|
|||
return closeTx | |||
} | |||
|
|||
// GetFee returns the commit fee to use for the given fee rate. | |||
func (lc *LightningChannel) GetFee(feeRate uint64) uint64 { |
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.
Related to the comment below about using "Get", maybe could call this "CalcFee"?
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.
👍
peer.go
Outdated
|
||
delete(chanShutdowns, req.ChannelID) | ||
initiatorSig := append(initiatorShutdownSigs[req.ChannelID], | ||
byte(txscript.SigHashAll)) |
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.
Due to all the nesting, there are a lot of lines here that are quite a bit longer than 80 columns. Would it make sense to refactor this to reduce all the long lines?
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 surprising. I have my editor (Atom) set to a line length of 80, and it looks good to me. How do you determine the length?
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.
Ok, I think what causes the issue is that my current tab length is set to 2. Should we use 4, or something else?
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.
You should be using gofmt
, as it's used uniformly within the codebase.
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.
As discussed, my editor was showing a 2 space tab length, which is not checked by gofmt
(a tab is just a tab). I switched to using a 8 space tab length, so from now it should be showing the line lengths correctly, and I reduced the long lines.
peer_test.go
Outdated
|
||
// Wait for closing tx to be broadcasted. | ||
<-publTx | ||
} |
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.
Another test that could be added (may be out of scope for this PR) would be a negotiation failure test, where in the event that the two sides don't converge on a fee, the initiator force closes.
peer.go
Outdated
|
||
// Our new proposed fee must be strictly between what we proposed before | ||
// and what the peer proposed. | ||
isStrictlyBetween := false |
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.
Maybe rename this to something like "isAcceptable"?
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.
done.
peer.go
Outdated
} | ||
|
||
if !isStrictlyBetween { | ||
// TODO(halseth): fail 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.
By "fail channel", do you mean initiate force close?
Also, I think we can actually take out the isStrictlyBetween variable altogether? Couldn't we just check the fee and fail the channel if the fee isn't in the acceptable range?
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.
BOLT#2 only says "one side fails the channel", but I guess that means a force close.
We could definitely take out the variable, but couldn't find a very readable way:
if !((fee < peerFeeProposal && fee > ourFeeProp) ||
(fee < ourFeeProp && fee > peerFeeProposal)) {
// fail channel
}
Suggestions?
peer.go
Outdated
// Our new proposed fee must be strictly between what we proposed before | ||
// and what the peer proposed. | ||
isStrictlyBetween := false | ||
if fee < peerFeeProposal && fee > ourFeeProp { |
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.
Not sure if it's more or less readable, but couldn't these two conditionals be combined with an ||?
peer_test.go
Outdated
return nil, nil, nil, nil, err | ||
} | ||
bobCommitTx, err := lnwallet.CreateCommitTx(fundingTxIn, bobKeyPub, | ||
aliceKeyPub, bobRevokeKey, csvTimeoutBob, channelBal, channelBal, bobDustLimit) |
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.
Line is a bit long.
lnwallet/channel.go
Outdated
// closed. | ||
channelClosing | ||
ChannelClosing |
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 do these states need to be visible outside the package at all? FWIW, most of them aren't even being used within the package. Still getting through the PR, so there may be a reason that becomes apparent for me later on.
lnwallet/channel.go
Outdated
@@ -2614,22 +2614,21 @@ 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, uint64, | |||
func (lc *LightningChannel) CreateCloseProposal(proposedFee uint64) ([]byte, uint64, |
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.
If the computation is performed correctly, both side should always arrive at the same fee rate.
lnwallet/channel.go
Outdated
// If we're already closing the channel, then ignore this request. | ||
if lc.status == channelClosing || lc.status == channelClosed { | ||
// If we've already closed the channel, then ignore this request. | ||
if lc.status == ChannelClosed { |
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 was the second clause of the conditional removed?
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 was done because in a negotiation situation, we potentially want to create several close proposals. Before CreateCloseProposal
could only be called once, and would fail if called again later.
I moved the lc.status == channelClosed
check to to peer.go
such that this would be checked only the first time it was called (following a shutdown request), but as discussed elsewhere this state should probably be internal to channel.go
. Maybe we should make CreateCloseProposal
non-mutating? (since it's not obvious from the name that it actually changes the channelState)
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.
Gotcha, make sense.
lnwallet/channel.go
Outdated
|
||
// CalcFee returns the commit fee to use for the given fee rate. | ||
func (lc *LightningChannel) CalcFee(feeRate uint64) uint64 { | ||
return uint64(btcutil.Amount(feeRate) * commitWeight / 1000) |
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.
Does this even compile? Where is the commitWeight
local being pulled in from?
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.
See above comment
lnwallet/channel.go
Outdated
@@ -2851,3 +2849,16 @@ func CreateCooperativeCloseTx(fundingTxIn *wire.TxIn, | |||
|
|||
return closeTx | |||
} | |||
|
|||
// CalcFee returns the commit fee to use for the given fee rate. |
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.
commit fee -> commitment fee
Also it seems that this doesn't need to be a method of LightningChannel
at all.
On the naming of the function: it isn't clear from the function itself what unit is being passed in.
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.
commitWeight
is a constant declared in channel.go
(commitWeight = btcutil.Amount(724)
). This is why CalcFee
is a method on LightningChannel
.
Changed the unit to btcutil.Amount
peer_test.go
Outdated
broadcastTxChan := make(chan *wire.MsgTx) | ||
|
||
responder, responderChannel, initiatorChannel, cleanUp, err := | ||
createTestPeer(notifier, broadcastTxChan) |
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.
Odd line breakage here. The function should reside on the same line as the assignment operator.
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.
responder, responderChannel, initiatorChannel, cleanUp, err
:= createTestPeer(notifier, broadcastTxChan)
does not work out of the box. Do you prefer something like
responder, responderChannel, initiatorChannel, cleanUp,
err := createTestPeer(notifier, broadcastTxChan)
or another way of breaking it up?
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.
How about
responder, responderChannel, initiatorChannel, cleanUp, err := createTestPeer(
notifier, broadcastTxChan,
)
?
There're three instances above here that would need to be modified as well.
peer_test.go
Outdated
"github.com/roasbeef/btcd/wire" | ||
) | ||
|
||
// Test the shutdown responder's behavior if we can agree on the fee |
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 prefixed with TestChannelClosureAcceptFeeResponder.
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.
done
peer_test.go
Outdated
|
||
// Test the shutdown initiator's behavior if we can agree on the fee | ||
// immediately. | ||
func TestChannelClosureAcceptFeeInitiator(t *testing.T) { |
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 prefixed with TestChannelClosureAcceptFeeInitiator
.
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.
done
peer_test.go
Outdated
|
||
// Test the shutdown responder's behavior in the case where we must do several | ||
// rounds of fee negotiation before we agree on a fee. | ||
func TestChannelClosureFeeNegotiationsResponder(t *testing.T) { |
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 prefixed with TestChannelClosureFeeNegotiationsResponder
.
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.
done
peer_test.go
Outdated
|
||
// Test the shutdown initiator's behavior in the case where we must do several | ||
// rounds of fee negotiation before we agree on a fee. | ||
func TestChannelClosureFeeNegotiationsInitiator(t *testing.T) { |
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 prefixed with TestChannelClosureFeeNegotiationsInitiator
.
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.
done
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.
Another iteration pushed. Mostly fixes related to the long lines and style issues.
For the issues regarding visibility of channelState, feeRate vs fee
, the return error of PublishTransaction
and a few other, I left comments and questions for you. PTAL.
peer.go
Outdated
|
||
delete(chanShutdowns, req.ChannelID) | ||
initiatorSig := append(initiatorShutdownSigs[req.ChannelID], | ||
byte(txscript.SigHashAll)) |
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.
As discussed, my editor was showing a 2 space tab length, which is not checked by gofmt
(a tab is just a tab). I switched to using a 8 space tab length, so from now it should be showing the line lengths correctly, and I reduced the long lines.
peer.go
Outdated
// To finalize this shtudown, we'll now send a matching close signed | ||
// message to the other party, and broadcast the closing transaction | ||
// to the network. If the fees are still being negotiated, | ||
// handleInitClosingSigned returns the signature and proposed fee we |
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.
Fixd.
peer.go
Outdated
return nil, 0 | ||
} | ||
|
||
// If we're already closing the channel, then ignore this request. |
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.
Good idea! I did this by checking if we already had a signature created for the shutdown response.
lnwallet/channel.go
Outdated
// closed. | ||
channelClosing | ||
ChannelClosing |
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 initially had to make some of these public to move the "is closing" check to peer.go
(see below comment). I deleted this check by instead using the state in channelManager
to determine of a shutdown is already in progress, but the peer tests are still using these states to check that the channel is ChannelClosing/ChannelClosed
when we expect it. Should these checks not be done by the peer tests (since the states really are internal to channel.go
), or should I use another way of querying the state of the channel from the peer tests?
lnwallet/channel.go
Outdated
@@ -2614,22 +2614,21 @@ 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, uint64, | |||
func (lc *LightningChannel) CreateCloseProposal(proposedFee uint64) ([]byte, uint64, |
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.
Let 666
be an example fee rate. The old CreateCloseProposal
would calculate a proposed fee of
666 * 724 / 1000 = 482,184 => 482 as uint64
If the receiver of this proposal agreed on this fee, he would need to create his own close proposal with the same fee rate, calculated as
482 * 1000 / 724 = 665,7458563536 => 665 as uint64
which leads to incompatible proposed fees.
We could make this work by using a more granular feeRate, but I found it easier to just use the fee directly. Any better way to do this, and what are the advantages of using the fee rate?
peer_test.go
Outdated
|
||
// Test the shutdown initiator's behavior if we can agree on the fee | ||
// immediately. | ||
func TestChannelClosureAcceptFeeInitiator(t *testing.T) { |
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.
done
peer_test.go
Outdated
|
||
// Test the shutdown responder's behavior in the case where we must do several | ||
// rounds of fee negotiation before we agree on a fee. | ||
func TestChannelClosureFeeNegotiationsResponder(t *testing.T) { |
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.
done
peer_test.go
Outdated
|
||
chanID := lnwire.NewChanIDFromOutPoint(responderChannel.ChannelPoint()) | ||
|
||
// We send a shutdown request to Alice. She will now be the responding node |
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.
fixed
peer_test.go
Outdated
avgFee := (preferredRespFee + increasedFee) / 2 | ||
peerFee := responderClosingSigned.FeeSatoshis | ||
if peerFee != avgFee { | ||
t.Fatalf("expected ClosingSigned with fee %v, got %v", 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.
Done.
peer_test.go
Outdated
|
||
// Test the shutdown initiator's behavior in the case where we must do several | ||
// rounds of fee negotiation before we agree on a fee. | ||
func TestChannelClosureFeeNegotiationsInitiator(t *testing.T) { |
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.
done
@@ -0,0 +1,154 @@ | |||
package main | |||
|
|||
import ( |
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.
Worth noting that this will need to be reconciled with the similar mock code added in the outstanding funding persistence PR.
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.
Will do when funding persistance PR is merged, I think that one is closest.
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 on the test coverage for this PR! I have some minor comments (main one is to fix a possible panic), but beyond that the PR is nearly complete.
After the comment are addressed, and the PR is rebased, I'll take a final look before merging. Also, when rebasing+squashing the review commits in, it would be appreciated if the commits were split up into a granular set of commits. Ideally one per sub-system, and if within a sub-system there're several changes, the should be broken up if possible.
// If such a signature exists, it means we | ||
// already have sent a response to a shutdown | ||
// message for this channel, so ignore this one. | ||
_, exists := responderShutdownSigs[req.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.
Why is this explicitly needed? In order to handle re-transmitted shutdown messages over the same connection?
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 what replaces the if channel.ChannelStatus() == lnwallet.ChannelClosing
check I removed from CreateCloseProposal
(and initially moved to handleShutdownResponse
). Similar to that check, this will reject any attempt to start the channel shutdown procedure twice.
I don't really know if it is needed, I just tried to keep the original behavior. Should I remove it?
// accept. | ||
return maxFee | ||
default: | ||
// Cannot accept the average, as we consider it too low. |
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.
Can you put a TODO
here to remind us to ensure that the fee proposed doesn't go over the current fee on the commitment transaction? Thanks!
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.
Added a TODO, but this check should be fairly straightforward to just add in this PR I think. Where do I fetch the current fee? Create a getter for channel.channelState.CommitFee
?
peer.go
Outdated
} | ||
|
||
// Finally, launch a goroutine which will request to be notified by the | ||
// ChainNotifier once the closure transaction obtains a single | ||
// confirmation. | ||
notifier := p.server.cc.chainNotifier | ||
go waitForChanToClose(uint32(bestHeight), notifier, req.Err, | ||
req.ChanPoint, &closingTxid, func() { | ||
var errChan chan 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.
This will cause a panic if an error occurs and needs to be sent across this channel. In the case that the local request isn't nil, then this channel should be initialized with a buffer of one.
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.
waitForChanToClose
actually handles a nil
error channel, and is currently called with the error chan set to nil in master
. I see your point though, maybe we should rather start a goroutine to read the err chan in case of localReq == nil
and print it?
lnwallet/channel.go
Outdated
@@ -2851,3 +2849,16 @@ func CreateCooperativeCloseTx(fundingTxIn *wire.TxIn, | |||
|
|||
return closeTx | |||
} | |||
|
|||
// CalcFee returns the commitment fee to use for the given fee rate. | |||
func (lc *LightningChannel) CalcFee(feeRate btcutil.Amount) uint64 { |
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.
Can you add a comment here noting the units of the expected fee rate (fee-per-kw)? Thanks!
peer_test.go
Outdated
broadcastTxChan := make(chan *wire.MsgTx) | ||
|
||
responder, responderChannel, initiatorChannel, cleanUp, err := | ||
createTestPeer(notifier, broadcastTxChan) |
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.
How about
responder, responderChannel, initiatorChannel, cleanUp, err := createTestPeer(
notifier, broadcastTxChan,
)
?
There're three instances above here that would need to be modified as well.
test_utils.go
Outdated
Port: 18555, | ||
} | ||
|
||
aliceChannelState.SyncPending(addr, 0) |
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 error is ignored here.
test_utils.go
Outdated
Port: 18556, | ||
} | ||
|
||
bobChannelState.SyncPending(addr, 0) |
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.
Error is also ignored here.
5cdf04c
to
ec6f48c
Compare
1 similar comment
lnwallet/channel.go
Outdated
// detected within the channel. | ||
channelDispute | ||
|
||
// channelPendingPayment indicates that there a currently outstanding | ||
// ChannelPendingPayment indicates that there a currently outstanding |
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 godoc
comments were changed, yet the variables themselves are the same. I'm guessing this was left over before reverting a prior change to make all of these public.
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.
Yep, fixed.
c38af18
to
c32eb75
Compare
c32eb75
to
4e1686a
Compare
60bb5c3
to
b8a30bd
Compare
0ef5c5e
to
40fd123
Compare
This commit adds the fee negotiation procedure performed on channel shutdown. The current algorithm picks an ideal a fee based on the FeeEstimator and commit weigth, then accepts the remote's fee if it is at most 50%-200% away from the ideal. The fee negotiation procedure is similar both as sender and receiver of the initial shutdown message, and this commit also make both sides use the same code path for handling these messages.
40fd123
to
e8e8732
Compare
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 🏄🏾
This PR adds a simple fee negotiation flow on channel shutdown. Currently, the algorithm works as follows:
[0.5*desired, 2*desired]
.Both nodes in the channel will broadcast the closing transaction the moment they have the signatures for a transaction with the agreed fee.
TODO:
If we cannot agree on a fee, we should fail the channel.