From 926949ef0fb8e7254f7bf24f1b9658e1a437a8db Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Mon, 22 Jan 2018 20:38:17 -0800 Subject: [PATCH 1/2] channeldb/db: adds FetchClosedChannel This commit adds a FetchClosedChannel method to the channeldb, which allows querying based on a known channel point. This will be used in the nursery to load channel close summaries, which can be used to provide more accurate height hints when recovering from failures. --- channeldb/db.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) 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 From aaebc28206607574c6345a413e024fecc7c4d869 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Mon, 22 Jan 2018 20:39:54 -0800 Subject: [PATCH 2/2] utxonursery: properly set height hint when recovering commit sweep This commit alters the restart logic in the nursery to load channel close summaries from disk when restarting the process of sweeping commit txns. The channel close summary contains a closing height, which is used to set the lower bound of the height hint when resubscribing to spend notifications. --- utxonursery.go | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) 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 }