contractcourt: add method handleCommitSpend

To prepare for the blockbeat handler.
This commit is contained in:
yyforyongyu 2024-11-16 08:42:04 +08:00
parent c1a9390c36
commit 4e30598263
No known key found for this signature in database
GPG Key ID: 9BCD95C4FF296868

@ -652,116 +652,11 @@ func (c *chainWatcher) closeObserver() {
return
}
// Otherwise, the remote party might have broadcast a prior
// revoked state...!!!
commitTxBroadcast := commitSpend.SpendingTx
// First, we'll construct the chainset which includes all the
// data we need to dispatch an event to our subscribers about
// this possible channel close event.
chainSet, err := newChainSet(c.cfg.chanState)
err := c.handleCommitSpend(commitSpend)
if err != nil {
log.Errorf("unable to create commit set: %v", err)
return
log.Errorf("Failed to handle commit spend: %v", err)
}
// Decode the state hint encoded within the commitment
// transaction to determine if this is a revoked state or not.
obfuscator := c.stateHintObfuscator
broadcastStateNum := c.cfg.extractStateNumHint(
commitTxBroadcast, obfuscator,
)
// We'll go on to check whether it could be our own commitment
// that was published and know is confirmed.
ok, err = c.handleKnownLocalState(
commitSpend, broadcastStateNum, chainSet,
)
if err != nil {
log.Errorf("Unable to handle known local state: %v",
err)
return
}
if ok {
return
}
// Now that we know it is neither a non-cooperative closure nor
// a local close with the latest state, we check if it is the
// remote that closed with any prior or current state.
ok, err = c.handleKnownRemoteState(
commitSpend, broadcastStateNum, chainSet,
)
if err != nil {
log.Errorf("Unable to handle known remote state: %v",
err)
return
}
if ok {
return
}
// Next, we'll check to see if this is a cooperative channel
// closure or not. This is characterized by having an input
// sequence number that's finalized. This won't happen with
// regular commitment transactions due to the state hint
// encoding scheme.
switch commitTxBroadcast.TxIn[0].Sequence {
case wire.MaxTxInSequenceNum:
fallthrough
case mempool.MaxRBFSequence:
// TODO(roasbeef): rare but possible, need itest case
// for
err := c.dispatchCooperativeClose(commitSpend)
if err != nil {
log.Errorf("unable to handle co op close: %v", err)
}
return
}
log.Warnf("Unknown commitment broadcast for "+
"ChannelPoint(%v) ", c.cfg.chanState.FundingOutpoint)
// We'll try to recover as best as possible from losing state.
// We first check if this was a local unknown state. This could
// happen if we force close, then lose state or attempt
// recovery before the commitment confirms.
ok, err = c.handleUnknownLocalState(
commitSpend, broadcastStateNum, chainSet,
)
if err != nil {
log.Errorf("Unable to handle known local state: %v",
err)
return
}
if ok {
return
}
// Since it was neither a known remote state, nor a local state
// that was published, it most likely mean we lost state and
// the remote node closed. In this case we must start the DLP
// protocol in hope of getting our money back.
ok, err = c.handleUnknownRemoteState(
commitSpend, broadcastStateNum, chainSet,
)
if err != nil {
log.Errorf("Unable to handle unknown remote state: %v",
err)
return
}
if ok {
return
}
log.Warnf("Unable to handle spending tx %v of channel point %v",
commitTxBroadcast.TxHash(), c.cfg.chanState.FundingOutpoint)
return
// The chainWatcher has been signalled to exit, so we'll do so now.
case <-c.quit:
return
@ -1456,3 +1351,104 @@ func deriveHeightHint(chanState *channeldb.OpenChannel) uint32 {
return heightHint
}
// handleCommitSpend takes a spending tx of the funding output and handles the
// channel close based on the closure type.
func (c *chainWatcher) handleCommitSpend(
commitSpend *chainntnfs.SpendDetail) error {
commitTxBroadcast := commitSpend.SpendingTx
// First, we'll construct the chainset which includes all the data we
// need to dispatch an event to our subscribers about this possible
// channel close event.
chainSet, err := newChainSet(c.cfg.chanState)
if err != nil {
return fmt.Errorf("create commit set: %w", err)
}
// Decode the state hint encoded within the commitment transaction to
// determine if this is a revoked state or not.
obfuscator := c.stateHintObfuscator
broadcastStateNum := c.cfg.extractStateNumHint(
commitTxBroadcast, obfuscator,
)
// We'll go on to check whether it could be our own commitment that was
// published and know is confirmed.
ok, err := c.handleKnownLocalState(
commitSpend, broadcastStateNum, chainSet,
)
if err != nil {
return fmt.Errorf("handle known local state: %w", err)
}
if ok {
return nil
}
// Now that we know it is neither a non-cooperative closure nor a local
// close with the latest state, we check if it is the remote that
// closed with any prior or current state.
ok, err = c.handleKnownRemoteState(
commitSpend, broadcastStateNum, chainSet,
)
if err != nil {
return fmt.Errorf("handle known remote state: %w", err)
}
if ok {
return nil
}
// Next, we'll check to see if this is a cooperative channel closure or
// not. This is characterized by having an input sequence number that's
// finalized. This won't happen with regular commitment transactions
// due to the state hint encoding scheme.
switch commitTxBroadcast.TxIn[0].Sequence {
case wire.MaxTxInSequenceNum:
fallthrough
case mempool.MaxRBFSequence:
// TODO(roasbeef): rare but possible, need itest case for
err := c.dispatchCooperativeClose(commitSpend)
if err != nil {
return fmt.Errorf("handle coop close: %w", err)
}
return nil
}
log.Warnf("Unknown commitment broadcast for ChannelPoint(%v) ",
c.cfg.chanState.FundingOutpoint)
// We'll try to recover as best as possible from losing state. We
// first check if this was a local unknown state. This could happen if
// we force close, then lose state or attempt recovery before the
// commitment confirms.
ok, err = c.handleUnknownLocalState(
commitSpend, broadcastStateNum, chainSet,
)
if err != nil {
return fmt.Errorf("handle known local state: %w", err)
}
if ok {
return nil
}
// Since it was neither a known remote state, nor a local state that
// was published, it most likely mean we lost state and the remote node
// closed. In this case we must start the DLP protocol in hope of
// getting our money back.
ok, err = c.handleUnknownRemoteState(
commitSpend, broadcastStateNum, chainSet,
)
if err != nil {
return fmt.Errorf("handle unknown remote state: %w", err)
}
if ok {
return nil
}
log.Errorf("Unable to handle spending tx %v of channel point %v",
commitTxBroadcast.TxHash(), c.cfg.chanState.FundingOutpoint)
return nil
}