From fc8a6568c98c9251ac0e8fcde5e78861d093e8ed Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 16 Jan 2018 20:55:57 -0800 Subject: [PATCH] nursery_store: detect Late Registrations when promoting to kindergarten MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- nursery_store.go | 45 ++++++++++++++++++++++++++++++++++++------- nursery_store_test.go | 10 +++++++--- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/nursery_store.go b/nursery_store.go index 5167a7ab..6b76d492 100644 --- a/nursery_store.go +++ b/nursery_store.go @@ -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 diff --git a/nursery_store_test.go b/nursery_store_test.go index 1fce278b..ca0b1540 100644 --- a/nursery_store_test.go +++ b/nursery_store_test.go @@ -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) }