nursery_store: detect Late Registrations when promoting to kindergarten

In this commit, we aim to address a lingering bug caused by a Late
Registration of a kid output from preschool to kindergarten. In this
scenario, an output is promoted, but *after* it’s target maturity
period, meaning that we won’t graduate the output until we restart. To
avoid this, we’ll now detect this case, and bump the graduation height
by one to ensure that when the new block arrives, we properly handle
the output.
This commit is contained in:
Olaoluwa Osuntokun 2018-01-16 20:55:57 -08:00
parent d0f8b5f194
commit fc8a6568c9
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21
2 changed files with 45 additions and 10 deletions

View File

@ -400,6 +400,10 @@ func (ns *nurseryStore) CribToKinder(bby *babyOutput) error {
return err
}
utxnLog.Tracef("Placing (crib -> baby) output for "+
"chan_point=%v at height_index=%v", chanPoint,
maturityHeight)
// Register the kindergarten output's prefixed output key in the
// height-channel bucket corresponding to its maturity height.
// This informs the utxo nursery that it should attempt to spend
@ -413,7 +417,6 @@ func (ns *nurseryStore) CribToKinder(bby *babyOutput) error {
// confirmation of the preschool output's commitment transaction.
func (ns *nurseryStore) PreschoolToKinder(kid *kidOutput) error {
return ns.db.Update(func(tx *bolt.Tx) error {
// Create or retrieve the channel bucket corresponding to the
// kid output's origin channel point.
chanPoint := kid.OriginChanPoint()
@ -459,13 +462,41 @@ func (ns *nurseryStore) PreschoolToKinder(kid *kidOutput) error {
return err
}
// Since the CSV delay on the kid output has now begun ticking,
// we must insert a record of in the height index to remind us
// to revisit this output once it has fully matured.
// If this output has an absolute time lock, then we'll set the
// maturity height directly.
var maturityHeight uint32
if kid.BlocksToMaturity() == 0 {
maturityHeight = kid.absoluteMaturity
} else {
// Otherwise, since the CSV delay on the kid output has
// now begun ticking, we must insert a record of in the
// height index to remind us to revisit this output
// once it has fully matured.
//
// Compute the maturity height, by adding the output's
// CSV delay to its confirmation height.
maturityHeight = kid.ConfHeight() + kid.BlocksToMaturity()
}
// Compute the maturity height, by adding the output's CSV delay
// to its confirmation height.
maturityHeight := kid.ConfHeight() + kid.BlocksToMaturity()
// In the case of a Late Registration, we've already graduated
// the class that this kid is destined for. So we'll bump it's
// height by one to ensure we don't forget to graduate it.
lastGradHeight, err := ns.getLastGraduatedHeight(tx)
if err != nil {
return err
}
if maturityHeight <= lastGradHeight {
utxnLog.Debugf("Late Registration for kid output=%v "+
"detected: class_height=%v, "+
"last_graduated_height=%v", kid.OutPoint(),
maturityHeight, lastGradHeight)
maturityHeight = lastGradHeight + 1
}
utxnLog.Tracef("Placing (crib -> kid) output for "+
"chan_point=%v at height_index=%v", chanPoint,
maturityHeight)
// Create or retrieve the height-channel bucket for this
// channel. This method will first create a height bucket for

View File

@ -127,7 +127,11 @@ func TestNurseryStoreIncubate(t *testing.T) {
// Begin incubating all of the outputs provided in this test
// vector.
err = ns.Incubate(test.commOutput, test.htlcOutputs)
var kids []kidOutput
if test.commOutput != nil {
kids = append(kids, *test.commOutput)
}
err = ns.Incubate(kids, test.htlcOutputs)
if err != nil {
t.Fatalf("unable to incubate outputs"+
"on test #%d: %v", i, err)
@ -362,7 +366,7 @@ func TestNurseryStoreFinalize(t *testing.T) {
// Begin incubating the commitment output, which will be placed in the
// preschool bucket.
err = ns.Incubate(kid, nil)
err = ns.Incubate([]kidOutput{*kid}, nil)
if err != nil {
t.Fatalf("unable to incubate commitment output: %v", err)
}
@ -449,7 +453,7 @@ func TestNurseryStoreGraduate(t *testing.T) {
// First, add a commitment output to the nursery store, which is
// initially inserted in the preschool bucket.
err = ns.Incubate(kid, nil)
err = ns.Incubate([]kidOutput{*kid}, nil)
if err != nil {
t.Fatalf("unable to incubate commitment output: %v", err)
}