contractcourt: for taproot channels, wait for conf in closeObserver

This ensures that we end up playing the target output on chain for
taproot channels.
This commit is contained in:
Olaoluwa Osuntokun 2023-07-25 17:19:47 -07:00
parent 638516879f
commit d4a6be1c9f
No known key found for this signature in database
GPG key ID: 3BBD59E99B280306

View file

@ -207,6 +207,13 @@ type chainWatcher struct {
// the current state number on the commitment transactions.
stateHintObfuscator [lnwallet.StateHintSize]byte
// fundingPkScript is the pkScript of the funding output.
fundingPkScript []byte
// heightHint is the height hint used to checkpoint scans on chain for
// conf/spend events.
heightHint uint32
// All the fields below are protected by this mutex.
sync.Mutex
@ -267,9 +274,9 @@ func (c *chainWatcher) Start() error {
// As a height hint, we'll try to use the opening height, but if the
// channel isn't yet open, then we'll use the height it was broadcast
// at. This may be an unconfirmed zero-conf channel.
heightHint := c.cfg.chanState.ShortChanID().BlockHeight
if heightHint == 0 {
heightHint = chanState.BroadcastHeight()
c.heightHint = c.cfg.chanState.ShortChanID().BlockHeight
if c.heightHint == 0 {
c.heightHint = chanState.BroadcastHeight()
}
// Since no zero-conf state is stored in a channel backup, the below
@ -279,11 +286,11 @@ func (c *chainWatcher) Start() error {
if chanState.ZeroConfConfirmed() {
// If the zero-conf channel is confirmed, we'll use the
// confirmed SCID's block height.
heightHint = chanState.ZeroConfRealScid().BlockHeight
c.heightHint = chanState.ZeroConfRealScid().BlockHeight
} else {
// The zero-conf channel is unconfirmed. We'll need to
// use the FundingBroadcastHeight.
heightHint = chanState.BroadcastHeight()
c.heightHint = chanState.BroadcastHeight()
}
}
@ -291,11 +298,10 @@ func (c *chainWatcher) Start() error {
remoteKey := chanState.RemoteChanCfg.MultiSigKey.PubKey
var (
pkScript []byte
err error
err error
)
if chanState.ChanType.IsTaproot() {
pkScript, _, err = input.GenTaprootFundingScript(
c.fundingPkScript, _, err = input.GenTaprootFundingScript(
localKey, remoteKey, 0,
)
if err != nil {
@ -309,14 +315,14 @@ func (c *chainWatcher) Start() error {
if err != nil {
return err
}
pkScript, err = input.WitnessScriptHash(multiSigScript)
c.fundingPkScript, err = input.WitnessScriptHash(multiSigScript)
if err != nil {
return err
}
}
spendNtfn, err := c.cfg.notifier.RegisterSpendNtfn(
fundingOut, pkScript, heightHint,
fundingOut, c.fundingPkScript, c.heightHint,
)
if err != nil {
return err
@ -582,6 +588,33 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
log.Infof("Close observer for ChannelPoint(%v) active",
c.cfg.chanState.FundingOutpoint)
// If this is a taproot channel, before we proceed, we want to ensure
// that the expected funding output has confirmed on chain.
if c.cfg.chanState.ChanType.IsTaproot() {
fundingPoint := c.cfg.chanState.FundingOutpoint
confNtfn, err := c.cfg.notifier.RegisterConfirmationsNtfn(
&fundingPoint.Hash, c.fundingPkScript, 1, c.heightHint,
)
if err != nil {
log.Warnf("unable to register for conf: %v", err)
}
log.Infof("Waiting for taproot ChannelPoint(%v) to confirm...",
c.cfg.chanState.FundingOutpoint)
select {
case _, ok := <-confNtfn.Confirmed:
// If the channel was closed, then this means that the
// notifier exited, so we will as well.
if !ok {
return
}
case <-c.quit:
return
}
}
select {
// We've detected a spend of the channel onchain! Depending on the type
// of spend, we'll act accordingly, so we'll examine the spending