utxonursery: update NurseryReport with details of new output types

The utxo nursery is now responsible for two additional output types:
outgoing HTLC’s on the commitment transaction of the remote party, and
second-level claim transactions that we broadcast. In this commit,
we’ve updated the NurseryReport to now include details, so users are
able to properly keep track of the status of all their pending coins.
This commit is contained in:
Olaoluwa Osuntokun 2018-01-16 20:46:55 -08:00
parent 13b5019cc6
commit 12babb3cea
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21

@ -508,7 +508,7 @@ func (u *utxoNursery) NurseryReport(
// Each crib output represents a stage one htlc, and // Each crib output represents a stage one htlc, and
// will contribute towards the limbo balance. // will contribute towards the limbo balance.
report.AddLimboStage1Htlc(&baby) report.AddLimboStage1TimeoutHtlc(&baby)
case bytes.HasPrefix(k, psclPrefix), case bytes.HasPrefix(k, psclPrefix),
bytes.HasPrefix(k, kndrPrefix), bytes.HasPrefix(k, kndrPrefix),
@ -521,15 +521,24 @@ func (u *utxoNursery) NurseryReport(
return err return err
} }
// Now, use the state prefixes to determine how the this // Now, use the state prefixes to determine how the
// output should be represented in the nursery report. // this output should be represented in the nursery
// An output's funds are always in limbo until reaching // report. An output's funds are always in limbo until
// the graduate state. // reaching the graduate state.
switch { switch {
case bytes.HasPrefix(k, psclPrefix): case bytes.HasPrefix(k, psclPrefix):
// Preschool outputs are awaiting the // Preschool outputs are awaiting the
// confirmation of the commitment transaction. // confirmation of the commitment transaction.
report.AddLimboCommitment(&kid) switch kid.WitnessType() {
case lnwallet.CommitmentTimeLock:
report.AddLimboCommitment(&kid)
// An HTLC output on our commitment transaction
// where the second-layer transaction hasn't
// yet confirmed.
case lnwallet.HtlcAcceptedSuccessSecondLevel:
report.AddLimboStage1SuccessHtlc(&kid)
}
case bytes.HasPrefix(k, kndrPrefix): case bytes.HasPrefix(k, kndrPrefix):
// Kindergarten outputs may originate from // Kindergarten outputs may originate from
@ -543,10 +552,20 @@ func (u *utxoNursery) NurseryReport(
// delay to expire. // delay to expire.
report.AddLimboCommitment(&kid) report.AddLimboCommitment(&kid)
case lnwallet.HtlcOfferedTimeout: case lnwallet.HtlcOfferedRemoteTimeout:
// The htlc timeout transaction has // This is an HTLC output on the
// confirmed, and the CSV delay has // commitment transaction of the remote
// begun ticking. // party. The CLTV timelock has
// expired, and we only need to sweep
// it.
report.AddLimboDirectHtlc(&kid)
case lnwallet.HtlcAcceptedSuccessSecondLevel:
fallthrough
case lnwallet.HtlcOfferedTimeoutSecondLevel:
// The htlc timeout or success
// transaction has confirmed, and the
// CSV delay has begun ticking.
report.AddLimboStage2Htlc(&kid) report.AddLimboStage2Htlc(&kid)
} }
@ -562,10 +581,14 @@ func (u *utxoNursery) NurseryReport(
// regular p2wkh output. // regular p2wkh output.
report.AddRecoveredCommitment(&kid) report.AddRecoveredCommitment(&kid)
case lnwallet.HtlcOfferedTimeout: case lnwallet.HtlcAcceptedSuccessSecondLevel:
// This htlc output successfully resides fallthrough
// in a p2wkh output belonging to the case lnwallet.HtlcOfferedTimeoutSecondLevel:
// user. fallthrough
case lnwallet.HtlcOfferedRemoteTimeout:
// This htlc output successfully
// resides in a p2wkh output belonging
// to the user.
report.AddRecoveredHtlc(&kid) report.AddRecoveredHtlc(&kid)
} }
} }
@ -600,11 +623,11 @@ func (u *utxoNursery) reloadPreschool(heightHint uint32) error {
} }
// reloadClasses reinitializes any height-dependent state transitions for which // reloadClasses reinitializes any height-dependent state transitions for which
// the utxonursery has not recevied confirmation, and replays the graduation of // the utxonursery has not received confirmation, and replays the graduation of
// all kindergarten and crib outputs for heights that have not been finalized. // all kindergarten and crib outputs for heights that have not been finalized.
// This allows the nursery to reinitialize all state to continue sweeping // This allows the nursery to reinitialize all state to continue sweeping
// outputs, even in the event that we missed blocks while offline. reloadClasses // outputs, even in the event that we missed blocks while offline.
// is called during the startup of the UTXO Nursery. // reloadClasses is called during the startup of the UTXO Nursery.
func (u *utxoNursery) reloadClasses(lastGradHeight uint32) error { func (u *utxoNursery) reloadClasses(lastGradHeight uint32) error {
// Begin by loading all of the still-active heights up to and including // Begin by loading all of the still-active heights up to and including
// the last height we successfully graduated. // the last height we successfully graduated.
@ -672,11 +695,11 @@ func (u *utxoNursery) reloadClasses(lastGradHeight uint32) error {
// properly registered, so they can be driven by the chain notifier. No // properly registered, so they can be driven by the chain notifier. No
// transactions or signing are done as a result of this step. // transactions or signing are done as a result of this step.
func (u *utxoNursery) regraduateClass(classHeight uint32) error { func (u *utxoNursery) regraduateClass(classHeight uint32) error {
// Fetch all information about the crib and kindergarten outputs at this // Fetch all information about the crib and kindergarten outputs at
// height. In addition to the outputs, we also retrieve the finalized // this height. In addition to the outputs, we also retrieve the
// kindergarten sweep txn, which will be nil if we have not attempted // finalized kindergarten sweep txn, which will be nil if we have not
// this height before, or if no kindergarten outputs exist at this // attempted this height before, or if no kindergarten outputs exist at
// height. // this height.
finalTx, kgtnOutputs, cribOutputs, err := u.cfg.Store.FetchClass( finalTx, kgtnOutputs, cribOutputs, err := u.cfg.Store.FetchClass(
classHeight) classHeight)
if err != nil { if err != nil {
@ -774,11 +797,11 @@ func (u *utxoNursery) graduateClass(classHeight uint32) error {
u.bestHeight = classHeight u.bestHeight = classHeight
// Fetch all information about the crib and kindergarten outputs at this // Fetch all information about the crib and kindergarten outputs at
// height. In addition to the outputs, we also retrieve the finalized // this height. In addition to the outputs, we also retrieve the
// kindergarten sweep txn, which will be nil if we have not attempted // finalized kindergarten sweep txn, which will be nil if we have not
// this height before, or if no kindergarten outputs exist at this // attempted this height before, or if no kindergarten outputs exist at
// height. // this height.
finalTx, kgtnOutputs, cribOutputs, err := u.cfg.Store.FetchClass( finalTx, kgtnOutputs, cribOutputs, err := u.cfg.Store.FetchClass(
classHeight) classHeight)
if err != nil { if err != nil {
@ -1344,11 +1367,12 @@ func (c *contractMaturityReport) AddRecoveredCommitment(kid *kidOutput) {
c.maturityHeight = kid.BlocksToMaturity() + kid.ConfHeight() c.maturityHeight = kid.BlocksToMaturity() + kid.ConfHeight()
} }
// AddLimboStage1Htlc adds an htlc crib output to the maturity report's // AddLimboStage1TimeoutHtlc adds an htlc crib output to the maturity report's
// htlcs, and contributes its amount to the limbo balance. // htlcs, and contributes its amount to the limbo balance.
func (c *contractMaturityReport) AddLimboStage1Htlc(baby *babyOutput) { func (c *contractMaturityReport) AddLimboStage1TimeoutHtlc(baby *babyOutput) {
c.limboBalance += baby.Amount() c.limboBalance += baby.Amount()
// TODO(roasbeef): bool to indicate stage 1 vs stage 2?
c.htlcs = append(c.htlcs, htlcMaturityReport{ c.htlcs = append(c.htlcs, htlcMaturityReport{
outpoint: *baby.OutPoint(), outpoint: *baby.OutPoint(),
amount: baby.Amount(), amount: baby.Amount(),
@ -1358,6 +1382,38 @@ func (c *contractMaturityReport) AddLimboStage1Htlc(baby *babyOutput) {
}) })
} }
// AddLimboDirectHtlc adds a direct HTLC on the commitment transaction of the
// remote party to the maturity report. This a CLTV time-locked output that
// hasn't yet expired.
func (c *contractMaturityReport) AddLimboDirectHtlc(kid *kidOutput) {
c.limboBalance += kid.Amount()
htlcReport := htlcMaturityReport{
outpoint: *kid.OutPoint(),
amount: kid.Amount(),
confHeight: kid.ConfHeight(),
maturityHeight: kid.absoluteMaturity,
stage: 2,
}
c.htlcs = append(c.htlcs, htlcReport)
}
// AddLimboStage1SuccessHtlcHtlc adds an htlc crib output to the maturity
// report's set of HTLC's. We'll use this to report any incoming HTLC sweeps
// where the second level transaction hasn't yet confirmed.
func (c *contractMaturityReport) AddLimboStage1SuccessHtlc(kid *kidOutput) {
c.limboBalance += kid.Amount()
c.htlcs = append(c.htlcs, htlcMaturityReport{
outpoint: *kid.OutPoint(),
amount: kid.Amount(),
confHeight: kid.ConfHeight(),
maturityRequirement: kid.BlocksToMaturity(),
stage: 1,
})
}
// AddLimboStage2Htlc adds an htlc kindergarten output to the maturity report's // AddLimboStage2Htlc adds an htlc kindergarten output to the maturity report's
// htlcs, and contributes its amount to the limbo balance. // htlcs, and contributes its amount to the limbo balance.
func (c *contractMaturityReport) AddLimboStage2Htlc(kid *kidOutput) { func (c *contractMaturityReport) AddLimboStage2Htlc(kid *kidOutput) {
@ -1469,33 +1525,43 @@ type CsvSpendableOutput interface {
// htlc outputs through incubation. The first stage requires broadcasting a // htlc outputs through incubation. The first stage requires broadcasting a
// presigned timeout txn that spends from the CLTV locked output on the // presigned timeout txn that spends from the CLTV locked output on the
// commitment txn. A babyOutput is treated as a subset of CsvSpendableOutputs, // commitment txn. A babyOutput is treated as a subset of CsvSpendableOutputs,
// with the additional constraint that a transaction must be broadcast before it // with the additional constraint that a transaction must be broadcast before
// can be spent. Each baby transaction embeds the kidOutput that can later be // it can be spent. Each baby transaction embeds the kidOutput that can later
// used to spend the CSV output contained in the timeout txn. // be used to spend the CSV output contained in the timeout txn.
//
// TODO(roasbeef): re-rename to timeout tx
// * create CltvCsvSpendableOutput
type babyOutput struct { type babyOutput struct {
// expiry is the absolute block height at which the timeoutTx should be // expiry is the absolute block height at which the secondLevelTx
// broadcast to the network. // should be broadcast to the network.
//
// NOTE: This value will be zero if this is a baby output for a prior
// incoming HTLC.
expiry uint32 expiry uint32
// timeoutTx is a fully-signed transaction that, upon confirmation, // timeoutTx is a fully-signed transaction that, upon confirmation,
// transitions the htlc into the delay+claim stage. // transitions the htlc into the delay+claim stage.
timeoutTx *wire.MsgTx timeoutTx *wire.MsgTx
// kidOutput represents the CSV output to be swept from the timeoutTx // kidOutput represents the CSV output to be swept from the
// after it has been broadcast and confirmed. // secondLevelTx after it has been broadcast and confirmed.
kidOutput kidOutput
} }
// makeBabyOutput constructs a baby output the wraps a future kidOutput. The // makeBabyOutput constructs a baby output that wraps a future kidOutput. The
// provided sign descriptors and witness types will be used once the output // provided sign descriptors and witness types will be used once the output
// reaches the delay and claim stage. // reaches the delay and claim stage.
func makeBabyOutput(outpoint, originChanPoint *wire.OutPoint, func makeBabyOutput(chanPoint *wire.OutPoint,
blocksToMaturity uint32, witnessType lnwallet.WitnessType,
htlcResolution *lnwallet.OutgoingHtlcResolution) babyOutput { htlcResolution *lnwallet.OutgoingHtlcResolution) babyOutput {
kid := makeKidOutput(outpoint, originChanPoint, htlcOutpoint := htlcResolution.ClaimOutpoint
blocksToMaturity, witnessType, blocksToMaturity := htlcResolution.CsvDelay
&htlcResolution.SweepSignDesc) witnessType := lnwallet.HtlcOfferedTimeoutSecondLevel
kid := makeKidOutput(
&htlcOutpoint, chanPoint, blocksToMaturity, witnessType,
&htlcResolution.SweepSignDesc, 0,
)
return babyOutput{ return babyOutput{
kidOutput: kid, kidOutput: kid,