Skip to content

Commit 8dee76a

Browse files
Roasbeefguggero
authored andcommitted
peer: decorate delivery addr w/ internal key
In this commit, we move to add the internal key to the delivery addr. This way, we give the aux chan closer the extra information it may need to properly augment the normal co-op close process.
1 parent 44ab7e6 commit 8dee76a

File tree

4 files changed

+131
-17
lines changed

4 files changed

+131
-17
lines changed

lntest/mock/walletcontroller.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,8 @@ func (w *WalletController) ConfirmedBalance(int32, string) (btcutil.Amount,
7878
func (w *WalletController) NewAddress(lnwallet.AddressType, bool,
7979
string) (btcutil.Address, error) {
8080

81-
addr, _ := btcutil.NewAddressPubKey(
82-
w.RootKey.PubKey().SerializeCompressed(), &chaincfg.MainNetParams,
83-
)
81+
pkh := btcutil.Hash160(w.RootKey.PubKey().SerializeCompressed())
82+
addr, _ := btcutil.NewAddressPubKeyHash(pkh, &chaincfg.MainNetParams)
8483
return addr, nil
8584
}
8685

lnwallet/chancloser/chancloser.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"fmt"
66

7+
"github.com/btcsuite/btcd/btcec/v2"
78
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
89
"github.com/btcsuite/btcd/btcutil"
910
"github.com/btcsuite/btcd/chaincfg"
@@ -106,6 +107,18 @@ const (
106107
defaultMaxFeeMultiplier = 3
107108
)
108109

110+
// DeliveryAddrWithKey wraps a normal delivery addr, but also includes the
111+
// internal key for the delivery addr if known.
112+
type DeliveryAddrWithKey struct {
113+
// DeliveryAddress is the raw, serialized pkScript of the delivery
114+
// address.
115+
lnwire.DeliveryAddress
116+
117+
// InternalKey is the Taproot internal key of the delivery address, if
118+
// the address is a P2TR output.
119+
InternalKey fn.Option[btcec.PublicKey]
120+
}
121+
109122
// ChanCloseCfg holds all the items that a ChanCloser requires to carry out its
110123
// duties.
111124
type ChanCloseCfg struct {
@@ -208,6 +221,10 @@ type ChanCloser struct {
208221
// funds to.
209222
localDeliveryScript []byte
210223

224+
// localInternalKey is the local delivery address Taproot internal key,
225+
// if the local delivery script is a P2TR output.
226+
localInternalKey fn.Option[btcec.PublicKey]
227+
211228
// remoteDeliveryScript is the script that we'll send the remote party's
212229
// settled channel funds to.
213230
remoteDeliveryScript []byte
@@ -284,7 +301,7 @@ func (d *SimpleCoopFeeEstimator) EstimateFee(chanType channeldb.ChannelType,
284301
// NewChanCloser creates a new instance of the channel closure given the passed
285302
// configuration, and delivery+fee preference. The final argument should only
286303
// be populated iff, we're the initiator of this closing request.
287-
func NewChanCloser(cfg ChanCloseCfg, deliveryScript []byte,
304+
func NewChanCloser(cfg ChanCloseCfg, deliveryScript DeliveryAddrWithKey,
288305
idealFeePerKw chainfee.SatPerKWeight, negotiationHeight uint32,
289306
closeReq *htlcswitch.ChanClose,
290307
closer lntypes.ChannelParty) *ChanCloser {
@@ -299,7 +316,8 @@ func NewChanCloser(cfg ChanCloseCfg, deliveryScript []byte,
299316
cfg: cfg,
300317
negotiationHeight: negotiationHeight,
301318
idealFeeRate: idealFeePerKw,
302-
localDeliveryScript: deliveryScript,
319+
localInternalKey: deliveryScript.InternalKey,
320+
localDeliveryScript: deliveryScript.DeliveryAddress,
303321
priorFeeOffers: make(
304322
map[btcutil.Amount]*lnwire.ClosingSigned,
305323
),
@@ -362,6 +380,7 @@ func (c *ChanCloser) initChanShutdown() (*lnwire.Shutdown, error) {
362380
ChanPoint: c.chanPoint,
363381
ShortChanID: c.cfg.Channel.ShortChanID(),
364382
Initiator: c.cfg.Channel.IsInitiator(),
383+
InternalKey: c.localInternalKey,
365384
CommitBlob: c.cfg.Channel.LocalCommitmentBlob(),
366385
FundingBlob: c.cfg.Channel.FundingBlob(),
367386
})
@@ -966,6 +985,7 @@ func (c *ChanCloser) ReceiveClosingSigned( //nolint:funlen
966985
req := AuxShutdownReq{
967986
ChanPoint: c.chanPoint,
968987
ShortChanID: c.cfg.Channel.ShortChanID(),
988+
InternalKey: c.localInternalKey,
969989
Initiator: channel.IsInitiator(),
970990
CommitBlob: channel.LocalCommitmentBlob(),
971991
FundingBlob: channel.FundingBlob(),
@@ -1042,6 +1062,7 @@ func (c *ChanCloser) auxCloseOutputs(
10421062
req := AuxShutdownReq{
10431063
ChanPoint: c.chanPoint,
10441064
ShortChanID: c.cfg.Channel.ShortChanID(),
1065+
InternalKey: c.localInternalKey,
10451066
Initiator: c.cfg.Channel.IsInitiator(),
10461067
CommitBlob: c.cfg.Channel.LocalCommitmentBlob(),
10471068
FundingBlob: c.cfg.Channel.FundingBlob(),

lnwallet/chancloser/chancloser_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,8 @@ func TestMaxFeeClamp(t *testing.T) {
361361
Channel: &channel,
362362
MaxFee: test.inputMaxFee,
363363
FeeEstimator: &SimpleCoopFeeEstimator{},
364-
}, nil, test.idealFee, 0, nil, lntypes.Remote,
364+
}, DeliveryAddrWithKey{}, test.idealFee, 0, nil,
365+
lntypes.Remote,
365366
)
366367

367368
// We'll call initFeeBaseline early here since we need
@@ -402,7 +403,8 @@ func TestMaxFeeBailOut(t *testing.T) {
402403
MaxFee: idealFee * 2,
403404
}
404405
chanCloser := NewChanCloser(
405-
closeCfg, nil, idealFee, 0, nil, lntypes.Remote,
406+
closeCfg, DeliveryAddrWithKey{}, idealFee, 0,
407+
nil, lntypes.Remote,
406408
)
407409

408410
// We'll now force the channel state into the
@@ -526,7 +528,7 @@ func TestTaprootFastClose(t *testing.T) {
526528
DisableChannel: func(wire.OutPoint) error {
527529
return nil
528530
},
529-
}, nil, idealFee, 0, nil, lntypes.Local,
531+
}, DeliveryAddrWithKey{}, idealFee, 0, nil, lntypes.Local,
530532
)
531533
aliceCloser.initFeeBaseline()
532534

@@ -543,7 +545,7 @@ func TestTaprootFastClose(t *testing.T) {
543545
DisableChannel: func(wire.OutPoint) error {
544546
return nil
545547
},
546-
}, nil, idealFee, 0, nil, lntypes.Remote,
548+
}, DeliveryAddrWithKey{}, idealFee, 0, nil, lntypes.Remote,
547549
)
548550
bobCloser.initFeeBaseline()
549551

peer/brontide.go

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ import (
1313
"time"
1414

1515
"github.com/btcsuite/btcd/btcec/v2"
16+
"github.com/btcsuite/btcd/btcutil"
1617
"github.com/btcsuite/btcd/chaincfg/chainhash"
1718
"github.com/btcsuite/btcd/connmgr"
1819
"github.com/btcsuite/btcd/txscript"
1920
"github.com/btcsuite/btcd/wire"
2021
"github.com/btcsuite/btclog"
22+
"github.com/btcsuite/btcwallet/waddrmgr"
2123
"github.com/davecgh/go-spew/spew"
2224
"github.com/lightningnetwork/lnd/buffer"
2325
"github.com/lightningnetwork/lnd/build"
@@ -885,6 +887,73 @@ func (p *Brontide) QuitSignal() <-chan struct{} {
885887
return p.quit
886888
}
887889

890+
// internalKeyForAddr returns the internal key associated with a taproot
891+
// address.
892+
func internalKeyForAddr(wallet *lnwallet.LightningWallet,
893+
deliveryScript []byte) (fn.Option[btcec.PublicKey], error) {
894+
895+
none := fn.None[btcec.PublicKey]()
896+
897+
pkScript, err := txscript.ParsePkScript(deliveryScript)
898+
if err != nil {
899+
return none, err
900+
}
901+
addr, err := pkScript.Address(&wallet.Cfg.NetParams)
902+
if err != nil {
903+
return none, err
904+
}
905+
906+
// If it's not a taproot address, we don't require to know the internal
907+
// key in the first place. So we don't return an error here, but also no
908+
// internal key.
909+
_, isTaproot := addr.(*btcutil.AddressTaproot)
910+
if !isTaproot {
911+
return none, nil
912+
}
913+
914+
walletAddr, err := wallet.AddressInfo(addr)
915+
if err != nil {
916+
return none, err
917+
}
918+
919+
// If the address isn't known to the wallet, we can't determine the
920+
// internal key.
921+
if walletAddr == nil {
922+
return none, nil
923+
}
924+
925+
pubKeyAddr, ok := walletAddr.(waddrmgr.ManagedPubKeyAddress)
926+
if !ok {
927+
return none, fmt.Errorf("expected pubkey addr, got %T",
928+
pubKeyAddr)
929+
}
930+
931+
return fn.Some(*pubKeyAddr.PubKey()), nil
932+
}
933+
934+
// addrWithInternalKey takes a delivery script, then attempts to supplement it
935+
// with information related to the internal key for the addr, but only if it's
936+
// a taproot addr.
937+
func (p *Brontide) addrWithInternalKey(
938+
deliveryScript []byte) fn.Result[chancloser.DeliveryAddrWithKey] {
939+
940+
// TODO(roasbeef): not compatible with external shutdown addr?
941+
// Currently, custom channels cannot be created with external upfront
942+
// shutdown addresses, so this shouldn't be an issue. We only require
943+
// the internal key for taproot addresses to be able to provide a non
944+
// inclusion proof of any scripts.
945+
946+
internalKey, err := internalKeyForAddr(p.cfg.Wallet, deliveryScript)
947+
if err != nil {
948+
return fn.Err[chancloser.DeliveryAddrWithKey](err)
949+
}
950+
951+
return fn.Ok(chancloser.DeliveryAddrWithKey{
952+
DeliveryAddress: deliveryScript,
953+
InternalKey: internalKey,
954+
})
955+
}
956+
888957
// loadActiveChannels creates indexes within the peer for tracking all active
889958
// channels returned by the database. It returns a slice of channel reestablish
890959
// messages that should be sent to the peer immediately, in case we have borked
@@ -1125,9 +1194,16 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) (
11251194
return
11261195
}
11271196

1197+
addr, err := p.addrWithInternalKey(
1198+
info.DeliveryScript.Val,
1199+
).Unpack()
1200+
if err != nil {
1201+
shutdownInfoErr = fmt.Errorf("unable to make "+
1202+
"delivery addr: %w", err)
1203+
return
1204+
}
11281205
chanCloser, err := p.createChanCloser(
1129-
lnChan, info.DeliveryScript.Val, feePerKw, nil,
1130-
info.Closer(),
1206+
lnChan, addr, feePerKw, nil, info.Closer(),
11311207
)
11321208
if err != nil {
11331209
shutdownInfoErr = fmt.Errorf("unable to "+
@@ -2886,8 +2962,12 @@ func (p *Brontide) fetchActiveChanCloser(chanID lnwire.ChannelID) (
28862962
return nil, fmt.Errorf("unable to estimate fee")
28872963
}
28882964

2965+
addr, err := p.addrWithInternalKey(deliveryScript).Unpack()
2966+
if err != nil {
2967+
return nil, fmt.Errorf("unable to parse addr: %w", err)
2968+
}
28892969
chanCloser, err = p.createChanCloser(
2890-
channel, deliveryScript, feePerKw, nil, lntypes.Remote,
2970+
channel, addr, feePerKw, nil, lntypes.Remote,
28912971
)
28922972
if err != nil {
28932973
p.log.Errorf("unable to create chan closer: %v", err)
@@ -3129,8 +3209,12 @@ func (p *Brontide) restartCoopClose(lnChan *lnwallet.LightningChannel) (
31293209
closingParty = lntypes.Local
31303210
}
31313211

3212+
addr, err := p.addrWithInternalKey(deliveryScript).Unpack()
3213+
if err != nil {
3214+
return nil, fmt.Errorf("unable to parse addr: %w", err)
3215+
}
31323216
chanCloser, err := p.createChanCloser(
3133-
lnChan, deliveryScript, feePerKw, nil, closingParty,
3217+
lnChan, addr, feePerKw, nil, closingParty,
31343218
)
31353219
if err != nil {
31363220
p.log.Errorf("unable to create chan closer: %v", err)
@@ -3157,8 +3241,8 @@ func (p *Brontide) restartCoopClose(lnChan *lnwallet.LightningChannel) (
31573241
// createChanCloser constructs a ChanCloser from the passed parameters and is
31583242
// used to de-duplicate code.
31593243
func (p *Brontide) createChanCloser(channel *lnwallet.LightningChannel,
3160-
deliveryScript lnwire.DeliveryAddress, fee chainfee.SatPerKWeight,
3161-
req *htlcswitch.ChanClose,
3244+
deliveryScript chancloser.DeliveryAddrWithKey,
3245+
fee chainfee.SatPerKWeight, req *htlcswitch.ChanClose,
31623246
closer lntypes.ChannelParty) (*chancloser.ChanCloser, error) {
31633247

31643248
_, startingHeight, err := p.cfg.ChainIO.GetBestBlock()
@@ -3179,6 +3263,7 @@ func (p *Brontide) createChanCloser(channel *lnwallet.LightningChannel,
31793263
MusigSession: NewMusigChanCloser(channel),
31803264
FeeEstimator: &chancloser.SimpleCoopFeeEstimator{},
31813265
BroadcastTx: p.cfg.Wallet.PublishTransaction,
3266+
AuxCloser: p.cfg.AuxChanCloser,
31823267
DisableChannel: func(op wire.OutPoint) error {
31833268
return p.cfg.ChanStatusMgr.RequestDisable(
31843269
op, false,
@@ -3250,10 +3335,17 @@ func (p *Brontide) handleLocalCloseReq(req *htlcswitch.ChanClose) {
32503335
return
32513336
}
32523337
}
3338+
addr, err := p.addrWithInternalKey(deliveryScript).Unpack()
3339+
if err != nil {
3340+
err = fmt.Errorf("unable to parse addr for channel "+
3341+
"%v: %w", req.ChanPoint, err)
3342+
p.log.Errorf(err.Error())
3343+
req.Err <- err
32533344

3345+
return
3346+
}
32543347
chanCloser, err := p.createChanCloser(
3255-
channel, deliveryScript, req.TargetFeePerKw, req,
3256-
lntypes.Local,
3348+
channel, addr, req.TargetFeePerKw, req, lntypes.Local,
32573349
)
32583350
if err != nil {
32593351
p.log.Errorf(err.Error())

0 commit comments

Comments
 (0)