diff --git a/channeldb/db.go b/channeldb/db.go index 26d14fd2..29aac40a 100644 --- a/channeldb/db.go +++ b/channeldb/db.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/boltdb/bolt" + "github.com/go-errors/errors" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/wire" ) @@ -434,6 +435,42 @@ func (d *DB) FetchClosedChannels(pendingOnly bool) ([]*ChannelCloseSummary, erro return chanSummaries, nil } +// ErrClosedChannelNotFound signals that a closed channel could not be found in +// the channeldb. +var ErrClosedChannelNotFound = errors.New("unable to find closed channel summary") + +// FetchClosedChannel queries for a channel close summary using the channel +// point of the channel in question. +func (d *DB) FetchClosedChannel(chanID *wire.OutPoint) (*ChannelCloseSummary, error) { + var chanSummary *ChannelCloseSummary + if err := d.View(func(tx *bolt.Tx) error { + closeBucket := tx.Bucket(closedChannelBucket) + if closeBucket == nil { + return ErrClosedChannelNotFound + } + + var b bytes.Buffer + var err error + if err = writeOutpoint(&b, chanID); err != nil { + return err + } + + summaryBytes := closeBucket.Get(b.Bytes()) + if summaryBytes == nil { + return ErrClosedChannelNotFound + } + + summaryReader := bytes.NewReader(summaryBytes) + chanSummary, err = deserializeCloseChannelSummary(summaryReader) + + return err + }); err != nil { + return nil, err + } + + return chanSummary, nil +} + // MarkChanFullyClosed marks a channel as fully closed within the database. A // channel should be marked as fully closed if the channel was initially // cooperatively closed and it's reach a single confirmation, or after all the diff --git a/utxonursery.go b/utxonursery.go index 114012c8..077e6c4f 100644 --- a/utxonursery.go +++ b/utxonursery.go @@ -302,7 +302,7 @@ func (u *utxoNursery) Start() error { // NOTE: The next two steps *may* spawn go routines, thus from this // point forward, we must close the nursery's quit channel if we detect // any failures during startup to ensure they terminate. - if err := u.reloadPreschool(lastGraduatedHeight); err != nil { + if err := u.reloadPreschool(); err != nil { newBlockChan.Cancel() close(u.quit) return err @@ -608,14 +608,39 @@ func (u *utxoNursery) NurseryReport( // reloadPreschool re-initializes the chain notifier with all of the outputs // that had been saved to the "preschool" database bucket prior to shutdown. -func (u *utxoNursery) reloadPreschool(heightHint uint32) error { +func (u *utxoNursery) reloadPreschool() error { psclOutputs, err := u.cfg.Store.FetchPreschools() if err != nil { return err } + // For each of the preschool outputs stored in the nursery store, load + // it's close summary from disk so that we can get an accurate height + // hint from which to start our range for spend notifications. for i := range psclOutputs { - err := u.registerPreschoolConf(&psclOutputs[i], heightHint) + kid := &psclOutputs[i] + chanPoint := kid.OriginChanPoint() + + // Load the close summary for this output's channel point. + closeSummary, err := u.cfg.DB.FetchClosedChannel(chanPoint) + if err == channeldb.ErrClosedChannelNotFound { + // This should never happen since the close summary + // should only be removed after the channel has been + // swept completely. + utxnLog.Warnf("Close summary not found for "+ + "chan_point=%v, can't determine height hint"+ + "to sweep commit txn", chanPoint) + continue + + } else if err != nil { + return err + } + + // Use the close height from the channel summary as our height + // hint to drive our spend notifications, with our confirmation + // depth as a buffer for reorgs. + heightHint := closeSummary.CloseHeight - u.cfg.ConfDepth + err = u.registerPreschoolConf(kid, heightHint) if err != nil { return err }