multi: address lingering TODO by no longer wiping out local HTLCs on remote close

In this commit, we fix a lingering TOOD statement in the channel arb.
Before this commitment, we would simply wipe our our local HTLC set of
the HTLC set that was on the remote commitment transaction on force
close. This was incorrect as if our commitment transaction had an HTLC
that the remote commitment didn't, then we would fail to cancel that
back, and cause both channels to time out on chain.

In order to remedy this, we introduce a new `HtlcSetKey` struct to track
all 3 possible in-flight set of HTLCs: ours, theirs, and their pending.

We also we start to tack on additional data to all the unilateral close
messages we send to subscribers. This new data is the CommitSet, or the
set of valid commitments at channel closure time. This new information
will be used by the channel arb in an upcoming commit to ensure it will
cancel back HTLCs in the case of split commitment state.

Finally, we start to thread through an optional *CommitSet to the
advanceState method. This additional information will give the channel
arb addition information it needs to ensure it properly cancels back
HTLCs that are about to time out or may time out depending on which
commitment is played.

Within the htlcswitch pakage, we modify the `SignNextCommitment` method
to return the new set of pending HTLCs for the remote party's commitment
transaction and `ReceiveRevocation` to return the latest set of
commitment transactions on the remote party's commitment as well. This
is a preparatory change which is part of a larger change to address a
lingering TODO in the cnct.

Additionally, rather than just send of the set of HTLCs after the we
revoke, we'll also send of the set of HTLCs after the remote party
revokes, and we create a pending commitment state for it.
This commit is contained in:
Olaoluwa Osuntokun 2019-05-16 17:23:26 -07:00
parent 6e102d64b9
commit 5f0fad85be
11 changed files with 449 additions and 237 deletions

@ -1946,7 +1946,7 @@ func createHTLC(data int, amount lnwire.MilliSatoshi) (*lnwire.UpdateAddHTLC, [3
// pending updates. // pending updates.
// TODO(conner) remove code duplication // TODO(conner) remove code duplication
func forceStateTransition(chanA, chanB *lnwallet.LightningChannel) error { func forceStateTransition(chanA, chanB *lnwallet.LightningChannel) error {
aliceSig, aliceHtlcSigs, err := chanA.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := chanA.SignNextCommitment()
if err != nil { if err != nil {
return err return err
} }
@ -1958,12 +1958,13 @@ func forceStateTransition(chanA, chanB *lnwallet.LightningChannel) error {
if err != nil { if err != nil {
return err return err
} }
bobSig, bobHtlcSigs, err := chanB.SignNextCommitment() bobSig, bobHtlcSigs, _, err := chanB.SignNextCommitment()
if err != nil { if err != nil {
return err return err
} }
if _, _, _, err := chanA.ReceiveRevocation(bobRevocation); err != nil { _, _, _, _, err = chanA.ReceiveRevocation(bobRevocation)
if err != nil {
return err return err
} }
if err := chanA.ReceiveNewCommitment(bobSig, bobHtlcSigs); err != nil { if err := chanA.ReceiveNewCommitment(bobSig, bobHtlcSigs); err != nil {
@ -1974,7 +1975,8 @@ func forceStateTransition(chanA, chanB *lnwallet.LightningChannel) error {
if err != nil { if err != nil {
return err return err
} }
if _, _, _, err := chanB.ReceiveRevocation(aliceRevocation); err != nil { _, _, _, _, err = chanB.ReceiveRevocation(aliceRevocation)
if err != nil {
return err return err
} }

@ -296,8 +296,25 @@ func newActiveChannelArbitrator(channel *channeldb.OpenChannel,
return c.resolveContract(chanPoint, chanLog) return c.resolveContract(chanPoint, chanLog)
} }
// Finally, we'll need to construct a series of htlc Sets based on all
// currently known valid commitments.
htlcSets := make(map[HtlcSetKey]htlcSet)
htlcSets[LocalHtlcSet] = newHtlcSet(channel.LocalCommitment.Htlcs)
htlcSets[RemoteHtlcSet] = newHtlcSet(channel.RemoteCommitment.Htlcs)
pendingRemoteCommitment, err := channel.RemoteCommitChainTip()
if err != nil && err != channeldb.ErrNoPendingCommit {
blockEpoch.Cancel()
return nil, err
}
if pendingRemoteCommitment != nil {
htlcSets[RemotePendingHtlcSet] = newHtlcSet(
pendingRemoteCommitment.Commitment.Htlcs,
)
}
return NewChannelArbitrator( return NewChannelArbitrator(
arbCfg, channel.LocalCommitment.Htlcs, chanLog, arbCfg, htlcSets, chanLog,
), nil ), nil
} }
@ -557,15 +574,27 @@ func (c *ChainArbitrator) Stop() error {
return nil return nil
} }
// ContractUpdate is a message packages the latest set of active HTLCs on a
// commitment, and also identifies which commitment received a new set of
// HTLCs.
type ContractUpdate struct {
// HtlcKey identifies which commitment the HTLCs below are present on.
HtlcKey HtlcSetKey
// Htlcs are the of active HTLCs on the commitment identified by the
// above HtlcKey.
Htlcs []channeldb.HTLC
}
// ContractSignals wraps the two signals that affect the state of a channel // ContractSignals wraps the two signals that affect the state of a channel
// being watched by an arbitrator. The two signals we care about are: the // being watched by an arbitrator. The two signals we care about are: the
// channel has a new set of HTLC's, and the remote party has just broadcast // channel has a new set of HTLC's, and the remote party has just broadcast
// their version of the commitment transaction. // their version of the commitment transaction.
type ContractSignals struct { type ContractSignals struct {
// HtlcUpdates is a channel that once we new commitment updates takes // HtlcUpdates is a channel that the link will use to update the
// place, the later set of HTLC's on the commitment transaction should // designated channel arbitrator when the set of HTLCs on any valid
// be sent over. // commitment changes.
HtlcUpdates chan []channeldb.HTLC HtlcUpdates chan *ContractUpdate
// ShortChanID is the up to date short channel ID for a contract. This // ShortChanID is the up to date short channel ID for a contract. This
// can change either if when the contract was added it didn't yet have // can change either if when the contract was added it didn't yet have

@ -30,20 +30,77 @@ const (
maxCommitPointPollTimeout = 10 * time.Minute maxCommitPointPollTimeout = 10 * time.Minute
) )
// LocalUnilateralCloseInfo encapsulates all the informnation we need to act // LocalUnilateralCloseInfo encapsulates all the information we need to act on
// on a local force close that gets confirmed. // a local force close that gets confirmed.
type LocalUnilateralCloseInfo struct { type LocalUnilateralCloseInfo struct {
*chainntnfs.SpendDetail *chainntnfs.SpendDetail
*lnwallet.LocalForceCloseSummary *lnwallet.LocalForceCloseSummary
*channeldb.ChannelCloseSummary *channeldb.ChannelCloseSummary
// CommitSet is the set of known valid commitments at the time the
// remote party's commitment hit the chain.
CommitSet CommitSet
} }
// CooperativeCloseInfo encapsulates all the informnation we need to act // CooperativeCloseInfo encapsulates all the information we need to act on a
// on a cooperative close that gets confirmed. // cooperative close that gets confirmed.
type CooperativeCloseInfo struct { type CooperativeCloseInfo struct {
*channeldb.ChannelCloseSummary *channeldb.ChannelCloseSummary
} }
// RemoteUnilateralCloseInfo wraps the normal UnilateralCloseSummary to couple
// the CommitSet at the time of channel closure.
type RemoteUnilateralCloseInfo struct {
*lnwallet.UnilateralCloseSummary
// CommitSet is the set of known valid commitments at the time the
// remote party's commitemnt hit the chain.
CommitSet CommitSet
}
// CommitSet is a collection of the set of known valid commitments at a given
// instant. If ConfCommitKey is set, then the commitment identified by the
// HtlcSetKey has hit the chain. This struct will be used to examine all live
// HTLCs to determine if any additional actions need to be made based on the
// remote party's commitments.
type CommitSet struct {
// ConfCommitKey if non-nil, identifies the commitment that was
// confirmed in the chain.
ConfCommitKey *HtlcSetKey
// HtlcSets stores the set of all known active HTLC for each active
// commitment at the time of channel closure.
HtlcSets map[HtlcSetKey][]channeldb.HTLC
}
// IsEmpty returns true if there are no HTLCs at all within all commitments
// that are a part of this commitment diff.
func (c *CommitSet) IsEmpty() bool {
if c == nil {
return true
}
for _, htlcs := range c.HtlcSets {
if len(htlcs) != 0 {
return false
}
}
return true
}
// toActiveHTLCSets returns the set of all active HTLCs across all commitment
// transactions.
func (c *CommitSet) toActiveHTLCSets() map[HtlcSetKey]htlcSet {
htlcSets := make(map[HtlcSetKey]htlcSet)
for htlcSetKey, htlcs := range c.HtlcSets {
htlcSets[htlcSetKey] = newHtlcSet(htlcs)
}
return htlcSets
}
// ChainEventSubscription is a struct that houses a subscription to be notified // ChainEventSubscription is a struct that houses a subscription to be notified
// for any on-chain events related to a channel. There are three types of // for any on-chain events related to a channel. There are three types of
// possible on-chain events: a cooperative channel closure, a unilateral // possible on-chain events: a cooperative channel closure, a unilateral
@ -55,7 +112,7 @@ type ChainEventSubscription struct {
// RemoteUnilateralClosure is a channel that will be sent upon in the // RemoteUnilateralClosure is a channel that will be sent upon in the
// event that the remote party's commitment transaction is confirmed. // event that the remote party's commitment transaction is confirmed.
RemoteUnilateralClosure chan *lnwallet.UnilateralCloseSummary RemoteUnilateralClosure chan *RemoteUnilateralCloseInfo
// LocalUnilateralClosure is a channel that will be sent upon in the // LocalUnilateralClosure is a channel that will be sent upon in the
// event that our commitment transaction is confirmed. // event that our commitment transaction is confirmed.
@ -249,7 +306,7 @@ func (c *chainWatcher) SubscribeChannelEvents() *ChainEventSubscription {
sub := &ChainEventSubscription{ sub := &ChainEventSubscription{
ChanPoint: c.cfg.chanState.FundingOutpoint, ChanPoint: c.cfg.chanState.FundingOutpoint,
RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), RemoteUnilateralClosure: make(chan *RemoteUnilateralCloseInfo, 1),
LocalUnilateralClosure: make(chan *LocalUnilateralCloseInfo, 1), LocalUnilateralClosure: make(chan *LocalUnilateralCloseInfo, 1),
CooperativeClosure: make(chan *CooperativeCloseInfo, 1), CooperativeClosure: make(chan *CooperativeCloseInfo, 1),
ContractBreach: make(chan *lnwallet.BreachRetribution, 1), ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
@ -373,6 +430,30 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
return return
} }
// Fetch the current known commit height for the remote party,
// and their pending commitment chain tip if it exist.
remoteStateNum := remoteCommit.CommitHeight
remoteChainTip, err := c.cfg.chanState.RemoteCommitChainTip()
if err != nil && err != channeldb.ErrNoPendingCommit {
log.Errorf("unable to obtain chain tip for "+
"ChannelPoint(%v): %v",
c.cfg.chanState.FundingOutpoint, err)
return
}
// Now that we have all the possible valid commitments, we'll
// make the CommitSet the ChannelArbitrator will need it in
// order to carry out its duty.
commitSet := CommitSet{
HtlcSets: make(map[HtlcSetKey][]channeldb.HTLC),
}
commitSet.HtlcSets[LocalHtlcSet] = localCommit.Htlcs
commitSet.HtlcSets[RemoteHtlcSet] = remoteCommit.Htlcs
if remoteChainTip != nil {
htlcs := remoteChainTip.Commitment.Htlcs
commitSet.HtlcSets[RemotePendingHtlcSet] = htlcs
}
// We'll not retrieve the latest sate of the revocation store // We'll not retrieve the latest sate of the revocation store
// so we can populate the information within the channel state // so we can populate the information within the channel state
// object that we have. // object that we have.
@ -411,8 +492,10 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
// as we don't have any further processing we need to do (we // as we don't have any further processing we need to do (we
// can't cheat ourselves :p). // can't cheat ourselves :p).
if isOurCommit { if isOurCommit {
commitSet.ConfCommitKey = &LocalHtlcSet
if err := c.dispatchLocalForceClose( if err := c.dispatchLocalForceClose(
commitSpend, *localCommit, commitSpend, *localCommit, commitSet,
); err != nil { ); err != nil {
log.Errorf("unable to handle local"+ log.Errorf("unable to handle local"+
"close for chan_point=%v: %v", "close for chan_point=%v: %v",
@ -439,17 +522,6 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
log.Warnf("Unprompted commitment broadcast for "+ log.Warnf("Unprompted commitment broadcast for "+
"ChannelPoint(%v) ", c.cfg.chanState.FundingOutpoint) "ChannelPoint(%v) ", c.cfg.chanState.FundingOutpoint)
// Fetch the current known commit height for the remote party,
// and their pending commitment chain tip if it exist.
remoteStateNum := remoteCommit.CommitHeight
remoteChainTip, err := c.cfg.chanState.RemoteCommitChainTip()
if err != nil && err != channeldb.ErrNoPendingCommit {
log.Errorf("unable to obtain chain tip for "+
"ChannelPoint(%v): %v",
c.cfg.chanState.FundingOutpoint, err)
return
}
// If this channel has been recovered, then we'll modify our // If this channel has been recovered, then we'll modify our
// behavior as it isn't possible for us to close out the // behavior as it isn't possible for us to close out the
// channel off-chain ourselves. It can only be the remote party // channel off-chain ourselves. It can only be the remote party
@ -465,9 +537,10 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
// we'll trigger the unilateral close signal so subscribers can // we'll trigger the unilateral close signal so subscribers can
// clean up the state as necessary. // clean up the state as necessary.
case broadcastStateNum == remoteStateNum && !isRecoveredChan: case broadcastStateNum == remoteStateNum && !isRecoveredChan:
commitSet.ConfCommitKey = &RemoteHtlcSet
err := c.dispatchRemoteForceClose( err := c.dispatchRemoteForceClose(
commitSpend, *remoteCommit, commitSpend, *remoteCommit, commitSet,
c.cfg.chanState.RemoteCurrentRevocation, c.cfg.chanState.RemoteCurrentRevocation,
) )
if err != nil { if err != nil {
@ -484,8 +557,10 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
case broadcastStateNum == remoteStateNum+1 && case broadcastStateNum == remoteStateNum+1 &&
remoteChainTip != nil && !isRecoveredChan: remoteChainTip != nil && !isRecoveredChan:
commitSet.ConfCommitKey = &RemotePendingHtlcSet
err := c.dispatchRemoteForceClose( err := c.dispatchRemoteForceClose(
commitSpend, remoteChainTip.Commitment, commitSpend, *remoteCommit, commitSet,
c.cfg.chanState.RemoteNextRevocation, c.cfg.chanState.RemoteNextRevocation,
) )
if err != nil { if err != nil {
@ -553,14 +628,15 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
c.cfg.chanState.FundingOutpoint) c.cfg.chanState.FundingOutpoint)
// Since we don't have the commitment stored for this // Since we don't have the commitment stored for this
// state, we'll just pass an empty commitment. Note // state, we'll just pass an empty commitment within
// that this means we won't be able to recover any HTLC // the commitment set. Note that this means we won't be
// funds. // able to recover any HTLC funds.
// //
// TODO(halseth): can we try to recover some HTLCs? // TODO(halseth): can we try to recover some HTLCs?
commitSet.ConfCommitKey = &RemoteHtlcSet
err = c.dispatchRemoteForceClose( err = c.dispatchRemoteForceClose(
commitSpend, channeldb.ChannelCommitment{}, commitSpend, channeldb.ChannelCommitment{},
commitPoint, commitSet, commitPoint,
) )
if err != nil { if err != nil {
log.Errorf("unable to handle remote "+ log.Errorf("unable to handle remote "+
@ -691,7 +767,7 @@ func (c *chainWatcher) dispatchCooperativeClose(commitSpend *chainntnfs.SpendDet
// dispatchLocalForceClose processes a unilateral close by us being confirmed. // dispatchLocalForceClose processes a unilateral close by us being confirmed.
func (c *chainWatcher) dispatchLocalForceClose( func (c *chainWatcher) dispatchLocalForceClose(
commitSpend *chainntnfs.SpendDetail, commitSpend *chainntnfs.SpendDetail,
localCommit channeldb.ChannelCommitment) error { localCommit channeldb.ChannelCommitment, commitSet CommitSet) error {
log.Infof("Local unilateral close of ChannelPoint(%v) "+ log.Infof("Local unilateral close of ChannelPoint(%v) "+
"detected", c.cfg.chanState.FundingOutpoint) "detected", c.cfg.chanState.FundingOutpoint)
@ -749,7 +825,10 @@ func (c *chainWatcher) dispatchLocalForceClose(
// With the event processed, we'll now notify all subscribers of the // With the event processed, we'll now notify all subscribers of the
// event. // event.
closeInfo := &LocalUnilateralCloseInfo{ closeInfo := &LocalUnilateralCloseInfo{
commitSpend, forceClose, closeSummary, SpendDetail: commitSpend,
LocalForceCloseSummary: forceClose,
ChannelCloseSummary: closeSummary,
CommitSet: commitSet,
} }
c.Lock() c.Lock()
for _, sub := range c.clientSubscriptions { for _, sub := range c.clientSubscriptions {
@ -781,7 +860,7 @@ func (c *chainWatcher) dispatchLocalForceClose(
func (c *chainWatcher) dispatchRemoteForceClose( func (c *chainWatcher) dispatchRemoteForceClose(
commitSpend *chainntnfs.SpendDetail, commitSpend *chainntnfs.SpendDetail,
remoteCommit channeldb.ChannelCommitment, remoteCommit channeldb.ChannelCommitment,
commitPoint *btcec.PublicKey) error { commitSet CommitSet, commitPoint *btcec.PublicKey) error {
log.Infof("Unilateral close of ChannelPoint(%v) "+ log.Infof("Unilateral close of ChannelPoint(%v) "+
"detected", c.cfg.chanState.FundingOutpoint) "detected", c.cfg.chanState.FundingOutpoint)
@ -802,7 +881,10 @@ func (c *chainWatcher) dispatchRemoteForceClose(
c.Lock() c.Lock()
for _, sub := range c.clientSubscriptions { for _, sub := range c.clientSubscriptions {
select { select {
case sub.RemoteUnilateralClosure <- uniClose: case sub.RemoteUnilateralClosure <- &RemoteUnilateralCloseInfo{
UnilateralCloseSummary: uniClose,
CommitSet: commitSet,
}:
case <-c.quit: case <-c.quit:
c.Unlock() c.Unlock()
return fmt.Errorf("exiting") return fmt.Errorf("exiting")

@ -104,7 +104,7 @@ func TestChainWatcherRemoteUnilateralClose(t *testing.T) {
// We should get a new spend event over the remote unilateral close // We should get a new spend event over the remote unilateral close
// event channel. // event channel.
var uniClose *lnwallet.UnilateralCloseSummary var uniClose *RemoteUnilateralCloseInfo
select { select {
case uniClose = <-chanEvents.RemoteUnilateralClosure: case uniClose = <-chanEvents.RemoteUnilateralClosure:
case <-time.After(time.Second * 15): case <-time.After(time.Second * 15):
@ -186,7 +186,7 @@ func TestChainWatcherRemoteUnilateralClosePendingCommit(t *testing.T) {
// With the HTLC added, we'll now manually initiate a state transition // With the HTLC added, we'll now manually initiate a state transition
// from Alice to Bob. // from Alice to Bob.
_, _, err = aliceChannel.SignNextCommitment() _, _, _, err = aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -211,7 +211,7 @@ func TestChainWatcherRemoteUnilateralClosePendingCommit(t *testing.T) {
// We should get a new spend event over the remote unilateral close // We should get a new spend event over the remote unilateral close
// event channel. // event channel.
var uniClose *lnwallet.UnilateralCloseSummary var uniClose *RemoteUnilateralCloseInfo
select { select {
case uniClose = <-chanEvents.RemoteUnilateralClosure: case uniClose = <-chanEvents.RemoteUnilateralClosure:
case <-time.After(time.Second * 15): case <-time.After(time.Second * 15):
@ -343,7 +343,7 @@ func TestChainWatcherDataLossProtect(t *testing.T) {
// We should get a new uni close resolution that indicates we // We should get a new uni close resolution that indicates we
// processed the DLP scenario. // processed the DLP scenario.
var uniClose *lnwallet.UnilateralCloseSummary var uniClose *RemoteUnilateralCloseInfo
select { select {
case uniClose = <-chanEvents.RemoteUnilateralClosure: case uniClose = <-chanEvents.RemoteUnilateralClosure:
// If we processed this as a DLP case, then the remote // If we processed this as a DLP case, then the remote

@ -161,14 +161,14 @@ type ContractReport struct {
// htlcSet represents the set of active HTLCs on a given commitment // htlcSet represents the set of active HTLCs on a given commitment
// transaction. // transaction.
type htlcSet struct { type htlcSet struct {
// incomingHTLCs is a map of all incoming HTLCs on our commitment // incomingHTLCs is a map of all incoming HTLCs on the target
// transaction. We may potentially go onchain to claim the funds sent // commitment transaction. We may potentially go onchain to claim the
// to us within this set. // funds sent to us within this set.
incomingHTLCs map[uint64]channeldb.HTLC incomingHTLCs map[uint64]channeldb.HTLC
// outgoingHTLCs is a map of all outgoing HTLCs on our commitment // outgoingHTLCs is a map of all outgoing HTLCs on the target
// transaction. We may potentially go onchain to reclaim the funds that // commitment transaction. We may potentially go onchain to reclaim the
// are currently in limbo. // funds that are currently in limbo.
outgoingHTLCs map[uint64]channeldb.HTLC outgoingHTLCs map[uint64]channeldb.HTLC
} }
@ -191,6 +191,30 @@ func newHtlcSet(htlcs []channeldb.HTLC) htlcSet {
} }
} }
// HtlcSetKey is a two-tuple that uniquely identifies a set of HTLCs on a
// commitment transaction.
type HtlcSetKey struct {
// IsRemote denotes if the HTLCs are on the remote commitment
// transaction.
IsRemote bool
// IsPending denotes if the commitment transaction that HTLCS are on
// are pending (the higher of two unrevoked commitments).
IsPending bool
}
var (
// LocalHtlcSet is the HtlcSetKey used for local commitments.
LocalHtlcSet = HtlcSetKey{IsRemote: false, IsPending: false}
// RemoteHtlcSet is the HtlcSetKey used for remote commitments.
RemoteHtlcSet = HtlcSetKey{IsRemote: true, IsPending: false}
// RemotePendingHtlcSet is the HtlcSetKey used for dangling remote
// commitment transactions.
RemotePendingHtlcSet = HtlcSetKey{IsRemote: true, IsPending: true}
)
// ChannelArbitrator is the on-chain arbitrator for a particular channel. The // ChannelArbitrator is the on-chain arbitrator for a particular channel. The
// struct will keep in sync with the current set of HTLCs on the commitment // struct will keep in sync with the current set of HTLCs on the commitment
// transaction. The job of the attendant is to go on-chain to either settle or // transaction. The job of the attendant is to go on-chain to either settle or
@ -207,9 +231,9 @@ type ChannelArbitrator struct {
// its next action, and the state of any unresolved contracts. // its next action, and the state of any unresolved contracts.
log ArbitratorLog log ArbitratorLog
// activeHTLCs is the set of active incoming/outgoing HTLC's on the // activeHTLCs is the set of active incoming/outgoing HTLC's on all
// commitment transaction. // currently valid commitment transactions.
activeHTLCs htlcSet activeHTLCs map[HtlcSetKey]htlcSet
// cfg contains all the functionality that the ChannelArbitrator requires // cfg contains all the functionality that the ChannelArbitrator requires
// to do its duty. // to do its duty.
@ -222,7 +246,7 @@ type ChannelArbitrator struct {
// htlcUpdates is a channel that is sent upon with new updates from the // htlcUpdates is a channel that is sent upon with new updates from the
// active channel. Each time a new commitment state is accepted, the // active channel. Each time a new commitment state is accepted, the
// set of HTLC's on the new state should be sent across this channel. // set of HTLC's on the new state should be sent across this channel.
htlcUpdates <-chan []channeldb.HTLC htlcUpdates <-chan *ContractUpdate
// activeResolvers is a slice of any active resolvers. This is used to // activeResolvers is a slice of any active resolvers. This is used to
// be able to signal them for shutdown in the case that we shutdown. // be able to signal them for shutdown in the case that we shutdown.
@ -252,15 +276,15 @@ type ChannelArbitrator struct {
// NewChannelArbitrator returns a new instance of a ChannelArbitrator backed by // NewChannelArbitrator returns a new instance of a ChannelArbitrator backed by
// the passed config struct. // the passed config struct.
func NewChannelArbitrator(cfg ChannelArbitratorConfig, func NewChannelArbitrator(cfg ChannelArbitratorConfig,
startingHTLCs []channeldb.HTLC, log ArbitratorLog) *ChannelArbitrator { htlcSets map[HtlcSetKey]htlcSet, log ArbitratorLog) *ChannelArbitrator {
return &ChannelArbitrator{ return &ChannelArbitrator{
log: log, log: log,
signalUpdates: make(chan *signalUpdateMsg), signalUpdates: make(chan *signalUpdateMsg),
htlcUpdates: make(<-chan []channeldb.HTLC), htlcUpdates: make(<-chan *ContractUpdate),
resolutionSignal: make(chan struct{}), resolutionSignal: make(chan struct{}),
forceCloseReqs: make(chan *forceCloseReq), forceCloseReqs: make(chan *forceCloseReq),
activeHTLCs: newHtlcSet(startingHTLCs), activeHTLCs: htlcSets,
cfg: cfg, cfg: cfg,
quit: make(chan struct{}), quit: make(chan struct{}),
} }
@ -335,7 +359,9 @@ func (c *ChannelArbitrator) Start() error {
// We'll now attempt to advance our state forward based on the current // We'll now attempt to advance our state forward based on the current
// on-chain state, and our set of active contracts. // on-chain state, and our set of active contracts.
startingState := c.state startingState := c.state
nextState, _, err := c.advanceState(triggerHeight, trigger) nextState, _, err := c.advanceState(
triggerHeight, trigger, nil,
)
if err != nil { if err != nil {
switch err { switch err {
@ -600,8 +626,9 @@ func (t transitionTrigger) String() string {
// the appropriate state transition if necessary. The next state we transition // the appropriate state transition if necessary. The next state we transition
// to is returned, Additionally, if the next transition results in a commitment // to is returned, Additionally, if the next transition results in a commitment
// broadcast, the commitment transaction itself is returned. // broadcast, the commitment transaction itself is returned.
func (c *ChannelArbitrator) stateStep(triggerHeight uint32, func (c *ChannelArbitrator) stateStep(
trigger transitionTrigger) (ArbitratorState, *wire.MsgTx, error) { triggerHeight uint32, trigger transitionTrigger,
confCommitSet *CommitSet) (ArbitratorState, *wire.MsgTx, error) {
var ( var (
nextState ArbitratorState nextState ArbitratorState
@ -932,8 +959,9 @@ func (c *ChannelArbitrator) launchResolvers(resolvers []ContractResolver) {
// redundant transition, meaning that the state transition is a noop. The final // redundant transition, meaning that the state transition is a noop. The final
// param is a callback that allows the caller to execute an arbitrary action // param is a callback that allows the caller to execute an arbitrary action
// after each state transition. // after each state transition.
func (c *ChannelArbitrator) advanceState(triggerHeight uint32, func (c *ChannelArbitrator) advanceState(
trigger transitionTrigger) (ArbitratorState, *wire.MsgTx, error) { triggerHeight uint32, trigger transitionTrigger,
confCommitSet *CommitSet) (ArbitratorState, *wire.MsgTx, error) {
var ( var (
priorState ArbitratorState priorState ArbitratorState
@ -949,7 +977,7 @@ func (c *ChannelArbitrator) advanceState(triggerHeight uint32,
priorState) priorState)
nextState, closeTx, err := c.stateStep( nextState, closeTx, err := c.stateStep(
triggerHeight, trigger, triggerHeight, trigger, confCommitSet,
) )
if err != nil { if err != nil {
log.Errorf("ChannelArbitrator(%v): unable to advance "+ log.Errorf("ChannelArbitrator(%v): unable to advance "+
@ -1099,7 +1127,8 @@ func (c *ChannelArbitrator) checkChainActions(height uint32,
// First, we'll make an initial pass over the set of incoming and // First, we'll make an initial pass over the set of incoming and
// outgoing HTLC's to decide if we need to go on chain at all. // outgoing HTLC's to decide if we need to go on chain at all.
haveChainActions := false haveChainActions := false
for _, htlc := range c.activeHTLCs.outgoingHTLCs { localHTLCs := c.activeHTLCs[LocalHtlcSet]
for _, htlc := range localHTLCs.outgoingHTLCs {
// We'll need to go on-chain for an outgoing HTLC if it was // We'll need to go on-chain for an outgoing HTLC if it was
// never resolved downstream, and it's "close" to timing out. // never resolved downstream, and it's "close" to timing out.
toChain := c.shouldGoOnChain( toChain := c.shouldGoOnChain(
@ -1120,7 +1149,7 @@ func (c *ChannelArbitrator) checkChainActions(height uint32,
haveChainActions = haveChainActions || toChain haveChainActions = haveChainActions || toChain
} }
for _, htlc := range c.activeHTLCs.incomingHTLCs { for _, htlc := range localHTLCs.incomingHTLCs {
// We'll need to go on-chain to pull an incoming HTLC iff we // We'll need to go on-chain to pull an incoming HTLC iff we
// know the pre-image and it's close to timing out. We need to // know the pre-image and it's close to timing out. We need to
// ensure that we claim the funds that our rightfully ours // ensure that we claim the funds that our rightfully ours
@ -1166,7 +1195,7 @@ func (c *ChannelArbitrator) checkChainActions(height uint32,
// active outgoing HTLC's to see if we either need to: sweep them after // active outgoing HTLC's to see if we either need to: sweep them after
// a timeout (then cancel backwards), cancel them backwards // a timeout (then cancel backwards), cancel them backwards
// immediately, or watch them as they're still active contracts. // immediately, or watch them as they're still active contracts.
for _, htlc := range c.activeHTLCs.outgoingHTLCs { for _, htlc := range localHTLCs.outgoingHTLCs {
switch { switch {
// If the HTLC is dust, then we can cancel it backwards // If the HTLC is dust, then we can cancel it backwards
// immediately as there's no matching contract to arbitrate // immediately as there's no matching contract to arbitrate
@ -1221,7 +1250,7 @@ func (c *ChannelArbitrator) checkChainActions(height uint32,
// observe the output on-chain if we don't In this last, case we'll // observe the output on-chain if we don't In this last, case we'll
// either learn of it eventually from the outgoing HTLC, or the sender // either learn of it eventually from the outgoing HTLC, or the sender
// will timeout the HTLC. // will timeout the HTLC.
for _, htlc := range c.activeHTLCs.incomingHTLCs { for _, htlc := range localHTLCs.incomingHTLCs {
log.Tracef("ChannelArbitrator(%v): watching chain to decide "+ log.Tracef("ChannelArbitrator(%v): watching chain to decide "+
"action for incoming htlc=%x", c.cfg.ChanPoint, "action for incoming htlc=%x", c.cfg.ChanPoint,
htlc.RHash[:]) htlc.RHash[:])
@ -1671,7 +1700,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
// Now that a new block has arrived, we'll attempt to // Now that a new block has arrived, we'll attempt to
// advance our state forward. // advance our state forward.
nextState, _, err := c.advanceState( nextState, _, err := c.advanceState(
uint32(bestHeight), chainTrigger, uint32(bestHeight), chainTrigger, nil,
) )
if err != nil { if err != nil {
log.Errorf("unable to advance state: %v", err) log.Errorf("unable to advance state: %v", err)
@ -1703,16 +1732,19 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
// A new set of HTLC's has been added or removed from the // A new set of HTLC's has been added or removed from the
// commitment transaction. So we'll update our activeHTLCs map // commitment transaction. So we'll update our activeHTLCs map
// accordingly. // accordingly.
case newStateHTLCs := <-c.htlcUpdates: case htlcUpdate := <-c.htlcUpdates:
// We'll wipe out our old set of HTLC's and instead // We'll wipe out our old set of HTLC's for each
// monitor only the HTLC's that are still active on the // htlcSetKey type included in this update in order to
// current commitment state. // only monitor the HTLCs that are still active on this
c.activeHTLCs = newHtlcSet(newStateHTLCs) // target commitment.
c.activeHTLCs[htlcUpdate.HtlcKey] = newHtlcSet(
htlcUpdate.Htlcs,
)
log.Tracef("ChannelArbitrator(%v): fresh set of "+ log.Tracef("ChannelArbitrator(%v): fresh set of htlcs=%v",
"htlcs=%v", c.cfg.ChanPoint, c.cfg.ChanPoint,
newLogClosure(func() string { newLogClosure(func() string {
return spew.Sdump(c.activeHTLCs) return spew.Sdump(htlcUpdate)
}), }),
) )
@ -1734,7 +1766,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
// We'll now advance our state machine until it reaches // We'll now advance our state machine until it reaches
// a terminal state, and the channel is marked resolved. // a terminal state, and the channel is marked resolved.
_, _, err = c.advanceState( _, _, err = c.advanceState(
closeInfo.CloseHeight, coopCloseTrigger, closeInfo.CloseHeight, coopCloseTrigger, nil,
) )
if err != nil { if err != nil {
log.Errorf("unable to advance state: %v", err) log.Errorf("unable to advance state: %v", err)
@ -1794,7 +1826,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
// a terminal state. // a terminal state.
_, _, err = c.advanceState( _, _, err = c.advanceState(
uint32(closeInfo.SpendingHeight), uint32(closeInfo.SpendingHeight),
localCloseTrigger, localCloseTrigger, &closeInfo.CommitSet,
) )
if err != nil { if err != nil {
log.Errorf("unable to advance state: %v", err) log.Errorf("unable to advance state: %v", err)
@ -1804,7 +1836,6 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
// We'll examine our state to determine if we need to act at // We'll examine our state to determine if we need to act at
// all. // all.
case uniClosure := <-c.cfg.ChainEvents.RemoteUnilateralClosure: case uniClosure := <-c.cfg.ChainEvents.RemoteUnilateralClosure:
log.Infof("ChannelArbitrator(%v): remote party has "+ log.Infof("ChannelArbitrator(%v): remote party has "+
"closed channel out on-chain", c.cfg.ChanPoint) "closed channel out on-chain", c.cfg.ChanPoint)
@ -1817,12 +1848,6 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
HtlcResolutions: *uniClosure.HtlcResolutions, HtlcResolutions: *uniClosure.HtlcResolutions,
} }
// As we're now acting upon an event triggered by the
// broadcast of the remote commitment transaction,
// we'll swap out our active HTLC set with the set
// present on their commitment.
c.activeHTLCs = newHtlcSet(uniClosure.RemoteCommit.Htlcs)
// When processing a unilateral close event, we'll // When processing a unilateral close event, we'll
// transition to the ContractClosed state. We'll log // transition to the ContractClosed state. We'll log
// out the set of resolutions such that they are // out the set of resolutions such that they are
@ -1856,7 +1881,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
// a terminal state. // a terminal state.
_, _, err = c.advanceState( _, _, err = c.advanceState(
uint32(uniClosure.SpendingHeight), uint32(uniClosure.SpendingHeight),
remoteCloseTrigger, remoteCloseTrigger, &uniClosure.CommitSet,
) )
if err != nil { if err != nil {
log.Errorf("unable to advance state: %v", err) log.Errorf("unable to advance state: %v", err)
@ -1870,7 +1895,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
"fully resolved!", c.cfg.ChanPoint) "fully resolved!", c.cfg.ChanPoint)
nextState, _, err := c.advanceState( nextState, _, err := c.advanceState(
uint32(bestHeight), chainTrigger, uint32(bestHeight), chainTrigger, nil,
) )
if err != nil { if err != nil {
log.Errorf("unable to advance state: %v", err) log.Errorf("unable to advance state: %v", err)
@ -1904,7 +1929,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
} }
nextState, closeTx, err := c.advanceState( nextState, closeTx, err := c.advanceState(
uint32(bestHeight), userTrigger, uint32(bestHeight), userTrigger, nil,
) )
if err != nil { if err != nil {
log.Errorf("unable to advance state: %v", err) log.Errorf("unable to advance state: %v", err)

@ -152,7 +152,7 @@ func createTestChannelArbitrator(log ArbitratorLog) (*ChannelArbitrator,
chanPoint := wire.OutPoint{} chanPoint := wire.OutPoint{}
shortChanID := lnwire.ShortChannelID{} shortChanID := lnwire.ShortChannelID{}
chanEvents := &ChainEventSubscription{ chanEvents := &ChainEventSubscription{
RemoteUnilateralClosure: make(chan *lnwallet.UnilateralCloseSummary, 1), RemoteUnilateralClosure: make(chan *RemoteUnilateralCloseInfo, 1),
LocalUnilateralClosure: make(chan *LocalUnilateralCloseInfo, 1), LocalUnilateralClosure: make(chan *LocalUnilateralCloseInfo, 1),
CooperativeClosure: make(chan *CooperativeCloseInfo, 1), CooperativeClosure: make(chan *CooperativeCloseInfo, 1),
ContractBreach: make(chan *lnwallet.BreachRetribution, 1), ContractBreach: make(chan *lnwallet.BreachRetribution, 1),
@ -328,7 +328,13 @@ func TestChannelArbitratorRemoteForceClose(t *testing.T) {
SpendDetail: commitSpend, SpendDetail: commitSpend,
HtlcResolutions: &lnwallet.HtlcResolutions{}, HtlcResolutions: &lnwallet.HtlcResolutions{},
} }
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- &RemoteUnilateralCloseInfo{
UnilateralCloseSummary: uniClose,
CommitSet: CommitSet{
ConfCommitKey: &RemoteHtlcSet,
HtlcSets: make(map[HtlcSetKey][]channeldb.HTLC),
},
}
// It should transition StateDefault -> StateContractClosed -> // It should transition StateDefault -> StateContractClosed ->
// StateFullyResolved. // StateFullyResolved.
@ -430,12 +436,12 @@ func TestChannelArbitratorLocalForceClose(t *testing.T) {
// Now notify about the local force close getting confirmed. // Now notify about the local force close getting confirmed.
chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{ chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{
&chainntnfs.SpendDetail{}, SpendDetail: &chainntnfs.SpendDetail{},
&lnwallet.LocalForceCloseSummary{ LocalForceCloseSummary: &lnwallet.LocalForceCloseSummary{
CloseTx: &wire.MsgTx{}, CloseTx: &wire.MsgTx{},
HtlcResolutions: &lnwallet.HtlcResolutions{}, HtlcResolutions: &lnwallet.HtlcResolutions{},
}, },
&channeldb.ChannelCloseSummary{}, ChannelCloseSummary: &channeldb.ChannelCloseSummary{},
} }
// It should transition StateContractClosed -> StateFullyResolved. // It should transition StateContractClosed -> StateFullyResolved.
@ -483,7 +489,7 @@ func TestChannelArbitratorLocalForceClosePendingHtlc(t *testing.T) {
defer chanArb.Stop() defer chanArb.Stop()
// Create htlcUpdates channel. // Create htlcUpdates channel.
htlcUpdates := make(chan []channeldb.HTLC) htlcUpdates := make(chan *ContractUpdate)
signals := &ContractSignals{ signals := &ContractSignals{
HtlcUpdates: htlcUpdates, HtlcUpdates: htlcUpdates,
@ -492,14 +498,16 @@ func TestChannelArbitratorLocalForceClosePendingHtlc(t *testing.T) {
chanArb.UpdateContractSignals(signals) chanArb.UpdateContractSignals(signals)
// Add HTLC to channel arbitrator. // Add HTLC to channel arbitrator.
htlcIndex := uint64(99)
htlc := channeldb.HTLC{ htlc := channeldb.HTLC{
Incoming: false, Incoming: false,
Amt: 10000, Amt: 10000,
HtlcIndex: 0, HtlcIndex: htlcIndex,
} }
htlcUpdates <- []channeldb.HTLC{ htlcUpdates <- &ContractUpdate{
htlc, HtlcKey: LocalHtlcSet,
Htlcs: []channeldb.HTLC{htlc},
} }
errChan := make(chan error, 1) errChan := make(chan error, 1)
@ -572,8 +580,8 @@ func TestChannelArbitratorLocalForceClosePendingHtlc(t *testing.T) {
} }
chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{ chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{
&chainntnfs.SpendDetail{}, SpendDetail: &chainntnfs.SpendDetail{},
&lnwallet.LocalForceCloseSummary{ LocalForceCloseSummary: &lnwallet.LocalForceCloseSummary{
CloseTx: closeTx, CloseTx: closeTx,
HtlcResolutions: &lnwallet.HtlcResolutions{ HtlcResolutions: &lnwallet.HtlcResolutions{
OutgoingHTLCs: []lnwallet.OutgoingHtlcResolution{ OutgoingHTLCs: []lnwallet.OutgoingHtlcResolution{
@ -581,7 +589,13 @@ func TestChannelArbitratorLocalForceClosePendingHtlc(t *testing.T) {
}, },
}, },
}, },
&channeldb.ChannelCloseSummary{}, ChannelCloseSummary: &channeldb.ChannelCloseSummary{},
CommitSet: CommitSet{
ConfCommitKey: &LocalHtlcSet,
HtlcSets: map[HtlcSetKey][]channeldb.HTLC{
LocalHtlcSet: {htlc},
},
},
} }
assertStateTransitions( assertStateTransitions(
@ -627,7 +641,6 @@ func TestChannelArbitratorLocalForceClosePendingHtlc(t *testing.T) {
// At this point channel should be marked as resolved. // At this point channel should be marked as resolved.
assertStateTransitions(t, arbLog.newStates, StateFullyResolved) assertStateTransitions(t, arbLog.newStates, StateFullyResolved)
select { select {
case <-resolved: case <-resolved:
case <-time.After(5 * time.Second): case <-time.After(5 * time.Second):
@ -726,7 +739,9 @@ func TestChannelArbitratorLocalForceCloseRemoteConfirmed(t *testing.T) {
SpendDetail: commitSpend, SpendDetail: commitSpend,
HtlcResolutions: &lnwallet.HtlcResolutions{}, HtlcResolutions: &lnwallet.HtlcResolutions{},
} }
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- &RemoteUnilateralCloseInfo{
UnilateralCloseSummary: uniClose,
}
// It should transition StateContractClosed -> StateFullyResolved. // It should transition StateContractClosed -> StateFullyResolved.
assertStateTransitions(t, log.newStates, StateContractClosed, assertStateTransitions(t, log.newStates, StateContractClosed,
@ -832,7 +847,9 @@ func TestChannelArbitratorLocalForceDoubleSpend(t *testing.T) {
SpendDetail: commitSpend, SpendDetail: commitSpend,
HtlcResolutions: &lnwallet.HtlcResolutions{}, HtlcResolutions: &lnwallet.HtlcResolutions{},
} }
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- &RemoteUnilateralCloseInfo{
UnilateralCloseSummary: uniClose,
}
// It should transition StateContractClosed -> StateFullyResolved. // It should transition StateContractClosed -> StateFullyResolved.
assertStateTransitions(t, log.newStates, StateContractClosed, assertStateTransitions(t, log.newStates, StateContractClosed,
@ -878,7 +895,9 @@ func TestChannelArbitratorPersistence(t *testing.T) {
SpendDetail: commitSpend, SpendDetail: commitSpend,
HtlcResolutions: &lnwallet.HtlcResolutions{}, HtlcResolutions: &lnwallet.HtlcResolutions{},
} }
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- &RemoteUnilateralCloseInfo{
UnilateralCloseSummary: uniClose,
}
// Since writing the resolutions fail, the arbitrator should not // Since writing the resolutions fail, the arbitrator should not
// advance to the next state. // advance to the next state.
@ -909,7 +928,9 @@ func TestChannelArbitratorPersistence(t *testing.T) {
} }
// Send a new remote force close event. // Send a new remote force close event.
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- &RemoteUnilateralCloseInfo{
UnilateralCloseSummary: uniClose,
}
// Since closing the channel failed, the arbitrator should stay in the // Since closing the channel failed, the arbitrator should stay in the
// default state. // default state.
@ -934,7 +955,9 @@ func TestChannelArbitratorPersistence(t *testing.T) {
// Now make fetching the resolutions fail. // Now make fetching the resolutions fail.
log.failFetch = fmt.Errorf("intentional fetch failure") log.failFetch = fmt.Errorf("intentional fetch failure")
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- &RemoteUnilateralCloseInfo{
UnilateralCloseSummary: uniClose,
}
// Since logging the resolutions and closing the channel now succeeds, // Since logging the resolutions and closing the channel now succeeds,
// it should advance to StateContractClosed. // it should advance to StateContractClosed.
@ -1015,7 +1038,9 @@ func TestChannelArbitratorCommitFailure(t *testing.T) {
SpendDetail: commitSpend, SpendDetail: commitSpend,
HtlcResolutions: &lnwallet.HtlcResolutions{}, HtlcResolutions: &lnwallet.HtlcResolutions{},
} }
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- &RemoteUnilateralCloseInfo{
UnilateralCloseSummary: uniClose,
}
}, },
expectedStates: []ArbitratorState{StateContractClosed, StateFullyResolved}, expectedStates: []ArbitratorState{StateContractClosed, StateFullyResolved},
}, },
@ -1023,12 +1048,12 @@ func TestChannelArbitratorCommitFailure(t *testing.T) {
closeType: channeldb.LocalForceClose, closeType: channeldb.LocalForceClose,
sendEvent: func(chanArb *ChannelArbitrator) { sendEvent: func(chanArb *ChannelArbitrator) {
chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{ chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{
&chainntnfs.SpendDetail{}, SpendDetail: &chainntnfs.SpendDetail{},
&lnwallet.LocalForceCloseSummary{ LocalForceCloseSummary: &lnwallet.LocalForceCloseSummary{
CloseTx: &wire.MsgTx{}, CloseTx: &wire.MsgTx{},
HtlcResolutions: &lnwallet.HtlcResolutions{}, HtlcResolutions: &lnwallet.HtlcResolutions{},
}, },
&channeldb.ChannelCloseSummary{}, ChannelCloseSummary: &channeldb.ChannelCloseSummary{},
} }
}, },
expectedStates: []ArbitratorState{StateContractClosed, StateFullyResolved}, expectedStates: []ArbitratorState{StateContractClosed, StateFullyResolved},
@ -1192,8 +1217,8 @@ func TestChannelArbitratorAlreadyForceClosed(t *testing.T) {
case <-chanArb.quit: case <-chanArb.quit:
} }
// Finally, we should ensure that we are not able to do so by seeing the // Finally, we should ensure that we are not able to do so by seeing
// expected errAlreadyForceClosed error. // the expected errAlreadyForceClosed error.
select { select {
case err = <-errChan: case err = <-errChan:
if err != errAlreadyForceClosed { if err != errAlreadyForceClosed {

@ -320,7 +320,7 @@ type channelLink struct {
// htlcUpdates is a channel that we'll use to update outside // htlcUpdates is a channel that we'll use to update outside
// sub-systems with the latest set of active HTLC's on our channel. // sub-systems with the latest set of active HTLC's on our channel.
htlcUpdates chan []channeldb.HTLC htlcUpdates chan *contractcourt.ContractUpdate
// logCommitTimer is a timer which is sent upon if we go an interval // logCommitTimer is a timer which is sent upon if we go an interval
// without receiving/sending a commitment update. It's role is to // without receiving/sending a commitment update. It's role is to
@ -372,7 +372,7 @@ func NewChannelLink(cfg ChannelLinkConfig,
// TODO(roasbeef): just do reserve here? // TODO(roasbeef): just do reserve here?
logCommitTimer: time.NewTimer(300 * time.Millisecond), logCommitTimer: time.NewTimer(300 * time.Millisecond),
overflowQueue: newPacketQueue(input.MaxHTLCNumber / 2), overflowQueue: newPacketQueue(input.MaxHTLCNumber / 2),
htlcUpdates: make(chan []channeldb.HTLC), htlcUpdates: make(chan *contractcourt.ContractUpdate),
hodlMap: make(map[lntypes.Hash][]hodlHtlc), hodlMap: make(map[lntypes.Hash][]hodlHtlc),
hodlQueue: queue.NewConcurrentQueue(10), hodlQueue: queue.NewConcurrentQueue(10),
quit: make(chan struct{}), quit: make(chan struct{}),
@ -1721,7 +1721,10 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
// of HTLC's on our commitment, so we'll send them over our // of HTLC's on our commitment, so we'll send them over our
// HTLC update channel so any callers can be notified. // HTLC update channel so any callers can be notified.
select { select {
case l.htlcUpdates <- currentHtlcs: case l.htlcUpdates <- &contractcourt.ContractUpdate{
HtlcKey: contractcourt.LocalHtlcSet,
Htlcs: currentHtlcs,
}:
case <-l.quit: case <-l.quit:
return return
} }
@ -1761,7 +1764,9 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
// We've received a revocation from the remote chain, if valid, // We've received a revocation from the remote chain, if valid,
// this moves the remote chain forward, and expands our // this moves the remote chain forward, and expands our
// revocation window. // revocation window.
fwdPkg, adds, settleFails, err := l.channel.ReceiveRevocation(msg) fwdPkg, adds, settleFails, remoteHTLCs, err := l.channel.ReceiveRevocation(
msg,
)
if err != nil { if err != nil {
// TODO(halseth): force close? // TODO(halseth): force close?
l.fail(LinkFailureError{code: ErrInvalidRevocation}, l.fail(LinkFailureError{code: ErrInvalidRevocation},
@ -1769,6 +1774,18 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
return return
} }
// The remote party now has a new primary commitment, so we'll
// update the contract court to be aware of this new set (the
// prior old remote pending).
select {
case l.htlcUpdates <- &contractcourt.ContractUpdate{
HtlcKey: contractcourt.RemoteHtlcSet,
Htlcs: remoteHTLCs,
}:
case <-l.quit:
return
}
l.processRemoteSettleFails(fwdPkg, settleFails) l.processRemoteSettleFails(fwdPkg, settleFails)
needUpdate := l.processRemoteAdds(fwdPkg, adds) needUpdate := l.processRemoteAdds(fwdPkg, adds)
@ -1894,7 +1911,7 @@ func (l *channelLink) updateCommitTx() error {
return nil return nil
} }
theirCommitSig, htlcSigs, err := l.channel.SignNextCommitment() theirCommitSig, htlcSigs, pendingHTLCs, err := l.channel.SignNextCommitment()
if err == lnwallet.ErrNoWindow { if err == lnwallet.ErrNoWindow {
l.tracef("revocation window exhausted, unable to send: %v, "+ l.tracef("revocation window exhausted, unable to send: %v, "+
"dangling_opens=%v, dangling_closes%v", "dangling_opens=%v, dangling_closes%v",
@ -1910,6 +1927,18 @@ func (l *channelLink) updateCommitTx() error {
return err return err
} }
// The remote party now has a new pending commitment, so we'll update
// the contract court to be aware of this new set (the prior old remote
// pending).
select {
case l.htlcUpdates <- &contractcourt.ContractUpdate{
HtlcKey: contractcourt.RemotePendingHtlcSet,
Htlcs: pendingHTLCs,
}:
case <-l.quit:
return nil
}
if err := l.ackDownStreamPackets(); err != nil { if err := l.ackDownStreamPackets(); err != nil {
return err return err
} }

@ -1761,7 +1761,7 @@ func handleStateUpdate(link *channelLink,
} }
link.HandleChannelUpdate(remoteRev) link.HandleChannelUpdate(remoteRev)
remoteSig, remoteHtlcSigs, err := remoteChannel.SignNextCommitment() remoteSig, remoteHtlcSigs, _, err := remoteChannel.SignNextCommitment()
if err != nil { if err != nil {
return err return err
} }
@ -1782,7 +1782,7 @@ func handleStateUpdate(link *channelLink,
if !ok { if !ok {
return fmt.Errorf("expected RevokeAndAck got %T", msg) return fmt.Errorf("expected RevokeAndAck got %T", msg)
} }
_, _, _, err = remoteChannel.ReceiveRevocation(revoke) _, _, _, _, err = remoteChannel.ReceiveRevocation(revoke)
if err != nil { if err != nil {
return fmt.Errorf("unable to receive "+ return fmt.Errorf("unable to receive "+
"revocation: %v", err) "revocation: %v", err)
@ -1812,7 +1812,7 @@ func updateState(batchTick chan time.Time, link *channelLink,
// The remote is triggering the state update, emulate this by // The remote is triggering the state update, emulate this by
// signing and sending CommitSig to the link. // signing and sending CommitSig to the link.
remoteSig, remoteHtlcSigs, err := remoteChannel.SignNextCommitment() remoteSig, remoteHtlcSigs, _, err := remoteChannel.SignNextCommitment()
if err != nil { if err != nil {
return err return err
} }
@ -1836,7 +1836,7 @@ func updateState(batchTick chan time.Time, link *channelLink,
return fmt.Errorf("expected RevokeAndAck got %T", return fmt.Errorf("expected RevokeAndAck got %T",
msg) msg)
} }
_, _, _, err = remoteChannel.ReceiveRevocation(revoke) _, _, _, _, err = remoteChannel.ReceiveRevocation(revoke)
if err != nil { if err != nil {
return fmt.Errorf("unable to receive "+ return fmt.Errorf("unable to receive "+
"revocation: %v", err) "revocation: %v", err)
@ -4397,7 +4397,7 @@ func sendCommitSigBobToAlice(t *testing.T, aliceLink ChannelLink,
t.Helper() t.Helper()
sig, htlcSigs, err := bobChannel.SignNextCommitment() sig, htlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("error signing commitment: %v", err) t.Fatalf("error signing commitment: %v", err)
} }
@ -4435,7 +4435,7 @@ func receiveRevAndAckAliceToBob(t *testing.T, aliceMsgs chan lnwire.Message,
t.Fatalf("expected RevokeAndAck, got %T", msg) t.Fatalf("expected RevokeAndAck, got %T", msg)
} }
_, _, _, err := bobChannel.ReceiveRevocation(rev) _, _, _, _, err := bobChannel.ReceiveRevocation(rev)
if err != nil { if err != nil {
t.Fatalf("bob failed receiving revocation: %v", err) t.Fatalf("bob failed receiving revocation: %v", err)
} }
@ -5326,7 +5326,7 @@ func TestChannelLinkFail(t *testing.T) {
// Sign a commitment that will include // Sign a commitment that will include
// signature for the HTLC just sent. // signature for the HTLC just sent.
sig, htlcSigs, err := sig, htlcSigs, _, err :=
remoteChannel.SignNextCommitment() remoteChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("error signing commitment: %v", t.Fatalf("error signing commitment: %v",
@ -5358,7 +5358,7 @@ func TestChannelLinkFail(t *testing.T) {
// Sign a commitment that will include // Sign a commitment that will include
// signature for the HTLC just sent. // signature for the HTLC just sent.
sig, htlcSigs, err := sig, htlcSigs, _, err :=
remoteChannel.SignNextCommitment() remoteChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("error signing commitment: %v", t.Fatalf("error signing commitment: %v",

@ -3035,8 +3035,10 @@ func (lc *LightningChannel) createCommitDiff(
// The first return parameter is the signature for the commitment transaction // The first return parameter is the signature for the commitment transaction
// itself, while the second parameter is a slice of all HTLC signatures (if // itself, while the second parameter is a slice of all HTLC signatures (if
// any). The HTLC signatures are sorted according to the BIP 69 order of the // any). The HTLC signatures are sorted according to the BIP 69 order of the
// HTLC's on the commitment transaction. // HTLC's on the commitment transaction. Finally, the new set of pending HTLCs
func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, error) { // for the remote party's commitment are also returned.
func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, []channeldb.HTLC, error) {
lc.Lock() lc.Lock()
defer lc.Unlock() defer lc.Unlock()
@ -3052,7 +3054,7 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, erro
commitPoint := lc.channelState.RemoteNextRevocation commitPoint := lc.channelState.RemoteNextRevocation
if lc.remoteCommitChain.hasUnackedCommitment() || commitPoint == nil { if lc.remoteCommitChain.hasUnackedCommitment() || commitPoint == nil {
return sig, htlcSigs, ErrNoWindow return sig, htlcSigs, nil, ErrNoWindow
} }
// Determine the last update on the remote log that has been locked in. // Determine the last update on the remote log that has been locked in.
@ -3067,7 +3069,7 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, erro
remoteACKedIndex, lc.localUpdateLog.logIndex, true, nil, remoteACKedIndex, lc.localUpdateLog.logIndex, true, nil,
) )
if err != nil { if err != nil {
return sig, htlcSigs, err return sig, htlcSigs, nil, err
} }
// Grab the next commitment point for the remote party. This will be // Grab the next commitment point for the remote party. This will be
@ -3089,7 +3091,7 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, erro
remoteACKedIndex, remoteHtlcIndex, keyRing, remoteACKedIndex, remoteHtlcIndex, keyRing,
) )
if err != nil { if err != nil {
return sig, htlcSigs, err return sig, htlcSigs, nil, err
} }
walletLog.Tracef("ChannelPoint(%v): extending remote chain to height %v, "+ walletLog.Tracef("ChannelPoint(%v): extending remote chain to height %v, "+
@ -3114,7 +3116,7 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, erro
lc.localChanCfg, lc.remoteChanCfg, newCommitView, lc.localChanCfg, lc.remoteChanCfg, newCommitView,
) )
if err != nil { if err != nil {
return sig, htlcSigs, err return sig, htlcSigs, nil, err
} }
lc.sigPool.SubmitSignBatch(sigBatch) lc.sigPool.SubmitSignBatch(sigBatch)
@ -3125,12 +3127,12 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, erro
rawSig, err := lc.Signer.SignOutputRaw(newCommitView.txn, lc.signDesc) rawSig, err := lc.Signer.SignOutputRaw(newCommitView.txn, lc.signDesc)
if err != nil { if err != nil {
close(cancelChan) close(cancelChan)
return sig, htlcSigs, err return sig, htlcSigs, nil, err
} }
sig, err = lnwire.NewSigFromRawSignature(rawSig) sig, err = lnwire.NewSigFromRawSignature(rawSig)
if err != nil { if err != nil {
close(cancelChan) close(cancelChan)
return sig, htlcSigs, err return sig, htlcSigs, nil, err
} }
// We'll need to send over the signatures to the remote party in the // We'll need to send over the signatures to the remote party in the
@ -3150,7 +3152,7 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, erro
// jobs. // jobs.
if jobResp.Err != nil { if jobResp.Err != nil {
close(cancelChan) close(cancelChan)
return sig, htlcSigs, err return sig, htlcSigs, nil, err
} }
htlcSigs = append(htlcSigs, jobResp.Sig) htlcSigs = append(htlcSigs, jobResp.Sig)
@ -3161,11 +3163,11 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, erro
// can retransmit it if necessary. // can retransmit it if necessary.
commitDiff, err := lc.createCommitDiff(newCommitView, sig, htlcSigs) commitDiff, err := lc.createCommitDiff(newCommitView, sig, htlcSigs)
if err != nil { if err != nil {
return sig, htlcSigs, err return sig, htlcSigs, nil, err
} }
err = lc.channelState.AppendRemoteCommitChain(commitDiff) err = lc.channelState.AppendRemoteCommitChain(commitDiff)
if err != nil { if err != nil {
return sig, htlcSigs, err return sig, htlcSigs, nil, err
} }
// TODO(roasbeef): check that one eclair bug // TODO(roasbeef): check that one eclair bug
@ -3176,7 +3178,7 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, erro
// latest commitment update. // latest commitment update.
lc.remoteCommitChain.addCommitment(newCommitView) lc.remoteCommitChain.addCommitment(newCommitView)
return sig, htlcSigs, nil return sig, htlcSigs, commitDiff.Commitment.Htlcs, nil
} }
// ProcessChanSyncMsg processes a ChannelReestablish message sent by the remote // ProcessChanSyncMsg processes a ChannelReestablish message sent by the remote
@ -3352,7 +3354,7 @@ func (lc *LightningChannel) ProcessChanSyncMsg(
// revocation, but also initiate a state transition to re-sync // revocation, but also initiate a state transition to re-sync
// them. // them.
if !lc.FullySynced() { if !lc.FullySynced() {
commitSig, htlcSigs, err := lc.SignNextCommitment() commitSig, htlcSigs, _, err := lc.SignNextCommitment()
switch { switch {
// If we signed this state, then we'll accumulate // If we signed this state, then we'll accumulate
@ -4277,8 +4279,11 @@ func (lc *LightningChannel) RevokeCurrentCommitment() (*lnwire.RevokeAndAck, []c
// revocation. // revocation.
// 3. The PaymentDescriptor of any Settle/Fail HTLCs that were locked in by // 3. The PaymentDescriptor of any Settle/Fail HTLCs that were locked in by
// this revocation. // this revocation.
// 4. The set of HTLCs present on the current valid commitment transaction
// for the remote party.
func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) (
*channeldb.FwdPkg, []*PaymentDescriptor, []*PaymentDescriptor, error) { *channeldb.FwdPkg, []*PaymentDescriptor, []*PaymentDescriptor,
[]channeldb.HTLC, error) {
lc.Lock() lc.Lock()
defer lc.Unlock() defer lc.Unlock()
@ -4287,10 +4292,10 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) (
store := lc.channelState.RevocationStore store := lc.channelState.RevocationStore
revocation, err := chainhash.NewHash(revMsg.Revocation[:]) revocation, err := chainhash.NewHash(revMsg.Revocation[:])
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, nil, err
} }
if err := store.AddNextEntry(revocation); err != nil { if err := store.AddNextEntry(revocation); err != nil {
return nil, nil, nil, err return nil, nil, nil, nil, err
} }
// Verify that if we use the commitment point computed based off of the // Verify that if we use the commitment point computed based off of the
@ -4299,7 +4304,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) (
currentCommitPoint := lc.channelState.RemoteCurrentRevocation currentCommitPoint := lc.channelState.RemoteCurrentRevocation
derivedCommitPoint := input.ComputeCommitmentPoint(revMsg.Revocation[:]) derivedCommitPoint := input.ComputeCommitmentPoint(revMsg.Revocation[:])
if !derivedCommitPoint.IsEqual(currentCommitPoint) { if !derivedCommitPoint.IsEqual(currentCommitPoint) {
return nil, nil, nil, fmt.Errorf("revocation key mismatch") return nil, nil, nil, nil, fmt.Errorf("revocation key mismatch")
} }
// Now that we've verified that the prior commitment has been properly // Now that we've verified that the prior commitment has been properly
@ -4470,7 +4475,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) (
// commitment chain. // commitment chain.
err = lc.channelState.AdvanceCommitChainTail(fwdPkg) err = lc.channelState.AdvanceCommitChainTail(fwdPkg)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, nil, err
} }
// Since they revoked the current lowest height in their commitment // Since they revoked the current lowest height in their commitment
@ -4485,7 +4490,9 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) (
remoteChainTail, remoteChainTail,
) )
return fwdPkg, addsToForward, settleFailsToForward, nil remoteHTLCs := lc.channelState.RemoteCommitment.Htlcs
return fwdPkg, addsToForward, settleFailsToForward, remoteHTLCs, nil
} }
// LoadFwdPkgs loads any pending log updates from disk and returns the payment // LoadFwdPkgs loads any pending log updates from disk and returns the payment

@ -99,7 +99,7 @@ func TestSimpleAddSettleWorkflow(t *testing.T) {
// we expect the messages to be ordered, Bob will receive the HTLC we // we expect the messages to be ordered, Bob will receive the HTLC we
// just sent before he receives this signature, so the signature will // just sent before he receives this signature, so the signature will
// cover the HTLC. // cover the HTLC.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err) t.Fatalf("alice unable to sign commitment: %v", err)
} }
@ -123,7 +123,7 @@ func TestSimpleAddSettleWorkflow(t *testing.T) {
// This signature will cover the HTLC, since Bob will first send the // This signature will cover the HTLC, since Bob will first send the
// revocation just created. The revocation also acks every received // revocation just created. The revocation also acks every received
// HTLC up to the point where Alice sent here signature. // HTLC up to the point where Alice sent here signature.
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("bob unable to sign alice's commitment: %v", err) t.Fatalf("bob unable to sign alice's commitment: %v", err)
} }
@ -131,7 +131,7 @@ func TestSimpleAddSettleWorkflow(t *testing.T) {
// Alice then processes this revocation, sending her own revocation for // Alice then processes this revocation, sending her own revocation for
// her prior commitment transaction. Alice shouldn't have any HTLCs to // her prior commitment transaction. Alice shouldn't have any HTLCs to
// forward since she's sending an outgoing HTLC. // forward since she's sending an outgoing HTLC.
fwdPkg, _, _, err := aliceChannel.ReceiveRevocation(bobRevocation) fwdPkg, _, _, _, err := aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatalf("alice unable to process bob's revocation: %v", err) t.Fatalf("alice unable to process bob's revocation: %v", err)
} }
@ -162,7 +162,7 @@ func TestSimpleAddSettleWorkflow(t *testing.T) {
// is fully locked in within both commitment transactions. Bob should // is fully locked in within both commitment transactions. Bob should
// also be able to forward an HTLC now that the HTLC has been locked // also be able to forward an HTLC now that the HTLC has been locked
// into both commitment transactions. // into both commitment transactions.
fwdPkg, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) fwdPkg, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil { if err != nil {
t.Fatalf("bob unable to process alice's revocation: %v", err) t.Fatalf("bob unable to process alice's revocation: %v", err)
} }
@ -239,7 +239,7 @@ func TestSimpleAddSettleWorkflow(t *testing.T) {
t.Fatalf("alice unable to accept settle of outbound htlc: %v", err) t.Fatalf("alice unable to accept settle of outbound htlc: %v", err)
} }
bobSig2, bobHtlcSigs2, err := bobChannel.SignNextCommitment() bobSig2, bobHtlcSigs2, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("bob unable to sign settle commitment: %v", err) t.Fatalf("bob unable to sign settle commitment: %v", err)
} }
@ -252,12 +252,12 @@ func TestSimpleAddSettleWorkflow(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("alice unable to generate revocation: %v", err) t.Fatalf("alice unable to generate revocation: %v", err)
} }
aliceSig2, aliceHtlcSigs2, err := aliceChannel.SignNextCommitment() aliceSig2, aliceHtlcSigs2, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("alice unable to sign new commitment: %v", err) t.Fatalf("alice unable to sign new commitment: %v", err)
} }
fwdPkg, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation2) fwdPkg, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation2)
if err != nil { if err != nil {
t.Fatalf("bob unable to process alice's revocation: %v", err) t.Fatalf("bob unable to process alice's revocation: %v", err)
} }
@ -279,7 +279,7 @@ func TestSimpleAddSettleWorkflow(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("bob unable to revoke commitment: %v", err) t.Fatalf("bob unable to revoke commitment: %v", err)
} }
fwdPkg, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation2) fwdPkg, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation2)
if err != nil { if err != nil {
t.Fatalf("alice unable to process bob's revocation: %v", err) t.Fatalf("alice unable to process bob's revocation: %v", err)
} }
@ -1106,7 +1106,7 @@ func TestHTLCSigNumber(t *testing.T) {
aboveDust) aboveDust)
defer cleanUp() defer cleanUp()
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("Error signing next commitment: %v", err) t.Fatalf("Error signing next commitment: %v", err)
} }
@ -1130,7 +1130,7 @@ func TestHTLCSigNumber(t *testing.T) {
aliceChannel, bobChannel, cleanUp = createChanWithHTLC(aboveDust) aliceChannel, bobChannel, cleanUp = createChanWithHTLC(aboveDust)
defer cleanUp() defer cleanUp()
aliceSig, aliceHtlcSigs, err = aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err = aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("Error signing next commitment: %v", err) t.Fatalf("Error signing next commitment: %v", err)
} }
@ -1153,7 +1153,7 @@ func TestHTLCSigNumber(t *testing.T) {
aliceChannel, bobChannel, cleanUp = createChanWithHTLC(belowDust) aliceChannel, bobChannel, cleanUp = createChanWithHTLC(belowDust)
defer cleanUp() defer cleanUp()
aliceSig, aliceHtlcSigs, err = aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err = aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("Error signing next commitment: %v", err) t.Fatalf("Error signing next commitment: %v", err)
} }
@ -1176,7 +1176,7 @@ func TestHTLCSigNumber(t *testing.T) {
aliceChannel, bobChannel, cleanUp = createChanWithHTLC(aboveDust) aliceChannel, bobChannel, cleanUp = createChanWithHTLC(aboveDust)
defer cleanUp() defer cleanUp()
aliceSig, aliceHtlcSigs, err = aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err = aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("Error signing next commitment: %v", err) t.Fatalf("Error signing next commitment: %v", err)
} }
@ -1203,7 +1203,7 @@ func TestHTLCSigNumber(t *testing.T) {
// Alice should produce only one signature, since one HTLC is below // Alice should produce only one signature, since one HTLC is below
// dust. // dust.
aliceSig, aliceHtlcSigs, err = aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err = aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("Error signing next commitment: %v", err) t.Fatalf("Error signing next commitment: %v", err)
} }
@ -1989,7 +1989,7 @@ func TestUpdateFeeFail(t *testing.T) {
// Alice sends signature for commitment that does not cover any fee // Alice sends signature for commitment that does not cover any fee
// update. // update.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err) t.Fatalf("alice unable to sign commitment: %v", err)
} }
@ -2040,13 +2040,13 @@ func TestUpdateFeeConcurrentSig(t *testing.T) {
} }
// Alice signs a commitment, and sends this to bob. // Alice signs a commitment, and sends this to bob.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err) t.Fatalf("alice unable to sign commitment: %v", err)
} }
// At the same time, Bob signs a commitment. // At the same time, Bob signs a commitment.
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("bob unable to sign alice's commitment: %v", err) t.Fatalf("bob unable to sign alice's commitment: %v", err)
} }
@ -2127,7 +2127,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
// Alice signs a commitment, which will cover everything sent to Bob // Alice signs a commitment, which will cover everything sent to Bob
// (the HTLC and the fee update), and everything acked by Bob (nothing // (the HTLC and the fee update), and everything acked by Bob (nothing
// so far). // so far).
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err) t.Fatalf("alice unable to sign commitment: %v", err)
} }
@ -2157,7 +2157,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
// Bob commits to all updates he has received from Alice. This includes // Bob commits to all updates he has received from Alice. This includes
// the HTLC he received, and the fee update. // the HTLC he received, and the fee update.
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("bob unable to sign alice's commitment: %v", err) t.Fatalf("bob unable to sign alice's commitment: %v", err)
} }
@ -2165,7 +2165,8 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
// Alice receives the revocation of the old one, and can now assume // Alice receives the revocation of the old one, and can now assume
// that Bob's received everything up to the signature she sent, // that Bob's received everything up to the signature she sent,
// including the HTLC and fee update. // including the HTLC and fee update.
if _, _, _, err := aliceChannel.ReceiveRevocation(bobRevocation); err != nil { _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil {
t.Fatalf("alice unable to process bob's revocation: %v", err) t.Fatalf("alice unable to process bob's revocation: %v", err)
} }
@ -2192,7 +2193,8 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
} }
// Bob receives revocation from Alice. // Bob receives revocation from Alice.
if _, _, _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil { _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil {
t.Fatalf("bob unable to process alice's revocation: %v", err) t.Fatalf("bob unable to process alice's revocation: %v", err)
} }
@ -2239,7 +2241,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
// Bob commits to every change he has sent since last time (none). He // Bob commits to every change he has sent since last time (none). He
// does not commit to the received HTLC and fee update, since Alice // does not commit to the received HTLC and fee update, since Alice
// cannot know if he has received them. // cannot know if he has received them.
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err) t.Fatalf("alice unable to sign commitment: %v", err)
} }
@ -2260,14 +2262,15 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
} }
// Bob receives the revocation of the old commitment // Bob receives the revocation of the old commitment
if _, _, _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil { _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil {
t.Fatalf("alice unable to process bob's revocation: %v", err) t.Fatalf("alice unable to process bob's revocation: %v", err)
} }
// Alice will sign next commitment. Since she sent the revocation, she // Alice will sign next commitment. Since she sent the revocation, she
// also ack'ed everything received, but in this case this is nothing. // also ack'ed everything received, but in this case this is nothing.
// Since she sent the two updates, this signature will cover those two. // Since she sent the two updates, this signature will cover those two.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("bob unable to sign alice's commitment: %v", err) t.Fatalf("bob unable to sign alice's commitment: %v", err)
} }
@ -2297,14 +2300,15 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
// Bob will send a new signature, which will cover what he just acked: // Bob will send a new signature, which will cover what he just acked:
// the HTLC and fee update. // the HTLC and fee update.
bobSig, bobHtlcSigs, err = bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err = bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err) t.Fatalf("alice unable to sign commitment: %v", err)
} }
// Alice receives revocation from Bob, and can now be sure that Bob // Alice receives revocation from Bob, and can now be sure that Bob
// received the two updates, and they are considered locked in. // received the two updates, and they are considered locked in.
if _, _, _, err := aliceChannel.ReceiveRevocation(bobRevocation); err != nil { _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil {
t.Fatalf("bob unable to process alice's revocation: %v", err) t.Fatalf("bob unable to process alice's revocation: %v", err)
} }
@ -2331,7 +2335,8 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
} }
// Bob receives revocation from Alice. // Bob receives revocation from Alice.
if _, _, _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil { _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil {
t.Fatalf("bob unable to process alice's revocation: %v", err) t.Fatalf("bob unable to process alice's revocation: %v", err)
} }
} }
@ -2391,7 +2396,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
// Alice signs a commitment, which will cover everything sent to Bob // Alice signs a commitment, which will cover everything sent to Bob
// (the HTLC and the fee update), and everything acked by Bob (nothing // (the HTLC and the fee update), and everything acked by Bob (nothing
// so far). // so far).
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("alice unable to sign commitment: %v", err) t.Fatalf("alice unable to sign commitment: %v", err)
} }
@ -2437,7 +2442,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
// Bob commits to all updates he has received from Alice. This includes // Bob commits to all updates he has received from Alice. This includes
// the HTLC he received, and the fee update. // the HTLC he received, and the fee update.
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("bob unable to sign alice's commitment: %v", err) t.Fatalf("bob unable to sign alice's commitment: %v", err)
} }
@ -2445,7 +2450,8 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
// Alice receives the revocation of the old one, and can now assume that // Alice receives the revocation of the old one, and can now assume that
// Bob's received everything up to the signature she sent, including the // Bob's received everything up to the signature she sent, including the
// HTLC and fee update. // HTLC and fee update.
if _, _, _, err := aliceChannel.ReceiveRevocation(bobRevocation); err != nil { _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil {
t.Fatalf("alice unable to process bob's revocation: %v", err) t.Fatalf("alice unable to process bob's revocation: %v", err)
} }
@ -2471,7 +2477,8 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
} }
// Bob receives revocation from Alice. // Bob receives revocation from Alice.
if _, _, _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil { _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil {
t.Fatalf("bob unable to process alice's revocation: %v", err) t.Fatalf("bob unable to process alice's revocation: %v", err)
} }
} }
@ -2764,7 +2771,7 @@ func TestChanSyncOweCommitment(t *testing.T) {
// Now we'll begin the core of the test itself. Alice will extend a new // Now we'll begin the core of the test itself. Alice will extend a new
// commitment to Bob, but the connection drops before Bob can process // commitment to Bob, but the connection drops before Bob can process
// it. // it.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -2892,11 +2899,11 @@ func TestChanSyncOweCommitment(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to revoke bob commitment: %v", err) t.Fatalf("unable to revoke bob commitment: %v", err)
} }
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("bob unable to sign commitment: %v", err) t.Fatalf("bob unable to sign commitment: %v", err)
} }
_, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatalf("alice unable to recv revocation: %v", err) t.Fatalf("alice unable to recv revocation: %v", err)
} }
@ -2908,7 +2915,8 @@ func TestChanSyncOweCommitment(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("alice unable to revoke commitment: %v", err) t.Fatalf("alice unable to revoke commitment: %v", err)
} }
if _, _, _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil { _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil {
t.Fatalf("bob unable to recv revocation: %v", err) t.Fatalf("bob unable to recv revocation: %v", err)
} }
@ -3048,7 +3056,7 @@ func TestChanSyncOweRevocation(t *testing.T) {
// //
// Alice signs the next state, then Bob receives and sends his // Alice signs the next state, then Bob receives and sends his
// revocation message. // revocation message.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -3061,12 +3069,12 @@ func TestChanSyncOweRevocation(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to revoke bob commitment: %v", err) t.Fatalf("unable to revoke bob commitment: %v", err)
} }
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("bob unable to sign commitment: %v", err) t.Fatalf("bob unable to sign commitment: %v", err)
} }
_, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatalf("alice unable to recv revocation: %v", err) t.Fatalf("alice unable to recv revocation: %v", err)
} }
@ -3148,7 +3156,8 @@ func TestChanSyncOweRevocation(t *testing.T) {
// TODO(roasbeef): restart bob too??? // TODO(roasbeef): restart bob too???
// We'll continue by then allowing bob to process Alice's revocation message. // We'll continue by then allowing bob to process Alice's revocation message.
if _, _, _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil { _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil {
t.Fatalf("bob unable to recv revocation: %v", err) t.Fatalf("bob unable to recv revocation: %v", err)
} }
@ -3230,7 +3239,7 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) {
// Progressing the exchange: Alice will send her signature, Bob will // Progressing the exchange: Alice will send her signature, Bob will
// receive, send a revocation and also a signature for Alice's state. // receive, send a revocation and also a signature for Alice's state.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -3245,7 +3254,7 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to revoke bob commitment: %v", err) t.Fatalf("unable to revoke bob commitment: %v", err)
} }
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("bob unable to sign commitment: %v", err) t.Fatalf("bob unable to sign commitment: %v", err)
} }
@ -3326,7 +3335,7 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) {
// We'll now finish the state transition by having Alice process both // We'll now finish the state transition by having Alice process both
// messages, and send her final revocation. // messages, and send her final revocation.
_, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatalf("alice unable to recv revocation: %v", err) t.Fatalf("alice unable to recv revocation: %v", err)
} }
@ -3338,7 +3347,8 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("alice unable to revoke commitment: %v", err) t.Fatalf("alice unable to revoke commitment: %v", err)
} }
if _, _, _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil { _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil {
t.Fatalf("bob unable to recv revocation: %v", err) t.Fatalf("bob unable to recv revocation: %v", err)
} }
} }
@ -3407,7 +3417,7 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) {
} }
// Bob signs the new state update, and sends the signature to Alice. // Bob signs the new state update, and sends the signature to Alice.
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("bob unable to sign commitment: %v", err) t.Fatalf("bob unable to sign commitment: %v", err)
} }
@ -3425,7 +3435,8 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("alice unable to revoke commitment: %v", err) t.Fatalf("alice unable to revoke commitment: %v", err)
} }
if _, _, _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil { _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil {
t.Fatalf("bob unable to recv revocation: %v", err) t.Fatalf("bob unable to recv revocation: %v", err)
} }
@ -3442,7 +3453,7 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) {
// Progressing the exchange: Alice will send her signature, with Bob // Progressing the exchange: Alice will send her signature, with Bob
// processing the new state locally. // processing the new state locally.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -3554,7 +3565,7 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) {
// Now, we'll continue the exchange, sending Bob's revocation and // Now, we'll continue the exchange, sending Bob's revocation and
// signature message to Alice, ending with Alice sending her revocation // signature message to Alice, ending with Alice sending her revocation
// message to Bob. // message to Bob.
_, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatalf("alice unable to recv revocation: %v", err) t.Fatalf("alice unable to recv revocation: %v", err)
} }
@ -3568,7 +3579,8 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("alice unable to revoke commitment: %v", err) t.Fatalf("alice unable to revoke commitment: %v", err)
} }
if _, _, _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil { _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil {
t.Fatalf("bob unable to recv revocation: %v", err) t.Fatalf("bob unable to recv revocation: %v", err)
} }
} }
@ -3651,7 +3663,7 @@ func TestChanSyncFailure(t *testing.T) {
t.Fatalf("unable to recv bob's htlc: %v", err) t.Fatalf("unable to recv bob's htlc: %v", err)
} }
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign next commit: %v", err) t.Fatalf("unable to sign next commit: %v", err)
} }
@ -3883,7 +3895,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
// Now, Alice will send a new commitment to Bob, but we'll simulate a // Now, Alice will send a new commitment to Bob, but we'll simulate a
// connection failure, so Bob doesn't get her signature. // connection failure, so Bob doesn't get her signature.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -3986,11 +3998,11 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to revoke bob commitment: %v", err) t.Fatalf("unable to revoke bob commitment: %v", err)
} }
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("bob unable to sign commitment: %v", err) t.Fatalf("bob unable to sign commitment: %v", err)
} }
_, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatalf("alice unable to recv revocation: %v", err) t.Fatalf("alice unable to recv revocation: %v", err)
} }
@ -4002,7 +4014,8 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("alice unable to revoke commitment: %v", err) t.Fatalf("alice unable to revoke commitment: %v", err)
} }
if _, _, _, err := bobChannel.ReceiveRevocation(aliceRevocation); err != nil { _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil {
t.Fatalf("bob unable to recv revocation: %v", err) t.Fatalf("bob unable to recv revocation: %v", err)
} }
@ -4131,7 +4144,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) {
// Now, Alice will send a new commitment to Bob, but we'll simulate a // Now, Alice will send a new commitment to Bob, but we'll simulate a
// connection failure, so Bob doesn't get the signature. // connection failure, so Bob doesn't get the signature.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -4198,11 +4211,11 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to revoke bob commitment: %v", err) t.Fatalf("unable to revoke bob commitment: %v", err)
} }
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("bob unable to sign commitment: %v", err) t.Fatalf("bob unable to sign commitment: %v", err)
} }
_, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatalf("alice unable to recv revocation: %v", err) t.Fatalf("alice unable to recv revocation: %v", err)
} }
@ -4214,7 +4227,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("alice unable to revoke commitment: %v", err) t.Fatalf("alice unable to revoke commitment: %v", err)
} }
_, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil { if err != nil {
t.Fatalf("bob unable to recv revocation: %v", err) t.Fatalf("bob unable to recv revocation: %v", err)
} }
@ -4524,7 +4537,7 @@ func TestSignCommitmentFailNotLockedIn(t *testing.T) {
// If we now try to initiate a state update, then it should fail as // If we now try to initiate a state update, then it should fail as
// Alice is unable to actually create a new state. // Alice is unable to actually create a new state.
_, _, err = aliceChannel.SignNextCommitment() _, _, _, err = aliceChannel.SignNextCommitment()
if err != ErrNoWindow { if err != ErrNoWindow {
t.Fatalf("expected ErrNoWindow, instead have: %v", err) t.Fatalf("expected ErrNoWindow, instead have: %v", err)
} }
@ -4563,7 +4576,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
// We'll now manually initiate a state transition between Alice and // We'll now manually initiate a state transition between Alice and
// bob. // bob.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4577,7 +4590,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
} }
// Alice should detect that she doesn't need to forward any HTLC's. // Alice should detect that she doesn't need to forward any HTLC's.
fwdPkg, _, _, err := aliceChannel.ReceiveRevocation(bobRevocation) fwdPkg, _, _, _, err := aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4592,7 +4605,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
// Now, have Bob initiate a transition to lock in the Adds sent by // Now, have Bob initiate a transition to lock in the Adds sent by
// Alice. // Alice.
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4608,7 +4621,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
// Bob should now detect that he now has 2 incoming HTLC's that he can // Bob should now detect that he now has 2 incoming HTLC's that he can
// forward along. // forward along.
fwdPkg, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) fwdPkg, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4645,7 +4658,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
// We'll now initiate another state transition, but this time Bob will // We'll now initiate another state transition, but this time Bob will
// lead. // lead.
bobSig, bobHtlcSigs, err = bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err = bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4661,7 +4674,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
// At this point, Bob receives the revocation from Alice, which is now // At this point, Bob receives the revocation from Alice, which is now
// his signal to examine all the HTLC's that have been locked in to // his signal to examine all the HTLC's that have been locked in to
// process. // process.
fwdPkg, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) fwdPkg, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4680,7 +4693,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
// Now, begin another state transition led by Alice, and fail the second // Now, begin another state transition led by Alice, and fail the second
// HTLC part-way through the dance. // HTLC part-way through the dance.
aliceSig, aliceHtlcSigs, err = aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err = aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4707,7 +4720,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
// Alice should detect that she doesn't need to forward any Adds's, but // Alice should detect that she doesn't need to forward any Adds's, but
// that the Fail has been locked in an can be forwarded. // that the Fail has been locked in an can be forwarded.
_, adds, settleFails, err := aliceChannel.ReceiveRevocation(bobRevocation) _, adds, settleFails, _, err := aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4749,7 +4762,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
// Have Alice initiate a state transition, which does not include the // Have Alice initiate a state transition, which does not include the
// HTLCs just readded to the channel state. // HTLCs just readded to the channel state.
aliceSig, aliceHtlcSigs, err = aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err = aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4764,7 +4777,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
// Alice should detect that she doesn't need to forward any HTLC's, as // Alice should detect that she doesn't need to forward any HTLC's, as
// the updates haven't been committed by Bob yet. // the updates haven't been committed by Bob yet.
fwdPkg, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) fwdPkg, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4778,7 +4791,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
} }
// Now initiate a final update from Bob to lock in the final Fail. // Now initiate a final update from Bob to lock in the final Fail.
bobSig, bobHtlcSigs, err = bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err = bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4795,7 +4808,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
// Bob should detect that he has nothing to forward, as he hasn't // Bob should detect that he has nothing to forward, as he hasn't
// received any HTLCs. // received any HTLCs.
fwdPkg, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) fwdPkg, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4810,7 +4823,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
// Finally, have Bob initiate a state transition that locks in the Fail // Finally, have Bob initiate a state transition that locks in the Fail
// added after the restart. // added after the restart.
aliceSig, aliceHtlcSigs, err = aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err = aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4825,7 +4838,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
// When Alice receives the revocation, she should detect that she // When Alice receives the revocation, she should detect that she
// can now forward the freshly locked-in Fail. // can now forward the freshly locked-in Fail.
_, adds, settleFails, err = aliceChannel.ReceiveRevocation(bobRevocation) _, adds, settleFails, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -4869,7 +4882,7 @@ func TestInvalidCommitSigError(t *testing.T) {
} }
// Alice will now attempt to initiate a state transition. // Alice will now attempt to initiate a state transition.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign new commit: %v", err) t.Fatalf("unable to sign new commit: %v", err)
} }
@ -5075,7 +5088,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) {
// With the HTLC added, we'll now manually initiate a state transition // With the HTLC added, we'll now manually initiate a state transition
// from Alice to Bob. // from Alice to Bob.
_, _, err = aliceChannel.SignNextCommitment() _, _, _, err = aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -5861,7 +5874,7 @@ func TestChannelRestoreUpdateLogs(t *testing.T) {
} }
// Let Alice sign a new state, which will include the HTLC just sent. // Let Alice sign a new state, which will include the HTLC just sent.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -5881,7 +5894,7 @@ func TestChannelRestoreUpdateLogs(t *testing.T) {
// sent. However her local commitment chain still won't include the // sent. However her local commitment chain still won't include the
// state with the HTLC, since she hasn't received a new commitment // state with the HTLC, since she hasn't received a new commitment
// signature from Bob yet. // signature from Bob yet.
_, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatalf("unable to recive revocation: %v", err) t.Fatalf("unable to recive revocation: %v", err)
} }
@ -5899,7 +5912,7 @@ func TestChannelRestoreUpdateLogs(t *testing.T) {
// and remote commit chains are updated in an async fashion. Since the // and remote commit chains are updated in an async fashion. Since the
// remote chain was updated with the latest state (since Bob sent the // remote chain was updated with the latest state (since Bob sent the
// revocation earlier) we can keep advancing the remote commit chain. // revocation earlier) we can keep advancing the remote commit chain.
aliceSig, aliceHtlcSigs, err = aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err = aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -6064,7 +6077,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) {
restoreAndAssert(t, aliceChannel, 1, 0, 0, 0) restoreAndAssert(t, aliceChannel, 1, 0, 0, 0)
// Bob sends a signature. // Bob sends a signature.
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -6083,7 +6096,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to revoke commitment: %v", err) t.Fatalf("unable to revoke commitment: %v", err)
} }
_, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil { if err != nil {
t.Fatalf("bob unable to process alice's revocation: %v", err) t.Fatalf("bob unable to process alice's revocation: %v", err)
} }
@ -6097,7 +6110,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) {
// Now send a signature from Alice. This will give Bob a new commitment // Now send a signature from Alice. This will give Bob a new commitment
// where the HTLC is removed. // where the HTLC is removed.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -6119,7 +6132,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to revoke commitment: %v", err) t.Fatalf("unable to revoke commitment: %v", err)
} }
_, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatalf("unable to receive revocation: %v", err) t.Fatalf("unable to receive revocation: %v", err)
} }
@ -6177,7 +6190,7 @@ func TestDuplicateFailRejection(t *testing.T) {
// We'll now have Bob sign a new commitment to lock in the HTLC fail // We'll now have Bob sign a new commitment to lock in the HTLC fail
// for Alice. // for Alice.
_, _, err = bobChannel.SignNextCommitment() _, _, _, err = bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commit: %v", err) t.Fatalf("unable to sign commit: %v", err)
} }
@ -6257,7 +6270,7 @@ func TestDuplicateSettleRejection(t *testing.T) {
// We'll now have Bob sign a new commitment to lock in the HTLC fail // We'll now have Bob sign a new commitment to lock in the HTLC fail
// for Alice. // for Alice.
_, _, err = bobChannel.SignNextCommitment() _, _, _, err = bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commit: %v", err) t.Fatalf("unable to sign commit: %v", err)
} }
@ -6350,7 +6363,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
} }
// Let Alice sign a new state, which will include the HTLC just sent. // Let Alice sign a new state, which will include the HTLC just sent.
aliceSig, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -6376,7 +6389,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
bobChannel = restoreAndAssertCommitHeights(t, bobChannel, true, 0, 1, 0) bobChannel = restoreAndAssertCommitHeights(t, bobChannel, true, 0, 1, 0)
// Alice receives the revocation, ACKing her pending commitment. // Alice receives the revocation, ACKing her pending commitment.
_, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
if err != nil { if err != nil {
t.Fatalf("unable to recive revocation: %v", err) t.Fatalf("unable to recive revocation: %v", err)
} }
@ -6388,7 +6401,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
// Now let Bob send the commitment signature making the HTLC lock in on // Now let Bob send the commitment signature making the HTLC lock in on
// Alice's commitment. // Alice's commitment.
bobSig, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -6411,7 +6424,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
aliceChannel = restoreAndAssertCommitHeights(t, aliceChannel, false, aliceChannel = restoreAndAssertCommitHeights(t, aliceChannel, false,
0, 1, 1) 0, 1, 1)
_, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
if err != nil { if err != nil {
t.Fatalf("unable to recive revocation: %v", err) t.Fatalf("unable to recive revocation: %v", err)
} }
@ -6433,7 +6446,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
// Send a new signature from Alice to Bob, making Alice have a pending // Send a new signature from Alice to Bob, making Alice have a pending
// remote commitment. // remote commitment.
aliceSig, aliceHtlcSigs, err = aliceChannel.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err = aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -6463,7 +6476,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
// Sign a new state for Alice, making Bob have a pending remote // Sign a new state for Alice, making Bob have a pending remote
// commitment. // commitment.
bobSig, bobHtlcSigs, err = bobChannel.SignNextCommitment() bobSig, bobHtlcSigs, _, err = bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commitment: %v", err) t.Fatalf("unable to sign commitment: %v", err)
} }
@ -6519,7 +6532,7 @@ func TestForceCloseBorkedState(t *testing.T) {
// Do the commitment dance until Bob sends a revocation so Alice is // Do the commitment dance until Bob sends a revocation so Alice is
// able to receive the revocation, and then also make a new state // able to receive the revocation, and then also make a new state
// herself. // herself.
aliceSigs, aliceHtlcSigs, err := aliceChannel.SignNextCommitment() aliceSigs, aliceHtlcSigs, _, err := aliceChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commit: %v", err) t.Fatalf("unable to sign commit: %v", err)
} }
@ -6531,7 +6544,7 @@ func TestForceCloseBorkedState(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to revoke bob commitment: %v", err) t.Fatalf("unable to revoke bob commitment: %v", err)
} }
bobSigs, bobHtlcSigs, err := bobChannel.SignNextCommitment() bobSigs, bobHtlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil { if err != nil {
t.Fatalf("unable to sign commit: %v", err) t.Fatalf("unable to sign commit: %v", err)
} }
@ -6563,7 +6576,7 @@ func TestForceCloseBorkedState(t *testing.T) {
// At this point, all channel mutating methods should now fail as they // At this point, all channel mutating methods should now fail as they
// shouldn't be able to proceed if the channel is borked. // shouldn't be able to proceed if the channel is borked.
_, _, _, err = aliceChannel.ReceiveRevocation(revokeMsg) _, _, _, _, err = aliceChannel.ReceiveRevocation(revokeMsg)
if err != channeldb.ErrChanBorked { if err != channeldb.ErrChanBorked {
t.Fatalf("advance commitment tail should have failed") t.Fatalf("advance commitment tail should have failed")
} }
@ -6571,7 +6584,7 @@ func TestForceCloseBorkedState(t *testing.T) {
// We manually advance the commitment tail here since the above // We manually advance the commitment tail here since the above
// ReceiveRevocation call will fail before it's actually advanced. // ReceiveRevocation call will fail before it's actually advanced.
aliceChannel.remoteCommitChain.advanceTail() aliceChannel.remoteCommitChain.advanceTail()
_, _, err = aliceChannel.SignNextCommitment() _, _, _, err = aliceChannel.SignNextCommitment()
if err != channeldb.ErrChanBorked { if err != channeldb.ErrChanBorked {
t.Fatalf("sign commitment should have failed: %v", err) t.Fatalf("sign commitment should have failed: %v", err)
} }

@ -496,7 +496,7 @@ func calcStaticFee(numHTLCs int) btcutil.Amount {
// pending updates. This method is useful when testing interactions between two // pending updates. This method is useful when testing interactions between two
// live state machines. // live state machines.
func ForceStateTransition(chanA, chanB *LightningChannel) error { func ForceStateTransition(chanA, chanB *LightningChannel) error {
aliceSig, aliceHtlcSigs, err := chanA.SignNextCommitment() aliceSig, aliceHtlcSigs, _, err := chanA.SignNextCommitment()
if err != nil { if err != nil {
return err return err
} }
@ -508,12 +508,12 @@ func ForceStateTransition(chanA, chanB *LightningChannel) error {
if err != nil { if err != nil {
return err return err
} }
bobSig, bobHtlcSigs, err := chanB.SignNextCommitment() bobSig, bobHtlcSigs, _, err := chanB.SignNextCommitment()
if err != nil { if err != nil {
return err return err
} }
if _, _, _, err := chanA.ReceiveRevocation(bobRevocation); err != nil { if _, _, _, _, err := chanA.ReceiveRevocation(bobRevocation); err != nil {
return err return err
} }
if err := chanA.ReceiveNewCommitment(bobSig, bobHtlcSigs); err != nil { if err := chanA.ReceiveNewCommitment(bobSig, bobHtlcSigs); err != nil {
@ -524,7 +524,7 @@ func ForceStateTransition(chanA, chanB *LightningChannel) error {
if err != nil { if err != nil {
return err return err
} }
if _, _, _, err := chanB.ReceiveRevocation(aliceRevocation); err != nil { if _, _, _, _, err := chanB.ReceiveRevocation(aliceRevocation); err != nil {
return err return err
} }