From af70af27104d6e1fe3e61a09a713835398cd59b5 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 14 Apr 2023 14:55:40 +0200 Subject: [PATCH] funding+server: update switch after sending channel update With this commit we give the funding manager the ability to inform the switch about custom channel policies, right after we've announced the channel to the network. This change is necessary because before #6753 a channel could only be opened with the default forwarding policies, so the switch automatically had the "correct" default values. Since #6753 added the ability to specify forwarding policies at channel open time, we announced those policies to the network but never updated the switch to inform it about the changed policies (previously changing the policies was only possible through the UpdateChannelPolicy RPC which did call the switch). --- funding/manager.go | 29 +++++++++++++++++++++++++- funding/manager_test.go | 46 ++++++++++++++++++++++++++++++++++++----- server.go | 5 +++-- 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/funding/manager.go b/funding/manager.go index c55cbba32..ad66ba1c1 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -488,6 +488,11 @@ type Config struct { // transition from pending open to open. NotifyOpenChannelEvent func(wire.OutPoint) + // UpdateForwardingPolicies is used by the manager to update active + // links with a new policy. + UpdateForwardingPolicies func( + chanPolicies map[wire.OutPoint]htlcswitch.ForwardingPolicy) + // OpenChannelPredicate is a predicate on the lnwire.OpenChannel message // and on the requesting node's public key that returns a bool which // tells the funding manager whether or not to accept the channel. @@ -3159,6 +3164,28 @@ func (f *Manager) addToRouterGraph(completeChan *channeldb.OpenChannel, return ErrFundingManagerShuttingDown } + // The user can define non-default channel policies when opening a + // channel. They are stored in the database to be persisted from the + // moment of funding the channel to it being confirmed. We just + // announced those policies to the network, but we also need to update + // our local policy in the switch to make sure we can forward payments + // with the correct fees. We can't do this when creating the link + // initially as that only takes the static channel parameters. + updatedPolicy := map[wire.OutPoint]htlcswitch.ForwardingPolicy{ + completeChan.FundingOutpoint: { + MinHTLCOut: ann.chanUpdateAnn.HtlcMinimumMsat, + MaxHTLC: ann.chanUpdateAnn.HtlcMaximumMsat, + BaseFee: lnwire.MilliSatoshi( + ann.chanUpdateAnn.BaseFee, + ), + FeeRate: lnwire.MilliSatoshi( + ann.chanUpdateAnn.FeeRate, + ), + TimeLockDelta: uint32(ann.chanUpdateAnn.TimeLockDelta), + }, + } + f.cfg.UpdateForwardingPolicies(updatedPolicy) + return nil } @@ -3729,7 +3756,7 @@ func (f *Manager) newChanAnnouncement(localPubKey, chanUpdateAnn.FeeRate = uint32(storedFwdingPolicy.FeeRate) default: - log.Infof("No channel forwaring policy specified for channel "+ + log.Infof("No channel forwarding policy specified for channel "+ "announcement of ChannelID(%v). "+ "Assuming default fee parameters.", chanID) chanUpdateAnn.BaseFee = uint32( diff --git a/funding/manager_test.go b/funding/manager_test.go index d7205afd6..ec9a05c0f 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -250,6 +250,7 @@ type testNode struct { testDir string shutdownChannel chan struct{} reportScidChan chan struct{} + updatedPolicies chan map[wire.OutPoint]htlcswitch.ForwardingPolicy localFeatures []lnwire.FeatureBit remoteFeatures []lnwire.FeatureBit @@ -368,6 +369,9 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, publTxChan := make(chan *wire.MsgTx, 1) shutdownChan := make(chan struct{}) reportScidChan := make(chan struct{}) + updatedPolicies := make( + chan map[wire.OutPoint]htlcswitch.ForwardingPolicy, 1, + ) wc := &mock.WalletController{ RootKey: alicePrivKey, @@ -524,6 +528,11 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, return nil, nil }, AliasManager: aliasMgr, + UpdateForwardingPolicies: func( + p map[wire.OutPoint]htlcswitch.ForwardingPolicy) { + + updatedPolicies <- p + }, } for _, op := range options { @@ -548,6 +557,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, testDir: tempTestDir, shutdownChannel: shutdownChan, reportScidChan: reportScidChan, + updatedPolicies: updatedPolicies, addr: addr, } @@ -627,11 +637,12 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) { UpdateLabel: func(chainhash.Hash, string) error { return nil }, - ZombieSweeperInterval: oldCfg.ZombieSweeperInterval, - ReservationTimeout: oldCfg.ReservationTimeout, - OpenChannelPredicate: chainedAcceptor, - DeleteAliasEdge: oldCfg.DeleteAliasEdge, - AliasManager: oldCfg.AliasManager, + ZombieSweeperInterval: oldCfg.ZombieSweeperInterval, + ReservationTimeout: oldCfg.ReservationTimeout, + OpenChannelPredicate: chainedAcceptor, + DeleteAliasEdge: oldCfg.DeleteAliasEdge, + AliasManager: oldCfg.AliasManager, + UpdateForwardingPolicies: oldCfg.UpdateForwardingPolicies, }) require.NoError(t, err, "failed recreating aliceFundingManager") @@ -1119,6 +1130,21 @@ func assertChannelAnnouncements(t *testing.T, alice, bob *testNode, } } + // At this point we should also have gotten a policy update that + // was sent to the switch subsystem. Make sure it contains the + // same values. + var policyUpdate htlcswitch.ForwardingPolicy + select { + case policyUpdateMap := <-node.updatedPolicies: + require.Len(t, policyUpdateMap, 1) + for _, policy := range policyUpdateMap { + policyUpdate = policy + } + + case <-time.After(time.Second * 5): + t.Fatalf("node didn't send policy update") + } + gotChannelAnnouncement := false gotChannelUpdate := false for _, msg := range announcements { @@ -1147,24 +1173,34 @@ func assertChannelAnnouncements(t *testing.T, alice, bob *testNode, minHtlc = customMinHtlc[j] } require.Equal(t, minHtlc, m.HtlcMinimumMsat) + require.Equal( + t, minHtlc, policyUpdate.MinHTLCOut, + ) // We might expect a custom MaxHltc value. if len(customMaxHtlc) > 0 { maxHtlc = customMaxHtlc[j] } require.Equal(t, maxHtlc, m.HtlcMaximumMsat) + require.Equal(t, maxHtlc, policyUpdate.MaxHTLC) // We might expect a custom baseFee value. if len(baseFees) > 0 { baseFee = baseFees[j] } require.EqualValues(t, baseFee, m.BaseFee) + require.EqualValues( + t, baseFee, policyUpdate.BaseFee, + ) // We might expect a custom feeRate value. if len(feeRates) > 0 { feeRate = feeRates[j] } require.EqualValues(t, feeRate, m.FeeRate) + require.EqualValues( + t, feeRate, policyUpdate.FeeRate, + ) gotChannelUpdate = true } diff --git a/server.go b/server.go index 2224976e5..7d76353de 100644 --- a/server.go +++ b/server.go @@ -1439,8 +1439,9 @@ func newServer(cfg *Config, listenAddrs []net.Addr, RegisteredChains: cfg.registeredChains, MaxAnchorsCommitFeeRate: chainfee.SatPerKVByte( s.cfg.MaxCommitFeeRateAnchors * 1000).FeePerKWeight(), - DeleteAliasEdge: deleteAliasEdge, - AliasManager: s.aliasMgr, + DeleteAliasEdge: deleteAliasEdge, + AliasManager: s.aliasMgr, + UpdateForwardingPolicies: s.htlcSwitch.UpdateForwardingPolicies, }) if err != nil { return nil, err