diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 4caa8bbfa..d39dd84bb 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -1226,10 +1226,6 @@ type LightningChannel struct { // Capacity is the total capacity of this channel. Capacity btcutil.Amount - // stateHintObfuscator is a 48-bit state hint that's used to obfuscate - // the current state number on the commitment transactions. - stateHintObfuscator [StateHintSize]byte - // currentHeight is the current height of our local commitment chain. // This is also the same as the number of updates to the channel we've // accepted. @@ -1247,6 +1243,8 @@ type LightningChannel struct { channelState *channeldb.OpenChannel + commitBuilder *CommitmentBuilder + // [local|remote]Log is a (mostly) append-only log storing all the HTLC // updates to this channel. The log is walked backwards as HTLC updates // are applied in order to re-construct a commitment transaction from a @@ -1299,6 +1297,7 @@ func NewLightningChannel(signer input.Signer, remoteCommitChain: newCommitmentChain(), localCommitChain: newCommitmentChain(), channelState: state, + commitBuilder: NewCommitmentBuilder(state), localUpdateLog: localUpdateLog, remoteUpdateLog: remoteUpdateLog, ChanPoint: &state.FundingOutpoint, @@ -1322,8 +1321,6 @@ func NewLightningChannel(signer input.Signer, return nil, err } - lc.createStateHintObfuscator() - return lc, nil } @@ -2208,7 +2205,10 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool, } // Actually generate unsigned commitment transaction for this view. - if err := lc.createCommitmentTx(c, filteredHTLCView, keyRing); err != nil { + err := lc.commitBuilder.createCommitmentTx( + c, filteredHTLCView, keyRing, + ) + if err != nil { return nil, err } diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 8e08e86db..e163fae0e 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -127,27 +127,48 @@ func DeriveCommitmentKeys(commitPoint *btcec.PublicKey, return keyRing } +// CommitmentBuilder is a type that wraps the type of channel we are dealing +// with, and abstracts the various ways of constructing commitment +// transactions. +type CommitmentBuilder struct { + // chanState is the underlying channels's state struct, used to + // determine the type of channel we are dealing with, and relevant + // parameters. + chanState *channeldb.OpenChannel + + // obfuscator is a 48-bit state hint that's used to obfuscate the + // current state number on the commitment transactions. + obfuscator [StateHintSize]byte +} + +// NewCommitmentBuilder creates a new CommitmentBuilder from chanState. +func NewCommitmentBuilder(chanState *channeldb.OpenChannel) *CommitmentBuilder { + return &CommitmentBuilder{ + chanState: chanState, + obfuscator: createStateHintObfuscator(chanState), + } +} + // createStateHintObfuscator derives and assigns the state hint obfuscator for // the channel, which is used to encode the commitment height in the sequence // number of commitment transaction inputs. -func (lc *LightningChannel) createStateHintObfuscator() { - state := lc.channelState +func createStateHintObfuscator(state *channeldb.OpenChannel) [StateHintSize]byte { if state.IsInitiator { - lc.stateHintObfuscator = DeriveStateHintObfuscator( + return DeriveStateHintObfuscator( state.LocalChanCfg.PaymentBasePoint.PubKey, state.RemoteChanCfg.PaymentBasePoint.PubKey, ) - } else { - lc.stateHintObfuscator = DeriveStateHintObfuscator( - state.RemoteChanCfg.PaymentBasePoint.PubKey, - state.LocalChanCfg.PaymentBasePoint.PubKey, - ) } + + return DeriveStateHintObfuscator( + state.RemoteChanCfg.PaymentBasePoint.PubKey, + state.LocalChanCfg.PaymentBasePoint.PubKey, + ) } // createCommitmentTx generates the unsigned commitment transaction for a // commitment view and assigns to txn field. -func (lc *LightningChannel) createCommitmentTx(c *commitment, +func (cb *CommitmentBuilder) createCommitmentTx(c *commitment, filteredHTLCView *htlcView, keyRing *CommitmentKeyRing) error { ourBalance := c.ourBalance @@ -189,16 +210,16 @@ func (lc *LightningChannel) createCommitmentTx(c *commitment, // initiator. If the initiator is unable to pay the fee fully, then // their entire output is consumed. switch { - case lc.channelState.IsInitiator && commitFee > ourBalance.ToSatoshis(): + case cb.chanState.IsInitiator && commitFee > ourBalance.ToSatoshis(): ourBalance = 0 - case lc.channelState.IsInitiator: + case cb.chanState.IsInitiator: ourBalance -= commitFeeMSat - case !lc.channelState.IsInitiator && commitFee > theirBalance.ToSatoshis(): + case !cb.chanState.IsInitiator && commitFee > theirBalance.ToSatoshis(): theirBalance = 0 - case !lc.channelState.IsInitiator: + case !cb.chanState.IsInitiator: theirBalance -= commitFeeMSat } @@ -208,19 +229,19 @@ func (lc *LightningChannel) createCommitmentTx(c *commitment, ) // Depending on whether the transaction is ours or not, we call - // CreateCommitTx with parameters mathcing the perspective, to generate + // CreateCommitTx with parameters matching the perspective, to generate // a new commitment transaction with all the latest unsettled/un-timed // out HTLCs. if c.isOurs { commitTx, err = CreateCommitTx( - fundingTxIn(lc.channelState), keyRing, &lc.channelState.LocalChanCfg, - &lc.channelState.RemoteChanCfg, ourBalance.ToSatoshis(), + fundingTxIn(cb.chanState), keyRing, &cb.chanState.LocalChanCfg, + &cb.chanState.RemoteChanCfg, ourBalance.ToSatoshis(), theirBalance.ToSatoshis(), ) } else { commitTx, err = CreateCommitTx( - fundingTxIn(lc.channelState), keyRing, &lc.channelState.RemoteChanCfg, - &lc.channelState.LocalChanCfg, theirBalance.ToSatoshis(), + fundingTxIn(cb.chanState), keyRing, &cb.chanState.RemoteChanCfg, + &cb.chanState.LocalChanCfg, theirBalance.ToSatoshis(), ourBalance.ToSatoshis(), ) } @@ -266,7 +287,7 @@ func (lc *LightningChannel) createCommitmentTx(c *commitment, // Set the state hint of the commitment transaction to facilitate // quickly recovering the necessary penalty state in the case of an // uncooperative broadcast. - err = SetStateNumHint(commitTx, c.height, lc.stateHintObfuscator) + err = SetStateNumHint(commitTx, c.height, cb.obfuscator) if err != nil { return err } @@ -289,11 +310,11 @@ func (lc *LightningChannel) createCommitmentTx(c *commitment, for _, txOut := range commitTx.TxOut { totalOut += btcutil.Amount(txOut.Value) } - if totalOut > lc.channelState.Capacity { + if totalOut > cb.chanState.Capacity { return fmt.Errorf("height=%v, for ChannelPoint(%v) attempts "+ "to consume %v while channel capacity is %v", - c.height, lc.channelState.FundingOutpoint, - totalOut, lc.channelState.Capacity) + c.height, cb.chanState.FundingOutpoint, + totalOut, cb.chanState.Capacity) } c.txn = commitTx diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index d9f8d3cab..f5df61f68 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -324,6 +324,8 @@ func CreateTestChannels(tweaklessCommits bool) ( } alicePool.Start() + obfuscator := createStateHintObfuscator(aliceChannelState) + bobPool := NewSigPool(1, bobSigner) channelBob, err := NewLightningChannel( bobSigner, bobChannelState, bobPool, @@ -334,13 +336,13 @@ func CreateTestChannels(tweaklessCommits bool) ( bobPool.Start() err = SetStateNumHint( - aliceCommitTx, 0, channelAlice.stateHintObfuscator, + aliceCommitTx, 0, obfuscator, ) if err != nil { return nil, nil, nil, err } err = SetStateNumHint( - bobCommitTx, 0, channelAlice.stateHintObfuscator, + bobCommitTx, 0, obfuscator, ) if err != nil { return nil, nil, nil, err diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 24c2f4550..294f5f08b 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -421,14 +421,14 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) { // Construct a LightningChannel manually because we don't have nor need all // of the dependencies. channel := LightningChannel{ - channelState: &channelState, - Signer: signer, + channelState: &channelState, + Signer: signer, + commitBuilder: NewCommitmentBuilder(&channelState), } err = channel.createSignDesc() if err != nil { t.Fatalf("Failed to generate channel sign descriptor: %v", err) } - channel.createStateHintObfuscator() // The commitmentPoint is technically hidden in the spec, but we need it to // generate the correct tweak. @@ -803,7 +803,7 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) { dustLimit: tc.dustLimit, isOurs: true, } - err = channel.createCommitmentTx( + err = channel.commitBuilder.createCommitmentTx( commitmentView, theHTLCView, keys, ) if err != nil {