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

View File

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

View File

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

View File

@ -331,7 +331,8 @@ func (c *chainWatcher) SubscribeChannelEvents() *ChainEventSubscription {
// based off of only the set of outputs included.
func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
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
// 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
// revoke our own commitment.
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
// present if they have a non-dust balance on the commitment.
remotePkScript, err := input.CommitScriptUnencumbered(
commitKeyRing.NoDelayKey,
remoteDelay := uint32(remoteChanCfg.CsvDelay)
remoteScript, err := lnwallet.CommitScriptToRemote(
chanType, remoteDelay, commitKeyRing.ToRemoteKey,
)
if err != nil {
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
// delay if we breach.
localScript, err := input.CommitScriptToSelf(
uint32(localChanCfg.CsvDelay), commitKeyRing.DelayKey,
uint32(localChanCfg.CsvDelay), commitKeyRing.ToLocalKey,
commitKeyRing.RevocationKey,
)
if err != nil {
@ -382,7 +384,7 @@ func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
case bytes.Equal(localPkScript, pkScript):
return true, nil
case bytes.Equal(remotePkScript, pkScript):
case bytes.Equal(remoteScript.PkScript, pkScript):
return true, nil
}
}
@ -422,11 +424,6 @@ func (c *chainWatcher) closeObserver(spendNtfn *chainntnfs.SpendEvent) {
// revoked state...!!!
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()
if err != nil {
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.RemoteChanCfg, commitSpend,
broadcastStateNum, c.cfg.chanState.RevocationProducer,
tweaklessCommit,
c.cfg.chanState.ChanType,
)
if err != nil {
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
// as it isn't actually needed for recovery anymore.
commitPoint := c.cfg.chanState.RemoteCurrentRevocation
tweaklessCommit := c.cfg.chanState.ChanType.IsTweakless()
if !tweaklessCommit {
commitPoint = c.waitForCommitmentPoint()
if commitPoint == nil {
@ -928,8 +926,8 @@ func (c *chainWatcher) dispatchContractBreach(spendEvent *chainntnfs.SpendDetail
retribution.KeyRing.CommitPoint.Curve = nil
retribution.KeyRing.LocalHtlcKey = nil
retribution.KeyRing.RemoteHtlcKey = nil
retribution.KeyRing.DelayKey = nil
retribution.KeyRing.NoDelayKey = nil
retribution.KeyRing.ToLocalKey = nil
retribution.KeyRing.ToRemoteKey = nil
retribution.KeyRing.RevocationKey = nil
return spew.Sdump(retribution)
}))

View File

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

View File

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

View File

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

View File

@ -5,15 +5,6 @@ import (
"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 (
// witnessScaleFactor determines the level of "discount" witness data
// receives compared to "base" data. A scale factor of 4, denotes that
@ -168,6 +159,9 @@ const (
// WitnessCommitmentTxWeight 224 weight
WitnessCommitmentTxWeight = WitnessHeaderSize + WitnessSize
// CommitWeight 724 weight
CommitWeight = BaseCommitmentTxWeight + WitnessCommitmentTxWeight
// HTLCWeight 172 weight
HTLCWeight = witnessScaleFactor * HTLCSize

View File

@ -502,7 +502,7 @@ type commitment struct {
// evaluating all the add/remove/settle log entries before the listed
// indexes.
//
// NOTE: This is the balance *before* subtracting any commitment fee.
// NOTE: This is the balance *after* subtracting any commitment fee.
ourBalance lnwire.MilliSatoshi
theirBalance lnwire.MilliSatoshi
@ -866,11 +866,6 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool,
diskCommit *channeldb.ChannelCommitment, localCommitPoint,
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
// party used within this particular state. If this is a pending commit
// (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
if localCommitPoint != nil {
localCommitKeys = DeriveCommitmentKeys(
localCommitPoint, true, tweaklessCommit,
lc.localChanCfg, lc.remoteChanCfg,
localCommitPoint, true, lc.channelState.ChanType,
&lc.channelState.LocalChanCfg,
&lc.channelState.RemoteChanCfg,
)
}
if remoteCommitPoint != nil {
remoteCommitKeys = DeriveCommitmentKeys(
remoteCommitPoint, false, tweaklessCommit,
lc.localChanCfg, lc.remoteChanCfg,
remoteCommitPoint, false, lc.channelState.ChanType,
&lc.channelState.LocalChanCfg,
&lc.channelState.RemoteChanCfg,
)
}
@ -935,121 +932,6 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool,
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
// 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
@ -1339,10 +1221,6 @@ type LightningChannel struct {
// Capacity is the total capacity of this channel.
Capacity btcutil.Amount
// stateHintObfuscator is a 48-bit state hint that's used to obfuscate
// the current state number on the commitment transactions.
stateHintObfuscator [StateHintSize]byte
// currentHeight is the current height of our local commitment chain.
// This is also the same as the number of updates to the channel we've
// accepted.
@ -1360,9 +1238,7 @@ type LightningChannel struct {
channelState *channeldb.OpenChannel
localChanCfg *channeldb.ChannelConfig
remoteChanCfg *channeldb.ChannelConfig
commitBuilder *CommitmentBuilder
// [local|remote]Log is a (mostly) append-only log storing all the HTLC
// updates to this channel. The log is walked backwards as HTLC updates
@ -1416,8 +1292,7 @@ func NewLightningChannel(signer input.Signer,
remoteCommitChain: newCommitmentChain(),
localCommitChain: newCommitmentChain(),
channelState: state,
localChanCfg: &state.LocalChanCfg,
remoteChanCfg: &state.RemoteChanCfg,
commitBuilder: NewCommitmentBuilder(state),
localUpdateLog: localUpdateLog,
remoteUpdateLog: remoteUpdateLog,
ChanPoint: &state.FundingOutpoint,
@ -1441,16 +1316,16 @@ func NewLightningChannel(signer input.Signer,
return nil, err
}
lc.createStateHintObfuscator()
return lc, nil
}
// createSignDesc derives the SignDescriptor for commitment transactions from
// other fields on the LightningChannel.
func (lc *LightningChannel) createSignDesc() error {
localKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed()
remoteKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
localKey := lc.channelState.LocalChanCfg.MultiSigKey.PubKey.
SerializeCompressed()
remoteKey := lc.channelState.RemoteChanCfg.MultiSigKey.PubKey.
SerializeCompressed()
multiSigScript, err := input.GenMultiSigScript(localKey, remoteKey)
if err != nil {
@ -1462,7 +1337,7 @@ func (lc *LightningChannel) createSignDesc() error {
return err
}
lc.signDesc = &input.SignDescriptor{
KeyDesc: lc.localChanCfg.MultiSigKey,
KeyDesc: lc.channelState.LocalChanCfg.MultiSigKey,
WitnessScript: multiSigScript,
Output: &wire.TxOut{
PkScript: fundingPkScript,
@ -1475,24 +1350,6 @@ func (lc *LightningChannel) createSignDesc() error {
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
// ensures that any active goroutines which need to act based on on-chain
// events do so properly.
@ -1719,10 +1576,9 @@ func (lc *LightningChannel) restoreCommitState(
// We'll also re-create the set of commitment keys needed to
// fully re-derive the state.
tweaklessCommit := lc.channelState.ChanType.IsTweakless()
pendingRemoteKeyChain = DeriveCommitmentKeys(
pendingCommitPoint, false, tweaklessCommit,
lc.localChanCfg, lc.remoteChanCfg,
pendingCommitPoint, false, lc.channelState.ChanType,
&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
// keys we'll need to reconstruct the commitment state,
tweaklessCommit := chanState.ChanType.IsTweakless()
keyRing := DeriveCommitmentKeys(
commitmentPoint, false, tweaklessCommit,
commitmentPoint, false, chanState.ChanType,
&chanState.LocalChanCfg, &chanState.RemoteChanCfg,
)
// Next, reconstruct the scripts as they were present at this state
// number so we can have the proper witness script to sign and include
// within the final witness.
remoteDelay := uint32(chanState.RemoteChanCfg.CsvDelay)
remotePkScript, err := input.CommitScriptToSelf(
remoteDelay, keyRing.DelayKey, keyRing.RevocationKey,
theirDelay := uint32(chanState.RemoteChanCfg.CsvDelay)
theirPkScript, err := input.CommitScriptToSelf(
theirDelay, keyRing.ToLocalKey, keyRing.RevocationKey,
)
if err != nil {
return nil, err
}
remoteWitnessHash, err := input.WitnessScriptHash(remotePkScript)
theirWitnessHash, err := input.WitnessScriptHash(theirPkScript)
if err != nil {
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 {
return nil, err
}
// In order to fully populate the breach retribution struct, we'll need
// to find the exact index of the local+remote commitment outputs.
localOutpoint := wire.OutPoint{
// to find the exact index of the commitment outputs.
ourOutpoint := wire.OutPoint{
Hash: commitHash,
}
remoteOutpoint := wire.OutPoint{
theirOutpoint := wire.OutPoint{
Hash: commitHash,
}
for i, txOut := range revokedSnapshot.CommitTx.TxOut {
switch {
case bytes.Equal(txOut.PkScript, localPkScript):
localOutpoint.Index = uint32(i)
case bytes.Equal(txOut.PkScript, remoteWitnessHash):
remoteOutpoint.Index = uint32(i)
case bytes.Equal(txOut.PkScript, ourScript.PkScript):
ourOutpoint.Index = uint32(i)
case bytes.Equal(txOut.PkScript, theirWitnessHash):
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
// party's dust limit, the respective sign descriptor will be nil.
var (
localSignDesc *input.SignDescriptor
remoteSignDesc *input.SignDescriptor
ourSignDesc *input.SignDescriptor
theirSignDesc *input.SignDescriptor
)
// Compute the local and remote balances in satoshis.
localAmt := revokedSnapshot.LocalBalance.ToSatoshis()
remoteAmt := revokedSnapshot.RemoteBalance.ToSatoshis()
// Compute the balances in satoshis.
ourAmt := revokedSnapshot.LocalBalance.ToSatoshis()
theirAmt := revokedSnapshot.RemoteBalance.ToSatoshis()
// If the local balance exceeds the remote party's dust limit,
// instantiate the local sign descriptor.
if localAmt >= chanState.RemoteChanCfg.DustLimit {
localSignDesc = &input.SignDescriptor{
// If our balance exceeds the remote party's dust limit, instantiate
// the sign descriptor for our output.
if ourAmt >= chanState.RemoteChanCfg.DustLimit {
ourSignDesc = &input.SignDescriptor{
SingleTweak: keyRing.LocalCommitKeyTweak,
KeyDesc: chanState.LocalChanCfg.PaymentBasePoint,
WitnessScript: localPkScript,
WitnessScript: ourScript.WitnessScript,
Output: &wire.TxOut{
PkScript: localPkScript,
Value: int64(localAmt),
PkScript: ourScript.PkScript,
Value: int64(ourAmt),
},
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
// limit, assemble the remote sign descriptor.
if remoteAmt >= chanState.RemoteChanCfg.DustLimit {
remoteSignDesc = &input.SignDescriptor{
// Similarly, if their balance exceeds the remote party's dust limit,
// assemble the sign descriptor for their output, which we can sweep.
if theirAmt >= chanState.RemoteChanCfg.DustLimit {
theirSignDesc = &input.SignDescriptor{
KeyDesc: chanState.LocalChanCfg.RevocationBasePoint,
DoubleTweak: commitmentSecret,
WitnessScript: remotePkScript,
WitnessScript: theirPkScript,
Output: &wire.TxOut{
PkScript: remoteWitnessHash,
Value: int64(remoteAmt),
PkScript: theirWitnessHash,
Value: int64(theirAmt),
},
HashType: txscript.SigHashAll,
}
@ -2122,7 +1977,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
// remote commitment transaction, and *they* go to the second
// level.
secondLevelWitnessScript, err := input.SecondLevelHtlcScript(
keyRing.RevocationKey, keyRing.DelayKey, remoteDelay,
keyRing.RevocationKey, keyRing.ToLocalKey, theirDelay,
)
if err != nil {
return nil, err
@ -2188,13 +2043,13 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
BreachHeight: breachHeight,
RevokedStateNum: stateNum,
PendingHTLCs: revokedSnapshot.Htlcs,
LocalOutpoint: localOutpoint,
LocalOutputSignDesc: localSignDesc,
RemoteOutpoint: remoteOutpoint,
RemoteOutputSignDesc: remoteSignDesc,
LocalOutpoint: ourOutpoint,
LocalOutputSignDesc: ourSignDesc,
RemoteOutpoint: theirOutpoint,
RemoteOutputSignDesc: theirSignDesc,
HtlcRetributions: htlcRetributions,
KeyRing: keyRing,
RemoteDelay: remoteDelay,
RemoteDelay: theirDelay,
}, nil
}
@ -2305,33 +2160,41 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
keyRing *CommitmentKeyRing) (*commitment, error) {
commitChain := lc.localCommitChain
dustLimit := lc.channelState.LocalChanCfg.DustLimit
if remoteChain {
commitChain = lc.remoteCommitChain
dustLimit = lc.channelState.RemoteChanCfg.DustLimit
}
nextHeight := commitChain.tip().height + 1
// Run through all the HTLCs that will be covered by this transaction
// 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)
ourBalance, theirBalance, _, filteredHTLCView := lc.computeView(
htlcView, remoteChain, true,
)
feePerKw := filteredHTLCView.feePerKw
// Determine how many current HTLCs are over the dust limit, and should
// be counted for the purpose of fee calculation.
var dustLimit btcutil.Amount
if remoteChain {
dustLimit = lc.remoteChanCfg.DustLimit
} else {
dustLimit = lc.localChanCfg.DustLimit
// Actually generate unsigned commitment transaction for this view.
commitTx, err := lc.commitBuilder.createUnsignedCommitmentTx(
ourBalance, theirBalance, !remoteChain, feePerKw, nextHeight,
filteredHTLCView, keyRing,
)
if err != nil {
return nil, err
}
// With the commitment view created, store the resulting balances and
// transaction with the other parameters for this height.
c := &commitment{
ourBalance: ourBalance,
theirBalance: theirBalance,
ourBalance: commitTx.ourBalance,
theirBalance: commitTx.theirBalance,
txn: commitTx.txn,
fee: commitTx.fee,
ourMessageIndex: ourLogIndex,
ourHtlcIndex: ourHtlcIndex,
theirMessageIndex: theirLogIndex,
@ -2342,11 +2205,6 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
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
// commitment are mutated, we'll manually copy over each HTLC to its
// respective slice.
@ -2368,162 +2226,8 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
return c, nil
}
func (lc *LightningChannel) fundingTxIn() wire.TxIn {
return *wire.NewTxIn(&lc.channelState.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
func fundingTxIn(chanState *channeldb.OpenChannel) wire.TxIn {
return *wire.NewTxIn(&chanState.FundingOutpoint, nil, nil)
}
// evaluateHTLCView processes all update entries in both HTLC update logs,
@ -2852,7 +2556,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
sigJob.Tx, err = createHtlcTimeoutTx(
op, outputAmt, htlc.Timeout,
uint32(remoteChanCfg.CsvDelay),
keyRing.RevocationKey, keyRing.DelayKey,
keyRing.RevocationKey, keyRing.ToLocalKey,
)
if err != nil {
return nil, nil, err
@ -2903,7 +2607,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
}
sigJob.Tx, err = createHtlcSuccessTx(
op, outputAmt, uint32(remoteChanCfg.CsvDelay),
keyRing.RevocationKey, keyRing.DelayKey,
keyRing.RevocationKey, keyRing.ToLocalKey,
)
if err != nil {
return nil, nil, err
@ -3143,12 +2847,12 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
switch {
case ourBalance < ourInitialBalance &&
ourBalance < lnwire.NewMSatFromSatoshis(
lc.localChanCfg.ChanReserve):
lc.channelState.LocalChanCfg.ChanReserve):
return ErrBelowChanReserve
case theirBalance < theirInitialBalance &&
theirBalance < lnwire.NewMSatFromSatoshis(
lc.remoteChanCfg.ChanReserve):
lc.channelState.RemoteChanCfg.ChanReserve):
return ErrBelowChanReserve
}
@ -3199,7 +2903,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
// First check that the remote updates won't violate it's channel
// constraints.
err := validateUpdates(
filteredView.theirUpdates, lc.remoteChanCfg,
filteredView.theirUpdates, &lc.channelState.RemoteChanCfg,
)
if err != nil {
return err
@ -3208,7 +2912,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
// Secondly check that our updates won't violate our channel
// constraints.
err = validateUpdates(
filteredView.ourUpdates, lc.localChanCfg,
filteredView.ourUpdates, &lc.channelState.LocalChanCfg,
)
if err != nil {
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
// construct the commitment state.
keyRing := DeriveCommitmentKeys(
commitPoint, false, lc.channelState.ChanType.IsTweakless(),
lc.localChanCfg, lc.remoteChanCfg,
commitPoint, false, lc.channelState.ChanType,
&lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg,
)
// 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
// submit the set of signature jobs to the worker pool.
sigBatch, cancelChan, err := genRemoteHtlcSigJobs(keyRing,
lc.localChanCfg, lc.remoteChanCfg, newCommitView,
&lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg,
newCommitView,
)
if err != nil {
return sig, htlcSigs, nil, err
@ -3696,10 +3401,10 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool,
*htlcView) {
commitChain := lc.localCommitChain
dustLimit := lc.localChanCfg.DustLimit
dustLimit := lc.channelState.LocalChanCfg.DustLimit
if remoteChain {
commitChain = lc.remoteCommitChain
dustLimit = lc.remoteChanCfg.DustLimit
dustLimit = lc.channelState.RemoteChanCfg.DustLimit
}
// 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
}
totalHtlcWeight += input.HtlcWeight
totalHtlcWeight += input.HTLCWeight
}
for _, htlc := range filteredHTLCView.theirUpdates {
if htlcIsDust(!remoteChain, !remoteChain, feePerKw,
@ -3753,7 +3458,7 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool,
continue
}
totalHtlcWeight += input.HtlcWeight
totalHtlcWeight += input.HTLCWeight
}
totalCommitWeight := input.CommitWeight + totalHtlcWeight
@ -3814,7 +3519,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
successTx, err := createHtlcSuccessTx(op,
outputAmt, uint32(localChanCfg.CsvDelay),
keyRing.RevocationKey, keyRing.DelayKey)
keyRing.RevocationKey, keyRing.ToLocalKey)
if err != nil {
return nil, err
}
@ -3867,7 +3572,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
timeoutTx, err := createHtlcTimeoutTx(op,
outputAmt, htlc.Timeout,
uint32(localChanCfg.CsvDelay),
keyRing.RevocationKey, keyRing.DelayKey,
keyRing.RevocationKey, keyRing.ToLocalKey,
)
if err != nil {
return nil, err
@ -4032,8 +3737,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig,
}
commitPoint := input.ComputeCommitmentPoint(commitSecret[:])
keyRing := DeriveCommitmentKeys(
commitPoint, true, lc.channelState.ChanType.IsTweakless(),
lc.localChanCfg, lc.remoteChanCfg,
commitPoint, true, lc.channelState.ChanType,
&lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg,
)
// 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
// generated, we'll submit these jobs to the worker pool.
verifyJobs, err := genHtlcSigValidationJobs(
localCommitmentView, keyRing, htlcSigs, lc.localChanCfg,
lc.remoteChanCfg,
localCommitmentView, keyRing, htlcSigs,
&lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg,
)
if err != nil {
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
// signature.
verifyKey := btcec.PublicKey{
X: lc.remoteChanCfg.MultiSigKey.PubKey.X,
Y: lc.remoteChanCfg.MultiSigKey.PubKey.Y,
X: lc.channelState.RemoteChanCfg.MultiSigKey.PubKey.X,
Y: lc.channelState.RemoteChanCfg.MultiSigKey.PubKey.Y,
Curve: btcec.S256(),
}
cSig, err := commitSig.ToSignature()
@ -4930,101 +4635,6 @@ func (lc *LightningChannel) RemoteUpfrontShutdownScript() lnwire.DeliveryAddress
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
// populate it with witness data.
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
// required to spend from the multi-sig output.
ourKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed()
theirKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
ourKey := lc.channelState.LocalChanCfg.MultiSigKey.PubKey.
SerializeCompressed()
theirKey := lc.channelState.RemoteChanCfg.MultiSigKey.PubKey.
SerializeCompressed()
commitTx.TxIn[0].Witness = input.SpendMultiSig(
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
// so we can re-construct the HTLC state and also our payment key.
tweaklessCommit := chanState.ChanType.IsTweakless()
keyRing := DeriveCommitmentKeys(
commitPoint, false, tweaklessCommit, &chanState.LocalChanCfg,
&chanState.RemoteChanCfg,
commitPoint, false, chanState.ChanType,
&chanState.LocalChanCfg, &chanState.RemoteChanCfg,
)
// 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
// locate the output index of our non-delayed output on the commitment
// transaction.
selfP2WKH, err := input.CommitScriptUnencumbered(keyRing.NoDelayKey)
localDelay := uint32(chanState.LocalChanCfg.CsvDelay)
selfScript, err := CommitScriptToRemote(
chanState.ChanType, localDelay, keyRing.ToRemoteKey,
)
if err != nil {
return nil, fmt.Errorf("unable to create self commit "+
"script: %v", err)
@ -5165,7 +4779,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
)
for outputIndex, txOut := range commitTxBroadcast.TxOut {
if bytes.Equal(txOut.PkScript, selfP2WKH) {
if bytes.Equal(txOut.PkScript, selfScript.PkScript) {
selfPoint = &wire.OutPoint{
Hash: *commitSpend.SpenderTxHash,
Index: uint32(outputIndex),
@ -5186,21 +4800,15 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
SelfOutputSignDesc: input.SignDescriptor{
KeyDesc: localPayBase,
SingleTweak: keyRing.LocalCommitKeyTweak,
WitnessScript: selfP2WKH,
WitnessScript: selfScript.WitnessScript,
Output: &wire.TxOut{
Value: localBalance,
PkScript: selfP2WKH,
PkScript: selfScript.PkScript,
},
HashType: txscript.SigHashAll,
},
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{
@ -5396,7 +5004,7 @@ func newOutgoingHtlcResolution(signer input.Signer,
// transaction.
timeoutTx, err := createHtlcTimeoutTx(
op, secondLevelOutputAmt, htlc.RefundTimeout, csvDelay,
keyRing.RevocationKey, keyRing.DelayKey,
keyRing.RevocationKey, keyRing.ToLocalKey,
)
if err != nil {
return nil, err
@ -5436,7 +5044,7 @@ func newOutgoingHtlcResolution(signer input.Signer,
// transaction creates so we can generate the signDesc required to
// complete the claim process after a delay period.
htlcSweepScript, err := input.SecondLevelHtlcScript(
keyRing.RevocationKey, keyRing.DelayKey, csvDelay,
keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay,
)
if err != nil {
return nil, err
@ -5530,7 +5138,7 @@ func newIncomingHtlcResolution(signer input.Signer, localChanCfg *channeldb.Chan
secondLevelOutputAmt := htlc.Amt.ToSatoshis() - htlcFee
successTx, err := createHtlcSuccessTx(
op, secondLevelOutputAmt, csvDelay,
keyRing.RevocationKey, keyRing.DelayKey,
keyRing.RevocationKey, keyRing.ToLocalKey,
)
if err != nil {
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
// CSV delay has passed.
htlcSweepScript, err := input.SecondLevelHtlcScript(
keyRing.RevocationKey, keyRing.DelayKey, csvDelay,
keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay,
)
if err != nil {
return nil, err
@ -5788,11 +5396,13 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
}
commitPoint := input.ComputeCommitmentPoint(revocation[:])
keyRing := DeriveCommitmentKeys(
commitPoint, true, chanState.ChanType.IsTweakless(),
commitPoint, true, chanState.ChanType,
&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 {
return nil, err
}
@ -5826,9 +5436,6 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
// nil.
var commitResolution *CommitOutputResolution
if len(delayScript) != 0 {
singleTweak := input.SingleTweakBytes(
commitPoint, chanState.LocalChanCfg.DelayBasePoint.PubKey,
)
localBalance := localCommit.LocalBalance
commitResolution = &CommitOutputResolution{
SelfOutPoint: wire.OutPoint{
@ -5837,7 +5444,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
},
SelfOutputSignDesc: input.SignDescriptor{
KeyDesc: chanState.LocalChanCfg.DelayBasePoint,
SingleTweak: singleTweak,
SingleTweak: keyRing.LocalCommitKeyTweak,
WitnessScript: selfScript,
Output: &wire.TxOut{
PkScript: delayScript,
@ -5910,10 +5517,11 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount,
theirBalance = theirBalance - proposedFee + commitFee
}
closeTx := CreateCooperativeCloseTx(lc.fundingTxIn(),
lc.localChanCfg.DustLimit, lc.remoteChanCfg.DustLimit,
ourBalance, theirBalance, localDeliveryScript,
remoteDeliveryScript, lc.channelState.IsInitiator)
closeTx := CreateCooperativeCloseTx(
fundingTxIn(lc.channelState), lc.channelState.LocalChanCfg.DustLimit,
lc.channelState.RemoteChanCfg.DustLimit, ourBalance, theirBalance,
localDeliveryScript, remoteDeliveryScript,
)
// Ensure that the transaction doesn't explicitly violate any
// 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
// on this active channel back to both parties. In this current model,
// the initiator pays full fees for the cooperative close transaction.
closeTx := CreateCooperativeCloseTx(lc.fundingTxIn(),
lc.localChanCfg.DustLimit, lc.remoteChanCfg.DustLimit,
ourBalance, theirBalance, localDeliveryScript,
remoteDeliveryScript, lc.channelState.IsInitiator)
closeTx := CreateCooperativeCloseTx(
fundingTxIn(lc.channelState), lc.channelState.LocalChanCfg.DustLimit,
lc.channelState.RemoteChanCfg.DustLimit, ourBalance, theirBalance,
localDeliveryScript, remoteDeliveryScript,
)
// Ensure that the transaction doesn't explicitly validate any
// 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
// pubkeys+sigs on the stack.
ourKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed()
theirKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed()
ourKey := lc.channelState.LocalChanCfg.MultiSigKey.PubKey.
SerializeCompressed()
theirKey := lc.channelState.RemoteChanCfg.MultiSigKey.PubKey.
SerializeCompressed()
witness := input.SpendMultiSig(lc.signDesc.WitnessScript, ourKey,
localSig, theirKey, remoteSig)
closeTx.TxIn[0].Witness = witness
@ -6210,60 +5821,6 @@ func (lc *LightningChannel) generateRevocation(height uint64) (*lnwire.RevokeAnd
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
// parties, then broadcast cooperatively closes an active channel. The creation
// 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.
func CreateCooperativeCloseTx(fundingTxIn wire.TxIn,
localDust, remoteDust, ourBalance, theirBalance btcutil.Amount,
ourDeliveryScript, theirDeliveryScript []byte,
initiator bool) *wire.MsgTx {
ourDeliveryScript, theirDeliveryScript []byte) *wire.MsgTx {
// Construct the transaction to perform a cooperative closure of the
// 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.
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
@ -6470,5 +6026,5 @@ func (lc *LightningChannel) RemoteCommitHeight() uint64 {
// FwdMinHtlc returns the minimum HTLC value required by the remote node, i.e.
// the minimum value HTLC we can forward on this channel.
func (lc *LightningChannel) FwdMinHtlc() lnwire.MilliSatoshi {
return lc.localChanCfg.MinHTLC
return lc.channelState.LocalChanCfg.MinHTLC
}

View File

@ -600,7 +600,7 @@ func TestForceClose(t *testing.T) {
// Factoring in the fee rate, Alice's amount should properly reflect
// 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(
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
// pre-committed to at channel opening.
if aliceCommitResolution.MaturityDelay !=
uint32(aliceChannel.localChanCfg.CsvDelay) {
uint32(aliceChannel.channelState.LocalChanCfg.CsvDelay) {
t.Fatalf("alice: incorrect local CSV delay in ForceCloseSummary, "+
"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
// they can create small dust ouputs without going under
// their channel reserves.
aliceChannel.localChanCfg.ChanReserve = 0
bobChannel.localChanCfg.ChanReserve = 0
aliceChannel.remoteChanCfg.ChanReserve = 0
bobChannel.remoteChanCfg.ChanReserve = 0
aliceChannel.channelState.LocalChanCfg.ChanReserve = 0
bobChannel.channelState.LocalChanCfg.ChanReserve = 0
aliceChannel.channelState.RemoteChanCfg.ChanReserve = 0
bobChannel.channelState.RemoteChanCfg.ChanReserve = 0
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
// channel reserve to be 0.
aliceChannel.localChanCfg.ChanReserve = 0
bobChannel.remoteChanCfg.ChanReserve = 0
aliceChannel.channelState.LocalChanCfg.ChanReserve = 0
bobChannel.channelState.RemoteChanCfg.ChanReserve = 0
// This amount should leave an amount larger than Alice'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
// 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.
const numHTLCs = 3
@ -5346,14 +5346,14 @@ func TestMaxAcceptedHTLCs(t *testing.T) {
// Set the remote's required MaxAcceptedHtlcs. This means that alice
// can only offer the remote up to numHTLCs HTLCs.
aliceChannel.localChanCfg.MaxAcceptedHtlcs = numHTLCs
bobChannel.remoteChanCfg.MaxAcceptedHtlcs = numHTLCs
aliceChannel.channelState.LocalChanCfg.MaxAcceptedHtlcs = numHTLCs
bobChannel.channelState.RemoteChanCfg.MaxAcceptedHtlcs = numHTLCs
// Similarly, set the remote config's MaxAcceptedHtlcs. This means
// that the remote will be aware that Alice will only accept up to
// numHTLCsRecevied at a time.
aliceChannel.remoteChanCfg.MaxAcceptedHtlcs = numHTLCsReceived
bobChannel.localChanCfg.MaxAcceptedHtlcs = numHTLCsReceived
aliceChannel.channelState.RemoteChanCfg.MaxAcceptedHtlcs = numHTLCsReceived
bobChannel.channelState.LocalChanCfg.MaxAcceptedHtlcs = numHTLCsReceived
// Each HTLC amount is 0.1 BTC.
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
// cannot offer Bob HTLCs with a total value above this limit at a given
// time.
aliceChannel.localChanCfg.MaxPendingAmount = maxPending
bobChannel.remoteChanCfg.MaxPendingAmount = maxPending
aliceChannel.channelState.LocalChanCfg.MaxPendingAmount = maxPending
bobChannel.channelState.RemoteChanCfg.MaxPendingAmount = maxPending
// First, we'll add 2 HTLCs of 1.5 BTC each to Alice's commitment.
// 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,
// 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
// minimum reserve, and this will be found in his remote
// config.
bobChannel.remoteChanCfg.ChanReserve = aliceMinReserve
bobChannel.channelState.RemoteChanCfg.ChanReserve = aliceMinReserve
// We set Bob's channel reserve to a value that is larger than
// his current balance in the channel. This will ensure that
// after a channel is first opened, Bob can still receive HTLCs
// even though his balance is less than his channel reserve.
bobMinReserve := btcutil.Amount(6 * btcutil.SatoshiPerBitcoin)
bobChannel.localChanCfg.ChanReserve = bobMinReserve
aliceChannel.remoteChanCfg.ChanReserve = bobMinReserve
bobChannel.channelState.LocalChanCfg.ChanReserve = bobMinReserve
aliceChannel.channelState.RemoteChanCfg.ChanReserve = bobMinReserve
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
// remote will not accept any HTLCs of value less than specified.
aliceChannel.localChanCfg.MinHTLC = minValue
bobChannel.remoteChanCfg.MinHTLC = minValue
aliceChannel.channelState.LocalChanCfg.MinHTLC = minValue
bobChannel.channelState.RemoteChanCfg.MinHTLC = minValue
// First, we will add an HTLC of 0.5 BTC. This will not trigger
// ErrBelowMinHTLC.

578
lnwallet/commitment.go Normal file
View 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
}

View File

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

View File

@ -423,14 +423,12 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
channel := LightningChannel{
channelState: &channelState,
Signer: signer,
localChanCfg: &channelState.LocalChanCfg,
remoteChanCfg: &channelState.RemoteChanCfg,
commitBuilder: NewCommitmentBuilder(&channelState),
}
err = channel.createSignDesc()
if err != nil {
t.Fatalf("Failed to generate channel sign descriptor: %v", err)
}
channel.createStateHintObfuscator()
// The commitmentPoint is technically hidden in the spec, but we need it to
// generate the correct tweak.
@ -441,8 +439,8 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
LocalHtlcKeyTweak: tweak,
LocalHtlcKey: tc.localPaymentPubKey,
RemoteHtlcKey: tc.remotePaymentPubKey,
DelayKey: tc.localDelayPubKey,
NoDelayKey: tc.remotePaymentPubKey,
ToLocalKey: tc.localDelayPubKey,
ToRemoteKey: tc.remotePaymentPubKey,
RevocationKey: tc.localRevocationPubKey,
}
@ -796,23 +794,32 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
}
theHTLCView := htlcViewFromHTLCs(htlcs)
feePerKw := chainfee.SatPerKWeight(test.commitment.FeePerKw)
isOurs := true
height := test.commitment.CommitHeight
// Create unsigned commitment transaction.
commitmentView := &commitment{
height: test.commitment.CommitHeight,
ourBalance: test.commitment.LocalBalance,
theirBalance: test.commitment.RemoteBalance,
feePerKw: chainfee.SatPerKWeight(test.commitment.FeePerKw),
dustLimit: tc.dustLimit,
isOurs: true,
}
err = channel.createCommitmentTx(
commitmentView, theHTLCView, keys,
view, err := channel.commitBuilder.createUnsignedCommitmentTx(
test.commitment.LocalBalance,
test.commitment.RemoteBalance, isOurs, feePerKw,
height, theHTLCView, keys,
)
if err != nil {
t.Errorf("Case %d: Failed to create new commitment tx: %v", i, err)
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.
channelState.LocalCommitment = test.commitment
channelState.LocalCommitment.Htlcs = htlcs
@ -845,8 +852,8 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
// commitment tx.
htlcResolutions, err := extractHtlcResolutions(
chainfee.SatPerKWeight(test.commitment.FeePerKw), true, signer,
htlcs, keys, channel.localChanCfg, channel.remoteChanCfg,
commitTx.TxHash(),
htlcs, keys, &channel.channelState.LocalChanCfg,
&channel.channelState.RemoteChanCfg, commitTx.TxHash(),
)
if err != nil {
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)
const channelBalance = btcutil.Amount(1 * 10e8)
const csvTimeout = uint32(5)
const csvTimeout = 5
// We also set up set some resources for the commitment transaction.
// 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
// pubkey.
bobPayKey := input.TweakPubKey(bobKeyPub, commitPoint)
channelType := channeldb.SingleFunderBit
if tweakless {
bobPayKey = bobKeyPub
channelType = channeldb.SingleFunderTweaklessBit
}
aliceCommitTweak := input.SingleTweakBytes(commitPoint, aliceKeyPub)
@ -1052,6 +1061,20 @@ func testSpendValidation(t *testing.T, tweakless bool) {
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.
// We only focus on a single party's transactions, as the scripts are
// 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
// immediately with either the revocation key, or his regular key.
keyRing := &CommitmentKeyRing{
DelayKey: aliceDelayKey,
ToLocalKey: aliceDelayKey,
RevocationKey: revokePubKey,
NoDelayKey: bobPayKey,
ToRemoteKey: bobPayKey,
}
commitmentTx, err := CreateCommitTx(
*fakeFundingTxIn, keyRing, csvTimeout, channelBalance,
channelBalance, DefaultDustLimit(),
channelType, *fakeFundingTxIn, keyRing, aliceChanCfg,
bobChanCfg, channelBalance, channelBalance,
)
if err != nil {
t.Fatalf("unable to create commitment transaction: %v", nil)

View File

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

View File

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

View File

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

View File

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

View File

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