Merge pull request #3829 from halseth/pluggable-commitments-lnwallet

Pluggable commitments
This commit is contained in:
Johan T. Halseth 2020-01-07 09:58:12 +01:00 committed by GitHub
commit 3aca9d24b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 853 additions and 699 deletions

@ -1799,7 +1799,7 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns( aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(
channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint,
bobCommitPoint, *fundingTxIn, true, bobCommitPoint, *fundingTxIn, channeldb.SingleFunderTweaklessBit,
) )
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err

@ -320,10 +320,14 @@ type ChannelCommitment struct {
// LocalBalance is the current available settled balance within the // LocalBalance is the current available settled balance within the
// channel directly spendable by us. // channel directly spendable by us.
//
// NOTE: This is the balance *after* subtracting any commitment fee.
LocalBalance lnwire.MilliSatoshi LocalBalance lnwire.MilliSatoshi
// RemoteBalance is the current available settled balance within the // RemoteBalance is the current available settled balance within the
// channel directly spendable by the remote node. // channel directly spendable by the remote node.
//
// NOTE: This is the balance *after* subtracting any commitment fee.
RemoteBalance lnwire.MilliSatoshi RemoteBalance lnwire.MilliSatoshi
// CommitFee is the amount calculated to be paid in fees for the // CommitFee is the amount calculated to be paid in fees for the

@ -331,7 +331,8 @@ func (c *chainWatcher) SubscribeChannelEvents() *ChainEventSubscription {
// based off of only the set of outputs included. // based off of only the set of outputs included.
func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig, func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
commitSpend *chainntnfs.SpendDetail, broadcastStateNum uint64, commitSpend *chainntnfs.SpendDetail, broadcastStateNum uint64,
revocationProducer shachain.Producer, tweakless bool) (bool, error) { revocationProducer shachain.Producer,
chanType channeldb.ChannelType) (bool, error) {
// First, we'll re-derive our commitment point for this state since // First, we'll re-derive our commitment point for this state since
// this is what we use to randomize each of the keys for this state. // this is what we use to randomize each of the keys for this state.
@ -345,13 +346,14 @@ func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
// and remote keys for this state. We use our point as only we can // and remote keys for this state. We use our point as only we can
// revoke our own commitment. // revoke our own commitment.
commitKeyRing := lnwallet.DeriveCommitmentKeys( commitKeyRing := lnwallet.DeriveCommitmentKeys(
commitPoint, true, tweakless, &localChanCfg, &remoteChanCfg, commitPoint, true, chanType, &localChanCfg, &remoteChanCfg,
) )
// With the keys derived, we'll construct the remote script that'll be // With the keys derived, we'll construct the remote script that'll be
// present if they have a non-dust balance on the commitment. // present if they have a non-dust balance on the commitment.
remotePkScript, err := input.CommitScriptUnencumbered( remoteDelay := uint32(remoteChanCfg.CsvDelay)
commitKeyRing.NoDelayKey, remoteScript, err := lnwallet.CommitScriptToRemote(
chanType, remoteDelay, commitKeyRing.ToRemoteKey,
) )
if err != nil { if err != nil {
return false, err return false, err
@ -361,7 +363,7 @@ func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
// the remote party allowing them to claim this output before the CSV // the remote party allowing them to claim this output before the CSV
// delay if we breach. // delay if we breach.
localScript, err := input.CommitScriptToSelf( localScript, err := input.CommitScriptToSelf(
uint32(localChanCfg.CsvDelay), commitKeyRing.DelayKey, uint32(localChanCfg.CsvDelay), commitKeyRing.ToLocalKey,
commitKeyRing.RevocationKey, commitKeyRing.RevocationKey,
) )
if err != nil { if err != nil {
@ -382,7 +384,7 @@ func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
case bytes.Equal(localPkScript, pkScript): case bytes.Equal(localPkScript, pkScript):
return true, nil return true, nil
case bytes.Equal(remotePkScript, pkScript): case bytes.Equal(remoteScript.PkScript, pkScript):
return true, nil return true, nil
} }
} }
@ -422,11 +424,6 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
// revoked state...!!! // revoked state...!!!
commitTxBroadcast := commitSpend.SpendingTx commitTxBroadcast := commitSpend.SpendingTx
// An additional piece of information we need to properly
// dispatch a close event if is this channel was using the
// tweakless remove key format or not.
tweaklessCommit := c.cfg.chanState.ChanType.IsTweakless()
localCommit, remoteCommit, err := c.cfg.chanState.LatestCommitments() localCommit, remoteCommit, err := c.cfg.chanState.LatestCommitments()
if err != nil { if err != nil {
log.Errorf("Unable to fetch channel state for "+ log.Errorf("Unable to fetch channel state for "+
@ -484,7 +481,7 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
c.cfg.chanState.LocalChanCfg, c.cfg.chanState.LocalChanCfg,
c.cfg.chanState.RemoteChanCfg, commitSpend, c.cfg.chanState.RemoteChanCfg, commitSpend,
broadcastStateNum, c.cfg.chanState.RevocationProducer, broadcastStateNum, c.cfg.chanState.RevocationProducer,
tweaklessCommit, c.cfg.chanState.ChanType,
) )
if err != nil { if err != nil {
log.Errorf("unable to determine self commit for "+ log.Errorf("unable to determine self commit for "+
@ -596,6 +593,7 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
// close and sweep immediately using a fake commitPoint // close and sweep immediately using a fake commitPoint
// as it isn't actually needed for recovery anymore. // as it isn't actually needed for recovery anymore.
commitPoint := c.cfg.chanState.RemoteCurrentRevocation commitPoint := c.cfg.chanState.RemoteCurrentRevocation
tweaklessCommit := c.cfg.chanState.ChanType.IsTweakless()
if !tweaklessCommit { if !tweaklessCommit {
commitPoint = c.waitForCommitmentPoint() commitPoint = c.waitForCommitmentPoint()
if commitPoint == nil { if commitPoint == nil {
@ -928,8 +926,8 @@ func (c *chainWatcher) dispatchContractBreach(spendEvent *chainntnfs.SpendDetail
retribution.KeyRing.CommitPoint.Curve = nil retribution.KeyRing.CommitPoint.Curve = nil
retribution.KeyRing.LocalHtlcKey = nil retribution.KeyRing.LocalHtlcKey = nil
retribution.KeyRing.RemoteHtlcKey = nil retribution.KeyRing.RemoteHtlcKey = nil
retribution.KeyRing.DelayKey = nil retribution.KeyRing.ToLocalKey = nil
retribution.KeyRing.NoDelayKey = nil retribution.KeyRing.ToRemoteKey = nil
retribution.KeyRing.RevocationKey = nil retribution.KeyRing.RevocationKey = nil
return spew.Sdump(retribution) return spew.Sdump(retribution)
})) }))

@ -1973,7 +1973,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
t.Fatalf("unable to query fee estimator: %v", err) t.Fatalf("unable to query fee estimator: %v", err)
} }
htlcFee := lnwire.NewMSatFromSatoshis( htlcFee := lnwire.NewMSatFromSatoshis(
feePerKw.FeeForWeight(input.HtlcWeight), feePerKw.FeeForWeight(input.HTLCWeight),
) )
// The starting bandwidth of the channel should be exactly the amount // The starting bandwidth of the channel should be exactly the amount
@ -2462,7 +2462,7 @@ func TestChannelLinkBandwidthConsistencyOverflow(t *testing.T) {
// TODO(roasbeef): increase sleep // TODO(roasbeef): increase sleep
time.Sleep(time.Second * 1) time.Sleep(time.Second * 1)
commitWeight := input.CommitWeight + input.HtlcWeight*numHTLCs commitWeight := int64(input.CommitWeight + input.HTLCWeight*numHTLCs)
htlcFee := lnwire.NewMSatFromSatoshis( htlcFee := lnwire.NewMSatFromSatoshis(
feePerKw.FeeForWeight(commitWeight), feePerKw.FeeForWeight(commitWeight),
) )
@ -2646,7 +2646,7 @@ func TestChannelLinkTrimCircuitsPending(t *testing.T) {
defaultCommitFee := alice.channel.StateSnapshot().CommitFee defaultCommitFee := alice.channel.StateSnapshot().CommitFee
htlcFee := lnwire.NewMSatFromSatoshis( htlcFee := lnwire.NewMSatFromSatoshis(
feePerKw.FeeForWeight(input.HtlcWeight), feePerKw.FeeForWeight(input.HTLCWeight),
) )
// The starting bandwidth of the channel should be exactly the amount // The starting bandwidth of the channel should be exactly the amount
@ -2925,7 +2925,7 @@ func TestChannelLinkTrimCircuitsNoCommit(t *testing.T) {
defaultCommitFee := alice.channel.StateSnapshot().CommitFee defaultCommitFee := alice.channel.StateSnapshot().CommitFee
htlcFee := lnwire.NewMSatFromSatoshis( htlcFee := lnwire.NewMSatFromSatoshis(
feePerKw.FeeForWeight(input.HtlcWeight), feePerKw.FeeForWeight(input.HTLCWeight),
) )
// The starting bandwidth of the channel should be exactly the amount // The starting bandwidth of the channel should be exactly the amount
@ -3181,7 +3181,7 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
t.Fatalf("unable to query fee estimator: %v", err) t.Fatalf("unable to query fee estimator: %v", err)
} }
htlcFee := lnwire.NewMSatFromSatoshis( htlcFee := lnwire.NewMSatFromSatoshis(
feePerKw.FeeForWeight(input.HtlcWeight), feePerKw.FeeForWeight(input.HTLCWeight),
) )
// The starting bandwidth of the channel should be exactly the amount // The starting bandwidth of the channel should be exactly the amount

@ -262,7 +262,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns( aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(
aliceAmount, bobAmount, &aliceCfg, &bobCfg, aliceCommitPoint, aliceAmount, bobAmount, &aliceCfg, &bobCfg, aliceCommitPoint,
bobCommitPoint, *fundingTxIn, true, bobCommitPoint, *fundingTxIn, channeldb.SingleFunderTweaklessBit,
) )
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err

@ -17,9 +17,9 @@ var (
ErrTweakOverdose = errors.New("sign descriptor should only have one tweak") ErrTweakOverdose = errors.New("sign descriptor should only have one tweak")
) )
// SignDescriptor houses the necessary information required to successfully sign // SignDescriptor houses the necessary information required to successfully
// a given output. This struct is used by the Signer interface in order to gain // sign a given segwit output. This struct is used by the Signer interface in
// access to critical data needed to generate a valid signature. // order to gain access to critical data needed to generate a valid signature.
type SignDescriptor struct { type SignDescriptor struct {
// KeyDesc is a descriptor that precisely describes *which* key to use // KeyDesc is a descriptor that precisely describes *which* key to use
// for signing. This may provide the raw public key directly, or // for signing. This may provide the raw public key directly, or
@ -56,8 +56,9 @@ type SignDescriptor struct {
DoubleTweak *btcec.PrivateKey DoubleTweak *btcec.PrivateKey
// WitnessScript is the full script required to properly redeem the // WitnessScript is the full script required to properly redeem the
// output. This field will only be populated if a p2wsh or a p2sh // output. This field should be set to the full script if a p2wsh
// output is being signed. // output is being signed. For p2wkh it should be set to the hashed
// script (PkScript).
WitnessScript []byte WitnessScript []byte
// Output is the target output which should be signed. The PkScript and // Output is the target output which should be signed. The PkScript and

@ -5,15 +5,6 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
) )
const (
// CommitWeight is the weight of the base commitment transaction which
// includes: one p2wsh input, out p2wkh output, and one p2wsh output.
CommitWeight int64 = 724
// HtlcWeight is the weight of an HTLC output.
HtlcWeight int64 = 172
)
const ( const (
// witnessScaleFactor determines the level of "discount" witness data // witnessScaleFactor determines the level of "discount" witness data
// receives compared to "base" data. A scale factor of 4, denotes that // receives compared to "base" data. A scale factor of 4, denotes that
@ -168,6 +159,9 @@ const (
// WitnessCommitmentTxWeight 224 weight // WitnessCommitmentTxWeight 224 weight
WitnessCommitmentTxWeight = WitnessHeaderSize + WitnessSize WitnessCommitmentTxWeight = WitnessHeaderSize + WitnessSize
// CommitWeight 724 weight
CommitWeight = BaseCommitmentTxWeight + WitnessCommitmentTxWeight
// HTLCWeight 172 weight // HTLCWeight 172 weight
HTLCWeight = witnessScaleFactor * HTLCSize HTLCWeight = witnessScaleFactor * HTLCSize

@ -502,7 +502,7 @@ type commitment struct {
// evaluating all the add/remove/settle log entries before the listed // evaluating all the add/remove/settle log entries before the listed
// indexes. // indexes.
// //
// NOTE: This is the balance *before* subtracting any commitment fee. // NOTE: This is the balance *after* subtracting any commitment fee.
ourBalance lnwire.MilliSatoshi ourBalance lnwire.MilliSatoshi
theirBalance lnwire.MilliSatoshi theirBalance lnwire.MilliSatoshi
@ -866,11 +866,6 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool,
diskCommit *channeldb.ChannelCommitment, localCommitPoint, diskCommit *channeldb.ChannelCommitment, localCommitPoint,
remoteCommitPoint *btcec.PublicKey) (*commitment, error) { remoteCommitPoint *btcec.PublicKey) (*commitment, error) {
// If this commit is tweakless, then it'll affect the way we derive our
// keys, which will affect the commitment transaction reconstruction.
// So we'll determine this first, before we do anything else.
tweaklessCommit := lc.channelState.ChanType.IsTweakless()
// First, we'll need to re-derive the commitment key ring for each // First, we'll need to re-derive the commitment key ring for each
// party used within this particular state. If this is a pending commit // party used within this particular state. If this is a pending commit
// (we extended but weren't able to complete the commitment dance // (we extended but weren't able to complete the commitment dance
@ -879,14 +874,16 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool,
var localCommitKeys, remoteCommitKeys *CommitmentKeyRing var localCommitKeys, remoteCommitKeys *CommitmentKeyRing
if localCommitPoint != nil { if localCommitPoint != nil {
localCommitKeys = DeriveCommitmentKeys( localCommitKeys = DeriveCommitmentKeys(
localCommitPoint, true, tweaklessCommit, localCommitPoint, true, lc.channelState.ChanType,
lc.localChanCfg, lc.remoteChanCfg, &lc.channelState.LocalChanCfg,
&lc.channelState.RemoteChanCfg,
) )
} }
if remoteCommitPoint != nil { if remoteCommitPoint != nil {
remoteCommitKeys = DeriveCommitmentKeys( remoteCommitKeys = DeriveCommitmentKeys(
remoteCommitPoint, false, tweaklessCommit, remoteCommitPoint, false, lc.channelState.ChanType,
lc.localChanCfg, lc.remoteChanCfg, &lc.channelState.LocalChanCfg,
&lc.channelState.RemoteChanCfg,
) )
} }
@ -935,121 +932,6 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool,
return commit, nil return commit, nil
} }
// CommitmentKeyRing holds all derived keys needed to construct commitment and
// HTLC transactions. The keys are derived differently depending whether the
// commitment transaction is ours or the remote peer's. Private keys associated
// with each key may belong to the commitment owner or the "other party" which
// is referred to in the field comments, regardless of which is local and which
// is remote.
type CommitmentKeyRing struct {
// commitPoint is the "per commitment point" used to derive the tweak
// for each base point.
CommitPoint *btcec.PublicKey
// LocalCommitKeyTweak is the tweak used to derive the local public key
// from the local payment base point or the local private key from the
// base point secret. This may be included in a SignDescriptor to
// generate signatures for the local payment key.
LocalCommitKeyTweak []byte
// TODO(roasbeef): need delay tweak as well?
// LocalHtlcKeyTweak is the teak used to derive the local HTLC key from
// the local HTLC base point. This value is needed in order to
// derive the final key used within the HTLC scripts in the commitment
// transaction.
LocalHtlcKeyTweak []byte
// LocalHtlcKey is the key that will be used in the "to self" clause of
// any HTLC scripts within the commitment transaction for this key ring
// set.
LocalHtlcKey *btcec.PublicKey
// RemoteHtlcKey is the key that will be used in clauses within the
// HTLC script that send money to the remote party.
RemoteHtlcKey *btcec.PublicKey
// DelayKey is the commitment transaction owner's key which is included
// in HTLC success and timeout transaction scripts.
DelayKey *btcec.PublicKey
// NoDelayKey is the other party's payment key in the commitment tx.
// This is the key used to generate the unencumbered output within the
// commitment transaction.
NoDelayKey *btcec.PublicKey
// RevocationKey is the key that can be used by the other party to
// redeem outputs from a revoked commitment transaction if it were to
// be published.
RevocationKey *btcec.PublicKey
}
// DeriveCommitmentKey generates a new commitment key set using the base points
// and commitment point. The keys are derived differently depending whether the
// commitment transaction is ours or the remote peer's.
func DeriveCommitmentKeys(commitPoint *btcec.PublicKey,
isOurCommit, tweaklessCommit bool,
localChanCfg, remoteChanCfg *channeldb.ChannelConfig) *CommitmentKeyRing {
// First, we'll derive all the keys that don't depend on the context of
// whose commitment transaction this is.
keyRing := &CommitmentKeyRing{
CommitPoint: commitPoint,
LocalCommitKeyTweak: input.SingleTweakBytes(
commitPoint, localChanCfg.PaymentBasePoint.PubKey,
),
LocalHtlcKeyTweak: input.SingleTweakBytes(
commitPoint, localChanCfg.HtlcBasePoint.PubKey,
),
LocalHtlcKey: input.TweakPubKey(
localChanCfg.HtlcBasePoint.PubKey, commitPoint,
),
RemoteHtlcKey: input.TweakPubKey(
remoteChanCfg.HtlcBasePoint.PubKey, commitPoint,
),
}
// We'll now compute the delay, no delay, and revocation key based on
// the current commitment point. All keys are tweaked each state in
// order to ensure the keys from each state are unlinkable. To create
// the revocation key, we take the opposite party's revocation base
// point and combine that with the current commitment point.
var (
delayBasePoint *btcec.PublicKey
noDelayBasePoint *btcec.PublicKey
revocationBasePoint *btcec.PublicKey
)
if isOurCommit {
delayBasePoint = localChanCfg.DelayBasePoint.PubKey
noDelayBasePoint = remoteChanCfg.PaymentBasePoint.PubKey
revocationBasePoint = remoteChanCfg.RevocationBasePoint.PubKey
} else {
delayBasePoint = remoteChanCfg.DelayBasePoint.PubKey
noDelayBasePoint = localChanCfg.PaymentBasePoint.PubKey
revocationBasePoint = localChanCfg.RevocationBasePoint.PubKey
}
// With the base points assigned, we can now derive the actual keys
// using the base point, and the current commitment tweak.
keyRing.DelayKey = input.TweakPubKey(delayBasePoint, commitPoint)
keyRing.RevocationKey = input.DeriveRevocationPubkey(
revocationBasePoint, commitPoint,
)
// If this commitment should omit the tweak for the remote point, then
// we'll use that directly, and ignore the commitPoint tweak.
if tweaklessCommit {
keyRing.NoDelayKey = noDelayBasePoint
} else {
keyRing.NoDelayKey = input.TweakPubKey(
noDelayBasePoint, commitPoint,
)
}
return keyRing
}
// commitmentChain represents a chain of unrevoked commitments. The tail of the // commitmentChain represents a chain of unrevoked commitments. The tail of the
// chain is the latest fully signed, yet unrevoked commitment. Two chains are // chain is the latest fully signed, yet unrevoked commitment. Two chains are
// tracked, one for the local node, and another for the remote node. New // tracked, one for the local node, and another for the remote node. New
@ -1339,10 +1221,6 @@ type LightningChannel struct {
// Capacity is the total capacity of this channel. // Capacity is the total capacity of this channel.
Capacity btcutil.Amount 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. // 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 // This is also the same as the number of updates to the channel we've
// accepted. // accepted.
@ -1360,9 +1238,7 @@ type LightningChannel struct {
channelState *channeldb.OpenChannel channelState *channeldb.OpenChannel
localChanCfg *channeldb.ChannelConfig commitBuilder *CommitmentBuilder
remoteChanCfg *channeldb.ChannelConfig
// [local|remote]Log is a (mostly) append-only log storing all the HTLC // [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 // updates to this channel. The log is walked backwards as HTLC updates
@ -1416,8 +1292,7 @@ func NewLightningChannel(signer input.Signer,
remoteCommitChain: newCommitmentChain(), remoteCommitChain: newCommitmentChain(),
localCommitChain: newCommitmentChain(), localCommitChain: newCommitmentChain(),
channelState: state, channelState: state,
localChanCfg: &state.LocalChanCfg, commitBuilder: NewCommitmentBuilder(state),
remoteChanCfg: &state.RemoteChanCfg,
localUpdateLog: localUpdateLog, localUpdateLog: localUpdateLog,
remoteUpdateLog: remoteUpdateLog, remoteUpdateLog: remoteUpdateLog,
ChanPoint: &state.FundingOutpoint, ChanPoint: &state.FundingOutpoint,
@ -1441,16 +1316,16 @@ func NewLightningChannel(signer input.Signer,
return nil, err return nil, err
} }
lc.createStateHintObfuscator()
return lc, nil return lc, nil
} }
// createSignDesc derives the SignDescriptor for commitment transactions from // createSignDesc derives the SignDescriptor for commitment transactions from
// other fields on the LightningChannel. // other fields on the LightningChannel.
func (lc *LightningChannel) createSignDesc() error { func (lc *LightningChannel) createSignDesc() error {
localKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed() localKey := lc.channelState.LocalChanCfg.MultiSigKey.PubKey.
remoteKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed() SerializeCompressed()
remoteKey := lc.channelState.RemoteChanCfg.MultiSigKey.PubKey.
SerializeCompressed()
multiSigScript, err := input.GenMultiSigScript(localKey, remoteKey) multiSigScript, err := input.GenMultiSigScript(localKey, remoteKey)
if err != nil { if err != nil {
@ -1462,7 +1337,7 @@ func (lc *LightningChannel) createSignDesc() error {
return err return err
} }
lc.signDesc = &input.SignDescriptor{ lc.signDesc = &input.SignDescriptor{
KeyDesc: lc.localChanCfg.MultiSigKey, KeyDesc: lc.channelState.LocalChanCfg.MultiSigKey,
WitnessScript: multiSigScript, WitnessScript: multiSigScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
PkScript: fundingPkScript, PkScript: fundingPkScript,
@ -1475,24 +1350,6 @@ func (lc *LightningChannel) createSignDesc() error {
return nil return nil
} }
// 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
if state.IsInitiator {
lc.stateHintObfuscator = DeriveStateHintObfuscator(
state.LocalChanCfg.PaymentBasePoint.PubKey,
state.RemoteChanCfg.PaymentBasePoint.PubKey,
)
} else {
lc.stateHintObfuscator = DeriveStateHintObfuscator(
state.RemoteChanCfg.PaymentBasePoint.PubKey,
state.LocalChanCfg.PaymentBasePoint.PubKey,
)
}
}
// ResetState resets the state of the channel back to the default state. This // ResetState resets the state of the channel back to the default state. This
// ensures that any active goroutines which need to act based on on-chain // ensures that any active goroutines which need to act based on on-chain
// events do so properly. // events do so properly.
@ -1719,10 +1576,9 @@ func (lc *LightningChannel) restoreCommitState(
// We'll also re-create the set of commitment keys needed to // We'll also re-create the set of commitment keys needed to
// fully re-derive the state. // fully re-derive the state.
tweaklessCommit := lc.channelState.ChanType.IsTweakless()
pendingRemoteKeyChain = DeriveCommitmentKeys( pendingRemoteKeyChain = DeriveCommitmentKeys(
pendingCommitPoint, false, tweaklessCommit, pendingCommitPoint, false, lc.channelState.ChanType,
lc.localChanCfg, lc.remoteChanCfg, &lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg,
) )
} }
@ -2007,45 +1863,50 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
// With the commitment point generated, we can now generate the four // With the commitment point generated, we can now generate the four
// keys we'll need to reconstruct the commitment state, // keys we'll need to reconstruct the commitment state,
tweaklessCommit := chanState.ChanType.IsTweakless()
keyRing := DeriveCommitmentKeys( keyRing := DeriveCommitmentKeys(
commitmentPoint, false, tweaklessCommit, commitmentPoint, false, chanState.ChanType,
&chanState.LocalChanCfg, &chanState.RemoteChanCfg, &chanState.LocalChanCfg, &chanState.RemoteChanCfg,
) )
// Next, reconstruct the scripts as they were present at this state // Next, reconstruct the scripts as they were present at this state
// number so we can have the proper witness script to sign and include // number so we can have the proper witness script to sign and include
// within the final witness. // within the final witness.
remoteDelay := uint32(chanState.RemoteChanCfg.CsvDelay) theirDelay := uint32(chanState.RemoteChanCfg.CsvDelay)
remotePkScript, err := input.CommitScriptToSelf( theirPkScript, err := input.CommitScriptToSelf(
remoteDelay, keyRing.DelayKey, keyRing.RevocationKey, theirDelay, keyRing.ToLocalKey, keyRing.RevocationKey,
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
remoteWitnessHash, err := input.WitnessScriptHash(remotePkScript) theirWitnessHash, err := input.WitnessScriptHash(theirPkScript)
if err != nil { if err != nil {
return nil, err return nil, err
} }
localPkScript, err := input.CommitScriptUnencumbered(keyRing.NoDelayKey)
// Since it is the remote breach we are reconstructing, the output going
// to us will be a to-remote script with our local params.
ourDelay := uint32(chanState.LocalChanCfg.CsvDelay)
ourScript, err := CommitScriptToRemote(
chanState.ChanType, ourDelay, keyRing.ToRemoteKey,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// In order to fully populate the breach retribution struct, we'll need // In order to fully populate the breach retribution struct, we'll need
// to find the exact index of the local+remote commitment outputs. // to find the exact index of the commitment outputs.
localOutpoint := wire.OutPoint{ ourOutpoint := wire.OutPoint{
Hash: commitHash, Hash: commitHash,
} }
remoteOutpoint := wire.OutPoint{ theirOutpoint := wire.OutPoint{
Hash: commitHash, Hash: commitHash,
} }
for i, txOut := range revokedSnapshot.CommitTx.TxOut { for i, txOut := range revokedSnapshot.CommitTx.TxOut {
switch { switch {
case bytes.Equal(txOut.PkScript, localPkScript): case bytes.Equal(txOut.PkScript, ourScript.PkScript):
localOutpoint.Index = uint32(i) ourOutpoint.Index = uint32(i)
case bytes.Equal(txOut.PkScript, remoteWitnessHash): case bytes.Equal(txOut.PkScript, theirWitnessHash):
remoteOutpoint.Index = uint32(i) theirOutpoint.Index = uint32(i)
} }
} }
@ -2053,45 +1914,39 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
// commitment outputs. If either is considered dust using the remote // commitment outputs. If either is considered dust using the remote
// party's dust limit, the respective sign descriptor will be nil. // party's dust limit, the respective sign descriptor will be nil.
var ( var (
localSignDesc *input.SignDescriptor ourSignDesc *input.SignDescriptor
remoteSignDesc *input.SignDescriptor theirSignDesc *input.SignDescriptor
) )
// Compute the local and remote balances in satoshis. // Compute the balances in satoshis.
localAmt := revokedSnapshot.LocalBalance.ToSatoshis() ourAmt := revokedSnapshot.LocalBalance.ToSatoshis()
remoteAmt := revokedSnapshot.RemoteBalance.ToSatoshis() theirAmt := revokedSnapshot.RemoteBalance.ToSatoshis()
// If the local balance exceeds the remote party's dust limit, // If our balance exceeds the remote party's dust limit, instantiate
// instantiate the local sign descriptor. // the sign descriptor for our output.
if localAmt >= chanState.RemoteChanCfg.DustLimit { if ourAmt >= chanState.RemoteChanCfg.DustLimit {
localSignDesc = &input.SignDescriptor{ ourSignDesc = &input.SignDescriptor{
SingleTweak: keyRing.LocalCommitKeyTweak, SingleTweak: keyRing.LocalCommitKeyTweak,
KeyDesc: chanState.LocalChanCfg.PaymentBasePoint, KeyDesc: chanState.LocalChanCfg.PaymentBasePoint,
WitnessScript: localPkScript, WitnessScript: ourScript.WitnessScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
PkScript: localPkScript, PkScript: ourScript.PkScript,
Value: int64(localAmt), Value: int64(ourAmt),
}, },
HashType: txscript.SigHashAll, HashType: txscript.SigHashAll,
} }
// If this is a tweakless commitment, then we can safely blank
// out the SingleTweak value as it isn't needed.
if tweaklessCommit {
localSignDesc.SingleTweak = nil
}
} }
// Similarly, if the remote balance exceeds the remote party's dust // Similarly, if their balance exceeds the remote party's dust limit,
// limit, assemble the remote sign descriptor. // assemble the sign descriptor for their output, which we can sweep.
if remoteAmt >= chanState.RemoteChanCfg.DustLimit { if theirAmt >= chanState.RemoteChanCfg.DustLimit {
remoteSignDesc = &input.SignDescriptor{ theirSignDesc = &input.SignDescriptor{
KeyDesc: chanState.LocalChanCfg.RevocationBasePoint, KeyDesc: chanState.LocalChanCfg.RevocationBasePoint,
DoubleTweak: commitmentSecret, DoubleTweak: commitmentSecret,
WitnessScript: remotePkScript, WitnessScript: theirPkScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
PkScript: remoteWitnessHash, PkScript: theirWitnessHash,
Value: int64(remoteAmt), Value: int64(theirAmt),
}, },
HashType: txscript.SigHashAll, HashType: txscript.SigHashAll,
} }
@ -2122,7 +1977,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
// remote commitment transaction, and *they* go to the second // remote commitment transaction, and *they* go to the second
// level. // level.
secondLevelWitnessScript, err := input.SecondLevelHtlcScript( secondLevelWitnessScript, err := input.SecondLevelHtlcScript(
keyRing.RevocationKey, keyRing.DelayKey, remoteDelay, keyRing.RevocationKey, keyRing.ToLocalKey, theirDelay,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -2188,13 +2043,13 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
BreachHeight: breachHeight, BreachHeight: breachHeight,
RevokedStateNum: stateNum, RevokedStateNum: stateNum,
PendingHTLCs: revokedSnapshot.Htlcs, PendingHTLCs: revokedSnapshot.Htlcs,
LocalOutpoint: localOutpoint, LocalOutpoint: ourOutpoint,
LocalOutputSignDesc: localSignDesc, LocalOutputSignDesc: ourSignDesc,
RemoteOutpoint: remoteOutpoint, RemoteOutpoint: theirOutpoint,
RemoteOutputSignDesc: remoteSignDesc, RemoteOutputSignDesc: theirSignDesc,
HtlcRetributions: htlcRetributions, HtlcRetributions: htlcRetributions,
KeyRing: keyRing, KeyRing: keyRing,
RemoteDelay: remoteDelay, RemoteDelay: theirDelay,
}, nil }, nil
} }
@ -2305,33 +2160,41 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
keyRing *CommitmentKeyRing) (*commitment, error) { keyRing *CommitmentKeyRing) (*commitment, error) {
commitChain := lc.localCommitChain commitChain := lc.localCommitChain
dustLimit := lc.channelState.LocalChanCfg.DustLimit
if remoteChain { if remoteChain {
commitChain = lc.remoteCommitChain commitChain = lc.remoteCommitChain
dustLimit = lc.channelState.RemoteChanCfg.DustLimit
} }
nextHeight := commitChain.tip().height + 1 nextHeight := commitChain.tip().height + 1
// Run through all the HTLCs that will be covered by this transaction // Run through all the HTLCs that will be covered by this transaction
// in order to update their commitment addition height, and to adjust // in order to update their commitment addition height, and to adjust
// the balances on the commitment transaction accordingly. // the balances on the commitment transaction accordingly. Note that
// these balances will be *before* taking a commitment fee from the
// initiator.
htlcView := lc.fetchHTLCView(theirLogIndex, ourLogIndex) htlcView := lc.fetchHTLCView(theirLogIndex, ourLogIndex)
ourBalance, theirBalance, _, filteredHTLCView := lc.computeView( ourBalance, theirBalance, _, filteredHTLCView := lc.computeView(
htlcView, remoteChain, true, htlcView, remoteChain, true,
) )
feePerKw := filteredHTLCView.feePerKw feePerKw := filteredHTLCView.feePerKw
// Determine how many current HTLCs are over the dust limit, and should // Actually generate unsigned commitment transaction for this view.
// be counted for the purpose of fee calculation. commitTx, err := lc.commitBuilder.createUnsignedCommitmentTx(
var dustLimit btcutil.Amount ourBalance, theirBalance, !remoteChain, feePerKw, nextHeight,
if remoteChain { filteredHTLCView, keyRing,
dustLimit = lc.remoteChanCfg.DustLimit )
} else { if err != nil {
dustLimit = lc.localChanCfg.DustLimit return nil, err
} }
// With the commitment view created, store the resulting balances and
// transaction with the other parameters for this height.
c := &commitment{ c := &commitment{
ourBalance: ourBalance, ourBalance: commitTx.ourBalance,
theirBalance: theirBalance, theirBalance: commitTx.theirBalance,
txn: commitTx.txn,
fee: commitTx.fee,
ourMessageIndex: ourLogIndex, ourMessageIndex: ourLogIndex,
ourHtlcIndex: ourHtlcIndex, ourHtlcIndex: ourHtlcIndex,
theirMessageIndex: theirLogIndex, theirMessageIndex: theirLogIndex,
@ -2342,11 +2205,6 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
isOurs: !remoteChain, isOurs: !remoteChain,
} }
// Actually generate unsigned commitment transaction for this view.
if err := lc.createCommitmentTx(c, filteredHTLCView, keyRing); err != nil {
return nil, err
}
// In order to ensure _none_ of the HTLC's associated with this new // In order to ensure _none_ of the HTLC's associated with this new
// commitment are mutated, we'll manually copy over each HTLC to its // commitment are mutated, we'll manually copy over each HTLC to its
// respective slice. // respective slice.
@ -2368,162 +2226,8 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
return c, nil return c, nil
} }
func (lc *LightningChannel) fundingTxIn() wire.TxIn { func fundingTxIn(chanState *channeldb.OpenChannel) wire.TxIn {
return *wire.NewTxIn(&lc.channelState.FundingOutpoint, nil, nil) return *wire.NewTxIn(&chanState.FundingOutpoint, nil, nil)
}
// createCommitmentTx generates the unsigned commitment transaction for a
// commitment view and assigns to txn field.
func (lc *LightningChannel) createCommitmentTx(c *commitment,
filteredHTLCView *htlcView, keyRing *CommitmentKeyRing) error {
ourBalance := c.ourBalance
theirBalance := c.theirBalance
numHTLCs := int64(0)
for _, htlc := range filteredHTLCView.ourUpdates {
if htlcIsDust(false, c.isOurs, c.feePerKw,
htlc.Amount.ToSatoshis(), c.dustLimit) {
continue
}
numHTLCs++
}
for _, htlc := range filteredHTLCView.theirUpdates {
if htlcIsDust(true, c.isOurs, c.feePerKw,
htlc.Amount.ToSatoshis(), c.dustLimit) {
continue
}
numHTLCs++
}
// Next, we'll calculate the fee for the commitment transaction based
// on its total weight. Once we have the total weight, we'll multiply
// by the current fee-per-kw, then divide by 1000 to get the proper
// fee.
totalCommitWeight := input.CommitWeight + (input.HtlcWeight * numHTLCs)
// With the weight known, we can now calculate the commitment fee,
// ensuring that we account for any dust outputs trimmed above.
commitFee := c.feePerKw.FeeForWeight(totalCommitWeight)
commitFeeMSat := lnwire.NewMSatFromSatoshis(commitFee)
// Currently, within the protocol, the initiator always pays the fees.
// So we'll subtract the fee amount from the balance of the current
// 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():
ourBalance = 0
case lc.channelState.IsInitiator:
ourBalance -= commitFeeMSat
case !lc.channelState.IsInitiator && commitFee > theirBalance.ToSatoshis():
theirBalance = 0
case !lc.channelState.IsInitiator:
theirBalance -= commitFeeMSat
}
var (
delay uint32
delayBalance, p2wkhBalance btcutil.Amount
)
if c.isOurs {
delay = uint32(lc.localChanCfg.CsvDelay)
delayBalance = ourBalance.ToSatoshis()
p2wkhBalance = theirBalance.ToSatoshis()
} else {
delay = uint32(lc.remoteChanCfg.CsvDelay)
delayBalance = theirBalance.ToSatoshis()
p2wkhBalance = ourBalance.ToSatoshis()
}
// Generate a new commitment transaction with all the latest
// unsettled/un-timed out HTLCs.
commitTx, err := CreateCommitTx(lc.fundingTxIn(), keyRing, delay,
delayBalance, p2wkhBalance, c.dustLimit)
if err != nil {
return err
}
// We'll now add all the HTLC outputs to the commitment transaction.
// Each output includes an off-chain 2-of-2 covenant clause, so we'll
// need the objective local/remote keys for this particular commitment
// as well. For any non-dust HTLCs that are manifested on the commitment
// transaction, we'll also record its CLTV which is required to sort the
// commitment transaction below. The slice is initially sized to the
// number of existing outputs, since any outputs already added are
// commitment outputs and should correspond to zero values for the
// purposes of sorting.
cltvs := make([]uint32, len(commitTx.TxOut))
for _, htlc := range filteredHTLCView.ourUpdates {
if htlcIsDust(false, c.isOurs, c.feePerKw,
htlc.Amount.ToSatoshis(), c.dustLimit) {
continue
}
err := lc.addHTLC(commitTx, c.isOurs, false, htlc, keyRing)
if err != nil {
return err
}
cltvs = append(cltvs, htlc.Timeout)
}
for _, htlc := range filteredHTLCView.theirUpdates {
if htlcIsDust(true, c.isOurs, c.feePerKw,
htlc.Amount.ToSatoshis(), c.dustLimit) {
continue
}
err := lc.addHTLC(commitTx, c.isOurs, true, htlc, keyRing)
if err != nil {
return err
}
cltvs = append(cltvs, htlc.Timeout)
}
// 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)
if err != nil {
return err
}
// Sort the transactions according to the agreed upon canonical
// ordering. This lets us skip sending the entire transaction over,
// instead we'll just send signatures.
InPlaceCommitSort(commitTx, cltvs)
// Next, we'll ensure that we don't accidentally create a commitment
// transaction which would be invalid by consensus.
uTx := btcutil.NewTx(commitTx)
if err := blockchain.CheckTransactionSanity(uTx); err != nil {
return err
}
// Finally, we'll assert that were not attempting to draw more out of
// the channel that was originally placed within it.
var totalOut btcutil.Amount
for _, txOut := range commitTx.TxOut {
totalOut += btcutil.Amount(txOut.Value)
}
if totalOut > lc.channelState.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.txn = commitTx
c.fee = commitFee
c.ourBalance = ourBalance
c.theirBalance = theirBalance
return nil
} }
// evaluateHTLCView processes all update entries in both HTLC update logs, // evaluateHTLCView processes all update entries in both HTLC update logs,
@ -2852,7 +2556,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
sigJob.Tx, err = createHtlcTimeoutTx( sigJob.Tx, err = createHtlcTimeoutTx(
op, outputAmt, htlc.Timeout, op, outputAmt, htlc.Timeout,
uint32(remoteChanCfg.CsvDelay), uint32(remoteChanCfg.CsvDelay),
keyRing.RevocationKey, keyRing.DelayKey, keyRing.RevocationKey, keyRing.ToLocalKey,
) )
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -2903,7 +2607,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
} }
sigJob.Tx, err = createHtlcSuccessTx( sigJob.Tx, err = createHtlcSuccessTx(
op, outputAmt, uint32(remoteChanCfg.CsvDelay), op, outputAmt, uint32(remoteChanCfg.CsvDelay),
keyRing.RevocationKey, keyRing.DelayKey, keyRing.RevocationKey, keyRing.ToLocalKey,
) )
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -3143,12 +2847,12 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
switch { switch {
case ourBalance < ourInitialBalance && case ourBalance < ourInitialBalance &&
ourBalance < lnwire.NewMSatFromSatoshis( ourBalance < lnwire.NewMSatFromSatoshis(
lc.localChanCfg.ChanReserve): lc.channelState.LocalChanCfg.ChanReserve):
return ErrBelowChanReserve return ErrBelowChanReserve
case theirBalance < theirInitialBalance && case theirBalance < theirInitialBalance &&
theirBalance < lnwire.NewMSatFromSatoshis( theirBalance < lnwire.NewMSatFromSatoshis(
lc.remoteChanCfg.ChanReserve): lc.channelState.RemoteChanCfg.ChanReserve):
return ErrBelowChanReserve return ErrBelowChanReserve
} }
@ -3199,7 +2903,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
// First check that the remote updates won't violate it's channel // First check that the remote updates won't violate it's channel
// constraints. // constraints.
err := validateUpdates( err := validateUpdates(
filteredView.theirUpdates, lc.remoteChanCfg, filteredView.theirUpdates, &lc.channelState.RemoteChanCfg,
) )
if err != nil { if err != nil {
return err return err
@ -3208,7 +2912,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
// Secondly check that our updates won't violate our channel // Secondly check that our updates won't violate our channel
// constraints. // constraints.
err = validateUpdates( err = validateUpdates(
filteredView.ourUpdates, lc.localChanCfg, filteredView.ourUpdates, &lc.channelState.LocalChanCfg,
) )
if err != nil { if err != nil {
return err return err
@ -3275,8 +2979,8 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, []ch
// used within fetchCommitmentView to derive all the keys necessary to // used within fetchCommitmentView to derive all the keys necessary to
// construct the commitment state. // construct the commitment state.
keyRing := DeriveCommitmentKeys( keyRing := DeriveCommitmentKeys(
commitPoint, false, lc.channelState.ChanType.IsTweakless(), commitPoint, false, lc.channelState.ChanType,
lc.localChanCfg, lc.remoteChanCfg, &lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg,
) )
// Create a new commitment view which will calculate the evaluated // Create a new commitment view which will calculate the evaluated
@ -3313,7 +3017,8 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, []ch
// commitment state. We do so in two phases: first we generate and // commitment state. We do so in two phases: first we generate and
// submit the set of signature jobs to the worker pool. // submit the set of signature jobs to the worker pool.
sigBatch, cancelChan, err := genRemoteHtlcSigJobs(keyRing, sigBatch, cancelChan, err := genRemoteHtlcSigJobs(keyRing,
lc.localChanCfg, lc.remoteChanCfg, newCommitView, &lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg,
newCommitView,
) )
if err != nil { if err != nil {
return sig, htlcSigs, nil, err return sig, htlcSigs, nil, err
@ -3696,10 +3401,10 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool,
*htlcView) { *htlcView) {
commitChain := lc.localCommitChain commitChain := lc.localCommitChain
dustLimit := lc.localChanCfg.DustLimit dustLimit := lc.channelState.LocalChanCfg.DustLimit
if remoteChain { if remoteChain {
commitChain = lc.remoteCommitChain commitChain = lc.remoteCommitChain
dustLimit = lc.remoteChanCfg.DustLimit dustLimit = lc.channelState.RemoteChanCfg.DustLimit
} }
// Since the fetched htlc view will include all updates added after the // Since the fetched htlc view will include all updates added after the
@ -3745,7 +3450,7 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool,
continue continue
} }
totalHtlcWeight += input.HtlcWeight totalHtlcWeight += input.HTLCWeight
} }
for _, htlc := range filteredHTLCView.theirUpdates { for _, htlc := range filteredHTLCView.theirUpdates {
if htlcIsDust(!remoteChain, !remoteChain, feePerKw, if htlcIsDust(!remoteChain, !remoteChain, feePerKw,
@ -3753,7 +3458,7 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool,
continue continue
} }
totalHtlcWeight += input.HtlcWeight totalHtlcWeight += input.HTLCWeight
} }
totalCommitWeight := input.CommitWeight + totalHtlcWeight totalCommitWeight := input.CommitWeight + totalHtlcWeight
@ -3814,7 +3519,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
successTx, err := createHtlcSuccessTx(op, successTx, err := createHtlcSuccessTx(op,
outputAmt, uint32(localChanCfg.CsvDelay), outputAmt, uint32(localChanCfg.CsvDelay),
keyRing.RevocationKey, keyRing.DelayKey) keyRing.RevocationKey, keyRing.ToLocalKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -3867,7 +3572,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
timeoutTx, err := createHtlcTimeoutTx(op, timeoutTx, err := createHtlcTimeoutTx(op,
outputAmt, htlc.Timeout, outputAmt, htlc.Timeout,
uint32(localChanCfg.CsvDelay), uint32(localChanCfg.CsvDelay),
keyRing.RevocationKey, keyRing.DelayKey, keyRing.RevocationKey, keyRing.ToLocalKey,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -4032,8 +3737,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig,
} }
commitPoint := input.ComputeCommitmentPoint(commitSecret[:]) commitPoint := input.ComputeCommitmentPoint(commitSecret[:])
keyRing := DeriveCommitmentKeys( keyRing := DeriveCommitmentKeys(
commitPoint, true, lc.channelState.ChanType.IsTweakless(), commitPoint, true, lc.channelState.ChanType,
lc.localChanCfg, lc.remoteChanCfg, &lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg,
) )
// With the current commitment point re-calculated, construct the new // With the current commitment point re-calculated, construct the new
@ -4081,8 +3786,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig,
// pool to verify each of the HTLc signatures presented. Once // pool to verify each of the HTLc signatures presented. Once
// generated, we'll submit these jobs to the worker pool. // generated, we'll submit these jobs to the worker pool.
verifyJobs, err := genHtlcSigValidationJobs( verifyJobs, err := genHtlcSigValidationJobs(
localCommitmentView, keyRing, htlcSigs, lc.localChanCfg, localCommitmentView, keyRing, htlcSigs,
lc.remoteChanCfg, &lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg,
) )
if err != nil { if err != nil {
return err return err
@ -4095,8 +3800,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig,
// we'll ensure that the newly constructed commitment state has a valid // we'll ensure that the newly constructed commitment state has a valid
// signature. // signature.
verifyKey := btcec.PublicKey{ verifyKey := btcec.PublicKey{
X: lc.remoteChanCfg.MultiSigKey.PubKey.X, X: lc.channelState.RemoteChanCfg.MultiSigKey.PubKey.X,
Y: lc.remoteChanCfg.MultiSigKey.PubKey.Y, Y: lc.channelState.RemoteChanCfg.MultiSigKey.PubKey.Y,
Curve: btcec.S256(), Curve: btcec.S256(),
} }
cSig, err := commitSig.ToSignature() cSig, err := commitSig.ToSignature()
@ -4930,101 +4635,6 @@ func (lc *LightningChannel) RemoteUpfrontShutdownScript() lnwire.DeliveryAddress
return lc.channelState.RemoteShutdownScript return lc.channelState.RemoteShutdownScript
} }
// genHtlcScript generates the proper P2WSH public key scripts for the HTLC
// output modified by two-bits denoting if this is an incoming HTLC, and if the
// HTLC is being applied to their commitment transaction or ours.
func genHtlcScript(isIncoming, ourCommit bool, timeout uint32, rHash [32]byte,
keyRing *CommitmentKeyRing) ([]byte, []byte, error) {
var (
witnessScript []byte
err error
)
// Generate the proper redeem scripts for the HTLC output modified by
// two-bits denoting if this is an incoming HTLC, and if the HTLC is
// being applied to their commitment transaction or ours.
switch {
// The HTLC is paying to us, and being applied to our commitment
// transaction. So we need to use the receiver's version of HTLC the
// script.
case isIncoming && ourCommit:
witnessScript, err = input.ReceiverHTLCScript(timeout,
keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
keyRing.RevocationKey, rHash[:])
// We're being paid via an HTLC by the remote party, and the HTLC is
// being added to their commitment transaction, so we use the sender's
// version of the HTLC script.
case isIncoming && !ourCommit:
witnessScript, err = input.SenderHTLCScript(keyRing.RemoteHtlcKey,
keyRing.LocalHtlcKey, keyRing.RevocationKey, rHash[:])
// We're sending an HTLC which is being added to our commitment
// transaction. Therefore, we need to use the sender's version of the
// HTLC script.
case !isIncoming && ourCommit:
witnessScript, err = input.SenderHTLCScript(keyRing.LocalHtlcKey,
keyRing.RemoteHtlcKey, keyRing.RevocationKey, rHash[:])
// Finally, we're paying the remote party via an HTLC, which is being
// added to their commitment transaction. Therefore, we use the
// receiver's version of the HTLC script.
case !isIncoming && !ourCommit:
witnessScript, err = input.ReceiverHTLCScript(timeout, keyRing.LocalHtlcKey,
keyRing.RemoteHtlcKey, keyRing.RevocationKey, rHash[:])
}
if err != nil {
return nil, nil, err
}
// Now that we have the redeem scripts, create the P2WSH public key
// script for the output itself.
htlcP2WSH, err := input.WitnessScriptHash(witnessScript)
if err != nil {
return nil, nil, err
}
return htlcP2WSH, witnessScript, nil
}
// addHTLC adds a new HTLC to the passed commitment transaction. One of four
// full scripts will be generated for the HTLC output depending on if the HTLC
// is incoming and if it's being applied to our commitment transaction or that
// of the remote node's. Additionally, in order to be able to efficiently
// locate the added HTLC on the commitment transaction from the
// PaymentDescriptor that generated it, the generated script is stored within
// the descriptor itself.
func (lc *LightningChannel) addHTLC(commitTx *wire.MsgTx, ourCommit bool,
isIncoming bool, paymentDesc *PaymentDescriptor,
keyRing *CommitmentKeyRing) error {
timeout := paymentDesc.Timeout
rHash := paymentDesc.RHash
p2wsh, witnessScript, err := genHtlcScript(isIncoming, ourCommit,
timeout, rHash, keyRing)
if err != nil {
return err
}
// Add the new HTLC outputs to the respective commitment transactions.
amountPending := int64(paymentDesc.Amount.ToSatoshis())
commitTx.AddTxOut(wire.NewTxOut(amountPending, p2wsh))
// Store the pkScript of this particular PaymentDescriptor so we can
// quickly locate it within the commitment transaction later.
if ourCommit {
paymentDesc.ourPkScript = p2wsh
paymentDesc.ourWitnessScript = witnessScript
} else {
paymentDesc.theirPkScript = p2wsh
paymentDesc.theirWitnessScript = witnessScript
}
return nil
}
// getSignedCommitTx function take the latest commitment transaction and // getSignedCommitTx function take the latest commitment transaction and
// populate it with witness data. // populate it with witness data.
func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) { func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
@ -5046,8 +4656,10 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
// With the final signature generated, create the witness stack // With the final signature generated, create the witness stack
// required to spend from the multi-sig output. // required to spend from the multi-sig output.
ourKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed() ourKey := lc.channelState.LocalChanCfg.MultiSigKey.PubKey.
theirKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed() SerializeCompressed()
theirKey := lc.channelState.RemoteChanCfg.MultiSigKey.PubKey.
SerializeCompressed()
commitTx.TxIn[0].Witness = input.SpendMultiSig( commitTx.TxIn[0].Witness = input.SpendMultiSig(
lc.signDesc.WitnessScript, ourKey, lc.signDesc.WitnessScript, ourKey,
@ -5130,10 +4742,9 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
// First, we'll generate the commitment point and the revocation point // First, we'll generate the commitment point and the revocation point
// so we can re-construct the HTLC state and also our payment key. // so we can re-construct the HTLC state and also our payment key.
tweaklessCommit := chanState.ChanType.IsTweakless()
keyRing := DeriveCommitmentKeys( keyRing := DeriveCommitmentKeys(
commitPoint, false, tweaklessCommit, &chanState.LocalChanCfg, commitPoint, false, chanState.ChanType,
&chanState.RemoteChanCfg, &chanState.LocalChanCfg, &chanState.RemoteChanCfg,
) )
// Next, we'll obtain HTLC resolutions for all the outgoing HTLC's we // Next, we'll obtain HTLC resolutions for all the outgoing HTLC's we
@ -5153,7 +4764,10 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
// Before we can generate the proper sign descriptor, we'll need to // Before we can generate the proper sign descriptor, we'll need to
// locate the output index of our non-delayed output on the commitment // locate the output index of our non-delayed output on the commitment
// transaction. // transaction.
selfP2WKH, err := input.CommitScriptUnencumbered(keyRing.NoDelayKey) localDelay := uint32(chanState.LocalChanCfg.CsvDelay)
selfScript, err := CommitScriptToRemote(
chanState.ChanType, localDelay, keyRing.ToRemoteKey,
)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to create self commit "+ return nil, fmt.Errorf("unable to create self commit "+
"script: %v", err) "script: %v", err)
@ -5165,7 +4779,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
) )
for outputIndex, txOut := range commitTxBroadcast.TxOut { for outputIndex, txOut := range commitTxBroadcast.TxOut {
if bytes.Equal(txOut.PkScript, selfP2WKH) { if bytes.Equal(txOut.PkScript, selfScript.PkScript) {
selfPoint = &wire.OutPoint{ selfPoint = &wire.OutPoint{
Hash: *commitSpend.SpenderTxHash, Hash: *commitSpend.SpenderTxHash,
Index: uint32(outputIndex), Index: uint32(outputIndex),
@ -5186,21 +4800,15 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
SelfOutputSignDesc: input.SignDescriptor{ SelfOutputSignDesc: input.SignDescriptor{
KeyDesc: localPayBase, KeyDesc: localPayBase,
SingleTweak: keyRing.LocalCommitKeyTweak, SingleTweak: keyRing.LocalCommitKeyTweak,
WitnessScript: selfP2WKH, WitnessScript: selfScript.WitnessScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
Value: localBalance, Value: localBalance,
PkScript: selfP2WKH, PkScript: selfScript.PkScript,
}, },
HashType: txscript.SigHashAll, HashType: txscript.SigHashAll,
}, },
MaturityDelay: 0, MaturityDelay: 0,
} }
// If this is a tweakless commitment, then we can safely blank
// out the SingleTweak value as it isn't needed.
if tweaklessCommit {
commitResolution.SelfOutputSignDesc.SingleTweak = nil
}
} }
closeSummary := channeldb.ChannelCloseSummary{ closeSummary := channeldb.ChannelCloseSummary{
@ -5396,7 +5004,7 @@ func newOutgoingHtlcResolution(signer input.Signer,
// transaction. // transaction.
timeoutTx, err := createHtlcTimeoutTx( timeoutTx, err := createHtlcTimeoutTx(
op, secondLevelOutputAmt, htlc.RefundTimeout, csvDelay, op, secondLevelOutputAmt, htlc.RefundTimeout, csvDelay,
keyRing.RevocationKey, keyRing.DelayKey, keyRing.RevocationKey, keyRing.ToLocalKey,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -5436,7 +5044,7 @@ func newOutgoingHtlcResolution(signer input.Signer,
// transaction creates so we can generate the signDesc required to // transaction creates so we can generate the signDesc required to
// complete the claim process after a delay period. // complete the claim process after a delay period.
htlcSweepScript, err := input.SecondLevelHtlcScript( htlcSweepScript, err := input.SecondLevelHtlcScript(
keyRing.RevocationKey, keyRing.DelayKey, csvDelay, keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -5530,7 +5138,7 @@ func newIncomingHtlcResolution(signer input.Signer, localChanCfg *channeldb.Chan
secondLevelOutputAmt := htlc.Amt.ToSatoshis() - htlcFee secondLevelOutputAmt := htlc.Amt.ToSatoshis() - htlcFee
successTx, err := createHtlcSuccessTx( successTx, err := createHtlcSuccessTx(
op, secondLevelOutputAmt, csvDelay, op, secondLevelOutputAmt, csvDelay,
keyRing.RevocationKey, keyRing.DelayKey, keyRing.RevocationKey, keyRing.ToLocalKey,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -5573,7 +5181,7 @@ func newIncomingHtlcResolution(signer input.Signer, localChanCfg *channeldb.Chan
// creates so we can generate the proper signDesc to sweep it after the // creates so we can generate the proper signDesc to sweep it after the
// CSV delay has passed. // CSV delay has passed.
htlcSweepScript, err := input.SecondLevelHtlcScript( htlcSweepScript, err := input.SecondLevelHtlcScript(
keyRing.RevocationKey, keyRing.DelayKey, csvDelay, keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -5788,11 +5396,13 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
} }
commitPoint := input.ComputeCommitmentPoint(revocation[:]) commitPoint := input.ComputeCommitmentPoint(revocation[:])
keyRing := DeriveCommitmentKeys( keyRing := DeriveCommitmentKeys(
commitPoint, true, chanState.ChanType.IsTweakless(), commitPoint, true, chanState.ChanType,
&chanState.LocalChanCfg, &chanState.RemoteChanCfg, &chanState.LocalChanCfg, &chanState.RemoteChanCfg,
) )
selfScript, err := input.CommitScriptToSelf(csvTimeout, keyRing.DelayKey,
keyRing.RevocationKey) selfScript, err := input.CommitScriptToSelf(
csvTimeout, keyRing.ToLocalKey, keyRing.RevocationKey,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -5826,9 +5436,6 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
// nil. // nil.
var commitResolution *CommitOutputResolution var commitResolution *CommitOutputResolution
if len(delayScript) != 0 { if len(delayScript) != 0 {
singleTweak := input.SingleTweakBytes(
commitPoint, chanState.LocalChanCfg.DelayBasePoint.PubKey,
)
localBalance := localCommit.LocalBalance localBalance := localCommit.LocalBalance
commitResolution = &CommitOutputResolution{ commitResolution = &CommitOutputResolution{
SelfOutPoint: wire.OutPoint{ SelfOutPoint: wire.OutPoint{
@ -5837,7 +5444,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
}, },
SelfOutputSignDesc: input.SignDescriptor{ SelfOutputSignDesc: input.SignDescriptor{
KeyDesc: chanState.LocalChanCfg.DelayBasePoint, KeyDesc: chanState.LocalChanCfg.DelayBasePoint,
SingleTweak: singleTweak, SingleTweak: keyRing.LocalCommitKeyTweak,
WitnessScript: selfScript, WitnessScript: selfScript,
Output: &wire.TxOut{ Output: &wire.TxOut{
PkScript: delayScript, PkScript: delayScript,
@ -5910,10 +5517,11 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount,
theirBalance = theirBalance - proposedFee + commitFee theirBalance = theirBalance - proposedFee + commitFee
} }
closeTx := CreateCooperativeCloseTx(lc.fundingTxIn(), closeTx := CreateCooperativeCloseTx(
lc.localChanCfg.DustLimit, lc.remoteChanCfg.DustLimit, fundingTxIn(lc.channelState), lc.channelState.LocalChanCfg.DustLimit,
ourBalance, theirBalance, localDeliveryScript, lc.channelState.RemoteChanCfg.DustLimit, ourBalance, theirBalance,
remoteDeliveryScript, lc.channelState.IsInitiator) localDeliveryScript, remoteDeliveryScript,
)
// Ensure that the transaction doesn't explicitly violate any // Ensure that the transaction doesn't explicitly violate any
// consensus rules such as being too big, or having any value with a // consensus rules such as being too big, or having any value with a
@ -5980,10 +5588,11 @@ func (lc *LightningChannel) CompleteCooperativeClose(localSig, remoteSig []byte,
// Create the transaction used to return the current settled balance // Create the transaction used to return the current settled balance
// on this active channel back to both parties. In this current model, // on this active channel back to both parties. In this current model,
// the initiator pays full fees for the cooperative close transaction. // the initiator pays full fees for the cooperative close transaction.
closeTx := CreateCooperativeCloseTx(lc.fundingTxIn(), closeTx := CreateCooperativeCloseTx(
lc.localChanCfg.DustLimit, lc.remoteChanCfg.DustLimit, fundingTxIn(lc.channelState), lc.channelState.LocalChanCfg.DustLimit,
ourBalance, theirBalance, localDeliveryScript, lc.channelState.RemoteChanCfg.DustLimit, ourBalance, theirBalance,
remoteDeliveryScript, lc.channelState.IsInitiator) localDeliveryScript, remoteDeliveryScript,
)
// Ensure that the transaction doesn't explicitly validate any // Ensure that the transaction doesn't explicitly validate any
// consensus rules such as being too big, or having any value with a // consensus rules such as being too big, or having any value with a
@ -5996,8 +5605,10 @@ func (lc *LightningChannel) CompleteCooperativeClose(localSig, remoteSig []byte,
// Finally, construct the witness stack minding the order of the // Finally, construct the witness stack minding the order of the
// pubkeys+sigs on the stack. // pubkeys+sigs on the stack.
ourKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed() ourKey := lc.channelState.LocalChanCfg.MultiSigKey.PubKey.
theirKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed() SerializeCompressed()
theirKey := lc.channelState.RemoteChanCfg.MultiSigKey.PubKey.
SerializeCompressed()
witness := input.SpendMultiSig(lc.signDesc.WitnessScript, ourKey, witness := input.SpendMultiSig(lc.signDesc.WitnessScript, ourKey,
localSig, theirKey, remoteSig) localSig, theirKey, remoteSig)
closeTx.TxIn[0].Witness = witness closeTx.TxIn[0].Witness = witness
@ -6210,60 +5821,6 @@ func (lc *LightningChannel) generateRevocation(height uint64) (*lnwire.RevokeAnd
return revocationMsg, nil return revocationMsg, nil
} }
// CreateCommitTx creates a commitment transaction, spending from specified
// funding output. The commitment transaction contains two outputs: one paying
// to the "owner" of the commitment transaction which can be spent after a
// relative block delay or revocation event, and the other paying the
// counterparty within the channel, which can be spent immediately.
func CreateCommitTx(fundingOutput wire.TxIn,
keyRing *CommitmentKeyRing, csvTimeout uint32,
amountToSelf, amountToThem, dustLimit btcutil.Amount) (*wire.MsgTx, error) {
// First, we create the script for the delayed "pay-to-self" output.
// This output has 2 main redemption clauses: either we can redeem the
// output after a relative block delay, or the remote node can claim
// the funds with the revocation key if we broadcast a revoked
// commitment transaction.
ourRedeemScript, err := input.CommitScriptToSelf(csvTimeout, keyRing.DelayKey,
keyRing.RevocationKey)
if err != nil {
return nil, err
}
payToUsScriptHash, err := input.WitnessScriptHash(ourRedeemScript)
if err != nil {
return nil, err
}
// Next, we create the script paying to them. This is just a regular
// P2WPKH output, without any added CSV delay.
theirWitnessKeyHash, err := input.CommitScriptUnencumbered(keyRing.NoDelayKey)
if err != nil {
return nil, err
}
// Now that both output scripts have been created, we can finally create
// the transaction itself. We use a transaction version of 2 since CSV
// will fail unless the tx version is >= 2.
commitTx := wire.NewMsgTx(2)
commitTx.AddTxIn(&fundingOutput)
// Avoid creating dust outputs within the commitment transaction.
if amountToSelf >= dustLimit {
commitTx.AddTxOut(&wire.TxOut{
PkScript: payToUsScriptHash,
Value: int64(amountToSelf),
})
}
if amountToThem >= dustLimit {
commitTx.AddTxOut(&wire.TxOut{
PkScript: theirWitnessKeyHash,
Value: int64(amountToThem),
})
}
return commitTx, nil
}
// CreateCooperativeCloseTx creates a transaction which if signed by both // CreateCooperativeCloseTx creates a transaction which if signed by both
// parties, then broadcast cooperatively closes an active channel. The creation // parties, then broadcast cooperatively closes an active channel. The creation
// of the closure transaction is modified by a boolean indicating if the party // of the closure transaction is modified by a boolean indicating if the party
@ -6272,8 +5829,7 @@ func CreateCommitTx(fundingOutput wire.TxIn,
// transaction in full. // transaction in full.
func CreateCooperativeCloseTx(fundingTxIn wire.TxIn, func CreateCooperativeCloseTx(fundingTxIn wire.TxIn,
localDust, remoteDust, ourBalance, theirBalance btcutil.Amount, localDust, remoteDust, ourBalance, theirBalance btcutil.Amount,
ourDeliveryScript, theirDeliveryScript []byte, ourDeliveryScript, theirDeliveryScript []byte) *wire.MsgTx {
initiator bool) *wire.MsgTx {
// Construct the transaction to perform a cooperative closure of the // Construct the transaction to perform a cooperative closure of the
// channel. In the event that one side doesn't have any settled funds // channel. In the event that one side doesn't have any settled funds
@ -6445,7 +6001,7 @@ func (lc *LightningChannel) ActiveHtlcs() []channeldb.HTLC {
// LocalChanReserve returns our local ChanReserve requirement for the remote party. // LocalChanReserve returns our local ChanReserve requirement for the remote party.
func (lc *LightningChannel) LocalChanReserve() btcutil.Amount { func (lc *LightningChannel) LocalChanReserve() btcutil.Amount {
return lc.localChanCfg.ChanReserve return lc.channelState.LocalChanCfg.ChanReserve
} }
// NextLocalHtlcIndex returns the next unallocated local htlc index. To ensure // NextLocalHtlcIndex returns the next unallocated local htlc index. To ensure
@ -6470,5 +6026,5 @@ func (lc *LightningChannel) RemoteCommitHeight() uint64 {
// FwdMinHtlc returns the minimum HTLC value required by the remote node, i.e. // FwdMinHtlc returns the minimum HTLC value required by the remote node, i.e.
// the minimum value HTLC we can forward on this channel. // the minimum value HTLC we can forward on this channel.
func (lc *LightningChannel) FwdMinHtlc() lnwire.MilliSatoshi { func (lc *LightningChannel) FwdMinHtlc() lnwire.MilliSatoshi {
return lc.localChanCfg.MinHTLC return lc.channelState.LocalChanCfg.MinHTLC
} }

@ -600,7 +600,7 @@ func TestForceClose(t *testing.T) {
// Factoring in the fee rate, Alice's amount should properly reflect // Factoring in the fee rate, Alice's amount should properly reflect
// that we've added two additional HTLC to the commitment transaction. // that we've added two additional HTLC to the commitment transaction.
totalCommitWeight := input.CommitWeight + (input.HtlcWeight * 2) totalCommitWeight := int64(input.CommitWeight + (input.HTLCWeight * 2))
feePerKw := chainfee.SatPerKWeight( feePerKw := chainfee.SatPerKWeight(
aliceChannel.channelState.LocalCommitment.FeePerKw, aliceChannel.channelState.LocalCommitment.FeePerKw,
) )
@ -615,7 +615,7 @@ func TestForceClose(t *testing.T) {
// Alice's listed CSV delay should also match the delay that was // Alice's listed CSV delay should also match the delay that was
// pre-committed to at channel opening. // pre-committed to at channel opening.
if aliceCommitResolution.MaturityDelay != if aliceCommitResolution.MaturityDelay !=
uint32(aliceChannel.localChanCfg.CsvDelay) { uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay) {
t.Fatalf("alice: incorrect local CSV delay in ForceCloseSummary, "+ t.Fatalf("alice: incorrect local CSV delay in ForceCloseSummary, "+
"expected %v, got %v", "expected %v, got %v",
@ -816,10 +816,10 @@ func TestForceCloseDustOutput(t *testing.T) {
// We set both node's channel reserves to 0, to make sure // We set both node's channel reserves to 0, to make sure
// they can create small dust ouputs without going under // they can create small dust ouputs without going under
// their channel reserves. // their channel reserves.
aliceChannel.localChanCfg.ChanReserve = 0 aliceChannel.channelState.LocalChanCfg.ChanReserve = 0
bobChannel.localChanCfg.ChanReserve = 0 bobChannel.channelState.LocalChanCfg.ChanReserve = 0
aliceChannel.remoteChanCfg.ChanReserve = 0 aliceChannel.channelState.RemoteChanCfg.ChanReserve = 0
bobChannel.remoteChanCfg.ChanReserve = 0 bobChannel.channelState.RemoteChanCfg.ChanReserve = 0
htlcAmount := lnwire.NewMSatFromSatoshis(500) htlcAmount := lnwire.NewMSatFromSatoshis(500)
@ -1268,8 +1268,8 @@ func TestChannelBalanceDustLimit(t *testing.T) {
// To allow Alice's balance to get beneath her dust limit, set the // To allow Alice's balance to get beneath her dust limit, set the
// channel reserve to be 0. // channel reserve to be 0.
aliceChannel.localChanCfg.ChanReserve = 0 aliceChannel.channelState.LocalChanCfg.ChanReserve = 0
bobChannel.remoteChanCfg.ChanReserve = 0 bobChannel.channelState.RemoteChanCfg.ChanReserve = 0
// This amount should leave an amount larger than Alice's dust limit // This amount should leave an amount larger than Alice's dust limit
// once fees have been subtracted, but smaller than Bob's dust limit. // once fees have been subtracted, but smaller than Bob's dust limit.
@ -2553,7 +2553,7 @@ func TestAddHTLCNegativeBalance(t *testing.T) {
// We set the channel reserve to 0, such that we can add HTLCs all the // We set the channel reserve to 0, such that we can add HTLCs all the
// way to a negative balance. // way to a negative balance.
aliceChannel.localChanCfg.ChanReserve = 0 aliceChannel.channelState.LocalChanCfg.ChanReserve = 0
// First, we'll add 3 HTLCs of 1 BTC each to Alice's commitment. // First, we'll add 3 HTLCs of 1 BTC each to Alice's commitment.
const numHTLCs = 3 const numHTLCs = 3
@ -5346,14 +5346,14 @@ func TestMaxAcceptedHTLCs(t *testing.T) {
// Set the remote's required MaxAcceptedHtlcs. This means that alice // Set the remote's required MaxAcceptedHtlcs. This means that alice
// can only offer the remote up to numHTLCs HTLCs. // can only offer the remote up to numHTLCs HTLCs.
aliceChannel.localChanCfg.MaxAcceptedHtlcs = numHTLCs aliceChannel.channelState.LocalChanCfg.MaxAcceptedHtlcs = numHTLCs
bobChannel.remoteChanCfg.MaxAcceptedHtlcs = numHTLCs bobChannel.channelState.RemoteChanCfg.MaxAcceptedHtlcs = numHTLCs
// Similarly, set the remote config's MaxAcceptedHtlcs. This means // Similarly, set the remote config's MaxAcceptedHtlcs. This means
// that the remote will be aware that Alice will only accept up to // that the remote will be aware that Alice will only accept up to
// numHTLCsRecevied at a time. // numHTLCsRecevied at a time.
aliceChannel.remoteChanCfg.MaxAcceptedHtlcs = numHTLCsReceived aliceChannel.channelState.RemoteChanCfg.MaxAcceptedHtlcs = numHTLCsReceived
bobChannel.localChanCfg.MaxAcceptedHtlcs = numHTLCsReceived bobChannel.channelState.LocalChanCfg.MaxAcceptedHtlcs = numHTLCsReceived
// Each HTLC amount is 0.1 BTC. // Each HTLC amount is 0.1 BTC.
htlcAmt := lnwire.NewMSatFromSatoshis(0.1 * btcutil.SatoshiPerBitcoin) htlcAmt := lnwire.NewMSatFromSatoshis(0.1 * btcutil.SatoshiPerBitcoin)
@ -5409,8 +5409,8 @@ func TestMaxPendingAmount(t *testing.T) {
// We set the max pending amount of Alice's config. This mean that she // We set the max pending amount of Alice's config. This mean that she
// cannot offer Bob HTLCs with a total value above this limit at a given // cannot offer Bob HTLCs with a total value above this limit at a given
// time. // time.
aliceChannel.localChanCfg.MaxPendingAmount = maxPending aliceChannel.channelState.LocalChanCfg.MaxPendingAmount = maxPending
bobChannel.remoteChanCfg.MaxPendingAmount = maxPending bobChannel.channelState.RemoteChanCfg.MaxPendingAmount = maxPending
// First, we'll add 2 HTLCs of 1.5 BTC each to Alice's commitment. // First, we'll add 2 HTLCs of 1.5 BTC each to Alice's commitment.
// This won't trigger Alice's ErrMaxPendingAmount error. // This won't trigger Alice's ErrMaxPendingAmount error.
@ -5498,20 +5498,20 @@ func TestChanReserve(t *testing.T) {
// Alice will need to keep her reserve above aliceMinReserve, // Alice will need to keep her reserve above aliceMinReserve,
// so set this limit to here local config. // so set this limit to here local config.
aliceChannel.localChanCfg.ChanReserve = aliceMinReserve aliceChannel.channelState.LocalChanCfg.ChanReserve = aliceMinReserve
// During channel opening Bob will also get to know Alice's // During channel opening Bob will also get to know Alice's
// minimum reserve, and this will be found in his remote // minimum reserve, and this will be found in his remote
// config. // config.
bobChannel.remoteChanCfg.ChanReserve = aliceMinReserve bobChannel.channelState.RemoteChanCfg.ChanReserve = aliceMinReserve
// We set Bob's channel reserve to a value that is larger than // We set Bob's channel reserve to a value that is larger than
// his current balance in the channel. This will ensure that // his current balance in the channel. This will ensure that
// after a channel is first opened, Bob can still receive HTLCs // after a channel is first opened, Bob can still receive HTLCs
// even though his balance is less than his channel reserve. // even though his balance is less than his channel reserve.
bobMinReserve := btcutil.Amount(6 * btcutil.SatoshiPerBitcoin) bobMinReserve := btcutil.Amount(6 * btcutil.SatoshiPerBitcoin)
bobChannel.localChanCfg.ChanReserve = bobMinReserve bobChannel.channelState.LocalChanCfg.ChanReserve = bobMinReserve
aliceChannel.remoteChanCfg.ChanReserve = bobMinReserve aliceChannel.channelState.RemoteChanCfg.ChanReserve = bobMinReserve
return aliceChannel, bobChannel, cleanUp return aliceChannel, bobChannel, cleanUp
} }
@ -5713,8 +5713,8 @@ func TestMinHTLC(t *testing.T) {
// Setting the min value in Alice's local config means that the // Setting the min value in Alice's local config means that the
// remote will not accept any HTLCs of value less than specified. // remote will not accept any HTLCs of value less than specified.
aliceChannel.localChanCfg.MinHTLC = minValue aliceChannel.channelState.LocalChanCfg.MinHTLC = minValue
bobChannel.remoteChanCfg.MinHTLC = minValue bobChannel.channelState.RemoteChanCfg.MinHTLC = minValue
// First, we will add an HTLC of 0.5 BTC. This will not trigger // First, we will add an HTLC of 0.5 BTC. This will not trigger
// ErrBelowMinHTLC. // ErrBelowMinHTLC.

578
lnwallet/commitment.go Normal file

@ -0,0 +1,578 @@
package lnwallet
import (
"fmt"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
)
// CommitmentKeyRing holds all derived keys needed to construct commitment and
// HTLC transactions. The keys are derived differently depending whether the
// commitment transaction is ours or the remote peer's. Private keys associated
// with each key may belong to the commitment owner or the "other party" which
// is referred to in the field comments, regardless of which is local and which
// is remote.
type CommitmentKeyRing struct {
// CommitPoint is the "per commitment point" used to derive the tweak
// for each base point.
CommitPoint *btcec.PublicKey
// LocalCommitKeyTweak is the tweak used to derive the local public key
// from the local payment base point or the local private key from the
// base point secret. This may be included in a SignDescriptor to
// generate signatures for the local payment key.
//
// NOTE: This will always refer to "our" local key, regardless of
// whether this is our commit or not.
LocalCommitKeyTweak []byte
// TODO(roasbeef): need delay tweak as well?
// LocalHtlcKeyTweak is the tweak used to derive the local HTLC key
// from the local HTLC base point. This value is needed in order to
// derive the final key used within the HTLC scripts in the commitment
// transaction.
//
// NOTE: This will always refer to "our" local HTLC key, regardless of
// whether this is our commit or not.
LocalHtlcKeyTweak []byte
// LocalHtlcKey is the key that will be used in any clause paying to
// our node of any HTLC scripts within the commitment transaction for
// this key ring set.
//
// NOTE: This will always refer to "our" local HTLC key, regardless of
// whether this is our commit or not.
LocalHtlcKey *btcec.PublicKey
// RemoteHtlcKey is the key that will be used in clauses within the
// HTLC script that send money to the remote party.
//
// NOTE: This will always refer to "their" remote HTLC key, regardless
// of whether this is our commit or not.
RemoteHtlcKey *btcec.PublicKey
// ToLocalKey is the commitment transaction owner's key which is
// included in HTLC success and timeout transaction scripts. This is
// the public key used for the to_local output of the commitment
// transaction.
//
// NOTE: Who's key this is depends on the current perspective. If this
// is our commitment this will be our key.
ToLocalKey *btcec.PublicKey
// ToRemoteKey is the non-owner's payment key in the commitment tx.
// This is the key used to generate the to_remote output within the
// commitment transaction.
//
// NOTE: Who's key this is depends on the current perspective. If this
// is our commitment this will be their key.
ToRemoteKey *btcec.PublicKey
// RevocationKey is the key that can be used by the other party to
// redeem outputs from a revoked commitment transaction if it were to
// be published.
//
// NOTE: Who can sign for this key depends on the current perspective.
// If this is our commitment, it means the remote node can sign for
// this key in case of a breach.
RevocationKey *btcec.PublicKey
}
// DeriveCommitmentKeys generates a new commitment key set using the base points
// and commitment point. The keys are derived differently depending on the type
// of channel, and whether the commitment transaction is ours or the remote
// peer's.
func DeriveCommitmentKeys(commitPoint *btcec.PublicKey,
isOurCommit bool, chanType channeldb.ChannelType,
localChanCfg, remoteChanCfg *channeldb.ChannelConfig) *CommitmentKeyRing {
tweaklessCommit := chanType.IsTweakless()
// Depending on if this is our commit or not, we'll choose the correct
// base point.
localBasePoint := localChanCfg.PaymentBasePoint
if isOurCommit {
localBasePoint = localChanCfg.DelayBasePoint
}
// First, we'll derive all the keys that don't depend on the context of
// whose commitment transaction this is.
keyRing := &CommitmentKeyRing{
CommitPoint: commitPoint,
LocalCommitKeyTweak: input.SingleTweakBytes(
commitPoint, localBasePoint.PubKey,
),
LocalHtlcKeyTweak: input.SingleTweakBytes(
commitPoint, localChanCfg.HtlcBasePoint.PubKey,
),
LocalHtlcKey: input.TweakPubKey(
localChanCfg.HtlcBasePoint.PubKey, commitPoint,
),
RemoteHtlcKey: input.TweakPubKey(
remoteChanCfg.HtlcBasePoint.PubKey, commitPoint,
),
}
// We'll now compute the to_local, to_remote, and revocation key based
// on the current commitment point. All keys are tweaked each state in
// order to ensure the keys from each state are unlinkable. To create
// the revocation key, we take the opposite party's revocation base
// point and combine that with the current commitment point.
var (
toLocalBasePoint *btcec.PublicKey
toRemoteBasePoint *btcec.PublicKey
revocationBasePoint *btcec.PublicKey
)
if isOurCommit {
toLocalBasePoint = localChanCfg.DelayBasePoint.PubKey
toRemoteBasePoint = remoteChanCfg.PaymentBasePoint.PubKey
revocationBasePoint = remoteChanCfg.RevocationBasePoint.PubKey
} else {
toLocalBasePoint = remoteChanCfg.DelayBasePoint.PubKey
toRemoteBasePoint = localChanCfg.PaymentBasePoint.PubKey
revocationBasePoint = localChanCfg.RevocationBasePoint.PubKey
}
// With the base points assigned, we can now derive the actual keys
// using the base point, and the current commitment tweak.
keyRing.ToLocalKey = input.TweakPubKey(toLocalBasePoint, commitPoint)
keyRing.RevocationKey = input.DeriveRevocationPubkey(
revocationBasePoint, commitPoint,
)
// If this commitment should omit the tweak for the remote point, then
// we'll use that directly, and ignore the commitPoint tweak.
if tweaklessCommit {
keyRing.ToRemoteKey = toRemoteBasePoint
// If this is not our commitment, the above ToRemoteKey will be
// ours, and we blank out the local commitment tweak to
// indicate that the key should not be tweaked when signing.
if !isOurCommit {
keyRing.LocalCommitKeyTweak = nil
}
} else {
keyRing.ToRemoteKey = input.TweakPubKey(
toRemoteBasePoint, commitPoint,
)
}
return keyRing
}
// ScriptInfo holds a redeem script and hash.
type ScriptInfo struct {
// PkScript is the output's PkScript.
PkScript []byte
// WitnessScript is the full script required to properly redeem the
// output. This field should be set to the full script if a p2wsh
// output is being signed. For p2wkh it should be set equal to the
// PkScript.
WitnessScript []byte
}
// CommitScriptToRemote creates the script that will pay to the non-owner of
// the commitment transaction, adding a delay to the script based on the
// channel type.
func CommitScriptToRemote(_ channeldb.ChannelType, csvTimeout uint32,
key *btcec.PublicKey) (*ScriptInfo, error) {
p2wkh, err := input.CommitScriptUnencumbered(key)
if err != nil {
return nil, err
}
// Since this is a regular P2WKH, the WitnessScipt and PkScript should
// both be set to the script hash.
return &ScriptInfo{
WitnessScript: p2wkh,
PkScript: p2wkh,
}, nil
}
// 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 createStateHintObfuscator(state *channeldb.OpenChannel) [StateHintSize]byte {
if state.IsInitiator {
return DeriveStateHintObfuscator(
state.LocalChanCfg.PaymentBasePoint.PubKey,
state.RemoteChanCfg.PaymentBasePoint.PubKey,
)
}
return DeriveStateHintObfuscator(
state.RemoteChanCfg.PaymentBasePoint.PubKey,
state.LocalChanCfg.PaymentBasePoint.PubKey,
)
}
// unsignedCommitmentTx is the final commitment created from evaluating an HTLC
// view at a given height, along with some meta data.
type unsignedCommitmentTx struct {
// txn is the final, unsigned commitment transaction for this view.
txn *wire.MsgTx
// fee is the total fee of the commitment transaction.
fee btcutil.Amount
// ourBalance|theirBalance is the balances of this commitment. This can
// be different than the balances before creating the commitment
// transaction as one party must pay the commitment fee.
ourBalance lnwire.MilliSatoshi
theirBalance lnwire.MilliSatoshi
}
// createUnsignedCommitmentTx generates the unsigned commitment transaction for
// a commitment view and returns it as part of the unsignedCommitmentTx. The
// passed in balances should be balances *before* subtracting any commitment
// fees.
func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance,
theirBalance lnwire.MilliSatoshi, isOurs bool,
feePerKw chainfee.SatPerKWeight, height uint64,
filteredHTLCView *htlcView,
keyRing *CommitmentKeyRing) (*unsignedCommitmentTx, error) {
dustLimit := cb.chanState.LocalChanCfg.DustLimit
if !isOurs {
dustLimit = cb.chanState.RemoteChanCfg.DustLimit
}
numHTLCs := int64(0)
for _, htlc := range filteredHTLCView.ourUpdates {
if htlcIsDust(false, isOurs, feePerKw,
htlc.Amount.ToSatoshis(), dustLimit) {
continue
}
numHTLCs++
}
for _, htlc := range filteredHTLCView.theirUpdates {
if htlcIsDust(true, isOurs, feePerKw,
htlc.Amount.ToSatoshis(), dustLimit) {
continue
}
numHTLCs++
}
// Next, we'll calculate the fee for the commitment transaction based
// on its total weight. Once we have the total weight, we'll multiply
// by the current fee-per-kw, then divide by 1000 to get the proper
// fee.
totalCommitWeight := input.CommitWeight + (input.HTLCWeight * numHTLCs)
// With the weight known, we can now calculate the commitment fee,
// ensuring that we account for any dust outputs trimmed above.
commitFee := feePerKw.FeeForWeight(totalCommitWeight)
commitFeeMSat := lnwire.NewMSatFromSatoshis(commitFee)
// Currently, within the protocol, the initiator always pays the fees.
// So we'll subtract the fee amount from the balance of the current
// initiator. If the initiator is unable to pay the fee fully, then
// their entire output is consumed.
switch {
case cb.chanState.IsInitiator && commitFee > ourBalance.ToSatoshis():
ourBalance = 0
case cb.chanState.IsInitiator:
ourBalance -= commitFeeMSat
case !cb.chanState.IsInitiator && commitFee > theirBalance.ToSatoshis():
theirBalance = 0
case !cb.chanState.IsInitiator:
theirBalance -= commitFeeMSat
}
var (
commitTx *wire.MsgTx
err error
)
// Depending on whether the transaction is ours or not, we call
// CreateCommitTx with parameters matching the perspective, to generate
// a new commitment transaction with all the latest unsettled/un-timed
// out HTLCs.
if isOurs {
commitTx, err = CreateCommitTx(
cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
&cb.chanState.LocalChanCfg, &cb.chanState.RemoteChanCfg,
ourBalance.ToSatoshis(), theirBalance.ToSatoshis(),
)
} else {
commitTx, err = CreateCommitTx(
cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
&cb.chanState.RemoteChanCfg, &cb.chanState.LocalChanCfg,
theirBalance.ToSatoshis(), ourBalance.ToSatoshis(),
)
}
if err != nil {
return nil, err
}
// We'll now add all the HTLC outputs to the commitment transaction.
// Each output includes an off-chain 2-of-2 covenant clause, so we'll
// need the objective local/remote keys for this particular commitment
// as well. For any non-dust HTLCs that are manifested on the commitment
// transaction, we'll also record its CLTV which is required to sort the
// commitment transaction below. The slice is initially sized to the
// number of existing outputs, since any outputs already added are
// commitment outputs and should correspond to zero values for the
// purposes of sorting.
cltvs := make([]uint32, len(commitTx.TxOut))
for _, htlc := range filteredHTLCView.ourUpdates {
if htlcIsDust(false, isOurs, feePerKw,
htlc.Amount.ToSatoshis(), dustLimit) {
continue
}
err := addHTLC(commitTx, isOurs, false, htlc, keyRing)
if err != nil {
return nil, err
}
cltvs = append(cltvs, htlc.Timeout)
}
for _, htlc := range filteredHTLCView.theirUpdates {
if htlcIsDust(true, isOurs, feePerKw,
htlc.Amount.ToSatoshis(), dustLimit) {
continue
}
err := addHTLC(commitTx, isOurs, true, htlc, keyRing)
if err != nil {
return nil, err
}
cltvs = append(cltvs, htlc.Timeout)
}
// 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, height, cb.obfuscator)
if err != nil {
return nil, err
}
// Sort the transactions according to the agreed upon canonical
// ordering. This lets us skip sending the entire transaction over,
// instead we'll just send signatures.
InPlaceCommitSort(commitTx, cltvs)
// Next, we'll ensure that we don't accidentally create a commitment
// transaction which would be invalid by consensus.
uTx := btcutil.NewTx(commitTx)
if err := blockchain.CheckTransactionSanity(uTx); err != nil {
return nil, err
}
// Finally, we'll assert that were not attempting to draw more out of
// the channel that was originally placed within it.
var totalOut btcutil.Amount
for _, txOut := range commitTx.TxOut {
totalOut += btcutil.Amount(txOut.Value)
}
if totalOut > cb.chanState.Capacity {
return nil, fmt.Errorf("height=%v, for ChannelPoint(%v) "+
"attempts to consume %v while channel capacity is %v",
height, cb.chanState.FundingOutpoint,
totalOut, cb.chanState.Capacity)
}
return &unsignedCommitmentTx{
txn: commitTx,
fee: commitFee,
ourBalance: ourBalance,
theirBalance: theirBalance,
}, nil
}
// CreateCommitTx creates a commitment transaction, spending from specified
// funding output. The commitment transaction contains two outputs: one local
// output paying to the "owner" of the commitment transaction which can be
// spent after a relative block delay or revocation event, and a remote output
// paying the counterparty within the channel, which can be spent immediately
// or after a delay depending on the commitment type..
func CreateCommitTx(chanType channeldb.ChannelType,
fundingOutput wire.TxIn, keyRing *CommitmentKeyRing,
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
amountToLocal, amountToRemote btcutil.Amount) (*wire.MsgTx, error) {
// First, we create the script for the delayed "pay-to-self" output.
// This output has 2 main redemption clauses: either we can redeem the
// output after a relative block delay, or the remote node can claim
// the funds with the revocation key if we broadcast a revoked
// commitment transaction.
toLocalRedeemScript, err := input.CommitScriptToSelf(
uint32(localChanCfg.CsvDelay), keyRing.ToLocalKey,
keyRing.RevocationKey,
)
if err != nil {
return nil, err
}
toLocalScriptHash, err := input.WitnessScriptHash(
toLocalRedeemScript,
)
if err != nil {
return nil, err
}
// Next, we create the script paying to the remote.
toRemoteScript, err := CommitScriptToRemote(
chanType, uint32(remoteChanCfg.CsvDelay), keyRing.ToRemoteKey,
)
if err != nil {
return nil, err
}
// Now that both output scripts have been created, we can finally create
// the transaction itself. We use a transaction version of 2 since CSV
// will fail unless the tx version is >= 2.
commitTx := wire.NewMsgTx(2)
commitTx.AddTxIn(&fundingOutput)
// Avoid creating dust outputs within the commitment transaction.
if amountToLocal >= localChanCfg.DustLimit {
commitTx.AddTxOut(&wire.TxOut{
PkScript: toLocalScriptHash,
Value: int64(amountToLocal),
})
}
if amountToRemote >= localChanCfg.DustLimit {
commitTx.AddTxOut(&wire.TxOut{
PkScript: toRemoteScript.PkScript,
Value: int64(amountToRemote),
})
}
return commitTx, nil
}
// genHtlcScript generates the proper P2WSH public key scripts for the HTLC
// output modified by two-bits denoting if this is an incoming HTLC, and if the
// HTLC is being applied to their commitment transaction or ours.
func genHtlcScript(isIncoming, ourCommit bool, timeout uint32, rHash [32]byte,
keyRing *CommitmentKeyRing) ([]byte, []byte, error) {
var (
witnessScript []byte
err error
)
// Generate the proper redeem scripts for the HTLC output modified by
// two-bits denoting if this is an incoming HTLC, and if the HTLC is
// being applied to their commitment transaction or ours.
switch {
// The HTLC is paying to us, and being applied to our commitment
// transaction. So we need to use the receiver's version of HTLC the
// script.
case isIncoming && ourCommit:
witnessScript, err = input.ReceiverHTLCScript(timeout,
keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
keyRing.RevocationKey, rHash[:])
// We're being paid via an HTLC by the remote party, and the HTLC is
// being added to their commitment transaction, so we use the sender's
// version of the HTLC script.
case isIncoming && !ourCommit:
witnessScript, err = input.SenderHTLCScript(keyRing.RemoteHtlcKey,
keyRing.LocalHtlcKey, keyRing.RevocationKey, rHash[:])
// We're sending an HTLC which is being added to our commitment
// transaction. Therefore, we need to use the sender's version of the
// HTLC script.
case !isIncoming && ourCommit:
witnessScript, err = input.SenderHTLCScript(keyRing.LocalHtlcKey,
keyRing.RemoteHtlcKey, keyRing.RevocationKey, rHash[:])
// Finally, we're paying the remote party via an HTLC, which is being
// added to their commitment transaction. Therefore, we use the
// receiver's version of the HTLC script.
case !isIncoming && !ourCommit:
witnessScript, err = input.ReceiverHTLCScript(timeout, keyRing.LocalHtlcKey,
keyRing.RemoteHtlcKey, keyRing.RevocationKey, rHash[:])
}
if err != nil {
return nil, nil, err
}
// Now that we have the redeem scripts, create the P2WSH public key
// script for the output itself.
htlcP2WSH, err := input.WitnessScriptHash(witnessScript)
if err != nil {
return nil, nil, err
}
return htlcP2WSH, witnessScript, nil
}
// addHTLC adds a new HTLC to the passed commitment transaction. One of four
// full scripts will be generated for the HTLC output depending on if the HTLC
// is incoming and if it's being applied to our commitment transaction or that
// of the remote node's. Additionally, in order to be able to efficiently
// locate the added HTLC on the commitment transaction from the
// PaymentDescriptor that generated it, the generated script is stored within
// the descriptor itself.
func addHTLC(commitTx *wire.MsgTx, ourCommit bool,
isIncoming bool, paymentDesc *PaymentDescriptor,
keyRing *CommitmentKeyRing) error {
timeout := paymentDesc.Timeout
rHash := paymentDesc.RHash
p2wsh, witnessScript, err := genHtlcScript(isIncoming, ourCommit,
timeout, rHash, keyRing)
if err != nil {
return err
}
// Add the new HTLC outputs to the respective commitment transactions.
amountPending := int64(paymentDesc.Amount.ToSatoshis())
commitTx.AddTxOut(wire.NewTxOut(amountPending, p2wsh))
// Store the pkScript of this particular PaymentDescriptor so we can
// quickly locate it within the commitment transaction later.
if ourCommit {
paymentDesc.ourPkScript = p2wsh
paymentDesc.ourWitnessScript = witnessScript
} else {
paymentDesc.theirPkScript = p2wsh
paymentDesc.theirWitnessScript = witnessScript
}
return nil
}

@ -206,9 +206,14 @@ func CreateTestChannels(tweaklessCommits bool) (
} }
aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:]) aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:])
chanType := channeldb.SingleFunderTweaklessBit
if !tweaklessCommits {
chanType = channeldb.SingleFunderBit
}
aliceCommitTx, bobCommitTx, err := CreateCommitmentTxns( aliceCommitTx, bobCommitTx, err := CreateCommitmentTxns(
channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint,
bobCommitPoint, *fundingTxIn, tweaklessCommits, bobCommitPoint, *fundingTxIn, chanType,
) )
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
@ -275,7 +280,7 @@ func CreateTestChannels(tweaklessCommits bool) (
IdentityPub: aliceKeys[0].PubKey(), IdentityPub: aliceKeys[0].PubKey(),
FundingOutpoint: *prevOut, FundingOutpoint: *prevOut,
ShortChannelID: shortChanID, ShortChannelID: shortChanID,
ChanType: channeldb.SingleFunderTweaklessBit, ChanType: chanType,
IsInitiator: true, IsInitiator: true,
Capacity: channelCapacity, Capacity: channelCapacity,
RemoteCurrentRevocation: bobCommitPoint, RemoteCurrentRevocation: bobCommitPoint,
@ -293,7 +298,7 @@ func CreateTestChannels(tweaklessCommits bool) (
IdentityPub: bobKeys[0].PubKey(), IdentityPub: bobKeys[0].PubKey(),
FundingOutpoint: *prevOut, FundingOutpoint: *prevOut,
ShortChannelID: shortChanID, ShortChannelID: shortChanID,
ChanType: channeldb.SingleFunderTweaklessBit, ChanType: chanType,
IsInitiator: false, IsInitiator: false,
Capacity: channelCapacity, Capacity: channelCapacity,
RemoteCurrentRevocation: aliceCommitPoint, RemoteCurrentRevocation: aliceCommitPoint,
@ -305,11 +310,6 @@ func CreateTestChannels(tweaklessCommits bool) (
Packager: channeldb.NewChannelPackager(shortChanID), Packager: channeldb.NewChannelPackager(shortChanID),
} }
if !tweaklessCommits {
aliceChannelState.ChanType = channeldb.SingleFunderBit
bobChannelState.ChanType = channeldb.SingleFunderBit
}
aliceSigner := &input.MockSigner{Privkeys: aliceKeys} aliceSigner := &input.MockSigner{Privkeys: aliceKeys}
bobSigner := &input.MockSigner{Privkeys: bobKeys} bobSigner := &input.MockSigner{Privkeys: bobKeys}
@ -324,6 +324,8 @@ func CreateTestChannels(tweaklessCommits bool) (
} }
alicePool.Start() alicePool.Start()
obfuscator := createStateHintObfuscator(aliceChannelState)
bobPool := NewSigPool(1, bobSigner) bobPool := NewSigPool(1, bobSigner)
channelBob, err := NewLightningChannel( channelBob, err := NewLightningChannel(
bobSigner, bobChannelState, bobPool, bobSigner, bobChannelState, bobPool,
@ -334,13 +336,13 @@ func CreateTestChannels(tweaklessCommits bool) (
bobPool.Start() bobPool.Start()
err = SetStateNumHint( err = SetStateNumHint(
aliceCommitTx, 0, channelAlice.stateHintObfuscator, aliceCommitTx, 0, obfuscator,
) )
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
err = SetStateNumHint( err = SetStateNumHint(
bobCommitTx, 0, channelAlice.stateHintObfuscator, bobCommitTx, 0, obfuscator,
) )
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err

@ -423,14 +423,12 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
channel := LightningChannel{ channel := LightningChannel{
channelState: &channelState, channelState: &channelState,
Signer: signer, Signer: signer,
localChanCfg: &channelState.LocalChanCfg, commitBuilder: NewCommitmentBuilder(&channelState),
remoteChanCfg: &channelState.RemoteChanCfg,
} }
err = channel.createSignDesc() err = channel.createSignDesc()
if err != nil { if err != nil {
t.Fatalf("Failed to generate channel sign descriptor: %v", err) 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 // The commitmentPoint is technically hidden in the spec, but we need it to
// generate the correct tweak. // generate the correct tweak.
@ -441,8 +439,8 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
LocalHtlcKeyTweak: tweak, LocalHtlcKeyTweak: tweak,
LocalHtlcKey: tc.localPaymentPubKey, LocalHtlcKey: tc.localPaymentPubKey,
RemoteHtlcKey: tc.remotePaymentPubKey, RemoteHtlcKey: tc.remotePaymentPubKey,
DelayKey: tc.localDelayPubKey, ToLocalKey: tc.localDelayPubKey,
NoDelayKey: tc.remotePaymentPubKey, ToRemoteKey: tc.remotePaymentPubKey,
RevocationKey: tc.localRevocationPubKey, RevocationKey: tc.localRevocationPubKey,
} }
@ -796,23 +794,32 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
} }
theHTLCView := htlcViewFromHTLCs(htlcs) theHTLCView := htlcViewFromHTLCs(htlcs)
feePerKw := chainfee.SatPerKWeight(test.commitment.FeePerKw)
isOurs := true
height := test.commitment.CommitHeight
// Create unsigned commitment transaction. // Create unsigned commitment transaction.
commitmentView := &commitment{ view, err := channel.commitBuilder.createUnsignedCommitmentTx(
height: test.commitment.CommitHeight, test.commitment.LocalBalance,
ourBalance: test.commitment.LocalBalance, test.commitment.RemoteBalance, isOurs, feePerKw,
theirBalance: test.commitment.RemoteBalance, height, theHTLCView, keys,
feePerKw: chainfee.SatPerKWeight(test.commitment.FeePerKw),
dustLimit: tc.dustLimit,
isOurs: true,
}
err = channel.createCommitmentTx(
commitmentView, theHTLCView, keys,
) )
if err != nil { if err != nil {
t.Errorf("Case %d: Failed to create new commitment tx: %v", i, err) t.Errorf("Case %d: Failed to create new commitment tx: %v", i, err)
continue continue
} }
commitmentView := &commitment{
ourBalance: view.ourBalance,
theirBalance: view.theirBalance,
txn: view.txn,
fee: view.fee,
height: height,
feePerKw: feePerKw,
dustLimit: tc.dustLimit,
isOurs: isOurs,
}
// Initialize LocalCommit, which is used in getSignedCommitTx. // Initialize LocalCommit, which is used in getSignedCommitTx.
channelState.LocalCommitment = test.commitment channelState.LocalCommitment = test.commitment
channelState.LocalCommitment.Htlcs = htlcs channelState.LocalCommitment.Htlcs = htlcs
@ -845,8 +852,8 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
// commitment tx. // commitment tx.
htlcResolutions, err := extractHtlcResolutions( htlcResolutions, err := extractHtlcResolutions(
chainfee.SatPerKWeight(test.commitment.FeePerKw), true, signer, chainfee.SatPerKWeight(test.commitment.FeePerKw), true, signer,
htlcs, keys, channel.localChanCfg, channel.remoteChanCfg, htlcs, keys, &channel.channelState.LocalChanCfg,
commitTx.TxHash(), &channel.channelState.RemoteChanCfg, commitTx.TxHash(),
) )
if err != nil { if err != nil {
t.Errorf("Case %d: Failed to extract HTLC resolutions: %v", i, err) t.Errorf("Case %d: Failed to extract HTLC resolutions: %v", i, err)
@ -1017,7 +1024,7 @@ func testSpendValidation(t *testing.T, tweakless bool) {
fakeFundingTxIn := wire.NewTxIn(fundingOut, nil, nil) fakeFundingTxIn := wire.NewTxIn(fundingOut, nil, nil)
const channelBalance = btcutil.Amount(1 * 10e8) const channelBalance = btcutil.Amount(1 * 10e8)
const csvTimeout = uint32(5) const csvTimeout = 5
// We also set up set some resources for the commitment transaction. // We also set up set some resources for the commitment transaction.
// Each side currently has 1 BTC within the channel, with a total // Each side currently has 1 BTC within the channel, with a total
@ -1041,8 +1048,10 @@ func testSpendValidation(t *testing.T, tweakless bool) {
// our commitments, if it's tweakless, his key will just be his regular // our commitments, if it's tweakless, his key will just be his regular
// pubkey. // pubkey.
bobPayKey := input.TweakPubKey(bobKeyPub, commitPoint) bobPayKey := input.TweakPubKey(bobKeyPub, commitPoint)
channelType := channeldb.SingleFunderBit
if tweakless { if tweakless {
bobPayKey = bobKeyPub bobPayKey = bobKeyPub
channelType = channeldb.SingleFunderTweaklessBit
} }
aliceCommitTweak := input.SingleTweakBytes(commitPoint, aliceKeyPub) aliceCommitTweak := input.SingleTweakBytes(commitPoint, aliceKeyPub)
@ -1052,6 +1061,20 @@ func testSpendValidation(t *testing.T, tweakless bool) {
Privkeys: []*btcec.PrivateKey{aliceKeyPriv}, Privkeys: []*btcec.PrivateKey{aliceKeyPriv},
} }
aliceChanCfg := &channeldb.ChannelConfig{
ChannelConstraints: channeldb.ChannelConstraints{
DustLimit: DefaultDustLimit(),
CsvDelay: csvTimeout,
},
}
bobChanCfg := &channeldb.ChannelConfig{
ChannelConstraints: channeldb.ChannelConstraints{
DustLimit: DefaultDustLimit(),
CsvDelay: csvTimeout,
},
}
// With all the test data set up, we create the commitment transaction. // With all the test data set up, we create the commitment transaction.
// We only focus on a single party's transactions, as the scripts are // We only focus on a single party's transactions, as the scripts are
// identical with the roles reversed. // identical with the roles reversed.
@ -1060,13 +1083,13 @@ func testSpendValidation(t *testing.T, tweakless bool) {
// of 5 blocks before sweeping the output, while bob can spend // of 5 blocks before sweeping the output, while bob can spend
// immediately with either the revocation key, or his regular key. // immediately with either the revocation key, or his regular key.
keyRing := &CommitmentKeyRing{ keyRing := &CommitmentKeyRing{
DelayKey: aliceDelayKey, ToLocalKey: aliceDelayKey,
RevocationKey: revokePubKey, RevocationKey: revokePubKey,
NoDelayKey: bobPayKey, ToRemoteKey: bobPayKey,
} }
commitmentTx, err := CreateCommitTx( commitmentTx, err := CreateCommitTx(
*fakeFundingTxIn, keyRing, csvTimeout, channelBalance, channelType, *fakeFundingTxIn, keyRing, aliceChanCfg,
channelBalance, DefaultDustLimit(), bobChanCfg, channelBalance, channelBalance,
) )
if err != nil { if err != nil {
t.Fatalf("unable to create commitment transaction: %v", nil) t.Fatalf("unable to create commitment transaction: %v", nil)

@ -772,21 +772,20 @@ func (l *LightningWallet) handleFundingCancelRequest(req *fundingReserveCancelMs
func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount, func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount,
ourChanCfg, theirChanCfg *channeldb.ChannelConfig, ourChanCfg, theirChanCfg *channeldb.ChannelConfig,
localCommitPoint, remoteCommitPoint *btcec.PublicKey, localCommitPoint, remoteCommitPoint *btcec.PublicKey,
fundingTxIn wire.TxIn, fundingTxIn wire.TxIn, chanType channeldb.ChannelType) (
tweaklessCommit bool) (*wire.MsgTx, *wire.MsgTx, error) { *wire.MsgTx, *wire.MsgTx, error) {
localCommitmentKeys := DeriveCommitmentKeys( localCommitmentKeys := DeriveCommitmentKeys(
localCommitPoint, true, tweaklessCommit, ourChanCfg, localCommitPoint, true, chanType, ourChanCfg, theirChanCfg,
theirChanCfg,
) )
remoteCommitmentKeys := DeriveCommitmentKeys( remoteCommitmentKeys := DeriveCommitmentKeys(
remoteCommitPoint, false, tweaklessCommit, ourChanCfg, remoteCommitPoint, false, chanType, ourChanCfg, theirChanCfg,
theirChanCfg,
) )
ourCommitTx, err := CreateCommitTx(fundingTxIn, localCommitmentKeys, ourCommitTx, err := CreateCommitTx(
uint32(ourChanCfg.CsvDelay), localBalance, remoteBalance, chanType, fundingTxIn, localCommitmentKeys, ourChanCfg,
ourChanCfg.DustLimit) theirChanCfg, localBalance, remoteBalance,
)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -796,9 +795,10 @@ func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount,
return nil, nil, err return nil, nil, err
} }
theirCommitTx, err := CreateCommitTx(fundingTxIn, remoteCommitmentKeys, theirCommitTx, err := CreateCommitTx(
uint32(theirChanCfg.CsvDelay), remoteBalance, localBalance, chanType, fundingTxIn, remoteCommitmentKeys, theirChanCfg,
theirChanCfg.DustLimit) ourChanCfg, remoteBalance, localBalance,
)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -928,13 +928,12 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
// With the funding tx complete, create both commitment transactions. // With the funding tx complete, create both commitment transactions.
localBalance := pendingReservation.partialState.LocalCommitment.LocalBalance.ToSatoshis() localBalance := pendingReservation.partialState.LocalCommitment.LocalBalance.ToSatoshis()
remoteBalance := pendingReservation.partialState.LocalCommitment.RemoteBalance.ToSatoshis() remoteBalance := pendingReservation.partialState.LocalCommitment.RemoteBalance.ToSatoshis()
tweaklessCommits := pendingReservation.partialState.ChanType.IsTweakless()
ourCommitTx, theirCommitTx, err := CreateCommitmentTxns( ourCommitTx, theirCommitTx, err := CreateCommitmentTxns(
localBalance, remoteBalance, ourContribution.ChannelConfig, localBalance, remoteBalance, ourContribution.ChannelConfig,
theirContribution.ChannelConfig, theirContribution.ChannelConfig,
ourContribution.FirstCommitmentPoint, ourContribution.FirstCommitmentPoint,
theirContribution.FirstCommitmentPoint, fundingTxIn, theirContribution.FirstCommitmentPoint, fundingTxIn,
tweaklessCommits, pendingReservation.partialState.ChanType,
) )
if err != nil { if err != nil {
req.err <- err req.err <- err
@ -1288,14 +1287,13 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
// remote node's commitment transactions. // remote node's commitment transactions.
localBalance := pendingReservation.partialState.LocalCommitment.LocalBalance.ToSatoshis() localBalance := pendingReservation.partialState.LocalCommitment.LocalBalance.ToSatoshis()
remoteBalance := pendingReservation.partialState.LocalCommitment.RemoteBalance.ToSatoshis() remoteBalance := pendingReservation.partialState.LocalCommitment.RemoteBalance.ToSatoshis()
tweaklessCommits := pendingReservation.partialState.ChanType.IsTweakless()
ourCommitTx, theirCommitTx, err := CreateCommitmentTxns( ourCommitTx, theirCommitTx, err := CreateCommitmentTxns(
localBalance, remoteBalance, localBalance, remoteBalance,
pendingReservation.ourContribution.ChannelConfig, pendingReservation.ourContribution.ChannelConfig,
pendingReservation.theirContribution.ChannelConfig, pendingReservation.theirContribution.ChannelConfig,
pendingReservation.ourContribution.FirstCommitmentPoint, pendingReservation.ourContribution.FirstCommitmentPoint,
pendingReservation.theirContribution.FirstCommitmentPoint, pendingReservation.theirContribution.FirstCommitmentPoint,
*fundingTxIn, tweaklessCommits, *fundingTxIn, pendingReservation.partialState.ChanType,
) )
if err != nil { if err != nil {
req.err <- err req.err <- err

@ -195,7 +195,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier, publTx chan *wire.MsgTx,
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns( aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(
channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint,
bobCommitPoint, *fundingTxIn, true, bobCommitPoint, *fundingTxIn, channeldb.SingleFunderTweaklessBit,
) )
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err

@ -189,7 +189,7 @@ func (t *backupTask) craftSessionPayload(
justiceKit := &blob.JusticeKit{ justiceKit := &blob.JusticeKit{
SweepAddress: t.sweepPkScript, SweepAddress: t.sweepPkScript,
RevocationPubKey: toBlobPubKey(keyRing.RevocationKey), RevocationPubKey: toBlobPubKey(keyRing.RevocationKey),
LocalDelayPubKey: toBlobPubKey(keyRing.DelayKey), LocalDelayPubKey: toBlobPubKey(keyRing.ToLocalKey),
CSVDelay: t.breachInfo.RemoteDelay, CSVDelay: t.breachInfo.RemoteDelay,
} }
@ -199,7 +199,7 @@ func (t *backupTask) craftSessionPayload(
// output to spend from. // output to spend from.
if t.toRemoteInput != nil { if t.toRemoteInput != nil {
justiceKit.CommitToRemotePubKey = toBlobPubKey( justiceKit.CommitToRemotePubKey = toBlobPubKey(
keyRing.NoDelayKey, keyRing.ToRemoteKey,
) )
} }

@ -121,8 +121,8 @@ func genTaskTest(
BreachTransaction: breachTxn, BreachTransaction: breachTxn,
KeyRing: &lnwallet.CommitmentKeyRing{ KeyRing: &lnwallet.CommitmentKeyRing{
RevocationKey: revPK, RevocationKey: revPK,
DelayKey: toLocalPK, ToLocalKey: toLocalPK,
NoDelayKey: toRemotePK, ToRemoteKey: toRemotePK,
}, },
RemoteDelay: csvDelay, RemoteDelay: csvDelay,
} }
@ -565,9 +565,9 @@ func testBackupTask(t *testing.T, test backupTaskTest) {
} }
keyRing := test.breachInfo.KeyRing keyRing := test.breachInfo.KeyRing
expToLocalPK := keyRing.DelayKey.SerializeCompressed() expToLocalPK := keyRing.ToLocalKey.SerializeCompressed()
expRevPK := keyRing.RevocationKey.SerializeCompressed() expRevPK := keyRing.RevocationKey.SerializeCompressed()
expToRemotePK := keyRing.NoDelayKey.SerializeCompressed() expToRemotePK := keyRing.ToRemoteKey.SerializeCompressed()
// Assert that the blob contained the serialized revocation and to-local // Assert that the blob contained the serialized revocation and to-local
// pubkeys. // pubkeys.

@ -291,8 +291,8 @@ func (c *mockChannel) createRemoteCommitTx(t *testing.T) {
commitKeyRing := &lnwallet.CommitmentKeyRing{ commitKeyRing := &lnwallet.CommitmentKeyRing{
RevocationKey: c.revPK, RevocationKey: c.revPK,
NoDelayKey: c.toLocalPK, ToRemoteKey: c.toLocalPK,
DelayKey: c.toRemotePK, ToLocalKey: c.toRemotePK,
} }
retribution := &lnwallet.BreachRetribution{ retribution := &lnwallet.BreachRetribution{