diff --git a/contractcourt/chain_arbitrator.go b/contractcourt/chain_arbitrator.go index 7a9cb3095..4d97b10ba 100644 --- a/contractcourt/chain_arbitrator.go +++ b/contractcourt/chain_arbitrator.go @@ -220,6 +220,61 @@ func NewChainArbitrator(cfg ChainArbitratorConfig, } } +// arbChannel is a wrapper around an open channel that channel arbitrators +// interact with. +type arbChannel struct { + // channel is the in-memory channel state. + channel *channeldb.OpenChannel + + // c references the chain arbitrator and is used by arbChannel + // internally. + c *ChainArbitrator +} + +// ForceCloseChan should force close the contract that this attendant is +// watching over. We'll use this when we decide that we need to go to chain. It +// should in addition tell the switch to remove the corresponding link, such +// that we won't accept any new updates. The returned summary contains all items +// needed to eventually resolve all outputs on chain. +// +// NOTE: Part of the ArbChannel interface. +func (a *arbChannel) ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error) { + // First, we mark the channel as borked, this ensure + // that no new state transitions can happen, and also + // that the link won't be loaded into the switch. + if err := a.channel.MarkBorked(); err != nil { + return nil, err + } + + // With the channel marked as borked, we'll now remove + // the link from the switch if its there. If the link + // is active, then this method will block until it + // exits. + chanPoint := a.channel.FundingOutpoint + + if err := a.c.cfg.MarkLinkInactive(chanPoint); err != nil { + log.Errorf("unable to mark link inactive: %v", err) + } + + // Now that we know the link can't mutate the channel + // state, we'll read the channel from disk the target + // channel according to its channel point. + channel, err := a.c.chanSource.FetchChannel(chanPoint) + if err != nil { + return nil, err + } + + // Finally, we'll force close the channel completing + // the force close workflow. + chanMachine, err := lnwallet.NewLightningChannel( + a.c.cfg.Signer, channel, nil, + ) + if err != nil { + return nil, err + } + return chanMachine.ForceClose() +} + // newActiveChannelArbitrator creates a new instance of an active channel // arbitrator given the state of the target channel. func newActiveChannelArbitrator(channel *channeldb.OpenChannel, @@ -247,42 +302,10 @@ func newActiveChannelArbitrator(channel *channeldb.OpenChannel, // all interfaces and methods the arbitrator needs to do its job. arbCfg := ChannelArbitratorConfig{ ChanPoint: chanPoint, + Channel: c.getArbChannel(channel), ShortChanID: channel.ShortChanID(), BlockEpochs: blockEpoch, - ForceCloseChan: func() (*lnwallet.LocalForceCloseSummary, error) { - // First, we mark the channel as borked, this ensure - // that no new state transitions can happen, and also - // that the link won't be loaded into the switch. - if err := channel.MarkBorked(); err != nil { - return nil, err - } - // With the channel marked as borked, we'll now remove - // the link from the switch if its there. If the link - // is active, then this method will block until it - // exits. - if err := c.cfg.MarkLinkInactive(chanPoint); err != nil { - log.Errorf("unable to mark link inactive: %v", err) - } - - // Now that we know the link can't mutate the channel - // state, we'll read the channel from disk the target - // channel according to its channel point. - channel, err := c.chanSource.FetchChannel(chanPoint) - if err != nil { - return nil, err - } - - // Finally, we'll force close the channel completing - // the force close workflow. - chanMachine, err := lnwallet.NewLightningChannel( - c.cfg.Signer, channel, nil, - ) - if err != nil { - return nil, err - } - return chanMachine.ForceClose() - }, MarkCommitmentBroadcasted: channel.MarkCommitmentBroadcasted, MarkChannelClosed: func(summary *channeldb.ChannelCloseSummary, statuses ...channeldb.ChannelStatus) error { @@ -339,6 +362,16 @@ func newActiveChannelArbitrator(channel *channeldb.OpenChannel, ), nil } +// getArbChannel returns an open channel wrapper for use by channel arbitrators. +func (c *ChainArbitrator) getArbChannel( + channel *channeldb.OpenChannel) *arbChannel { + + return &arbChannel{ + channel: channel, + c: c, + } +} + // ResolveContract marks a contract as fully resolved within the database. // This is only to be done once all contracts which were live on the channel // before hitting the chain have been resolved. diff --git a/contractcourt/channel_arbitrator.go b/contractcourt/channel_arbitrator.go index 1c4ee8e81..8c6ba2712 100644 --- a/contractcourt/channel_arbitrator.go +++ b/contractcourt/channel_arbitrator.go @@ -64,6 +64,18 @@ type WitnessBeacon interface { AddPreimages(preimages ...lntypes.Preimage) error } +// ArbChannel is an abstraction that allows the channel arbitrator to interact +// with an open channel. +type ArbChannel interface { + // ForceCloseChan should force close the contract that this attendant + // is watching over. We'll use this when we decide that we need to go + // to chain. It should in addition tell the switch to remove the + // corresponding link, such that we won't accept any new updates. The + // returned summary contains all items needed to eventually resolve all + // outputs on chain. + ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error) +} + // ChannelArbitratorConfig contains all the functionality that the // ChannelArbitrator needs in order to properly arbitrate any contract dispute // on chain. @@ -72,6 +84,10 @@ type ChannelArbitratorConfig struct { // channel. ChanPoint wire.OutPoint + // Channel is the full channel data structure. For legacy channels, this + // field may not always be set after a restart. + Channel ArbChannel + // ShortChanID describes the exact location of the channel within the // chain. We'll use this to address any messages that we need to send // to the switch during contract resolution. @@ -88,14 +104,6 @@ type ChannelArbitratorConfig struct { // channel. ChainEvents *ChainEventSubscription - // ForceCloseChan should force close the contract that this attendant - // is watching over. We'll use this when we decide that we need to go - // to chain. It should in addition tell the switch to remove the - // corresponding link, such that we won't accept any new updates. The - // returned summary contains all items needed to eventually resolve all - // outputs on chain. - ForceCloseChan func() (*lnwallet.LocalForceCloseSummary, error) - // MarkCommitmentBroadcasted should mark the channel as the commitment // being broadcast, and we are waiting for the commitment to confirm. MarkCommitmentBroadcasted func(*wire.MsgTx, bool) error @@ -791,7 +799,7 @@ func (c *ChannelArbitrator) stateStep( // We'll tell the switch that it should remove the link for // this channel, in addition to fetching the force close // summary needed to close this channel on chain. - closeSummary, err := c.cfg.ForceCloseChan() + closeSummary, err := c.cfg.Channel.ForceCloseChan() if err != nil { log.Errorf("ChannelArbitrator(%v): unable to "+ "force close: %v", c.cfg.ChanPoint, err) diff --git a/contractcourt/channel_arbitrator_test.go b/contractcourt/channel_arbitrator_test.go index b33018928..46316d0cb 100644 --- a/contractcourt/channel_arbitrator_test.go +++ b/contractcourt/channel_arbitrator_test.go @@ -360,13 +360,7 @@ func createTestChannelArbitrator(t *testing.T, log ArbitratorLog, resolvedChan <- struct{}{} return nil }, - ForceCloseChan: func() (*lnwallet.LocalForceCloseSummary, error) { - summary := &lnwallet.LocalForceCloseSummary{ - CloseTx: &wire.MsgTx{}, - HtlcResolutions: &lnwallet.HtlcResolutions{}, - } - return summary, nil - }, + Channel: &mockChannel{}, MarkCommitmentBroadcasted: func(_ *wire.MsgTx, _ bool) error { return nil }, @@ -2088,3 +2082,13 @@ func TestRemoteCloseInitiator(t *testing.T) { }) } } + +type mockChannel struct{} + +func (m *mockChannel) ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error) { + summary := &lnwallet.LocalForceCloseSummary{ + CloseTx: &wire.MsgTx{}, + HtlcResolutions: &lnwallet.HtlcResolutions{}, + } + return summary, nil +}