lnwallet: create CommitmentBuilder

We define a new struct CommitmentBuilder that will be used to craft the
final commitment transaction based on the current active channel type.
This commit is contained in:
Johan T. Halseth 2020-01-06 11:42:03 +01:00
parent 13a108e578
commit 613d771daf
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
4 changed files with 58 additions and 35 deletions

@ -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
}

@ -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

@ -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

@ -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 {