utxonursery: minor post-merge formatting clean ups

This commit implements so minor changes in formatting: character column
limits, error returning, scoped, errors. The aforementioned changes are
a bit of minor clean up after the merge of the latest PR in order to
ensure the new code in the file conforms to the code style in the rest
of the project.
This commit is contained in:
Olaoluwa Osuntokun 2017-01-05 13:25:47 -08:00
parent beb6303e2f
commit 798b0b9c9f
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
1 changed files with 117 additions and 128 deletions

View File

@ -22,34 +22,35 @@ import (
var ( var (
// preschoolBucket stores outputs from commitment transactions that // preschoolBucket stores outputs from commitment transactions that
// have been broadcast, but not yet confirmed. This set of outputs is // have been broadcast, but not yet confirmed. This set of outputs is
// persisted in case the system is shut down between the time when // persisted in case the system is shut down between the time when the
// the commitment has been broadcast and the time the transaction // commitment has been broadcast and the time the transaction has been
// has been confirmed on the blockchain. // confirmed on the blockchain.
preschoolBucket = []byte("psc") preschoolBucket = []byte("psc")
// kindergartenBucket stores outputs from commitment transactions that // kindergartenBucket stores outputs from commitment transactions that
// have received an initial confirmation, but which aren't yet spendable // have received an initial confirmation, but which aren't yet
// because they require additional confirmations enforced by Check // spendable because they require additional confirmations enforced by
// Sequence Verify. Once required additional confirmations have been reported, // Check Sequence Verify. Once required additional confirmations have
// a sweep transaction will be created to move the funds out of these // been reported, a sweep transaction will be created to move the funds
// outputs. After a further six confirmations have been reported, the outputs // out of these outputs. After a further six confirmations have been
// will be deleted from this bucket. The purpose of this additional wait // reported, the outputs will be deleted from this bucket. The purpose
// time is to ensure that a block reorganization doesn't result in the // of this additional wait time is to ensure that a block
// sweep transaction getting re-organized out of the chain. // reorganization doesn't result in the sweep transaction getting
// re-organized out of the chain.
kindergartenBucket = []byte("kdg") kindergartenBucket = []byte("kdg")
// lastGraduatedHeightKey is used to persist the last blockheight that // lastGraduatedHeightKey is used to persist the last blockheight that
// has been checked for graduating outputs. When the nursery is restarted, // has been checked for graduating outputs. When the nursery is
// lastGraduatedHeightKey is used to determine the point from which it's // restarted, lastGraduatedHeightKey is used to determine the point
// necessary to catch up. // from which it's necessary to catch up.
lastGraduatedHeightKey = []byte("lgh") lastGraduatedHeightKey = []byte("lgh")
byteOrder = binary.BigEndian byteOrder = binary.BigEndian
) )
// witnessType determines how an output's witness will be generated. The default // witnessType determines how an output's witness will be generated. The
// commitmentTimeLock type will generate a witness that will allow spending of a // default commitmentTimeLock type will generate a witness that will allow
// time-locked transaction enforced by CheckSequenceVerify. // spending of a time-locked transaction enforced by CheckSequenceVerify.
type witnessType uint16 type witnessType uint16
const ( const (
@ -62,11 +63,13 @@ const (
// utxoNursery. // utxoNursery.
type witnessGenerator func(tx *wire.MsgTx, hc *txscript.TxSigHashes, inputIndex int) ([][]byte, error) type witnessGenerator func(tx *wire.MsgTx, hc *txscript.TxSigHashes, inputIndex int) ([][]byte, error)
// generateFunc will return the witnessGenerator function that a kidOutput uses to // generateFunc will return the witnessGenerator function that a kidOutput uses
// generate the witness for a sweep transaction. Currently there is only one witnessType // to generate the witness for a sweep transaction. Currently there is only one
// but this will be expanded. // witnessType but this will be expanded.
func (wt *witnessType) generateFunc(signer *lnwallet.Signer, descriptor *lnwallet.SignDescriptor) witnessGenerator { func (wt witnessType) generateFunc(signer *lnwallet.Signer,
switch *wt { descriptor *lnwallet.SignDescriptor) witnessGenerator {
switch wt {
case commitmentTimeLock: case commitmentTimeLock:
return func(tx *wire.MsgTx, hc *txscript.TxSigHashes, inputIndex int) ([][]byte, error) { return func(tx *wire.MsgTx, hc *txscript.TxSigHashes, inputIndex int) ([][]byte, error) {
desc := descriptor desc := descriptor
@ -152,12 +155,13 @@ func (u *utxoNursery) Start() error {
// reloadPreschool re-initializes the chain notifier with all of the outputs // reloadPreschool re-initializes the chain notifier with all of the outputs
// that had been saved to the "preschool" database bucket prior to shutdown. // that had been saved to the "preschool" database bucket prior to shutdown.
func (u *utxoNursery) reloadPreschool() error { func (u *utxoNursery) reloadPreschool() error {
err := u.db.View(func(tx *bolt.Tx) error { return u.db.View(func(tx *bolt.Tx) error {
psclBucket := tx.Bucket(preschoolBucket) psclBucket := tx.Bucket(preschoolBucket)
if psclBucket == nil { if psclBucket == nil {
return nil return nil
} }
if err := psclBucket.ForEach(func(outputBytes, kidBytes []byte) error {
return psclBucket.ForEach(func(outputBytes, kidBytes []byte) error {
psclOutput, err := deserializeKidOutput(bytes.NewBuffer(kidBytes)) psclOutput, err := deserializeKidOutput(bytes.NewBuffer(kidBytes))
outpoint := psclOutput.outPoint outpoint := psclOutput.outPoint
@ -172,16 +176,8 @@ func (u *utxoNursery) reloadPreschool() error {
"notification.", psclOutput.outPoint) "notification.", psclOutput.outPoint)
go psclOutput.waitForPromotion(u.db, confChan) go psclOutput.waitForPromotion(u.db, confChan)
return nil return nil
}); err != nil { })
return err
}
return nil
}) })
if err != nil {
return err
}
return nil
} }
// catchUpKindergarten handles the graduation of kindergarten outputs from // catchUpKindergarten handles the graduation of kindergarten outputs from
@ -214,20 +210,21 @@ func (u *utxoNursery) catchUpKindergarten() error {
return err return err
} }
// If we haven't yet seen any registered force closes, or we're already
// caught up with the current best chain, then we can exit early.
if lastGraduatedHeight == 0 || uint32(bestHeight) == lastGraduatedHeight {
return nil
}
utxnLog.Infof("Processing outputs from missed blocks. Starting with "+
"blockHeight: %v, to current blockHeight: %v", lastGraduatedHeight,
bestHeight)
// Loop through and check for graduating outputs at each of the missed // Loop through and check for graduating outputs at each of the missed
// block heights. // block heights.
if lastGraduatedHeight != 0 { for graduationHeight := lastGraduatedHeight + 1; graduationHeight <= uint32(bestHeight); graduationHeight++ {
graduationHeight := lastGraduatedHeight + 1 if err := u.graduateKindergarten(graduationHeight); err != nil {
return err
utxnLog.Infof("Processing outputs from missed blocks. Starting with "+
"blockheight: %v, to current blockheight: %v", graduationHeight,
bestHeight)
for graduationHeight <= uint32(bestHeight) {
if err := u.graduateKindergarten(graduationHeight); err != nil {
return err
}
graduationHeight = graduationHeight + 1
} }
} }
@ -250,10 +247,9 @@ func (u *utxoNursery) Stop() error {
} }
// kidOutput represents an output that's waiting for a required blockheight // kidOutput represents an output that's waiting for a required blockheight
// before its funds will be available to be moved into the user's wallet. // before its funds will be available to be moved into the user's wallet. The
// The struct includes a witnessGenerator closure which will be used to // struct includes a witnessGenerator closure which will be used to generate
// generate the witness required to sweep the output once it's mature. // the witness required to sweep the output once it's mature.
// TODO(roasbeef): make into interface? can't gob functions
type kidOutput struct { type kidOutput struct {
amt btcutil.Amount amt btcutil.Amount
outPoint wire.OutPoint outPoint wire.OutPoint
@ -294,14 +290,14 @@ func (u *utxoNursery) incubateOutputs(closeSummary *lnwallet.ForceCloseSummary)
} }
} }
// incubator is tasked with watching over all outputs from channel closes as they // incubator is tasked with watching over all outputs from channel closes as
// transition from being broadcast (at which point they move into the "preschool // they transition from being broadcast (at which point they move into the
// state"), then confirmed and waiting for the necessary number of blocks to // "preschool state"), then confirmed and waiting for the necessary number of
// be confirmed (as specified as kidOutput.blocksToMaturity and enforced by // blocks to be confirmed (as specified as kidOutput.blocksToMaturity and
// CheckSequenceVerify). When the necessary block height has been reached, the // enforced by CheckSequenceVerify). When the necessary block height has been
// output has "matured" and the waitForGraduation function will generate a // reached, the output has "matured" and the waitForGraduation function will
// sweep transaction to move funds from the commitment transaction into the // generate a sweep transaction to move funds from the commitment transaction
// user's wallet. // into the user's wallet.
func (u *utxoNursery) incubator(newBlockChan *chainntnfs.BlockEpochEvent) { func (u *utxoNursery) incubator(newBlockChan *chainntnfs.BlockEpochEvent) {
defer u.wg.Done() defer u.wg.Done()
@ -321,9 +317,10 @@ out:
continue continue
} }
// Register for a notification that will trigger graduation from // Register for a notification that will
// preschool to kindergarten when the channel close transaction // trigger graduation from preschool to
// has been confirmed. // kindergarten when the channel close
// transaction has been confirmed.
confChan, err := u.notifier.RegisterConfirmationsNtfn(&sourceTxid, 1) confChan, err := u.notifier.RegisterConfirmationsNtfn(&sourceTxid, 1)
if err != nil { if err != nil {
utxnLog.Errorf("unable to register output for confirmation: %v", utxnLog.Errorf("unable to register output for confirmation: %v",
@ -331,16 +328,18 @@ out:
continue continue
} }
// Launch a dedicated goroutine that will move the output from // Launch a dedicated goroutine that will move
// the preschool bucket to the kindergarten bucket once the // the output from the preschool bucket to the
// channel close transaction has been confirmed. // kindergarten bucket once the channel close
// transaction has been confirmed.
go output.waitForPromotion(u.db, confChan) go output.waitForPromotion(u.db, confChan)
} }
case epoch, ok := <-newBlockChan.Epochs: case epoch, ok := <-newBlockChan.Epochs:
// If the epoch channel has been closed, then the // If the epoch channel has been closed, then the
// ChainNotifier is exiting which means the daemon is // ChainNotifier is exiting which means the daemon is
// as well. Therefore, we exit early also in order to // as well. Therefore, we exit early also in order to
// ensure the daemon shutsdown gracefully, yet swiftly. // ensure the daemon shuts down gracefully, yet
// swiftly.
if !ok { if !ok {
return return
} }
@ -360,7 +359,7 @@ out:
// "preschool" stage, the daemon is waiting for the initial confirmation of the // "preschool" stage, the daemon is waiting for the initial confirmation of the
// commitment transaction. // commitment transaction.
func (k *kidOutput) enterPreschool(db *channeldb.DB) error { func (k *kidOutput) enterPreschool(db *channeldb.DB) error {
err := db.Update(func(tx *bolt.Tx) error { return db.Update(func(tx *bolt.Tx) error {
psclBucket, err := tx.CreateBucketIfNotExists(preschoolBucket) psclBucket, err := tx.CreateBucketIfNotExists(preschoolBucket)
if err != nil { if err != nil {
return err return err
@ -385,19 +384,14 @@ func (k *kidOutput) enterPreschool(db *channeldb.DB) error {
return nil return nil
}) })
if err != nil {
return err
}
return nil
} }
// waitForPromotion is intended to be run as a goroutine that will wait until // waitForPromotion is intended to be run as a goroutine that will wait until a
// a channel force close commitment transaction has been included in a // channel force close commitment transaction has been included in a confirmed
// confirmed block. Once the transaction has been confirmed (as reported by // block. Once the transaction has been confirmed (as reported by the Chain
// the Chain Notifier), waitForPromotion will delete the output from the // Notifier), waitForPromotion will delete the output from the "preschool"
// "preschool" database bucket and atomically add it to the "kindergarten" // database bucket and atomically add it to the "kindergarten" database bucket.
// database bucket. This is the second step in the output incubation process. // This is the second step in the output incubation process.
func (k *kidOutput) waitForPromotion(db *channeldb.DB, confChan *chainntnfs.ConfirmationEvent) { func (k *kidOutput) waitForPromotion(db *channeldb.DB, confChan *chainntnfs.ConfirmationEvent) {
txConfirmation, ok := <-confChan.Confirmed txConfirmation, ok := <-confChan.Confirmed
if !ok { if !ok {
@ -411,10 +405,10 @@ func (k *kidOutput) waitForPromotion(db *channeldb.DB, confChan *chainntnfs.Conf
k.confHeight = uint32(txConfirmation.BlockHeight) k.confHeight = uint32(txConfirmation.BlockHeight)
// The following block deletes a kidOutput from the preschool database bucket // The following block deletes a kidOutput from the preschool database
// and adds it to the kindergarten database bucket which is keyed by block // bucket and adds it to the kindergarten database bucket which is
// height. Keys and values are serialized into byte array form prior to // keyed by block height. Keys and values are serialized into byte
// database insertion. // array form prior to database insertion.
err := db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
psclBucket := tx.Bucket(preschoolBucket) psclBucket := tx.Bucket(preschoolBucket)
if psclBucket == nil { if psclBucket == nil {
@ -466,12 +460,12 @@ func (k *kidOutput) waitForPromotion(db *channeldb.DB, confChan *chainntnfs.Conf
} }
} }
// graduateKindergarten handles the steps involed with moving funds // graduateKindergarten handles the steps invoked with moving funds from a
// from a force close commitment transaction into a user's wallet after the output // force close commitment transaction into a user's wallet after the output
// from the commitment transaction has become spendable. graduateKindergarten // from the commitment transaction has become spendable. graduateKindergarten
// is called both when a new block notification has been received and also // is called both when a new block notification has been received and also at
// at startup in order to process graduations from blocks missed while the // startup in order to process graduations from blocks missed while the UTXO
// UTXO nursery was offline. // nursery was offline.
func (u *utxoNursery) graduateKindergarten(blockHeight uint32) error { func (u *utxoNursery) graduateKindergarten(blockHeight uint32) error {
kgtnOutputs, err := fetchGraduatingOutputs(u.db, u.wallet, blockHeight) kgtnOutputs, err := fetchGraduatingOutputs(u.db, u.wallet, blockHeight)
if err != nil { if err != nil {
@ -489,25 +483,22 @@ func (u *utxoNursery) graduateKindergarten(blockHeight uint32) error {
return err return err
} }
if err := putLastHeightGraduated(u.db, blockHeight); err != nil { return putLastHeightGraduated(u.db, blockHeight)
return err
}
return nil
} }
// fetchGraduatingOutputs checks the "kindergarten" database bucket whenever a // fetchGraduatingOutputs checks the "kindergarten" database bucket whenever a
// new block is received in order to determine if commitment transaction // new block is received in order to determine if commitment transaction
// outputs have become newly spendable. If fetchGraduatingOutputs finds // outputs have become newly spendable. If fetchGraduatingOutputs finds outputs
// outputs that are ready for "graduation," it passes them on to be swept. // that are ready for "graduation," it passes them on to be swept. This is the
// This is the third step in the output incubation process. // third step in the output incubation process.
func fetchGraduatingOutputs(db *channeldb.DB, wallet *lnwallet.LightningWallet, blockHeight uint32) ([]*kidOutput, error) { func fetchGraduatingOutputs(db *channeldb.DB, wallet *lnwallet.LightningWallet,
blockHeight uint32) ([]*kidOutput, error) {
var results []byte var results []byte
err := db.View(func(tx *bolt.Tx) error { if err := db.View(func(tx *bolt.Tx) error {
// A new block has just been connected, check to see if // A new block has just been connected, check to see if we have
// we have any new outputs that can be swept into the // any new outputs that can be swept into the wallet.
// wallet.
kgtnBucket := tx.Bucket(kindergartenBucket) kgtnBucket := tx.Bucket(kindergartenBucket)
if kgtnBucket == nil { if kgtnBucket == nil {
return nil return nil
@ -519,44 +510,44 @@ func fetchGraduatingOutputs(db *channeldb.DB, wallet *lnwallet.LightningWallet,
results = kgtnBucket.Get(heightBytes) results = kgtnBucket.Get(heightBytes)
return nil return nil
}) }); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(results) > 0 { if len(results) == 0 {
kgtnOutputs, err := deserializeKidList(bytes.NewBuffer(results)) return nil, nil
if err != nil {
utxnLog.Errorf("error while deserializing list of kidOutputs: %v", err)
}
for _, kgtnOutput := range kgtnOutputs {
kgtnOutput.witnessFunc =
kgtnOutput.witnessType.generateFunc(&wallet.Signer, kgtnOutput.signDescriptor)
}
utxnLog.Infof("New block: height=%v, sweeping %v mature outputs",
blockHeight, len(kgtnOutputs))
return kgtnOutputs, nil
} }
return nil, nil kgtnOutputs, err := deserializeKidList(bytes.NewReader(results))
if err != nil {
utxnLog.Errorf("error while deserializing list of kidOutputs: %v", err)
}
for _, kgtnOutput := range kgtnOutputs {
kgtnOutput.witnessFunc = kgtnOutput.witnessType.generateFunc(
&wallet.Signer, kgtnOutput.signDescriptor,
)
}
utxnLog.Infof("New block: height=%v, sweeping %v mature outputs",
blockHeight, len(kgtnOutputs))
return kgtnOutputs, nil
} }
// sweepGraduatingOutputs generates and broadcasts the transaction that // sweepGraduatingOutputs generates and broadcasts the transaction that
// transfers control of funds from a channel commitment transaction to the // transfers control of funds from a channel commitment transaction to the
// user's wallet. // user's wallet.
func sweepGraduatingOutputs(wallet *lnwallet.LightningWallet, kgtnOutputs []*kidOutput) error { func sweepGraduatingOutputs(wallet *lnwallet.LightningWallet, kgtnOutputs []*kidOutput) error {
// Create a transation which sweeps all the newly // Create a transaction which sweeps all the newly mature outputs into
// mature outputs into a output controlled by the // a output controlled by the wallet.
// wallet. // TODO(roasbeef): can be more intelligent about buffering outputs to
// TODO(roasbeef): can be more intelligent about // be more efficient on-chain.
// buffering outputs to be more efficient on-chain.
sweepTx, err := createSweepTx(wallet, kgtnOutputs) sweepTx, err := createSweepTx(wallet, kgtnOutputs)
if err != nil { if err != nil {
// TODO(roasbeef): retry logic? // TODO(roasbeef): retry logic?
utxnLog.Errorf("unable to create sweep tx: %v", err) utxnLog.Errorf("unable to create sweep tx: %v", err)
return err
} }
utxnLog.Infof("Sweeping %v time-locked outputs "+ utxnLog.Infof("Sweeping %v time-locked outputs "+
@ -565,12 +556,10 @@ func sweepGraduatingOutputs(wallet *lnwallet.LightningWallet, kgtnOutputs []*kid
return spew.Sdump(sweepTx) return spew.Sdump(sweepTx)
})) }))
// With the sweep transaction fully signed, broadcast // With the sweep transaction fully signed, broadcast the transaction
// the transaction to the network. Additionally, we can // to the network. Additionally, we can stop tracking these outputs as
// stop tracking these outputs as they've just been // they've just been swept.
// sweeped. if err := wallet.PublishTransaction(sweepTx); err != nil {
err = wallet.PublishTransaction(sweepTx)
if err != nil {
utxnLog.Errorf("unable to broadcast sweep tx: %v, %v", utxnLog.Errorf("unable to broadcast sweep tx: %v, %v",
err, spew.Sdump(sweepTx)) err, spew.Sdump(sweepTx))
} }
@ -651,7 +640,7 @@ func deleteGraduatedOutputs(db *channeldb.DB, deleteHeight uint32) error {
return err return err
} }
utxnLog.Info("Deleting %v swept outputs from kindergarten bucket "+ utxnLog.Infof("Deleting %v swept outputs from kindergarten bucket "+
"at block height: %v", len(sweptOutputs), deleteHeight) "at block height: %v", len(sweptOutputs), deleteHeight)
return nil return nil