From 2dddbc84d90e829bd8f2a45a3c9ae914d04bb41d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 11 Sep 2019 05:44:31 -0700 Subject: [PATCH] lncfg+lnd: gate usage of tweakless commitments based on local/global feature bits In this commit, we add a new legacy protocol command line flag: `committweak`. When set, this forces the node to NOT signal usage of the new commitment format. This allows us to test that we're able to properly establish channels with legacy nodes. Within the server, we'll now gate our signalling of this new feature based on the legacy protocol config. Finally, when accepting/initiating a new channel funding, we'll now check both the local and remote global feature bits, only using the new commitment format if both signal the global feature bit. --- fundingmanager.go | 37 +++++++++++++++++++++++++++++++----- lncfg/protocol_legacy_off.go | 6 ++++++ lncfg/protocol_legacy_on.go | 12 ++++++++++++ server.go | 11 +++++++++-- 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/fundingmanager.go b/fundingmanager.go index 05acb70a..b6108437 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -1199,6 +1199,18 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { // reservation attempt may be rejected. Note that since we're on the // responding side of a single funder workflow, we don't commit any // funds to the channel ourselves. + // + // Before we init the channel, we'll also check to see if we've + // negotiated the new tweakless commitment format. This is only the + // case if *both* us and the remote peer are signaling the proper + // feature bit. + localTweakless := fmsg.peer.LocalGlobalFeatures().HasFeature( + lnwire.StaticRemoteKeyOptional, + ) + remoteTweakless := fmsg.peer.RemoteGlobalFeatures().HasFeature( + lnwire.StaticRemoteKeyOptional, + ) + tweaklessCommitment := localTweakless && remoteTweakless chainHash := chainhash.Hash(msg.ChainHash) req := &lnwallet.InitFundingReserveMsg{ ChainHash: &chainHash, @@ -1211,6 +1223,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { PushMSat: msg.PushAmount, Flags: msg.ChannelFlags, MinConfs: 1, + Tweakless: tweaklessCommitment, } reservation, err := f.cfg.Wallet.InitChannelReservation(req) @@ -1246,8 +1259,9 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { } fndgLog.Infof("Requiring %v confirmations for pendingChan(%x): "+ - "amt=%v, push_amt=%v", numConfsReq, fmsg.msg.PendingChannelID, - amt, msg.PushAmount) + "amt=%v, push_amt=%v, tweakless=%v", numConfsReq, + fmsg.msg.PendingChannelID, amt, msg.PushAmount, + tweaklessCommitment) // Generate our required constraints for the remote party. remoteCsvDelay := f.cfg.RequiredRemoteDelay(amt) @@ -2440,7 +2454,7 @@ func (f *fundingManager) handleFundingLocked(fmsg *fundingLockedMsg) { // Launch a defer so we _ensure_ that the channel barrier is properly // closed even if the target peer is no longer online at this point. defer func() { - // Close the active channel barrier signalling the readHandler + // Close the active channel barrier signaling the readHandler // that commitment related modifications to this channel can // now proceed. f.barrierMtx.Lock() @@ -2748,6 +2762,18 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) { // Initialize a funding reservation with the local wallet. If the // wallet doesn't have enough funds to commit to this channel, then the // request will fail, and be aborted. + // + // Before we init the channel, we'll also check to see if we've + // negotiated the new tweakless commitment format. This is only the + // case if *both* us and the remote peer are signaling the proper + // feature bit. + localTweakless := msg.peer.LocalGlobalFeatures().HasFeature( + lnwire.StaticRemoteKeyOptional, + ) + remoteTweakless := msg.peer.RemoteGlobalFeatures().HasFeature( + lnwire.StaticRemoteKeyOptional, + ) + tweaklessCommitment := localTweakless && remoteTweakless req := &lnwallet.InitFundingReserveMsg{ ChainHash: &msg.chainHash, NodeID: peerKey, @@ -2760,6 +2786,7 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) { PushMSat: msg.pushAmt, Flags: channelFlags, MinConfs: msg.minConfs, + Tweakless: tweaklessCommitment, } reservation, err := f.cfg.Wallet.InitChannelReservation(req) @@ -2828,8 +2855,8 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) { maxValue := f.cfg.RequiredRemoteMaxValue(capacity) maxHtlcs := f.cfg.RequiredRemoteMaxHTLCs(capacity) - fndgLog.Infof("Starting funding workflow with %v for pendingID(%x)", - msg.peer.Address(), chanID) + fndgLog.Infof("Starting funding workflow with %v for pendingID(%x), "+ + "tweakless=%v", msg.peer.Address(), chanID, tweaklessCommitment) fundingOpen := lnwire.OpenChannel{ ChainHash: *f.cfg.Wallet.Cfg.NetParams.GenesisHash, diff --git a/lncfg/protocol_legacy_off.go b/lncfg/protocol_legacy_off.go index 6f5660e9..7e1e47d3 100644 --- a/lncfg/protocol_legacy_off.go +++ b/lncfg/protocol_legacy_off.go @@ -14,3 +14,9 @@ type LegacyProtocol struct { func (l *LegacyProtocol) LegacyOnion() bool { return false } + +// LegacyOnion returns true if the old commitment format should be used for new +// funded channels. +func (l *LegacyProtocol) LegacyCommitment() bool { + return false +} diff --git a/lncfg/protocol_legacy_on.go b/lncfg/protocol_legacy_on.go index 52bd9388..d384adc8 100644 --- a/lncfg/protocol_legacy_on.go +++ b/lncfg/protocol_legacy_on.go @@ -10,6 +10,12 @@ type LegacyProtocol struct { // As a result, nodes that include us in the route won't use the new // modern onion framing. Onion bool `long:"onion" description:"force node to not advertise the new modern TLV onion format"` + + // CommitmentTweak guards if we should use the old legacy commitment + // protocol, or the newer variant that doesn't have a tweak for the + // remote party's output in the commitment. If set to true, then we + // won't signal StaticRemoteKeyOptional. + CommitmentTweak bool `long:"committweak" description:"force node to not advertise the new commitment format"` } // LegacyOnion returns true if the old legacy onion format should be used when @@ -18,3 +24,9 @@ type LegacyProtocol struct { func (l *LegacyProtocol) LegacyOnion() bool { return l.Onion } + +// LegacyOnion returns true if the old commitment format should be used for new +// funded channels. +func (l *LegacyProtocol) LegacyCommitment() bool { + return l.CommitmentTweak +} diff --git a/server.go b/server.go index 05d8bed5..e8f0618b 100644 --- a/server.go +++ b/server.go @@ -324,6 +324,12 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, globalFeatures.Set(lnwire.TLVOnionPayloadOptional) } + // Similarly, we default to the new modern commitment format unless the + // legacy commitment config is set to true. + if !cfg.LegacyProtocol.LegacyCommitment() { + globalFeatures.Set(lnwire.StaticRemoteKeyOptional) + } + var serializedPubKey [33]byte copy(serializedPubKey[:], privKey.PubKey().SerializeCompressed()) @@ -388,8 +394,9 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, peerConnectedListeners: make(map[string][]chan<- lnpeer.Peer), peerDisconnectedListeners: make(map[string][]chan<- struct{}), - globalFeatures: lnwire.NewFeatureVector(globalFeatures, - lnwire.GlobalFeatures), + globalFeatures: lnwire.NewFeatureVector( + globalFeatures, lnwire.GlobalFeatures, + ), quit: make(chan struct{}), }