Merge pull request #7480 from maxwellsayles/fundExpiryOnPending
lnd: compute FundingExpiryBlocks in PendingOpenChannel message
This commit is contained in:
9 files changed
+1860
-1768
No files matched your search
@@ -53,6 +53,11 @@ package](https://github.com/lightningnetwork/lnd/pull/7356)
|
|||||||
a helpful note-to-self containing arbitrary useful information about the
|
a helpful note-to-self containing arbitrary useful information about the
|
||||||
channel.
|
channel.
|
||||||
|
|
||||||
|
* `PendingOpenChannel` now has the field
|
||||||
|
[`funding_expiry_blocks`](https://github.com/lightningnetwork/lnd/pull/7480)
|
||||||
|
that indicates the number of blocks until the funding transaction is
|
||||||
|
considered expired.
|
||||||
|
|
||||||
* [gRPC keepalive parameters can now be set in the
|
* [gRPC keepalive parameters can now be set in the
|
||||||
configuration](https://github.com/lightningnetwork/lnd/pull/7730). The `lnd`
|
configuration](https://github.com/lightningnetwork/lnd/pull/7730). The `lnd`
|
||||||
configuration settings `grpc.server-ping-time` and `grpc.server-ping-timeout`
|
configuration settings `grpc.server-ping-time` and `grpc.server-ping-timeout`
|
||||||
@@ -125,6 +130,7 @@ unlock or create.
|
|||||||
* hieblmi
|
* hieblmi
|
||||||
* Jordi Montes
|
* Jordi Montes
|
||||||
* Matt Morehouse
|
* Matt Morehouse
|
||||||
|
* Maxwell Sayles
|
||||||
* Michael Street
|
* Michael Street
|
||||||
* Oliver Gugger
|
* Oliver Gugger
|
||||||
* Shaurya Arora
|
* Shaurya Arora
|
||||||
|
|||||||
+7
-7
@@ -104,10 +104,10 @@ const (
|
|||||||
// TODO(roasbeef): tune.
|
// TODO(roasbeef): tune.
|
||||||
msgBufferSize = 50
|
msgBufferSize = 50
|
||||||
|
|
||||||
// maxWaitNumBlocksFundingConf is the maximum number of blocks to wait
|
// MaxWaitNumBlocksFundingConf is the maximum number of blocks to wait
|
||||||
// for the funding transaction to be confirmed before forgetting
|
// for the funding transaction to be confirmed before forgetting
|
||||||
// channels that aren't initiated by us. 2016 blocks is ~2 weeks.
|
// channels that aren't initiated by us. 2016 blocks is ~2 weeks.
|
||||||
maxWaitNumBlocksFundingConf = 2016
|
MaxWaitNumBlocksFundingConf = 2016
|
||||||
|
|
||||||
// pendingChansLimit is the maximum number of pending channels that we
|
// pendingChansLimit is the maximum number of pending channels that we
|
||||||
// can have. After this point, pending channel opens will start to be
|
// can have. After this point, pending channel opens will start to be
|
||||||
@@ -2535,7 +2535,7 @@ func (f *Manager) fundingTimeout(c *channeldb.OpenChannel,
|
|||||||
pendingID [32]byte) error {
|
pendingID [32]byte) error {
|
||||||
|
|
||||||
// We'll get a timeout if the number of blocks mined since the channel
|
// We'll get a timeout if the number of blocks mined since the channel
|
||||||
// was initiated reaches maxWaitNumBlocksFundingConf and we are not the
|
// was initiated reaches MaxWaitNumBlocksFundingConf and we are not the
|
||||||
// channel initiator.
|
// channel initiator.
|
||||||
localBalance := c.LocalCommitment.LocalBalance.ToSatoshis()
|
localBalance := c.LocalCommitment.LocalBalance.ToSatoshis()
|
||||||
closeInfo := &channeldb.ChannelCloseSummary{
|
closeInfo := &channeldb.ChannelCloseSummary{
|
||||||
@@ -2597,7 +2597,7 @@ func (f *Manager) fundingTimeout(c *channeldb.OpenChannel,
|
|||||||
|
|
||||||
// waitForFundingWithTimeout is a wrapper around waitForFundingConfirmation and
|
// waitForFundingWithTimeout is a wrapper around waitForFundingConfirmation and
|
||||||
// waitForTimeout that will return ErrConfirmationTimeout if we are not the
|
// waitForTimeout that will return ErrConfirmationTimeout if we are not the
|
||||||
// channel initiator and the maxWaitNumBlocksFundingConf has passed from the
|
// channel initiator and the MaxWaitNumBlocksFundingConf has passed from the
|
||||||
// funding broadcast height. In case of confirmation, the short channel ID of
|
// funding broadcast height. In case of confirmation, the short channel ID of
|
||||||
// the channel and the funding transaction will be returned.
|
// the channel and the funding transaction will be returned.
|
||||||
func (f *Manager) waitForFundingWithTimeout(
|
func (f *Manager) waitForFundingWithTimeout(
|
||||||
@@ -2754,7 +2754,7 @@ func (f *Manager) waitForFundingConfirmation(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// waitForTimeout will close the timeout channel if maxWaitNumBlocksFundingConf
|
// waitForTimeout will close the timeout channel if MaxWaitNumBlocksFundingConf
|
||||||
// has passed from the broadcast height of the given channel. In case of error,
|
// has passed from the broadcast height of the given channel. In case of error,
|
||||||
// the error is sent on timeoutChan. The wait can be canceled by closing the
|
// the error is sent on timeoutChan. The wait can be canceled by closing the
|
||||||
// cancelChan.
|
// cancelChan.
|
||||||
@@ -2777,7 +2777,7 @@ func (f *Manager) waitForTimeout(completeChan *channeldb.OpenChannel,
|
|||||||
|
|
||||||
// On block maxHeight we will cancel the funding confirmation wait.
|
// On block maxHeight we will cancel the funding confirmation wait.
|
||||||
broadcastHeight := completeChan.BroadcastHeight()
|
broadcastHeight := completeChan.BroadcastHeight()
|
||||||
maxHeight := broadcastHeight + maxWaitNumBlocksFundingConf
|
maxHeight := broadcastHeight + MaxWaitNumBlocksFundingConf
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case epoch, ok := <-epochClient.Epochs:
|
case epoch, ok := <-epochClient.Epochs:
|
||||||
@@ -2793,7 +2793,7 @@ func (f *Manager) waitForTimeout(completeChan *channeldb.OpenChannel,
|
|||||||
log.Warnf("Waited for %v blocks without "+
|
log.Warnf("Waited for %v blocks without "+
|
||||||
"seeing funding transaction confirmed,"+
|
"seeing funding transaction confirmed,"+
|
||||||
" cancelling.",
|
" cancelling.",
|
||||||
maxWaitNumBlocksFundingConf)
|
MaxWaitNumBlocksFundingConf)
|
||||||
|
|
||||||
// Notify the caller of the timeout.
|
// Notify the caller of the timeout.
|
||||||
close(timeoutChan)
|
close(timeoutChan)
|
||||||
|
|||||||
+11
-8
@@ -2192,14 +2192,15 @@ func TestFundingManagerFundingTimeout(t *testing.T) {
|
|||||||
// We expect Bob to forget the channel after 2016 blocks (2 weeks), so
|
// We expect Bob to forget the channel after 2016 blocks (2 weeks), so
|
||||||
// mine 2016-1, and check that it is still pending.
|
// mine 2016-1, and check that it is still pending.
|
||||||
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: fundingBroadcastHeight + maxWaitNumBlocksFundingConf - 1,
|
Height: fundingBroadcastHeight +
|
||||||
|
MaxWaitNumBlocksFundingConf - 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bob should still be waiting for the channel to open.
|
// Bob should still be waiting for the channel to open.
|
||||||
assertNumPendingChannelsRemains(t, bob, 1)
|
assertNumPendingChannelsRemains(t, bob, 1)
|
||||||
|
|
||||||
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: fundingBroadcastHeight + maxWaitNumBlocksFundingConf,
|
Height: fundingBroadcastHeight + MaxWaitNumBlocksFundingConf,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bob should have sent an Error message to Alice.
|
// Bob should have sent an Error message to Alice.
|
||||||
@@ -2245,14 +2246,16 @@ func TestFundingManagerFundingNotTimeoutInitiator(t *testing.T) {
|
|||||||
t.Fatalf("alice did not publish funding tx")
|
t.Fatalf("alice did not publish funding tx")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increase the height to 1 minus the maxWaitNumBlocksFundingConf
|
// Increase the height to 1 minus the MaxWaitNumBlocksFundingConf
|
||||||
// height.
|
// height.
|
||||||
alice.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
alice.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: fundingBroadcastHeight + maxWaitNumBlocksFundingConf - 1,
|
Height: fundingBroadcastHeight +
|
||||||
|
MaxWaitNumBlocksFundingConf - 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: fundingBroadcastHeight + maxWaitNumBlocksFundingConf - 1,
|
Height: fundingBroadcastHeight +
|
||||||
|
MaxWaitNumBlocksFundingConf - 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert both and Alice and Bob still have 1 pending channels.
|
// Assert both and Alice and Bob still have 1 pending channels.
|
||||||
@@ -2260,13 +2263,13 @@ func TestFundingManagerFundingNotTimeoutInitiator(t *testing.T) {
|
|||||||
|
|
||||||
assertNumPendingChannelsRemains(t, bob, 1)
|
assertNumPendingChannelsRemains(t, bob, 1)
|
||||||
|
|
||||||
// Increase both Alice and Bob to maxWaitNumBlocksFundingConf height.
|
// Increase both Alice and Bob to MaxWaitNumBlocksFundingConf height.
|
||||||
alice.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
alice.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: fundingBroadcastHeight + maxWaitNumBlocksFundingConf,
|
Height: fundingBroadcastHeight + MaxWaitNumBlocksFundingConf,
|
||||||
}
|
}
|
||||||
|
|
||||||
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: fundingBroadcastHeight + maxWaitNumBlocksFundingConf,
|
Height: fundingBroadcastHeight + MaxWaitNumBlocksFundingConf,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since Alice was the initiator, the channel should not have timed out.
|
// Since Alice was the initiator, the channel should not have timed out.
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ var allTestCases = []*lntest.TestCase{
|
|||||||
Name: "sphinx replay persistence",
|
Name: "sphinx replay persistence",
|
||||||
TestFunc: testSphinxReplayPersistence,
|
TestFunc: testSphinxReplayPersistence,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "funding expiry blocks on pending",
|
||||||
|
TestFunc: testFundingExpiryBlocksOnPending,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "list channels",
|
Name: "list channels",
|
||||||
TestFunc: testListChannels,
|
TestFunc: testListChannels,
|
||||||
|
|||||||
@@ -621,3 +621,34 @@ func verifyCloseUpdate(chanUpdate *lnrpc.ChannelEventUpdate,
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testFundingExpiryBlocksOnPending checks that after an OpenChannel, and
|
||||||
|
// before the funding transaction is confirmed, that the FundingExpiryBlocks
|
||||||
|
// field of a PendingChannels decreases.
|
||||||
|
func testFundingExpiryBlocksOnPending(ht *lntest.HarnessTest) {
|
||||||
|
alice, bob := ht.Alice, ht.Bob
|
||||||
|
param := lntest.OpenChannelParams{Amt: 100000}
|
||||||
|
update := ht.OpenChannelAssertPending(alice, bob, param)
|
||||||
|
|
||||||
|
// At this point, the channel's funding transaction will have been
|
||||||
|
// broadcast, but not confirmed. Alice and Bob's nodes should reflect
|
||||||
|
// this when queried via RPC. FundingExpiryBlock should decrease
|
||||||
|
// as blocks are mined, until the channel is confirmed. Empty blocks
|
||||||
|
// won't confirm the funding transaction, so let's mine a few empty
|
||||||
|
// blocks and verify the value of FundingExpiryBlock at each step.
|
||||||
|
const numEmptyBlocks = 3
|
||||||
|
for i := int32(0); i < numEmptyBlocks; i++ {
|
||||||
|
expectedVal := funding.MaxWaitNumBlocksFundingConf - i
|
||||||
|
pending := ht.AssertNumPendingOpenChannels(alice, 1)
|
||||||
|
require.Equal(ht, expectedVal, pending[0].FundingExpiryBlocks)
|
||||||
|
pending = ht.AssertNumPendingOpenChannels(bob, 1)
|
||||||
|
require.Equal(ht, expectedVal, pending[0].FundingExpiryBlocks)
|
||||||
|
ht.MineEmptyBlocks(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mine 1 block to confirm the funding transaction, and then close the
|
||||||
|
// channel.
|
||||||
|
ht.MineBlocksAndAssertNumTxes(1, 1)
|
||||||
|
chanPoint := lntest.ChanPointFromPendingUpdate(update)
|
||||||
|
ht.CloseChannel(alice, chanPoint)
|
||||||
|
}
|
||||||
+1770
-1750
File diff suppressed because it is too large.
Load diff
@@ -2581,6 +2581,17 @@ message PendingChannelsResponse {
|
|||||||
|
|
||||||
// Previously used for confirmation_height. Do not reuse.
|
// Previously used for confirmation_height. Do not reuse.
|
||||||
reserved 2;
|
reserved 2;
|
||||||
|
|
||||||
|
// The number of blocks until the funding transaction is considered
|
||||||
|
// expired. If this value gets close to zero, there is a risk that the
|
||||||
|
// channel funding will be canceled by the channel responder. The
|
||||||
|
// channel should be fee bumped using CPFP (see walletrpc.BumpFee) to
|
||||||
|
// ensure that the channel confirms in time. Otherwise a force-close
|
||||||
|
// will be necessary if the channel confirms after the funding
|
||||||
|
// transaction expires. A negative value means the channel responder has
|
||||||
|
// very likely canceled the funding and the channel will never become
|
||||||
|
// fully operational.
|
||||||
|
int32 funding_expiry_blocks = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message WaitingCloseChannel {
|
message WaitingCloseChannel {
|
||||||
|
|||||||
@@ -3052,6 +3052,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "int64",
|
"format": "int64",
|
||||||
"description": "The required number of satoshis per kilo-weight that the requester will\npay at all times, for both the funding transaction and commitment\ntransaction. This value can later be updated once the channel is open."
|
"description": "The required number of satoshis per kilo-weight that the requester will\npay at all times, for both the funding transaction and commitment\ntransaction. This value can later be updated once the channel is open."
|
||||||
|
},
|
||||||
|
"funding_expiry_blocks": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32",
|
||||||
|
"description": "The number of blocks until the funding transaction is considered\nexpired. If this value gets close to zero, there is a risk that the\nchannel funding will be canceled by the channel responder. The\nchannel should be fee bumped using CPFP (see walletrpc.BumpFee) to\nensure that the channel confirms in time. Otherwise a force-close\nwill be necessary if the channel confirms after the funding\ntransaction expires. A negative value means the channel responder has\nvery likely canceled the funding and the channel will never become\nfully operational."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+15
-3
@@ -3373,6 +3373,11 @@ func (r *rpcServer) fetchPendingOpenChannels() (pendingOpenChannels, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, currentHeight, err := r.server.cc.ChainIO.GetBestBlock()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
result := make(pendingOpenChannels, len(channels))
|
result := make(pendingOpenChannels, len(channels))
|
||||||
for i, pendingChan := range channels {
|
for i, pendingChan := range channels {
|
||||||
pub := pendingChan.IdentityPub.SerializeCompressed()
|
pub := pendingChan.IdentityPub.SerializeCompressed()
|
||||||
@@ -3389,6 +3394,12 @@ func (r *rpcServer) fetchPendingOpenChannels() (pendingOpenChannels, error) {
|
|||||||
commitBaseWeight := blockchain.GetTransactionWeight(utx)
|
commitBaseWeight := blockchain.GetTransactionWeight(utx)
|
||||||
commitWeight := commitBaseWeight + input.WitnessCommitmentTxWeight
|
commitWeight := commitBaseWeight + input.WitnessCommitmentTxWeight
|
||||||
|
|
||||||
|
// FundingExpiryBlocks is the distance from the current block
|
||||||
|
// height to the broadcast height + MaxWaitNumBlocksFundingConf.
|
||||||
|
maxFundingHeight := funding.MaxWaitNumBlocksFundingConf +
|
||||||
|
pendingChan.BroadcastHeight()
|
||||||
|
fundingExpiryBlocks := int32(maxFundingHeight) - currentHeight
|
||||||
|
|
||||||
result[i] = &lnrpc.PendingChannelsResponse_PendingOpenChannel{
|
result[i] = &lnrpc.PendingChannelsResponse_PendingOpenChannel{
|
||||||
Channel: &lnrpc.PendingChannelsResponse_PendingChannel{
|
Channel: &lnrpc.PendingChannelsResponse_PendingChannel{
|
||||||
RemoteNodePub: hex.EncodeToString(pub),
|
RemoteNodePub: hex.EncodeToString(pub),
|
||||||
@@ -3402,9 +3413,10 @@ func (r *rpcServer) fetchPendingOpenChannels() (pendingOpenChannels, error) {
|
|||||||
CommitmentType: rpcCommitmentType(pendingChan.ChanType),
|
CommitmentType: rpcCommitmentType(pendingChan.ChanType),
|
||||||
Private: isPrivate(pendingChan),
|
Private: isPrivate(pendingChan),
|
||||||
},
|
},
|
||||||
CommitWeight: commitWeight,
|
CommitWeight: commitWeight,
|
||||||
CommitFee: int64(localCommitment.CommitFee),
|
CommitFee: int64(localCommitment.CommitFee),
|
||||||
FeePerKw: int64(localCommitment.FeePerKw),
|
FeePerKw: int64(localCommitment.FeePerKw),
|
||||||
|
FundingExpiryBlocks: fundingExpiryBlocks,
|
||||||
// TODO(roasbeef): need to track confirmation height
|
// TODO(roasbeef): need to track confirmation height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user