diff --git a/channeldb/channel.go b/channeldb/channel.go index fefd2740..7bc9e480 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -869,6 +869,14 @@ func (c *OpenChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) { // allowing us to sweep our funds. if c.hasChanStatus(ChanStatusRestored) { currentCommitSecret[0] ^= 1 + + // If this is a tweakless channel, then we'll purposefully send + // a next local height taht's invalid to trigger a force close + // on their end. We do this as tweakless channels don't require + // that the commitment point is valid, only that it's present. + if c.ChanType.IsTweakless() { + nextLocalCommitHeight = 0 + } } return &lnwire.ChannelReestablish{ diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index a908afb3..4f48c4f6 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -257,9 +257,10 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte, } aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:]) - aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(aliceAmount, - bobAmount, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint, - *fundingTxIn) + aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns( + aliceAmount, bobAmount, &aliceCfg, &bobCfg, aliceCommitPoint, + bobCommitPoint, *fundingTxIn, true, + ) if err != nil { return nil, nil, nil, err } diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 90871498..bf8cdadb 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -863,6 +863,12 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool, diskCommit *channeldb.ChannelCommitment, localCommitPoint, remoteCommitPoint *btcec.PublicKey) (*commitment, error) { + // If this commit is tweakless, then it'll affect the way we derive our + // keys, which will affect the commitment transaction reconstruction. + // So we'll determine this first, before we do anything else. + tweaklessCommit := (lc.channelState.ChanType == + channeldb.SingleFunderTweakless) + // First, we'll need to re-derive the commitment key ring for each // party used within this particular state. If this is a pending commit // (we extended but weren't able to complete the commitment dance @@ -871,14 +877,14 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal bool, var localCommitKeys, remoteCommitKeys *CommitmentKeyRing if localCommitPoint != nil { localCommitKeys = deriveCommitmentKeys( - localCommitPoint, true, lc.localChanCfg, - lc.remoteChanCfg, + localCommitPoint, true, tweaklessCommit, + lc.localChanCfg, lc.remoteChanCfg, ) } if remoteCommitPoint != nil { remoteCommitKeys = deriveCommitmentKeys( - remoteCommitPoint, false, lc.localChanCfg, - lc.remoteChanCfg, + remoteCommitPoint, false, tweaklessCommit, + lc.localChanCfg, lc.remoteChanCfg, ) } @@ -978,7 +984,8 @@ type CommitmentKeyRing struct { // 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 bool, +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 @@ -1023,11 +1030,20 @@ func deriveCommitmentKeys(commitPoint *btcec.PublicKey, isOurCommit bool, // 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.NoDelayKey = input.TweakPubKey(noDelayBasePoint, 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 } @@ -1695,9 +1711,11 @@ func (lc *LightningChannel) restoreCommitState( // We'll also re-create the set of commitment keys needed to // fully re-derive the state. + tweaklessCommit := (lc.channelState.ChanType == + channeldb.SingleFunderTweakless) pendingRemoteKeyChain = deriveCommitmentKeys( - pendingCommitPoint, false, lc.localChanCfg, - lc.remoteChanCfg, + pendingCommitPoint, false, tweaklessCommit, + lc.localChanCfg, lc.remoteChanCfg, ) } @@ -1981,8 +1999,11 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // With the commitment point generated, we can now generate the four // keys we'll need to reconstruct the commitment state, - keyRing := deriveCommitmentKeys(commitmentPoint, false, - &chanState.LocalChanCfg, &chanState.RemoteChanCfg) + tweaklessCommit := chanState.ChanType == channeldb.SingleFunderTweakless + keyRing := deriveCommitmentKeys( + commitmentPoint, false, tweaklessCommit, + &chanState.LocalChanCfg, &chanState.RemoteChanCfg, + ) // Next, reconstruct the scripts as they were present at this state // number so we can have the proper witness script to sign and include @@ -3042,6 +3063,146 @@ func (lc *LightningChannel) createCommitDiff( }, nil } +// validateCommitmentSanity is used to validate the current state of the +// commitment transaction in terms of the ChannelConstraints that we and our +// remote peer agreed upon during the funding workflow. The predictAdded +// parameter should be set to a valid PaymentDescriptor if we are validating +// in the state when adding a new HTLC, or nil otherwise. +func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, + ourLogCounter uint64, remoteChain bool, + predictAdded *PaymentDescriptor) error { + + // Fetch all updates not committed. + view := lc.fetchHTLCView(theirLogCounter, ourLogCounter) + + // If we are checking if we can add a new HTLC, we add this to the + // update log, in order to validate the sanity of the commitment + // resulting from _actually adding_ this HTLC to the state. + if predictAdded != nil { + // If we are adding an HTLC, this will be an Add to the local + // update log. + view.ourUpdates = append(view.ourUpdates, predictAdded) + } + + commitChain := lc.localCommitChain + if remoteChain { + commitChain = lc.remoteCommitChain + } + ourInitialBalance := commitChain.tip().ourBalance + theirInitialBalance := commitChain.tip().theirBalance + + ourBalance, theirBalance, commitWeight, filteredView := lc.computeView( + view, remoteChain, false, + ) + feePerKw := filteredView.feePerKw + + // Calculate the commitment fee, and subtract it from the initiator's + // balance. + commitFee := feePerKw.FeeForWeight(commitWeight) + commitFeeMsat := lnwire.NewMSatFromSatoshis(commitFee) + if lc.channelState.IsInitiator { + ourBalance -= commitFeeMsat + } else { + theirBalance -= commitFeeMsat + } + + // As a quick sanity check, we'll ensure that if we interpret the + // balances as signed integers, they haven't dipped down below zero. If + // they have, then this indicates that a party doesn't have sufficient + // balance to satisfy the final evaluated HTLC's. + switch { + case int64(ourBalance) < 0: + return ErrBelowChanReserve + case int64(theirBalance) < 0: + return ErrBelowChanReserve + } + + // Ensure that the fee being applied is enough to be relayed across the + // network in a reasonable time frame. + if feePerKw < FeePerKwFloor { + return fmt.Errorf("commitment fee per kw %v below fee floor %v", + feePerKw, FeePerKwFloor) + } + + // If the added HTLCs will decrease the balance, make sure they won't + // dip the local and remote balances below the channel reserves. + switch { + case ourBalance < ourInitialBalance && + ourBalance < lnwire.NewMSatFromSatoshis( + lc.localChanCfg.ChanReserve): + + return ErrBelowChanReserve + case theirBalance < theirInitialBalance && + theirBalance < lnwire.NewMSatFromSatoshis( + lc.remoteChanCfg.ChanReserve): + + return ErrBelowChanReserve + } + + // validateUpdates take a set of updates, and validates them against + // the passed channel constraints. + validateUpdates := func(updates []*PaymentDescriptor, + constraints *channeldb.ChannelConfig) error { + + // We keep track of the number of HTLCs in flight for the + // commitment, and the amount in flight. + var numInFlight uint16 + var amtInFlight lnwire.MilliSatoshi + + // Go through all updates, checking that they don't violate the + // channel constraints. + for _, entry := range updates { + if entry.EntryType == Add { + // An HTLC is being added, this will add to the + // number and amount in flight. + amtInFlight += entry.Amount + numInFlight++ + + // Check that the value of the HTLC they added + // is above our minimum. + if entry.Amount < constraints.MinHTLC { + return ErrBelowMinHTLC + } + } + } + + // Now that we know the total value of added HTLCs, we check + // that this satisfy the MaxPendingAmont contraint. + if amtInFlight > constraints.MaxPendingAmount { + return ErrMaxPendingAmount + } + + // In this step, we verify that the total number of active + // HTLCs does not exceed the constraint of the maximum number + // of HTLCs in flight. + if numInFlight > constraints.MaxAcceptedHtlcs { + return ErrMaxHTLCNumber + } + + return nil + } + + // First check that the remote updates won't violate it's channel + // constraints. + err := validateUpdates( + filteredView.theirUpdates, lc.remoteChanCfg, + ) + if err != nil { + return err + } + + // Secondly check that our updates won't violate our channel + // constraints. + err = validateUpdates( + filteredView.ourUpdates, lc.localChanCfg, + ) + if err != nil { + return err + } + + return nil +} + // SignNextCommitment signs a new commitment which includes any previous // unsettled HTLCs, any new HTLCs, and any modifications to prior HTLCs // committed in previous commitment updates. Signing a new commitment @@ -3091,8 +3252,11 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, []ch // Grab the next commitment point for the remote party. This will be // used within fetchCommitmentView to derive all the keys necessary to // construct the commitment state. + tweaklessCommit := (lc.channelState.ChanType == + channeldb.SingleFunderTweakless) keyRing := deriveCommitmentKeys( - commitPoint, false, lc.localChanCfg, lc.remoteChanCfg, + commitPoint, false, tweaklessCommit, lc.localChanCfg, + lc.remoteChanCfg, ) // Create a new commitment view which will calculate the evaluated @@ -3495,7 +3659,10 @@ func (lc *LightningChannel) ProcessChanSyncMsg( commitPoint = lc.channelState.RemoteNextRevocation } - if commitPoint != nil && + // Only if this is a tweakless channel will we attempt to verify the + // commitment point, as otherwise it has no validity requirements. + tweakless := lc.channelState.ChanType.IsTweakless() + if !tweakless && commitPoint != nil && !commitPoint.IsEqual(msg.LocalUnrevokedCommitPoint) { walletLog.Errorf("ChannelPoint(%v), sync failed: remote "+ @@ -3585,138 +3752,6 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool, return ourBalance, theirBalance, totalCommitWeight, filteredHTLCView } -// validateCommitmentSanity is used to validate the current state of the -// commitment transaction in terms of the ChannelConstraints that we and our -// remote peer agreed upon during the funding workflow. The predictAdded -// parameter should be set to a valid PaymentDescriptor if we are validating -// in the state when adding a new HTLC, or nil otherwise. -func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, - ourLogCounter uint64, remoteChain bool, - predictAdded *PaymentDescriptor) error { - - // Fetch all updates not committed. - view := lc.fetchHTLCView(theirLogCounter, ourLogCounter) - - // If we are checking if we can add a new HTLC, we add this to the - // update log, in order to validate the sanity of the commitment - // resulting from _actually adding_ this HTLC to the state. - if predictAdded != nil { - // If we are adding an HTLC, this will be an Add to the local - // update log. - view.ourUpdates = append(view.ourUpdates, predictAdded) - } - - commitChain := lc.localCommitChain - if remoteChain { - commitChain = lc.remoteCommitChain - } - ourInitialBalance := commitChain.tip().ourBalance - theirInitialBalance := commitChain.tip().theirBalance - - ourBalance, theirBalance, commitWeight, filteredView := lc.computeView( - view, remoteChain, false, - ) - feePerKw := filteredView.feePerKw - - // Calculate the commitment fee, and subtract it from the initiator's - // balance. - commitFee := feePerKw.FeeForWeight(commitWeight) - commitFeeMsat := lnwire.NewMSatFromSatoshis(commitFee) - if lc.channelState.IsInitiator { - ourBalance -= commitFeeMsat - } else { - theirBalance -= commitFeeMsat - } - - // As a quick sanity check, we'll ensure that if we interpret the - // balances as signed integers, they haven't dipped down below zero. If - // they have, then this indicates that a party doesn't have sufficient - // balance to satisfy the final evaluated HTLC's. - switch { - case int64(ourBalance) < 0: - return ErrBelowChanReserve - case int64(theirBalance) < 0: - return ErrBelowChanReserve - } - - // If the added HTLCs will decrease the balance, make sure they won't - // dip the local and remote balances below the channel reserves. - if ourBalance < ourInitialBalance && - ourBalance < lnwire.NewMSatFromSatoshis( - lc.localChanCfg.ChanReserve) { - return ErrBelowChanReserve - } - - if theirBalance < theirInitialBalance && - theirBalance < lnwire.NewMSatFromSatoshis( - lc.remoteChanCfg.ChanReserve) { - return ErrBelowChanReserve - } - - // validateUpdates take a set of updates, and validates them against - // the passed channel constraints. - validateUpdates := func(updates []*PaymentDescriptor, - constraints *channeldb.ChannelConfig) error { - - // We keep track of the number of HTLCs in flight for the - // commitment, and the amount in flight. - var numInFlight uint16 - var amtInFlight lnwire.MilliSatoshi - - // Go through all updates, checking that they don't violate the - // channel constraints. - for _, entry := range updates { - if entry.EntryType == Add { - // An HTLC is being added, this will add to the - // number and amount in flight. - amtInFlight += entry.Amount - numInFlight++ - - // Check that the value of the HTLC they added - // is above our minimum. - if entry.Amount < constraints.MinHTLC { - return ErrBelowMinHTLC - } - } - } - - // Now that we know the total value of added HTLCs, we check - // that this satisfy the MaxPendingAmont contraint. - if amtInFlight > constraints.MaxPendingAmount { - return ErrMaxPendingAmount - } - - // In this step, we verify that the total number of active - // HTLCs does not exceed the constraint of the maximum number - // of HTLCs in flight. - if numInFlight > constraints.MaxAcceptedHtlcs { - return ErrMaxHTLCNumber - } - - return nil - } - - // First check that the remote updates won't violate it's channel - // constraints. - err := validateUpdates( - filteredView.theirUpdates, lc.remoteChanCfg, - ) - if err != nil { - return err - } - - // Secondly check that our updates won't violate our channel - // constraints. - err = validateUpdates( - filteredView.ourUpdates, lc.localChanCfg, - ) - if err != nil { - return err - } - - return nil -} - // genHtlcSigValidationJobs generates a series of signatures verification jobs // meant to verify all the signatures for HTLC's attached to a newly created // commitment state. The jobs generated are fully populated, and can be sent @@ -3976,8 +4011,11 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig, return err } commitPoint := input.ComputeCommitmentPoint(commitSecret[:]) + tweaklessCommit := (lc.channelState.ChanType == + channeldb.SingleFunderTweakless) keyRing := deriveCommitmentKeys( - commitPoint, true, lc.localChanCfg, lc.remoteChanCfg, + commitPoint, true, tweaklessCommit, lc.localChanCfg, + lc.remoteChanCfg, ) // With the current commitment point re-calculated, construct the new @@ -5006,8 +5044,9 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si // First, we'll generate the commitment point and the revocation point // so we can re-construct the HTLC state and also our payment key. + tweaklessCommit := chanState.ChanType == channeldb.SingleFunderTweakless keyRing := deriveCommitmentKeys( - commitPoint, false, &chanState.LocalChanCfg, + commitPoint, false, tweaklessCommit, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, ) @@ -5070,6 +5109,12 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si }, 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{ @@ -5655,8 +5700,11 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Si return nil, err } commitPoint := input.ComputeCommitmentPoint(revocation[:]) - keyRing := deriveCommitmentKeys(commitPoint, true, &chanState.LocalChanCfg, - &chanState.RemoteChanCfg) + tweaklessCommit := chanState.ChanType == channeldb.SingleFunderTweakless + keyRing := deriveCommitmentKeys( + commitPoint, true, tweaklessCommit, &chanState.LocalChanCfg, + &chanState.RemoteChanCfg, + ) selfScript, err := input.CommitScriptToSelf(csvTimeout, keyRing.DelayKey, keyRing.RevocationKey) if err != nil { diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index ca686d34..91e5e17d 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -52,22 +52,13 @@ func assertOutputExistsByValue(t *testing.T, commitTx *wire.MsgTx, spew.Sdump(commitTx)) } -// TestSimpleAddSettleWorkflow tests a simple channel scenario wherein the -// local node (Alice in this case) creates a new outgoing HTLC to bob, commits -// this change, then bob immediately commits a settlement of the HTLC after the -// initial add is fully committed in both commit chains. -// -// TODO(roasbeef): write higher level framework to exercise various states of -// the state machine -// * DSL language perhaps? -// * constructed via input/output files -func TestSimpleAddSettleWorkflow(t *testing.T) { - t.Parallel() - +// testAddSettleWorkflow tests a simple channel scenario where Alice and Bob +// add, the settle an HTLC between themselves. +func testAddSettleWorkflow(t *testing.T, tweakless bool) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(tweakless) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -295,10 +286,10 @@ func TestSimpleAddSettleWorkflow(t *testing.T) { "instead forwarding: %v", len(fwdPkg.SettleFails)) } - // At this point, Bob should have 6 BTC settled, with Alice still having - // 4 BTC. Alice's channel should show 1 BTC sent and Bob's channel - // should show 1 BTC received. They should also be at commitment height - // two, with the revocation window extended by 1 (5). + // At this point, Bob should have 6 BTC settled, with Alice still + // having 4 BTC. Alice's channel should show 1 BTC sent and Bob's + // channel should show 1 BTC received. They should also be at + // commitment height two, with the revocation window extended by 1 (5). mSatTransferred := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin) if aliceChannel.channelState.TotalMSatSent != mSatTransferred { t.Fatalf("alice satoshis sent incorrect %v vs %v expected", @@ -348,6 +339,26 @@ func TestSimpleAddSettleWorkflow(t *testing.T) { } } +// TestSimpleAddSettleWorkflow tests a simple channel scenario wherein the +// local node (Alice in this case) creates a new outgoing HTLC to bob, commits +// this change, then bob immediately commits a settlement of the HTLC after the +// initial add is fully committed in both commit chains. +// +// TODO(roasbeef): write higher level framework to exercise various states of +// the state machine +// * DSL language perhaps? +// * constructed via input/output files +func TestSimpleAddSettleWorkflow(t *testing.T) { + t.Parallel() + + for _, tweakless := range []bool{true, false} { + tweakless := tweakless + t.Run(fmt.Sprintf("tweakless=%v", tweakless), func(t *testing.T) { + testAddSettleWorkflow(t, tweakless) + }) + } +} + // TestCheckCommitTxSize checks that estimation size of commitment // transaction with some degree of error corresponds to the actual size. func TestCheckCommitTxSize(t *testing.T) { @@ -377,7 +388,7 @@ func TestCheckCommitTxSize(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -436,7 +447,7 @@ func TestCooperativeChannelClosure(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -473,7 +484,8 @@ func TestCooperativeChannelClosure(t *testing.T) { // transaction is well formed, and the signatures verify. aliceCloseTx, _, err := bobChannel.CompleteCooperativeClose( bobCloseSig, aliceCloseSig, bobDeliveryScript, - aliceDeliveryScript, bobFee) + aliceDeliveryScript, bobFee, + ) if err != nil { t.Fatalf("unable to complete alice cooperative close: %v", err) } @@ -481,7 +493,8 @@ func TestCooperativeChannelClosure(t *testing.T) { bobCloseTx, _, err := aliceChannel.CompleteCooperativeClose( aliceCloseSig, bobCloseSig, aliceDeliveryScript, - bobDeliveryScript, aliceFee) + bobDeliveryScript, aliceFee, + ) if err != nil { t.Fatalf("unable to complete bob cooperative close: %v", err) } @@ -503,7 +516,7 @@ func TestForceClose(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -787,7 +800,7 @@ func TestForceCloseDustOutput(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -905,7 +918,7 @@ func TestDustHTLCFees(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -982,7 +995,7 @@ func TestHTLCDustLimit(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1067,7 +1080,7 @@ func TestHTLCSigNumber(t *testing.T) { // Create a test channel funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. Alice's dustlimit is 200 sat, while // Bob has 1300 sat. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1237,7 +1250,7 @@ func TestChannelBalanceDustLimit(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1305,7 +1318,7 @@ func TestStateUpdatePersistence(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1353,7 +1366,7 @@ func TestStateUpdatePersistence(t *testing.T) { } // Also add a fee update to the update logs. - fee := SatPerKWeight(111) + fee := SatPerKWeight(333) if err := aliceChannel.UpdateFee(fee); err != nil { t.Fatalf("unable to send fee update") } @@ -1646,7 +1659,7 @@ func TestCancelHTLC(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1760,7 +1773,7 @@ func TestCooperativeCloseDustAdherence(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1921,7 +1934,7 @@ func TestCooperativeCloseDustAdherence(t *testing.T) { func TestUpdateFeeAdjustments(t *testing.T) { t.Parallel() - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1959,7 +1972,7 @@ func TestUpdateFeeAdjustments(t *testing.T) { // Finally, we'll attempt to adjust the fee down and use a fee which is // smaller than the initial base fee rate. The fee application and // state transition should proceed without issue. - newFee = SatPerKWeight(baseFeeRate / 100) + newFee = SatPerKWeight(baseFeeRate / 10) if err := aliceChannel.UpdateFee(newFee); err != nil { t.Fatalf("unable to alice update fee: %v", err) } @@ -1976,7 +1989,7 @@ func TestUpdateFeeAdjustments(t *testing.T) { func TestUpdateFeeFail(t *testing.T) { t.Parallel() - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -1984,7 +1997,9 @@ func TestUpdateFeeFail(t *testing.T) { // Bob receives the update, that will apply to his commitment // transaction. - bobChannel.ReceiveUpdateFee(111) + if err := bobChannel.ReceiveUpdateFee(333); err != nil { + t.Fatalf("unable to apply fee update: %v", err) + } // Alice sends signature for commitment that does not cover any fee // update. @@ -2008,7 +2023,7 @@ func TestUpdateFeeFail(t *testing.T) { func TestUpdateFeeConcurrentSig(t *testing.T) { t.Parallel() - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2033,7 +2048,7 @@ func TestUpdateFeeConcurrentSig(t *testing.T) { } // Simulate Alice sending update fee message to bob. - fee := SatPerKWeight(111) + fee := SatPerKWeight(333) if err := aliceChannel.UpdateFee(fee); err != nil { t.Fatalf("unable to send fee update") } @@ -2094,7 +2109,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2119,7 +2134,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { } // Simulate Alice sending update fee message to bob. - fee := SatPerKWeight(111) + fee := SatPerKWeight(333) aliceChannel.UpdateFee(fee) bobChannel.ReceiveUpdateFee(fee) @@ -2208,7 +2223,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2233,7 +2248,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { } // Simulate Alice sending update fee message to bob - fee := SatPerKWeight(111) + fee := SatPerKWeight(333) aliceChannel.UpdateFee(fee) bobChannel.ReceiveUpdateFee(fee) @@ -2349,7 +2364,7 @@ func TestUpdateFeeReceiverSendsUpdate(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2357,7 +2372,7 @@ func TestUpdateFeeReceiverSendsUpdate(t *testing.T) { // Since Alice is the channel initiator, she should fail when receiving // fee update - fee := SatPerKWeight(111) + fee := SatPerKWeight(333) err = aliceChannel.ReceiveUpdateFee(fee) if err == nil { t.Fatalf("expected alice to fail receiving fee update") @@ -2378,15 +2393,15 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } defer cleanUp() // Simulate Alice sending update fee message to bob. - fee1 := SatPerKWeight(111) - fee2 := SatPerKWeight(222) + fee1 := SatPerKWeight(333) + fee2 := SatPerKWeight(333) fee := SatPerKWeight(333) aliceChannel.UpdateFee(fee1) aliceChannel.UpdateFee(fee2) @@ -2490,7 +2505,7 @@ func TestAddHTLCNegativeBalance(t *testing.T) { // We'll kick off the test by creating our channels which both are // loaded with 5 BTC each. - aliceChannel, _, cleanUp, err := CreateTestChannels() + aliceChannel, _, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2571,7 +2586,7 @@ func TestChanSyncFullySynced(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -2691,7 +2706,7 @@ func TestChanSyncOweCommitment(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -3005,7 +3020,7 @@ func TestChanSyncOweRevocation(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -3195,7 +3210,7 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -3364,7 +3379,7 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -3593,7 +3608,7 @@ func TestChanSyncFailure(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(false) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -3846,7 +3861,7 @@ func TestFeeUpdateRejectInsaneFee(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, _, cleanUp, err := CreateTestChannels() + aliceChannel, _, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -3872,7 +3887,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4055,7 +4070,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4274,7 +4289,7 @@ func TestChanSyncUnableToSync(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(false) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4311,7 +4326,7 @@ func TestChanSyncInvalidLastSecret(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(false) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4401,7 +4416,7 @@ func TestChanAvailableBandwidth(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4524,7 +4539,7 @@ func TestSignCommitmentFailNotLockedIn(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, _, cleanUp, err := CreateTestChannels() + aliceChannel, _, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4549,7 +4564,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { t.Parallel() // First, we'll make a channel between Alice and Bob. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4863,7 +4878,7 @@ func TestInvalidCommitSigError(t *testing.T) { t.Parallel() // First, we'll make a channel between Alice and Bob. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -4910,7 +4925,7 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -5068,7 +5083,9 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, // and Bob having 5 BTC. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels( + false, + ) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -5166,7 +5183,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { }) aliceSignDesc.SigHashes = txscript.NewTxSigHashes(sweepTx) sweepTx.TxIn[0].Witness, err = input.CommitSpendNoDelay( - aliceChannel.Signer, &aliceSignDesc, sweepTx, + aliceChannel.Signer, &aliceSignDesc, sweepTx, false, ) if err != nil { t.Fatalf("unable to generate sweep witness: %v", err) @@ -5175,9 +5192,9 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { // If we validate the signature on the new sweep transaction, it should // be fully valid. vm, err := txscript.NewEngine( - aliceSignDesc.Output.PkScript, - sweepTx, 0, txscript.StandardVerifyFlags, nil, - nil, aliceSignDesc.Output.Value, + aliceSignDesc.Output.PkScript, sweepTx, 0, + txscript.StandardVerifyFlags, nil, nil, + aliceSignDesc.Output.Value, ) if err != nil { t.Fatalf("unable to create engine: %v", err) @@ -5194,7 +5211,7 @@ func TestDesyncHTLCs(t *testing.T) { // We'll kick off the test by creating our channels which both are // loaded with 5 BTC each. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -5261,7 +5278,7 @@ func TestMaxAcceptedHTLCs(t *testing.T) { // We'll kick off the test by creating our channels which both are // loaded with 5 BTC each. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -5322,7 +5339,7 @@ func TestMaxPendingAmount(t *testing.T) { // We'll kick off the test by creating our channels which both are // loaded with 5 BTC each. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -5410,7 +5427,9 @@ func TestChanReserve(t *testing.T) { setupChannels := func() (*LightningChannel, *LightningChannel, func()) { // We'll kick off the test by creating our channels which both // are loaded with 5 BTC each. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels( + true, + ) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -5626,7 +5645,7 @@ func TestMinHTLC(t *testing.T) { // We'll kick off the test by creating our channels which both are // loaded with 5 BTC each. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -5683,7 +5702,7 @@ func TestNewBreachRetributionSkipsDustHtlcs(t *testing.T) { // We'll kick off the test by creating our channels which both are // loaded with 5 BTC each. - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -5855,7 +5874,7 @@ func compareLogs(a, b *updateLog) error { func TestChannelRestoreUpdateLogs(t *testing.T) { t.Parallel() - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -6024,7 +6043,7 @@ func restoreAndAssert(t *testing.T, channel *LightningChannel, numAddsLocal, func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) { t.Parallel() - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -6145,7 +6164,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) { func TestDuplicateFailRejection(t *testing.T) { t.Parallel() - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -6223,7 +6242,7 @@ func TestDuplicateFailRejection(t *testing.T) { func TestDuplicateSettleRejection(t *testing.T) { t.Parallel() - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -6304,7 +6323,7 @@ func TestDuplicateSettleRejection(t *testing.T) { func TestChannelRestoreCommitHeight(t *testing.T) { t.Parallel() - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(true) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -6491,7 +6510,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { func TestForceCloseFailLocalDataLoss(t *testing.T) { t.Parallel() - aliceChannel, _, cleanUp, err := CreateTestChannels() + aliceChannel, _, cleanUp, err := CreateTestChannels(false) if err != nil { t.Fatalf("unable to create test channels: %v", err) } @@ -6522,7 +6541,7 @@ func TestForceCloseFailLocalDataLoss(t *testing.T) { func TestForceCloseBorkedState(t *testing.T) { t.Parallel() - aliceChannel, bobChannel, cleanUp, err := CreateTestChannels() + aliceChannel, bobChannel, cleanUp, err := CreateTestChannels(false) if err != nil { t.Fatalf("unable to create test channels: %v", err) } diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index 32c6db95..579622fa 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -89,7 +89,7 @@ var ( // function also returns a "cleanup" function that is meant to be called once // the test has been finalized. The clean up function will remote all temporary // files created -func CreateTestChannels() (*LightningChannel, *LightningChannel, func(), error) { +func CreateTestChannels(tweaklessCommits bool) (*LightningChannel, *LightningChannel, func(), error) { channelCapacity, err := btcutil.NewAmount(10) if err != nil { return nil, nil, nil, err @@ -202,9 +202,10 @@ func CreateTestChannels() (*LightningChannel, *LightningChannel, func(), error) } aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:]) - aliceCommitTx, bobCommitTx, err := CreateCommitmentTxns(channelBal, - channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint, - *fundingTxIn) + aliceCommitTx, bobCommitTx, err := CreateCommitmentTxns( + channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, + bobCommitPoint, *fundingTxIn, tweaklessCommits, + ) if err != nil { return nil, nil, nil, err } diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 50a3bdf4..ed4c533d 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -370,7 +370,7 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) { // Manually construct a new LightningChannel. channelState := channeldb.OpenChannel{ - ChanType: channeldb.SingleFunder, + ChanType: channeldb.SingleFunderTweakless, ChainHash: *tc.netParams.GenesisHash, FundingOutpoint: tc.fundingOutpoint, ShortChannelID: tc.shortChanID, @@ -999,18 +999,9 @@ func TestCommitTxStateHint(t *testing.T) { } } -// TestCommitmentSpendValidation test the spendability of both outputs within -// the commitment transaction. -// -// The following spending cases are covered by this test: -// * Alice's spend from the delayed output on her commitment transaction. -// * Bob's spend from Alice's delayed output when she broadcasts a revoked -// commitment transaction. -// * Bob's spend from his unencumbered output within Alice's commitment -// transaction. -func TestCommitmentSpendValidation(t *testing.T) { - t.Parallel() - +// testSpendValidation ensures that we're able to spend all outputs in the +// commitment transaction that we create. +func testSpendValidation(t *testing.T, tweakless bool) { // We generate a fake output, and the corresponding txin. This output // doesn't need to exist, as we'll only be validating spending from the // transaction that references this. @@ -1030,18 +1021,28 @@ func TestCommitmentSpendValidation(t *testing.T) { // We also set up set some resources for the commitment transaction. // Each side currently has 1 BTC within the channel, with a total // channel capacity of 2BTC. - aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), - testWalletPrivKey) - bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), - bobsPrivKey) + aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes( + btcec.S256(), testWalletPrivKey, + ) + bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes( + btcec.S256(), bobsPrivKey, + ) revocationPreimage := testHdSeed.CloneBytes() - commitSecret, commitPoint := btcec.PrivKeyFromBytes(btcec.S256(), - revocationPreimage) + commitSecret, commitPoint := btcec.PrivKeyFromBytes( + btcec.S256(), revocationPreimage, + ) revokePubKey := input.DeriveRevocationPubkey(bobKeyPub, commitPoint) aliceDelayKey := input.TweakPubKey(aliceKeyPub, commitPoint) + + // Bob will have the channel "force closed" on him, so for the sake of + // our commitments, if it's tweakless, his key will just be his regular + // pubkey. bobPayKey := input.TweakPubKey(bobKeyPub, commitPoint) + if tweakless { + bobPayKey = bobKeyPub + } aliceCommitTweak := input.SingleTweakBytes(commitPoint, aliceKeyPub) bobCommitTweak := input.SingleTweakBytes(commitPoint, bobKeyPub) @@ -1062,8 +1063,10 @@ func TestCommitmentSpendValidation(t *testing.T) { RevocationKey: revokePubKey, NoDelayKey: bobPayKey, } - commitmentTx, err := CreateCommitTx(*fakeFundingTxIn, keyRing, csvTimeout, - channelBalance, channelBalance, DefaultDustLimit()) + commitmentTx, err := CreateCommitTx( + *fakeFundingTxIn, keyRing, csvTimeout, channelBalance, + channelBalance, DefaultDustLimit(), + ) if err != nil { t.Fatalf("unable to create commitment transaction: %v", nil) } @@ -1088,8 +1091,9 @@ func TestCommitmentSpendValidation(t *testing.T) { }) // First, we'll test spending with Alice's key after the timeout. - delayScript, err := input.CommitScriptToSelf(csvTimeout, aliceDelayKey, - revokePubKey) + delayScript, err := input.CommitScriptToSelf( + csvTimeout, aliceDelayKey, revokePubKey, + ) if err != nil { t.Fatalf("unable to generate alice delay script: %v", err) } @@ -1107,8 +1111,9 @@ func TestCommitmentSpendValidation(t *testing.T) { HashType: txscript.SigHashAll, InputIndex: 0, } - aliceWitnessSpend, err := input.CommitSpendTimeout(aliceSelfOutputSigner, - signDesc, sweepTx) + aliceWitnessSpend, err := input.CommitSpendTimeout( + aliceSelfOutputSigner, signDesc, sweepTx, + ) if err != nil { t.Fatalf("unable to generate delay commit spend witness: %v", err) } @@ -1177,7 +1182,6 @@ func TestCommitmentSpendValidation(t *testing.T) { KeyDesc: keychain.KeyDescriptor{ PubKey: bobKeyPub, }, - SingleTweak: bobCommitTweak, WitnessScript: bobScriptP2WKH, SigHashes: txscript.NewTxSigHashes(sweepTx), Output: &wire.TxOut{ @@ -1187,15 +1191,21 @@ func TestCommitmentSpendValidation(t *testing.T) { HashType: txscript.SigHashAll, InputIndex: 0, } - bobRegularSpend, err := input.CommitSpendNoDelay(bobSigner, signDesc, - sweepTx) + if !tweakless { + signDesc.SingleTweak = bobCommitTweak + } + bobRegularSpend, err := input.CommitSpendNoDelay( + bobSigner, signDesc, sweepTx, tweakless, + ) if err != nil { t.Fatalf("unable to create bob regular spend: %v", err) } sweepTx.TxIn[0].Witness = bobRegularSpend - vm, err = txscript.NewEngine(regularOutput.PkScript, + vm, err = txscript.NewEngine( + regularOutput.PkScript, sweepTx, 0, txscript.StandardVerifyFlags, nil, - nil, int64(channelBalance)) + nil, int64(channelBalance), + ) if err != nil { t.Fatalf("unable to create engine: %v", err) } @@ -1203,3 +1213,26 @@ func TestCommitmentSpendValidation(t *testing.T) { t.Fatalf("bob p2wkh spend is invalid: %v", err) } } + +// TestCommitmentSpendValidation test the spendability of both outputs within +// the commitment transaction. +// +// The following spending cases are covered by this test: +// * Alice's spend from the delayed output on her commitment transaction. +// * Bob's spend from Alice's delayed output when she broadcasts a revoked +// commitment transaction. +// * Bob's spend from his unencumbered output within Alice's commitment +// transaction. +func TestCommitmentSpendValidation(t *testing.T) { + t.Parallel() + + // In the modern network, all channels use the new tweakless format, + // but we also need to support older nodes that want to open channels + // with the legacy format, so we'll test spending in both scenarios. + for _, tweakless := range []bool{true, false} { + tweakless := tweakless + t.Run(fmt.Sprintf("tweak=%v", tweakless), func(t *testing.T) { + testSpendValidation(t, tweakless) + }) + } +} diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index c3ac2638..f86afb61 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -655,12 +655,17 @@ func (l *LightningWallet) handleFundingCancelRequest(req *fundingReserveCancelMs func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount, ourChanCfg, theirChanCfg *channeldb.ChannelConfig, localCommitPoint, remoteCommitPoint *btcec.PublicKey, - fundingTxIn wire.TxIn) (*wire.MsgTx, *wire.MsgTx, error) { + fundingTxIn wire.TxIn, + tweaklessCommit bool) (*wire.MsgTx, *wire.MsgTx, error) { - localCommitmentKeys := deriveCommitmentKeys(localCommitPoint, true, - ourChanCfg, theirChanCfg) - remoteCommitmentKeys := deriveCommitmentKeys(remoteCommitPoint, false, - ourChanCfg, theirChanCfg) + localCommitmentKeys := deriveCommitmentKeys( + localCommitPoint, true, tweaklessCommit, ourChanCfg, + theirChanCfg, + ) + remoteCommitmentKeys := deriveCommitmentKeys( + remoteCommitPoint, false, tweaklessCommit, ourChanCfg, + theirChanCfg, + ) ourCommitTx, err := CreateCommitTx(fundingTxIn, localCommitmentKeys, uint32(ourChanCfg.CsvDelay), localBalance, remoteBalance, @@ -827,11 +832,13 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // With the funding tx complete, create both commitment transactions. localBalance := pendingReservation.partialState.LocalCommitment.LocalBalance.ToSatoshis() remoteBalance := pendingReservation.partialState.LocalCommitment.RemoteBalance.ToSatoshis() + tweaklessCommits := pendingReservation.partialState.ChanType.IsTweakless() ourCommitTx, theirCommitTx, err := CreateCommitmentTxns( localBalance, remoteBalance, ourContribution.ChannelConfig, theirContribution.ChannelConfig, ourContribution.FirstCommitmentPoint, theirContribution.FirstCommitmentPoint, fundingTxIn, + tweaklessCommits, ) if err != nil { req.err <- err @@ -1151,13 +1158,14 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { // remote node's commitment transactions. localBalance := pendingReservation.partialState.LocalCommitment.LocalBalance.ToSatoshis() remoteBalance := pendingReservation.partialState.LocalCommitment.RemoteBalance.ToSatoshis() + tweaklessCommits := pendingReservation.partialState.ChanType.IsTweakless() ourCommitTx, theirCommitTx, err := CreateCommitmentTxns( localBalance, remoteBalance, pendingReservation.ourContribution.ChannelConfig, pendingReservation.theirContribution.ChannelConfig, pendingReservation.ourContribution.FirstCommitmentPoint, pendingReservation.theirContribution.FirstCommitmentPoint, - *fundingTxIn, + *fundingTxIn, tweaklessCommits, ) if err != nil { req.err <- err diff --git a/test_utils.go b/test_utils.go index 7a5a1c34..cce87d54 100644 --- a/test_utils.go +++ b/test_utils.go @@ -186,9 +186,10 @@ func createTestPeer(notifier chainntnfs.ChainNotifier, } aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:]) - aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(channelBal, - channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, bobCommitPoint, - *fundingTxIn) + aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns( + channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint, + bobCommitPoint, *fundingTxIn, true, + ) if err != nil { return nil, nil, nil, nil, err }