Merge pull request #3829 from halseth/pluggable-commitments-lnwallet
Pluggable commitments
This commit is contained in:
commit
3aca9d24b8
@ -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
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{
|
||||||
|
Loading…
Reference in New Issue
Block a user