mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 01:36:24 +01:00
utxonursery: update output sweeping to be aware of new output types
In this commit, we modify the logic surrounding sweeping outputs to be aware of the new types of outputs that the nursery is now responsible for. Namely: incoming HTLC’s on our commitment transaction as well as outgoing HTLC’s on the commitment transaction for the remote party. For the latter class of HTLC, we’ll now set the lock time on the sweeping transaction in order to satisfy the CLTV clause in the output we’re spending.
This commit is contained in:
parent
fb17f3aeb4
commit
2283960000
1 changed files with 167 additions and 86 deletions
253
utxonursery.go
253
utxonursery.go
|
@ -613,7 +613,7 @@ func (u *utxoNursery) reloadPreschool(heightHint uint32) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range psclOutputs {
|
for i := range psclOutputs {
|
||||||
err := u.registerCommitConf(&psclOutputs[i], heightHint)
|
err := u.registerPreschoolConf(&psclOutputs[i], heightHint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -808,6 +808,9 @@ func (u *utxoNursery) graduateClass(classHeight uint32) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utxnLog.Infof("Attempting to graduate height=%v: num_kids=%v, "+
|
||||||
|
"num_babies=%v", classHeight, len(kgtnOutputs), len(cribOutputs))
|
||||||
|
|
||||||
// Load the last finalized height, so we can determine if the
|
// Load the last finalized height, so we can determine if the
|
||||||
// kindergarten sweep txn should be crafted.
|
// kindergarten sweep txn should be crafted.
|
||||||
lastFinalizedHeight, err := u.cfg.Store.LastFinalizedHeight()
|
lastFinalizedHeight, err := u.cfg.Store.LastFinalizedHeight()
|
||||||
|
@ -823,9 +826,9 @@ func (u *utxoNursery) graduateClass(classHeight uint32) error {
|
||||||
if classHeight > lastFinalizedHeight {
|
if classHeight > lastFinalizedHeight {
|
||||||
// If this height has never been finalized, we have never
|
// If this height has never been finalized, we have never
|
||||||
// generated a sweep txn for this height. Generate one if there
|
// generated a sweep txn for this height. Generate one if there
|
||||||
// are kindergarten outputs to be spent.
|
// are kindergarten outputs or cltv crib outputs to be spent.
|
||||||
if len(kgtnOutputs) > 0 {
|
if len(kgtnOutputs) > 0 {
|
||||||
finalTx, err = u.createSweepTx(kgtnOutputs)
|
finalTx, err = u.createSweepTx(kgtnOutputs, classHeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utxnLog.Errorf("Failed to create sweep txn at "+
|
utxnLog.Errorf("Failed to create sweep txn at "+
|
||||||
"height=%d", classHeight)
|
"height=%d", classHeight)
|
||||||
|
@ -834,8 +837,8 @@ func (u *utxoNursery) graduateClass(classHeight uint32) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Persist the kindergarten sweep txn to the nursery store. It
|
// Persist the kindergarten sweep txn to the nursery store. It
|
||||||
// is safe to store a nil finalTx, which happens if there are no
|
// is safe to store a nil finalTx, which happens if there are
|
||||||
// graduating kindergarten outputs.
|
// no graduating kindergarten outputs.
|
||||||
err = u.cfg.Store.FinalizeKinder(classHeight, finalTx)
|
err = u.cfg.Store.FinalizeKinder(classHeight, finalTx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utxnLog.Errorf("Failed to finalize kindergarten at "+
|
utxnLog.Errorf("Failed to finalize kindergarten at "+
|
||||||
|
@ -853,21 +856,21 @@ func (u *utxoNursery) graduateClass(classHeight uint32) error {
|
||||||
|
|
||||||
// Now that the kindergarten sweep txn has either been finalized or
|
// Now that the kindergarten sweep txn has either been finalized or
|
||||||
// restored, broadcast the txn, and set up notifications that will
|
// restored, broadcast the txn, and set up notifications that will
|
||||||
// transition the swept kindergarten outputs into graduated outputs.
|
// transition the swept kindergarten outputs and cltvCrib into
|
||||||
|
// graduated outputs.
|
||||||
if finalTx != nil {
|
if finalTx != nil {
|
||||||
err := u.sweepGraduatingKinders(classHeight, finalTx,
|
err := u.sweepMatureOutputs(classHeight, finalTx, kgtnOutputs)
|
||||||
kgtnOutputs)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utxnLog.Errorf("Failed to sweep %d kindergarten outputs "+
|
utxnLog.Errorf("Failed to sweep %d kindergarten "+
|
||||||
"at height=%d: %v", len(kgtnOutputs), classHeight,
|
"outputs at height=%d: %v",
|
||||||
err)
|
len(kgtnOutputs), classHeight, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, we broadcast all pre-signed htlc txns from the crib outputs at
|
// Now, we broadcast all pre-signed htlc txns from the csv crib outputs
|
||||||
// this height. There is no need to finalize these txns, since the txid
|
// at this height. There is no need to finalize these txns, since the
|
||||||
// is predetermined when signed in the wallet.
|
// txid is predetermined when signed in the wallet.
|
||||||
for i := range cribOutputs {
|
for i := range cribOutputs {
|
||||||
err := u.sweepCribOutput(classHeight, &cribOutputs[i])
|
err := u.sweepCribOutput(classHeight, &cribOutputs[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -881,41 +884,78 @@ func (u *utxoNursery) graduateClass(classHeight uint32) error {
|
||||||
return u.cfg.Store.GraduateHeight(classHeight)
|
return u.cfg.Store.GraduateHeight(classHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
// craftSweepTx accepts accepts a list of kindergarten outputs, and signs and
|
// craftSweepTx accepts accepts a list of kindergarten outputs, and baby
|
||||||
// generates a signed txn that spends from them. This method also makes an
|
// outputs which don't required a second-layer claim, and signs and generates a
|
||||||
// accurate fee estimate before generating the required witnesses.
|
// signed txn that spends from them. This method also makes an accurate fee
|
||||||
func (u *utxoNursery) createSweepTx(kgtnOutputs []kidOutput) (*wire.MsgTx, error) {
|
// estimate before generating the required witnesses.
|
||||||
|
func (u *utxoNursery) createSweepTx(kgtnOutputs []kidOutput,
|
||||||
|
classHeight uint32) (*wire.MsgTx, error) {
|
||||||
|
|
||||||
// Create a transaction which sweeps all the newly mature outputs into
|
// Create a transaction which sweeps all the newly mature outputs into
|
||||||
// a output controlled by the wallet.
|
// a output controlled by the wallet.
|
||||||
|
|
||||||
// TODO(roasbeef): can be more intelligent about buffering outputs to
|
// TODO(roasbeef): can be more intelligent about buffering outputs to
|
||||||
// be more efficient on-chain.
|
// be more efficient on-chain.
|
||||||
|
|
||||||
// Assemble the kindergarten class into a slice csv spendable outputs,
|
// Assemble the kindergarten class into a slice csv spendable outputs,
|
||||||
// while also computing an estimate for the total transaction weight.
|
// and also a set of regular spendable outputs. The set of regular
|
||||||
|
// outputs are CLTV locked outputs that have had their timelocks
|
||||||
|
// expire.
|
||||||
var (
|
var (
|
||||||
csvSpendableOutputs []CsvSpendableOutput
|
csvOutputs []CsvSpendableOutput
|
||||||
weightEstimate lnwallet.TxWeightEstimator
|
cltvOutputs []SpendableOutput
|
||||||
|
weightEstimate lnwallet.TxWeightEstimator
|
||||||
)
|
)
|
||||||
|
|
||||||
// Allocate enough room for each of the kindergarten outputs.
|
// Allocate enough room for both types of kindergarten outputs.
|
||||||
csvSpendableOutputs = make([]CsvSpendableOutput, 0, len(kgtnOutputs))
|
csvOutputs = make([]CsvSpendableOutput, 0, len(kgtnOutputs))
|
||||||
|
cltvOutputs = make([]SpendableOutput, 0, len(kgtnOutputs))
|
||||||
|
|
||||||
// Our sweep transaction will pay to a single segwit p2wkh address,
|
// Our sweep transaction will pay to a single segwit p2wkh address,
|
||||||
// ensure it contributes to our weight estimate.
|
// ensure it contributes to our weight estimate.
|
||||||
weightEstimate.AddP2WKHOutput()
|
weightEstimate.AddP2WKHOutput()
|
||||||
|
|
||||||
// For each kindergarten output, use its witness type to determine the
|
// For each kindergarten output, use its witness type to determine the
|
||||||
// estimate weight of its witness.
|
// estimate weight of its witness, and add it to the proper set of
|
||||||
|
// spendable outputs.
|
||||||
for i := range kgtnOutputs {
|
for i := range kgtnOutputs {
|
||||||
input := &kgtnOutputs[i]
|
input := &kgtnOutputs[i]
|
||||||
|
|
||||||
var witnessWeight int
|
|
||||||
switch input.WitnessType() {
|
switch input.WitnessType() {
|
||||||
case lnwallet.CommitmentTimeLock:
|
|
||||||
witnessWeight = lnwallet.ToLocalTimeoutWitnessSize
|
|
||||||
|
|
||||||
case lnwallet.HtlcOfferedTimeout:
|
// Outputs on a past commitment transaction that pay directly
|
||||||
witnessWeight = lnwallet.OfferedHtlcTimeoutWitnessSize
|
// to us.
|
||||||
|
case lnwallet.CommitmentTimeLock:
|
||||||
|
weightEstimate.AddWitnessInput(
|
||||||
|
lnwallet.ToLocalTimeoutWitnessSize,
|
||||||
|
)
|
||||||
|
csvOutputs = append(csvOutputs, input)
|
||||||
|
|
||||||
|
// Outgoing second layer HTLC's that have confirmed within the
|
||||||
|
// chain, and the output they produced is now mature enough to
|
||||||
|
// sweep.
|
||||||
|
case lnwallet.HtlcOfferedTimeoutSecondLevel:
|
||||||
|
weightEstimate.AddWitnessInput(
|
||||||
|
lnwallet.OfferedHtlcTimeoutWitnessSize,
|
||||||
|
)
|
||||||
|
csvOutputs = append(csvOutputs, input)
|
||||||
|
|
||||||
|
// Incoming second layer HTLC's that have confirmed within the
|
||||||
|
// chain, and the output they produced is now mature enough to
|
||||||
|
// sweep.
|
||||||
|
case lnwallet.HtlcAcceptedSuccessSecondLevel:
|
||||||
|
weightEstimate.AddWitnessInput(
|
||||||
|
lnwallet.AcceptedHtlcSuccessWitnessSize,
|
||||||
|
)
|
||||||
|
csvOutputs = append(csvOutputs, input)
|
||||||
|
|
||||||
|
// An HTLC on the commitment transaction of the remote party,
|
||||||
|
// that has had its absolute timelock expire.
|
||||||
|
case lnwallet.HtlcOfferedRemoteTimeout:
|
||||||
|
weightEstimate.AddWitnessInput(
|
||||||
|
lnwallet.AcceptedHtlcTimeoutWitnessSize,
|
||||||
|
)
|
||||||
|
cltvOutputs = append(cltvOutputs, input)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
utxnLog.Warnf("kindergarten output in nursery store "+
|
utxnLog.Warnf("kindergarten output in nursery store "+
|
||||||
|
@ -923,25 +963,22 @@ func (u *utxoNursery) createSweepTx(kgtnOutputs []kidOutput) (*wire.MsgTx, error
|
||||||
input.WitnessType())
|
input.WitnessType())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the kindergarten output's input and witness to our
|
|
||||||
// running estimate.
|
|
||||||
weightEstimate.AddWitnessInput(witnessWeight)
|
|
||||||
|
|
||||||
// Include this input in the transaction.
|
|
||||||
csvSpendableOutputs = append(csvSpendableOutputs, input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utxnLog.Infof("Creating sweep transaction for %v CSV inputs, %v CLTV "+
|
||||||
|
"inputs", len(csvOutputs), len(cltvOutputs))
|
||||||
|
|
||||||
txWeight := uint64(weightEstimate.Weight())
|
txWeight := uint64(weightEstimate.Weight())
|
||||||
return u.sweepCsvSpendableOutputsTxn(txWeight, csvSpendableOutputs)
|
return u.populateSweepTx(txWeight, classHeight, csvOutputs, cltvOutputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sweepCsvSpendableOutputsTxn creates a final sweeping transaction with all
|
// populateSweepTx populate the final sweeping transaction with all witnesses
|
||||||
// witnesses in place for all inputs using the provided txn fee. The created
|
// in place for all inputs using the provided txn fee. The created transaction
|
||||||
// transaction has a single output sending all the funds back to the source
|
// has a single output sending all the funds back to the source wallet, after
|
||||||
// wallet, after accounting for the fee estimate.
|
// accounting for the fee estimate.
|
||||||
func (u *utxoNursery) sweepCsvSpendableOutputsTxn(txWeight uint64,
|
func (u *utxoNursery) populateSweepTx(txWeight uint64, classHeight uint32,
|
||||||
inputs []CsvSpendableOutput) (*wire.MsgTx, error) {
|
csvInputs []CsvSpendableOutput,
|
||||||
|
cltvInputs []SpendableOutput) (*wire.MsgTx, error) {
|
||||||
|
|
||||||
// Generate the receiving script to which the funds will be swept.
|
// Generate the receiving script to which the funds will be swept.
|
||||||
pkScript, err := u.cfg.GenSweepScript()
|
pkScript, err := u.cfg.GenSweepScript()
|
||||||
|
@ -951,7 +988,10 @@ func (u *utxoNursery) sweepCsvSpendableOutputsTxn(txWeight uint64,
|
||||||
|
|
||||||
// Sum up the total value contained in the inputs.
|
// Sum up the total value contained in the inputs.
|
||||||
var totalSum btcutil.Amount
|
var totalSum btcutil.Amount
|
||||||
for _, o := range inputs {
|
for _, o := range csvInputs {
|
||||||
|
totalSum += o.Amount()
|
||||||
|
}
|
||||||
|
for _, o := range cltvInputs {
|
||||||
totalSum += o.Amount()
|
totalSum += o.Amount()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -974,12 +1014,23 @@ func (u *utxoNursery) sweepCsvSpendableOutputsTxn(txWeight uint64,
|
||||||
Value: sweepAmt,
|
Value: sweepAmt,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add all of our inputs, including the respective CSV delays.
|
// We'll also ensure that the transaction has the required lock time if
|
||||||
for _, input := range inputs {
|
// we're sweeping any cltvInputs.
|
||||||
|
if len(cltvInputs) > 0 {
|
||||||
|
sweepTx.LockTime = classHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all inputs to the sweep transaction. Ensure that for each
|
||||||
|
// csvInput, we set the sequence number properly.
|
||||||
|
for _, input := range csvInputs {
|
||||||
|
sweepTx.AddTxIn(&wire.TxIn{
|
||||||
|
PreviousOutPoint: *input.OutPoint(),
|
||||||
|
Sequence: input.BlocksToMaturity(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, input := range cltvInputs {
|
||||||
sweepTx.AddTxIn(&wire.TxIn{
|
sweepTx.AddTxIn(&wire.TxIn{
|
||||||
PreviousOutPoint: *input.OutPoint(),
|
PreviousOutPoint: *input.OutPoint(),
|
||||||
// TODO(roasbeef): assumes pure block delays
|
|
||||||
Sequence: input.BlocksToMaturity(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -997,8 +1048,10 @@ func (u *utxoNursery) sweepCsvSpendableOutputsTxn(txWeight uint64,
|
||||||
|
|
||||||
// With all the inputs in place, use each output's unique witness
|
// With all the inputs in place, use each output's unique witness
|
||||||
// function to generate the final witness required for spending.
|
// function to generate the final witness required for spending.
|
||||||
addWitness := func(idx int, tso CsvSpendableOutput) error {
|
addWitness := func(idx int, tso SpendableOutput) error {
|
||||||
witness, err := tso.BuildWitness(u.cfg.Signer, sweepTx, hashCache, idx)
|
witness, err := tso.BuildWitness(
|
||||||
|
u.cfg.Signer, sweepTx, hashCache, idx,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1008,7 +1061,14 @@ func (u *utxoNursery) sweepCsvSpendableOutputsTxn(txWeight uint64,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, input := range inputs {
|
// Finally we'll attach a valid witness to each csv and cltv input
|
||||||
|
// within the sweeping transaction.
|
||||||
|
for i, input := range csvInputs {
|
||||||
|
if err := addWitness(i, input); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, input := range cltvInputs {
|
||||||
if err := addWitness(i, input); err != nil {
|
if err := addWitness(i, input); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1017,15 +1077,16 @@ func (u *utxoNursery) sweepCsvSpendableOutputsTxn(txWeight uint64,
|
||||||
return sweepTx, nil
|
return sweepTx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sweepGraduatingKinders generates and broadcasts the transaction that
|
// sweepMatureOutputs generates and broadcasts the transaction that transfers
|
||||||
// transfers control of funds from a channel commitment transaction to the
|
// control of funds from a prior channel commitment transaction to the user's
|
||||||
// user's wallet.
|
// wallet. The outputs swept were previously time locked (either absolute or
|
||||||
func (u *utxoNursery) sweepGraduatingKinders(classHeight uint32,
|
// relative), but are not mature enough to sweep into the wallet.
|
||||||
finalTx *wire.MsgTx, kgtnOutputs []kidOutput) error {
|
func (u *utxoNursery) sweepMatureOutputs(classHeight uint32, finalTx *wire.MsgTx,
|
||||||
|
kgtnOutputs []kidOutput) error {
|
||||||
|
|
||||||
utxnLog.Infof("Sweeping %v CSV-delayed outputs with sweep tx "+
|
utxnLog.Infof("Sweeping %v CSV-delayed outputs with sweep tx "+
|
||||||
"(txid=%v): %v", len(kgtnOutputs), finalTx.TxHash(),
|
"(txid=%v): %v", len(kgtnOutputs),
|
||||||
newLogClosure(func() string {
|
finalTx.TxHash(), newLogClosure(func() string {
|
||||||
return spew.Sdump(finalTx)
|
return spew.Sdump(finalTx)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -1034,8 +1095,8 @@ func (u *utxoNursery) sweepGraduatingKinders(classHeight uint32,
|
||||||
// to the network. Additionally, we can stop tracking these outputs as
|
// to the network. Additionally, we can stop tracking these outputs as
|
||||||
// they've just been swept.
|
// they've just been swept.
|
||||||
// TODO(conner): handle concrete error types returned from publication
|
// TODO(conner): handle concrete error types returned from publication
|
||||||
if err := u.cfg.PublishTransaction(finalTx); err != nil &&
|
err := u.cfg.PublishTransaction(finalTx)
|
||||||
!strings.Contains(err.Error(), "TX rejected:") {
|
if err != nil && !strings.Contains(err.Error(), "TX rejected:") {
|
||||||
utxnLog.Errorf("unable to broadcast sweep tx: %v, %v",
|
utxnLog.Errorf("unable to broadcast sweep tx: %v, %v",
|
||||||
err, spew.Sdump(finalTx))
|
err, spew.Sdump(finalTx))
|
||||||
return err
|
return err
|
||||||
|
@ -1132,14 +1193,16 @@ func (u *utxoNursery) waitForSweepConf(classHeight uint32,
|
||||||
// notification that will advance it to the kindergarten bucket upon
|
// notification that will advance it to the kindergarten bucket upon
|
||||||
// confirmation.
|
// confirmation.
|
||||||
func (u *utxoNursery) sweepCribOutput(classHeight uint32, baby *babyOutput) error {
|
func (u *utxoNursery) sweepCribOutput(classHeight uint32, baby *babyOutput) error {
|
||||||
utxnLog.Infof("Publishing CTLV-delayed HTLC output using timeout tx "+
|
utxnLog.Infof("Publishing CLTV-delayed HTLC output using timeout tx "+
|
||||||
"(txid=%v): %v", baby.timeoutTx.TxHash(),
|
"(txid=%v): %v", baby.timeoutTx.TxHash(),
|
||||||
newLogClosure(func() string {
|
newLogClosure(func() string {
|
||||||
return spew.Sdump(baby.timeoutTx)
|
return spew.Sdump(baby.timeoutTx)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Broadcast HTLC transaction
|
// We'll now broadcast the HTLC transaction, then wait for it to be
|
||||||
|
// confirmed before transitioning it to kindergarten.
|
||||||
|
//
|
||||||
// TODO(conner): handle concrete error types returned from publication
|
// TODO(conner): handle concrete error types returned from publication
|
||||||
err := u.cfg.PublishTransaction(baby.timeoutTx)
|
err := u.cfg.PublishTransaction(baby.timeoutTx)
|
||||||
if err != nil &&
|
if err != nil &&
|
||||||
|
@ -1154,8 +1217,8 @@ func (u *utxoNursery) sweepCribOutput(classHeight uint32, baby *babyOutput) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerTimeoutConf is responsible for subscribing to confirmation
|
// registerTimeoutConf is responsible for subscribing to confirmation
|
||||||
// notification for an htlc timeout transaction. If successful, a goroutine will
|
// notification for an htlc timeout transaction. If successful, a goroutine
|
||||||
// be spawned that will transition the provided baby output into the
|
// will be spawned that will transition the provided baby output into the
|
||||||
// kindergarten state within the nursery store.
|
// kindergarten state within the nursery store.
|
||||||
func (u *utxoNursery) registerTimeoutConf(baby *babyOutput, heightHint uint32) error {
|
func (u *utxoNursery) registerTimeoutConf(baby *babyOutput, heightHint uint32) error {
|
||||||
|
|
||||||
|
@ -1216,34 +1279,48 @@ func (u *utxoNursery) waitForTimeoutConf(baby *babyOutput,
|
||||||
"kindergarten", baby.OutPoint())
|
"kindergarten", baby.OutPoint())
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerCommitConf is responsible for subscribing to the confirmation of a
|
// registerPreschoolConf is responsible for subscribing to the confirmation of
|
||||||
// commitment transaction. If successful, the provided preschool output will be
|
// a commitment transaction, or an htlc success transaction for an incoming
|
||||||
// moved persistently into the kindergarten state within the nursery store.
|
// HTLC on our commitment transaction.. If successful, the provided preschool
|
||||||
func (u *utxoNursery) registerCommitConf(kid *kidOutput, heightHint uint32) error {
|
// output will be moved persistently into the kindergarten state within the
|
||||||
|
// nursery store.
|
||||||
|
func (u *utxoNursery) registerPreschoolConf(kid *kidOutput, heightHint uint32) error {
|
||||||
txID := kid.OutPoint().Hash
|
txID := kid.OutPoint().Hash
|
||||||
|
|
||||||
|
// TODO(roasbeef): ensure we don't already have one waiting, need to
|
||||||
|
// de-duplicate
|
||||||
|
// * need to do above?
|
||||||
|
|
||||||
confChan, err := u.cfg.Notifier.RegisterConfirmationsNtfn(&txID,
|
confChan, err := u.cfg.Notifier.RegisterConfirmationsNtfn(&txID,
|
||||||
u.cfg.ConfDepth, heightHint)
|
u.cfg.ConfDepth, heightHint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
utxnLog.Infof("Commitment outpoint %v registered for "+
|
var outputType string
|
||||||
"confirmation notification.", kid.OutPoint())
|
if kid.isHtlc {
|
||||||
|
outputType = "HTLC"
|
||||||
|
} else {
|
||||||
|
outputType = "Commitment"
|
||||||
|
}
|
||||||
|
|
||||||
|
utxnLog.Infof("%v outpoint %v registered for "+
|
||||||
|
"confirmation notification.", outputType, kid.OutPoint())
|
||||||
|
|
||||||
u.wg.Add(1)
|
u.wg.Add(1)
|
||||||
go u.waitForCommitConf(kid, confChan)
|
go u.waitForPreschoolConf(kid, confChan)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// waitForCommitConf is intended to be run as a goroutine that will wait until a
|
// waitForPreschoolConf is intended to be run as a goroutine that will wait until
|
||||||
// channel force close commitment transaction has been included in a confirmed
|
// a channel force close commitment transaction, or a second layer HTLC success
|
||||||
// block. Once the transaction has been confirmed (as reported by the Chain
|
// transaction has been included in a confirmed block. Once the transaction has
|
||||||
// Notifier), waitForCommitConf will delete the output from the "preschool"
|
// been confirmed (as reported by the Chain Notifier), waitForPreschoolConf
|
||||||
// database bucket and atomically add it to the "kindergarten" database bucket.
|
// will delete the output from the "preschool" database bucket and atomically
|
||||||
// This is the second step in the output incubation process.
|
// add it to the "kindergarten" database bucket. This is the second step in
|
||||||
func (u *utxoNursery) waitForCommitConf(kid *kidOutput,
|
// the output incubation process.
|
||||||
|
func (u *utxoNursery) waitForPreschoolConf(kid *kidOutput,
|
||||||
confChan *chainntnfs.ConfirmationEvent) {
|
confChan *chainntnfs.ConfirmationEvent) {
|
||||||
|
|
||||||
defer u.wg.Done()
|
defer u.wg.Done()
|
||||||
|
@ -1268,16 +1345,20 @@ func (u *utxoNursery) waitForCommitConf(kid *kidOutput,
|
||||||
|
|
||||||
// TODO(conner): add retry logic?
|
// TODO(conner): add retry logic?
|
||||||
|
|
||||||
err := u.cfg.Store.PreschoolToKinder(kid)
|
var outputType string
|
||||||
if err != nil {
|
if kid.isHtlc {
|
||||||
utxnLog.Errorf("Unable to move commitment output "+
|
outputType = "HTLC"
|
||||||
"from preschool to kindergarten bucket: %v",
|
} else {
|
||||||
err)
|
outputType = "Commitment"
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
utxnLog.Infof("Commitment output %v promoted to "+
|
err := u.cfg.Store.PreschoolToKinder(kid)
|
||||||
"kindergarten, csv=%v", kid.OutPoint(), kid.BlocksToMaturity())
|
if err != nil {
|
||||||
|
utxnLog.Errorf("Unable to move %v output "+
|
||||||
|
"from preschool to kindergarten bucket: %v",
|
||||||
|
outputType, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractMaturityReport is a report that details the maturity progress of a
|
// contractMaturityReport is a report that details the maturity progress of a
|
||||||
|
|
Loading…
Add table
Reference in a new issue