Skip to content

Commit

Permalink
lnwallet: refactor revocation tests to test for tweakless+taproot
Browse files Browse the repository at this point in the history
  • Loading branch information
Roasbeef committed Jul 21, 2023
1 parent 2279ef2 commit 5c09d61
Showing 1 changed file with 179 additions and 38 deletions.
217 changes: 179 additions & 38 deletions lnwallet/channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
Expand Down Expand Up @@ -2809,6 +2810,23 @@ func assertNoChanSyncNeeded(t *testing.T, aliceChannel *LightningChannel,
t.Fatalf("line #%v: unable to produce chan sync msg: %v",
line, err)
}
bobChanSyncMsg, err := bobChannel.channelState.ChanSyncMsg()
if err != nil {
t.Fatalf("line #%v: unable to produce chan sync msg: %v",
line, err)
}

// For taproot channels, simulate the link/peer binding the generated
// nonces.
if aliceChannel.channelState.ChanType.IsTaproot() {
aliceChannel.pendingVerificationNonce = &musig2.Nonces{
PubNonce: *aliceChanSyncMsg.LocalNonce,
}
bobChannel.pendingVerificationNonce = &musig2.Nonces{
PubNonce: *bobChanSyncMsg.LocalNonce,
}
}

bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceChanSyncMsg)
if err != nil {
t.Fatalf("line #%v: unable to process ChannelReestablish "+
Expand All @@ -2819,11 +2837,6 @@ func assertNoChanSyncNeeded(t *testing.T, aliceChannel *LightningChannel,
"instead wants to send: %v", line, spew.Sdump(bobMsgsToSend))
}

bobChanSyncMsg, err := bobChannel.channelState.ChanSyncMsg()
if err != nil {
t.Fatalf("line #%v: unable to produce chan sync msg: %v",
line, err)
}
aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobChanSyncMsg)
if err != nil {
t.Fatalf("line #%v: unable to process ChannelReestablish "+
Expand Down Expand Up @@ -3338,19 +3351,14 @@ func TestChanSyncOweCommitmentPendingRemote(t *testing.T) {
}
}

// TestChanSyncOweRevocation tests that if Bob restarts (and then Alice) before
// he receiver's Alice's RevokeAndAck message, then Alice concludes that she
// needs to re-send the RevokeAndAck. After the revocation has been sent, both
// nodes should be able to successfully complete another state transition.
func TestChanSyncOweRevocation(t *testing.T) {
t.Parallel()

// testChanSyncOweRevocation is the internal version of
// TestChanSyncOweRevocation that is parameterized based on the type of channel
// being used in the test.
func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) {
// 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, err := CreateTestChannels(
t, channeldb.SingleFunderTweaklessBit,
)
aliceChannel, bobChannel, err := CreateTestChannels(t, chanType)
require.NoError(t, err, "unable to create test channels")

chanID := lnwire.NewChanIDFromOutPoint(
Expand Down Expand Up @@ -3417,8 +3425,23 @@ func TestChanSyncOweRevocation(t *testing.T) {
bobSyncMsg, err := bobChannel.channelState.ChanSyncMsg()
require.NoError(t, err, "unable to produce chan sync msg")

// For taproot channels, simulate the link/peer binding the generated
// nonces.
if chanType.IsTaproot() {
aliceChannel.pendingVerificationNonce = &musig2.Nonces{
PubNonce: *aliceSyncMsg.LocalNonce,
}
bobChannel.pendingVerificationNonce = &musig2.Nonces{
PubNonce: *bobSyncMsg.LocalNonce,
}
}

assertAliceOwesRevoke := func() {
aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobSyncMsg)
t.Helper()

aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(
bobSyncMsg,
)
if err != nil {
t.Fatalf("unable to process chan sync msg: %v", err)
}
Expand All @@ -3442,7 +3465,8 @@ func TestChanSyncOweRevocation(t *testing.T) {
}
if !reflect.DeepEqual(expectedRevocation, aliceReRevoke) {
t.Fatalf("wrong re-revocation: expected %v, got %v",
expectedRevocation, aliceReRevoke)
spew.Sdump(expectedRevocation),
spew.Sdump(aliceReRevoke))
}
}

Expand All @@ -3462,11 +3486,24 @@ func TestChanSyncOweRevocation(t *testing.T) {
// revocation message to Bob.
aliceChannel, err = restartChannel(aliceChannel)
require.NoError(t, err, "unable to restart alice")

// For taproot channels, simulate the link/peer binding the generated
// nonces.
if chanType.IsTaproot() {
aliceChannel.pendingVerificationNonce = &musig2.Nonces{
PubNonce: *aliceSyncMsg.LocalNonce,
}
bobChannel.pendingVerificationNonce = &musig2.Nonces{
PubNonce: *bobSyncMsg.LocalNonce,
}
}

assertAliceOwesRevoke()

// TODO(roasbeef): restart bob too???

// We'll continue by then allowing bob to process Alice's revocation message.
// We'll continue by then allowing bob to process Alice's revocation
// message.
_, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation)
require.NoError(t, err, "bob unable to recv revocation")

Expand Down Expand Up @@ -3495,19 +3532,33 @@ func TestChanSyncOweRevocation(t *testing.T) {
assertNoChanSyncNeeded(t, aliceChannel, bobChannel)
}

// TestChanSyncOweRevocationAndCommit tests that if Alice initiates a state
// transition with Bob and Bob sends both a RevokeAndAck and CommitSig message
// but Alice doesn't receive them before the connection dies, then he'll
// retransmit them both.
func TestChanSyncOweRevocationAndCommit(t *testing.T) {
// TestChanSyncOweRevocation tests that if Bob restarts (and then Alice) before
// he receiver's Alice's RevokeAndAck message, then Alice concludes that she
// needs to re-send the RevokeAndAck. After the revocation has been sent, both
// nodes should be able to successfully complete another state transition.
func TestChanSyncOweRevocation(t *testing.T) {
t.Parallel()

t.Run("tweakless", func(t *testing.T) {
testChanSyncOweRevocation(t, channeldb.SingleFunderTweaklessBit)
})
t.Run("taproot", func(t *testing.T) {
taprootBits := channeldb.SimpleTaprootFeatureBit |
channeldb.AnchorOutputsBit |
channeldb.ZeroHtlcTxFeeBit |
channeldb.SingleFunderTweaklessBit

testChanSyncOweRevocation(t, taprootBits)
})
}

func testChanSyncOweRevocationAndCommit(t *testing.T,
chanType channeldb.ChannelType) {

// 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, err := CreateTestChannels(
t, channeldb.SingleFunderTweaklessBit,
)
aliceChannel, bobChannel, err := CreateTestChannels(t, chanType)
require.NoError(t, err, "unable to create test channels")

htlcAmt := lnwire.NewMSatFromSatoshis(20000)
Expand Down Expand Up @@ -3559,6 +3610,17 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) {
bobSyncMsg, err := bobChannel.channelState.ChanSyncMsg()
require.NoError(t, err, "unable to produce chan sync msg")

// For taproot channels, simulate the link/peer binding the generated
// nonces.
if chanType.IsTaproot() {
aliceChannel.pendingVerificationNonce = &musig2.Nonces{
PubNonce: *aliceSyncMsg.LocalNonce,
}
bobChannel.pendingVerificationNonce = &musig2.Nonces{
PubNonce: *bobSyncMsg.LocalNonce,
}
}

aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobSyncMsg)
require.NoError(t, err, "unable to process chan sync msg")
if len(aliceMsgsToSend) != 0 {
Expand All @@ -3567,6 +3629,8 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) {
}

assertBobSendsRevokeAndCommit := func() {
t.Helper()

bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg)
if err != nil {
t.Fatalf("unable to process chan sync msg: %v", err)
Expand Down Expand Up @@ -3618,6 +3682,18 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) {
// Bob. He should still re-send the exact same set of messages.
bobChannel, err = restartChannel(bobChannel)
require.NoError(t, err, "unable to restart channel")

// For taproot channels, simulate the link/peer binding the generated
// nonces.
if chanType.IsTaproot() {
aliceChannel.pendingVerificationNonce = &musig2.Nonces{
PubNonce: *aliceSyncMsg.LocalNonce,
}
bobChannel.pendingVerificationNonce = &musig2.Nonces{
PubNonce: *bobSyncMsg.LocalNonce,
}
}

assertBobSendsRevokeAndCommit()

// We'll now finish the state transition by having Alice process both
Expand All @@ -3632,21 +3708,35 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) {
require.NoError(t, err, "bob unable to recv revocation")
}

// TestChanSyncOweRevocationAndCommitForceTransition tests that if Alice
// initiates a state transition with Bob, but Alice fails to receive his
// RevokeAndAck and the connection dies before Bob sends his CommitSig message,
// then Bob will re-send her RevokeAndAck message. Bob will also send and
// _identical_ CommitSig as he detects his commitment chain is ahead of
// Alice's.
func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) {
// TestChanSyncOweRevocationAndCommit tests that if Alice initiates a state
// transition with Bob and Bob sends both a RevokeAndAck and CommitSig message
// but Alice doesn't receive them before the connection dies, then he'll
// retransmit them both.
func TestChanSyncOweRevocationAndCommit(t *testing.T) {
t.Parallel()

t.Run("tweakless", func(t *testing.T) {
testChanSyncOweRevocationAndCommit(
t, channeldb.SingleFunderTweaklessBit,
)
})
t.Run("taproot", func(t *testing.T) {
taprootBits := channeldb.SimpleTaprootFeatureBit |
channeldb.AnchorOutputsBit |
channeldb.ZeroHtlcTxFeeBit |
channeldb.SingleFunderTweaklessBit

testChanSyncOweRevocationAndCommit(t, taprootBits)
})
}

func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T,
chanType channeldb.ChannelType) {

// 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, err := CreateTestChannels(
t, channeldb.SingleFunderTweaklessBit,
)
aliceChannel, bobChannel, err := CreateTestChannels(t, chanType)
require.NoError(t, err, "unable to create test channels")

htlcAmt := lnwire.NewMSatFromSatoshis(20000)
Expand Down Expand Up @@ -3730,6 +3820,17 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) {
bobSyncMsg, err := bobChannel.channelState.ChanSyncMsg()
require.NoError(t, err, "unable to produce chan sync msg")

// For taproot channels, simulate the link/peer binding the generated
// nonces.
if chanType.IsTaproot() {
aliceChannel.pendingVerificationNonce = &musig2.Nonces{
PubNonce: *aliceSyncMsg.LocalNonce,
}
bobChannel.pendingVerificationNonce = &musig2.Nonces{
PubNonce: *bobSyncMsg.LocalNonce,
}
}

aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobSyncMsg)
require.NoError(t, err, "unable to process chan sync msg")
if len(aliceMsgsToSend) != 0 {
Expand Down Expand Up @@ -3804,14 +3905,28 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) {
}
}

// If this is a taproot channel, then we'll also have Bob generate their
// current nonce, and also process Alice's.
if chanType.IsTaproot() {
_, err = bobChannel.GenMusigNonces()
require.NoError(t, err)

aliceNonces, err := aliceChannel.GenMusigNonces()
require.NoError(t, err)

err = bobChannel.InitRemoteMusigNonces(aliceNonces)
require.NoError(t, err)
}

// Now, we'll continue the exchange, sending Bob's revocation and
// signature message to Alice, ending with Alice sending her revocation
// message to Bob.
_, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
require.NoError(t, err, "alice unable to recv revocation")
err = aliceChannel.ReceiveNewCommitment(&CommitSigs{
CommitSig: bobSigMsg.CommitSig,
HtlcSigs: bobSigMsg.HtlcSigs,
CommitSig: bobSigMsg.CommitSig,
HtlcSigs: bobSigMsg.HtlcSigs,
PartialSig: bobSigMsg.PartialSig,
})
require.NoError(t, err, "alice unable to rev bob's commitment")
aliceRevocation, _, _, err = aliceChannel.RevokeCurrentCommitment()
Expand All @@ -3820,6 +3935,32 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) {
require.NoError(t, err, "bob unable to recv revocation")
}

// TestChanSyncOweRevocationAndCommitForceTransition tests that if Alice
// initiates a state transition with Bob, but Alice fails to receive his
// RevokeAndAck and the connection dies before Bob sends his CommitSig message,
// then Bob will re-send her RevokeAndAck message. Bob will also send and
// _identical_ CommitSig as he detects his commitment chain is ahead of
// Alice's.
func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) {
t.Parallel()

t.Run("tweakless", func(t *testing.T) {
testChanSyncOweRevocationAndCommitForceTransition(
t, channeldb.SingleFunderTweaklessBit,
)
})
t.Run("taproot", func(t *testing.T) {
taprootBits := channeldb.SimpleTaprootFeatureBit |
channeldb.AnchorOutputsBit |
channeldb.ZeroHtlcTxFeeBit |
channeldb.SingleFunderTweaklessBit

testChanSyncOweRevocationAndCommitForceTransition(
t, taprootBits,
)
})
}

// TestChanSyncFailure tests the various scenarios during channel sync where we
// should be able to detect that the channels cannot be synced because of
// invalid state.
Expand Down

0 comments on commit 5c09d61

Please sign in to comment.