mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 14:45:23 +01:00
nursery_store: tracks last graduated and adds HeightsBelowOrEqual
This commit removes the use of a purge height from the nursery store, instead tracking the last graduated height. This serves to indicate the last height that the nursery store successfully graduated. It also adds a method for retrieving the set of all active heights below a particular threshold, e.g. the last graduated height, allowing the utxo nursery to replay these heights on startup for the specific purpose of re-registering for confirmations of outputs in the height index.
This commit is contained in:
parent
2ef821ed9a
commit
ed0c3dbc58
163
nursery_store.go
163
nursery_store.go
@ -25,19 +25,18 @@ import (
|
|||||||
// |
|
// |
|
||||||
// | LAST PURGED + FINALIZED HEIGHTS
|
// | LAST PURGED + FINALIZED HEIGHTS
|
||||||
// |
|
// |
|
||||||
// | Each nursery store tracks a "last purged height", which records the
|
// | Each nursery store tracks a "last graduated height", which records the
|
||||||
// | most recent block height for which the nursery store has purged all
|
// | most recent block height for which the nursery store has successfully
|
||||||
// | state. This value lags behind the best block height for reorg safety,
|
// | graduated all outputs. It also tracks a "last finalized height", which
|
||||||
// | and serves as a starting height for rescans after a restart. It also
|
// | records the last block height that the nursery attempted to graduate
|
||||||
// | tracks a "last finalized height", which records the last block height
|
// | If a finalized height has kindergarten outputs, the sweep txn for these
|
||||||
// | that the nursery attempted to graduate. If a finalized height has
|
// | outputs will be stored in the height bucket. This ensure that the same
|
||||||
// | kindergarten outputs, the sweep txn for these outputs will be stored in
|
// | txid will be used after restarts. Otherwise, the nursery will be unable
|
||||||
// | the height bucket. This ensure that the same txid will be used after
|
// | to recover the txid of kindergarten sweep transaction it has already
|
||||||
// | restarts. Otherwise, the nursery will be unable to recover the txid
|
// | broadcast.
|
||||||
// | of kindergarten sweep transaction it has already broadcast.
|
|
||||||
// |
|
// |
|
||||||
// ├── last-purged-height-key: <last-purged-height>
|
|
||||||
// ├── last-finalized-height-key: <last-finalized-height>
|
// ├── last-finalized-height-key: <last-finalized-height>
|
||||||
|
// ├── last-graduated-height-key: <last-graduated-height>
|
||||||
// |
|
// |
|
||||||
// | CHANNEL INDEX
|
// | CHANNEL INDEX
|
||||||
// |
|
// |
|
||||||
@ -142,13 +141,17 @@ type NurseryStore interface {
|
|||||||
// nursery store finalized a kindergarten class.
|
// nursery store finalized a kindergarten class.
|
||||||
LastFinalizedHeight() (uint32, error)
|
LastFinalizedHeight() (uint32, error)
|
||||||
|
|
||||||
// PurgeHeight deletes specified the height bucket if it exists, and
|
// GraduateHeight records the provided height as the last height for
|
||||||
// records it as that last purged height.
|
// which the nursery store successfully graduated all outputs.
|
||||||
PurgeHeight(height uint32) error
|
GraduateHeight(height uint32) error
|
||||||
|
|
||||||
// LastPurgedHeight returns the last block height for which the nursery
|
// LastGraduatedHeight returns the last block height for which the
|
||||||
// store has purged all persistent state.
|
// nursery store successfully graduated all outputs.
|
||||||
LastPurgedHeight() (uint32, error)
|
LastGraduatedHeight() (uint32, error)
|
||||||
|
|
||||||
|
// HeightsBelowOrEqual returns the lowest non-empty heights in the
|
||||||
|
// height index, that exist at or below the provided upper bound.
|
||||||
|
HeightsBelowOrEqual(height uint32) ([]uint32, error)
|
||||||
|
|
||||||
// ForChanOutputs iterates over all outputs being incubated for a
|
// ForChanOutputs iterates over all outputs being incubated for a
|
||||||
// particular channel point. This method accepts a callback that allows
|
// particular channel point. This method accepts a callback that allows
|
||||||
@ -179,9 +182,9 @@ var (
|
|||||||
// last finalized height.
|
// last finalized height.
|
||||||
lastFinalizedHeightKey = []byte("last-finalized-height")
|
lastFinalizedHeightKey = []byte("last-finalized-height")
|
||||||
|
|
||||||
// lastPurgedHeightKey is a static key used to retrieve the height of
|
// lastGraduatedHeightKey is a static key used to retrieve the height of
|
||||||
// the last bucket that was purged.
|
// the last bucket that successfully graduated all outputs.
|
||||||
lastPurgedHeightKey = []byte("last-purged-height")
|
lastGraduatedHeightKey = []byte("last-graduated-height")
|
||||||
|
|
||||||
// channelIndexKey is a static key used to lookup the bucket containing
|
// channelIndexKey is a static key used to lookup the bucket containing
|
||||||
// all of the nursery's active channels.
|
// all of the nursery's active channels.
|
||||||
@ -557,10 +560,10 @@ func (ns *nurseryStore) GraduateKinder(height uint32) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// FinalizeKinder accepts a block height as a parameter and purges its
|
// FinalizeKinder accepts a block height and a finalized kindergarten sweep
|
||||||
// persistent state for all outputs at that height. During a restart, the utxo
|
// transaction, persisting the transaction at the appropriate height bucket. The
|
||||||
// nursery will begin it's recovery procedure from the next height that has
|
// nursery store's last finalized height is also updated with the provided
|
||||||
// yet to be finalized.
|
// height.
|
||||||
func (ns *nurseryStore) FinalizeKinder(height uint32,
|
func (ns *nurseryStore) FinalizeKinder(height uint32,
|
||||||
finalTx *wire.MsgTx) error {
|
finalTx *wire.MsgTx) error {
|
||||||
|
|
||||||
@ -569,17 +572,12 @@ func (ns *nurseryStore) FinalizeKinder(height uint32,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// PurgeHeight accepts a block height as a parameter and purges its persistent
|
// GraduateHeight persists the provided height as the nursery store's last
|
||||||
// state for all outputs at that height. During a restart, the utxo nursery will
|
// graduated height.
|
||||||
// begin it's recovery procedure from the next height that has yet to be
|
func (ns *nurseryStore) GraduateHeight(height uint32) error {
|
||||||
// finalized.
|
|
||||||
func (ns *nurseryStore) PurgeHeight(height uint32) error {
|
|
||||||
return ns.db.Update(func(tx *bolt.Tx) error {
|
|
||||||
if err := ns.purgeHeightBucket(tx, height); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ns.putLastPurgedHeight(tx, height)
|
return ns.db.Update(func(tx *bolt.Tx) error {
|
||||||
|
return ns.putLastGraduatedHeight(tx, height)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,6 +723,45 @@ func (ns *nurseryStore) FetchPreschools() ([]kidOutput, error) {
|
|||||||
return kids, nil
|
return kids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HeightsBelowOrEqual returns a slice of all non-empty heights in the height
|
||||||
|
// index at or below the provided upper bound.
|
||||||
|
func (ns *nurseryStore) HeightsBelowOrEqual(height uint32) ([]uint32, error) {
|
||||||
|
var activeHeights []uint32
|
||||||
|
err := ns.db.View(func(tx *bolt.Tx) error {
|
||||||
|
// Ensure that the chain bucket for this nursery store exists.
|
||||||
|
chainBucket := tx.Bucket(ns.pfxChainKey)
|
||||||
|
if chainBucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the height index has been properly initialized for this
|
||||||
|
// chain.
|
||||||
|
hghtIndex := chainBucket.Bucket(heightIndexKey)
|
||||||
|
if hghtIndex == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize the provided height, as this will form the name of the
|
||||||
|
// bucket.
|
||||||
|
var lower, upper [4]byte
|
||||||
|
byteOrder.PutUint32(upper[:], height)
|
||||||
|
|
||||||
|
c := hghtIndex.Cursor()
|
||||||
|
for k, _ := c.Seek(lower[:]); bytes.Compare(k, upper[:]) <= 0 &&
|
||||||
|
len(k) == 4; k, _ = c.Next() {
|
||||||
|
|
||||||
|
activeHeights = append(activeHeights, byteOrder.Uint32(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return activeHeights, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ForChanOutputs iterates over all outputs being incubated for a particular
|
// ForChanOutputs iterates over all outputs being incubated for a particular
|
||||||
// channel point. This method accepts a callback that allows the caller to
|
// channel point. This method accepts a callback that allows the caller to
|
||||||
// process each key-value pair. The key will be a prefixed outpoint, and the
|
// process each key-value pair. The key will be a prefixed outpoint, and the
|
||||||
@ -863,8 +900,7 @@ func (ns *nurseryStore) RemoveChannel(chanPoint *wire.OutPoint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LastFinalizedHeight returns the last block height for which the nursery
|
// LastFinalizedHeight returns the last block height for which the nursery
|
||||||
// store has purged all persistent state. This occurs after a fixed interval
|
// store has finalized a kindergarten class.
|
||||||
// for reorg safety.
|
|
||||||
func (ns *nurseryStore) LastFinalizedHeight() (uint32, error) {
|
func (ns *nurseryStore) LastFinalizedHeight() (uint32, error) {
|
||||||
var lastFinalizedHeight uint32
|
var lastFinalizedHeight uint32
|
||||||
err := ns.db.View(func(tx *bolt.Tx) error {
|
err := ns.db.View(func(tx *bolt.Tx) error {
|
||||||
@ -876,18 +912,17 @@ func (ns *nurseryStore) LastFinalizedHeight() (uint32, error) {
|
|||||||
return lastFinalizedHeight, err
|
return lastFinalizedHeight, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastPurgedHeight returns the last block height for which the nursery store
|
// LastGraduatedHeight returns the last block height for which the nursery
|
||||||
// has purged all persistent state. This occurs after a fixed interval for reorg
|
// store has successfully graduated all outputs.
|
||||||
// safety.
|
func (ns *nurseryStore) LastGraduatedHeight() (uint32, error) {
|
||||||
func (ns *nurseryStore) LastPurgedHeight() (uint32, error) {
|
var lastGraduatedHeight uint32
|
||||||
var lastPurgedHeight uint32
|
|
||||||
err := ns.db.View(func(tx *bolt.Tx) error {
|
err := ns.db.View(func(tx *bolt.Tx) error {
|
||||||
var err error
|
var err error
|
||||||
lastPurgedHeight, err = ns.getLastPurgedHeight(tx)
|
lastGraduatedHeight, err = ns.getLastGraduatedHeight(tx)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
return lastPurgedHeight, err
|
return lastGraduatedHeight, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper Methods
|
// Helper Methods
|
||||||
@ -1091,24 +1126,6 @@ func (ns *nurseryStore) getHeightBucket(tx *bolt.Tx,
|
|||||||
return hghtBucket
|
return hghtBucket
|
||||||
}
|
}
|
||||||
|
|
||||||
// purgeHeightBucket ensures that the height bucket at the provided index is
|
|
||||||
// purged from the nursery store.
|
|
||||||
func (ns *nurseryStore) purgeHeightBucket(tx *bolt.Tx, height uint32) error {
|
|
||||||
// Ensure that the height bucket already exists.
|
|
||||||
_, hghtIndex, hghtBucket := ns.getHeightBucketPath(tx, height)
|
|
||||||
if hghtBucket == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize the provided height, as this will form the name of the
|
|
||||||
// bucket.
|
|
||||||
var heightBytes [4]byte
|
|
||||||
byteOrder.PutUint32(heightBytes[:], height)
|
|
||||||
|
|
||||||
// Finally, delete the bucket in question.
|
|
||||||
return removeBucketIfExists(hghtIndex, heightBytes[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// createHeightChanBucket creates or retrieves an existing height-channel bucket
|
// createHeightChanBucket creates or retrieves an existing height-channel bucket
|
||||||
// for the provided block height and channel point. This method will attempt to
|
// for the provided block height and channel point. This method will attempt to
|
||||||
// instantiate all buckets along the path if required.
|
// instantiate all buckets along the path if required.
|
||||||
@ -1365,29 +1382,29 @@ func (ns *nurseryStore) getFinalizedTxn(tx *bolt.Tx,
|
|||||||
return txn, nil
|
return txn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLastPurgedHeight is a helper method that retrieves the last height for
|
// getLastGraduatedHeight is a helper method that retrieves the last height for
|
||||||
// which the database purged its persistent state.
|
// which the database graduated all outputs successfully.
|
||||||
func (ns *nurseryStore) getLastPurgedHeight(tx *bolt.Tx) (uint32, error) {
|
func (ns *nurseryStore) getLastGraduatedHeight(tx *bolt.Tx) (uint32, error) {
|
||||||
// Retrieve the chain bucket associated with the given nursery store.
|
// Retrieve the chain bucket associated with the given nursery store.
|
||||||
chainBucket := tx.Bucket(ns.pfxChainKey)
|
chainBucket := tx.Bucket(ns.pfxChainKey)
|
||||||
if chainBucket == nil {
|
if chainBucket == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lookup the last purged height in the top-level chain bucket.
|
// Lookup the last graduated height in the top-level chain bucket.
|
||||||
heightBytes := chainBucket.Get(lastPurgedHeightKey)
|
heightBytes := chainBucket.Get(lastGraduatedHeightKey)
|
||||||
if heightBytes == nil {
|
if heightBytes == nil {
|
||||||
// We have never purged before, return height 0.
|
// We have never graduated before, return height 0.
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, parse the bytes and return the last purged height.
|
// Otherwise, parse the bytes and return the last graduated height.
|
||||||
return byteOrder.Uint32(heightBytes), nil
|
return byteOrder.Uint32(heightBytes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// pubLastPurgedHeight is a helper method that writes the provided height under
|
// pubLastGraduatedHeight is a helper method that writes the provided height under
|
||||||
// the last purged height key.
|
// the last graduated height key.
|
||||||
func (ns *nurseryStore) putLastPurgedHeight(tx *bolt.Tx, height uint32) error {
|
func (ns *nurseryStore) putLastGraduatedHeight(tx *bolt.Tx, height uint32) error {
|
||||||
|
|
||||||
// Ensure that the chain bucket for this nursery store exists.
|
// Ensure that the chain bucket for this nursery store exists.
|
||||||
chainBucket, err := tx.CreateBucketIfNotExists(ns.pfxChainKey)
|
chainBucket, err := tx.CreateBucketIfNotExists(ns.pfxChainKey)
|
||||||
@ -1395,12 +1412,12 @@ func (ns *nurseryStore) putLastPurgedHeight(tx *bolt.Tx, height uint32) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize the provided last-purged height, and store it in the
|
// Serialize the provided last-gradauted height, and store it in the
|
||||||
// top-level chain bucket for this nursery store.
|
// top-level chain bucket for this nursery store.
|
||||||
var lastHeightBytes [4]byte
|
var lastHeightBytes [4]byte
|
||||||
byteOrder.PutUint32(lastHeightBytes[:], height)
|
byteOrder.PutUint32(lastHeightBytes[:], height)
|
||||||
|
|
||||||
return chainBucket.Put(lastPurgedHeightKey, lastHeightBytes[:])
|
return chainBucket.Put(lastGraduatedHeightKey, lastHeightBytes[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// errBucketNotEmpty signals that an attempt to prune a particular
|
// errBucketNotEmpty signals that an attempt to prune a particular
|
||||||
|
Loading…
Reference in New Issue
Block a user