funding: add unit tests for reg+zero conf taproot chans

This commit is contained in:
Olaoluwa Osuntokun committed 2023-08-22 16:32:27 -07:00
1 parent 91e855d59a
commit 9f6ed65d80
1 file changed
+210 -30
+210 -30
View File
@@ -33,6 +33,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntest/mock" "github.com/lightningnetwork/lnd/lntest/mock"
"github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntest/wait"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
@@ -384,9 +385,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
wc := &mock.WalletController{ wc := &mock.WalletController{
RootKey: alicePrivKey, RootKey: alicePrivKey,
} }
signer := &mock.SingleSigner{ signer := mock.NewSingleSigner(alicePrivKey)
Privkey: alicePrivKey,
}
bio := &mock.ChainIO{ bio := &mock.ChainIO{
BestHeight: fundingBroadcastHeight, BestHeight: fundingBroadcastHeight,
} }
@@ -720,12 +719,13 @@ func tearDownFundingManagers(t *testing.T, a, b *testNode) {
// transaction is confirmed on-chain. Returns the funding out point. // transaction is confirmed on-chain. Returns the funding out point.
func openChannel(t *testing.T, alice, bob *testNode, localFundingAmt, func openChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
pushAmt btcutil.Amount, numConfs uint32, pushAmt btcutil.Amount, numConfs uint32,
updateChan chan *lnrpc.OpenStatusUpdate, announceChan bool) ( updateChan chan *lnrpc.OpenStatusUpdate, announceChan bool,
chanType *lnwire.ChannelType) (
*wire.OutPoint, *wire.MsgTx) { *wire.OutPoint, *wire.MsgTx) {
publ := fundChannel( publ := fundChannel(
t, alice, bob, localFundingAmt, pushAmt, false, 0, 0, numConfs, t, alice, bob, localFundingAmt, pushAmt, false, 0, 0, numConfs,
updateChan, announceChan, nil, updateChan, announceChan, chanType,
) )
fundingOutPoint := &wire.OutPoint{ fundingOutPoint := &wire.OutPoint{
Hash: publ.TxHash(), Hash: publ.TxHash(),
@@ -734,6 +734,18 @@ func openChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
return fundingOutPoint, publ return fundingOutPoint, publ
} }
func isTaprootChanType(chanType *lnwire.ChannelType) bool {
if chanType == nil {
return false
}
featVec := lnwire.RawFeatureVector(*chanType)
return featVec.IsSet(
lnwire.SimpleTaprootChannelsRequired,
)
}
// fundChannel takes the funding process to the point where the funding // fundChannel takes the funding process to the point where the funding
// transaction is confirmed on-chain. Returns the funding tx. // transaction is confirmed on-chain. Returns the funding tx.
func fundChannel(t *testing.T, alice, bob *testNode, localFundingAmt, func fundChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
@@ -761,6 +773,12 @@ func fundChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
Err: errChan, Err: errChan,
} }
// If this is a taproot channel, then we want to force it to be a
// private channel, as that's the only channel type supported for now.
if isTaprootChanType(chanType) {
initReq.Private = true
}
alice.fundingMgr.InitFundingWorkflow(initReq) alice.fundingMgr.InitFundingWorkflow(initReq)
// Alice should have sent the OpenChannel message to Bob. // Alice should have sent the OpenChannel message to Bob.
@@ -785,6 +803,11 @@ func fundChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
"alice, instead got %T", aliceMsg) "alice, instead got %T", aliceMsg)
} }
// For taproot channel types, the nonce should be set.
if isTaprootChanType(chanType) {
require.NotNil(t, openChannelReq.LocalNonce)
}
// Let Bob handle the init message. // Let Bob handle the init message.
bob.fundingMgr.ProcessFundingMsg(openChannelReq, alice) bob.fundingMgr.ProcessFundingMsg(openChannelReq, alice)
@@ -793,6 +816,11 @@ func fundChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
t, bob.msgChan, "AcceptChannel", t, bob.msgChan, "AcceptChannel",
).(*lnwire.AcceptChannel) ).(*lnwire.AcceptChannel)
// For taproot channel types, the nonce should be set.
if isTaprootChanType(chanType) {
require.NotNil(t, acceptChannelResponse.LocalNonce)
}
// They now should both have pending reservations for this channel // They now should both have pending reservations for this channel
// active. // active.
assertNumPendingReservations(t, alice, bobPubKey, 1) assertNumPendingReservations(t, alice, bobPubKey, 1)
@@ -814,6 +842,11 @@ func fundChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
t, alice.msgChan, "FundingCreated", t, alice.msgChan, "FundingCreated",
).(*lnwire.FundingCreated) ).(*lnwire.FundingCreated)
// For taproot channel types, the partial signature should be set.
if isTaprootChanType(chanType) {
require.NotNil(t, fundingCreated.PartialSig)
}
// Give the message to Bob. // Give the message to Bob.
bob.fundingMgr.ProcessFundingMsg(fundingCreated, alice) bob.fundingMgr.ProcessFundingMsg(fundingCreated, alice)
@@ -822,6 +855,11 @@ func fundChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
t, bob.msgChan, "FundingSigned", t, bob.msgChan, "FundingSigned",
).(*lnwire.FundingSigned) ).(*lnwire.FundingSigned)
// For taproot channel types, the partial signature should be set.
if isTaprootChanType(chanType) {
require.NotNil(t, fundingSigned.PartialSig)
}
// Forward the signature to Alice. // Forward the signature to Alice.
alice.fundingMgr.ProcessFundingMsg(fundingSigned, bob) alice.fundingMgr.ProcessFundingMsg(fundingSigned, bob)
@@ -1244,6 +1282,25 @@ func assertAnnouncementSignatures(t *testing.T, alice, bob *testNode) {
} }
} }
func assertType[T any](t *testing.T, typ any) T {
value, ok := typ.(T)
require.True(t, ok)
return value
}
func assertNodeAnnSent(t *testing.T, alice, bob *testNode) {
t.Helper()
for _, node := range []*testNode{alice, bob} {
nodeAnn, err := lnutils.RecvOrTimeout(
node.msgChan, time.Second*5,
)
require.NoError(t, err)
assertType[*lnwire.NodeAnnouncement](t, *nodeAnn)
}
}
func waitForOpenUpdate(t *testing.T, updateChan chan *lnrpc.OpenStatusUpdate) { func waitForOpenUpdate(t *testing.T, updateChan chan *lnrpc.OpenStatusUpdate) {
var openUpdate *lnrpc.OpenStatusUpdate var openUpdate *lnrpc.OpenStatusUpdate
select { select {
@@ -1349,14 +1406,31 @@ func assertHandleChannelReady(t *testing.T, alice, bob *testNode,
} }
} }
func TestFundingManagerNormalWorkflow(t *testing.T) { func testNormalWorkflow(t *testing.T, chanType *lnwire.ChannelType) {
t.Parallel()
alice, bob := setupFundingManagers(t) alice, bob := setupFundingManagers(t)
t.Cleanup(func() { t.Cleanup(func() {
tearDownFundingManagers(t, alice, bob) tearDownFundingManagers(t, alice, bob)
}) })
// If the channel type is set, then we need to make sure both parties
// support explicit channel type negotiation.
if chanType != nil {
// Alice and Bob will have the same set of feature bits in our
// test.
featureBits := []lnwire.FeatureBit{
lnwire.ZeroConfOptional,
lnwire.ScidAliasOptional,
lnwire.ExplicitChannelTypeOptional,
lnwire.StaticRemoteKeyOptional,
lnwire.AnchorsZeroFeeHtlcTxOptional,
lnwire.SimpleTaprootChannelsOptional,
}
alice.localFeatures = featureBits
alice.remoteFeatures = featureBits
bob.localFeatures = featureBits
bob.remoteFeatures = featureBits
}
// We will consume the channel updates as we go, so no buffering is // We will consume the channel updates as we go, so no buffering is
// needed. // needed.
updateChan := make(chan *lnrpc.OpenStatusUpdate) updateChan := make(chan *lnrpc.OpenStatusUpdate)
@@ -1368,6 +1442,7 @@ func TestFundingManagerNormalWorkflow(t *testing.T) {
capacity := localAmt + pushAmt capacity := localAmt + pushAmt
fundingOutPoint, fundingTx := openChannel( fundingOutPoint, fundingTx := openChannel(
t, alice, bob, localAmt, pushAmt, 1, updateChan, true, t, alice, bob, localAmt, pushAmt, 1, updateChan, true,
chanType,
) )
// Check that neither Alice nor Bob sent an error message. // Check that neither Alice nor Bob sent an error message.
@@ -1400,6 +1475,13 @@ func TestFundingManagerNormalWorkflow(t *testing.T) {
).(*lnwire.ChannelReady) ).(*lnwire.ChannelReady)
require.True(t, ok) require.True(t, ok)
// For taproot channels, the funding locked messages should have the
// next set of verification nonces.
if isTaprootChanType(chanType) {
require.NotNil(t, channelReadyAlice.NextLocalNonce)
require.NotNil(t, channelReadyBob.NextLocalNonce)
}
// Check that the state machine is updated accordingly // Check that the state machine is updated accordingly
assertChannelReadySent(t, alice, bob, fundingOutPoint) assertChannelReadySent(t, alice, bob, fundingOutPoint)
@@ -1431,8 +1513,20 @@ func TestFundingManagerNormalWorkflow(t *testing.T) {
Tx: fundingTx, Tx: fundingTx,
} }
// Make sure the fundingManagers exchange announcement signatures. switch {
assertAnnouncementSignatures(t, alice, bob) // For taproot channels, we expect them to only send a node
// announcement message at this point. These channels aren't advertised
// so we don't expect the other messages.
case isTaprootChanType(chanType):
assertNodeAnnSent(t, alice, bob)
// For regular channels, we'll make sure the fundingManagers exchange
// announcement signatures.
case chanType == nil:
fallthrough
default:
assertAnnouncementSignatures(t, alice, bob)
}
// The internal state-machine should now have deleted the channelStates // The internal state-machine should now have deleted the channelStates
// from the database, as the channel is announced. // from the database, as the channel is announced.
@@ -1443,6 +1537,37 @@ func TestFundingManagerNormalWorkflow(t *testing.T) {
assertNoFwdingPolicy(t, alice, bob, channelReadyAlice.ChanID) assertNoFwdingPolicy(t, alice, bob, channelReadyAlice.ChanID)
} }
// TestFundingManagerNormalWorkflow tests that the funding manager is able to
// do a routine channel funding flow back and forth.
func TestFundingManagerNormalWorkflow(t *testing.T) {
t.Parallel()
taprootChanType := lnwire.ChannelType(*lnwire.NewRawFeatureVector(
lnwire.SimpleTaprootChannelsRequired,
))
testCases := []struct {
typeName string
chanType *lnwire.ChannelType
}{
{
typeName: "normal",
chanType: nil,
},
{
typeName: "taproot",
chanType: &taprootChanType,
},
}
//nolint:paralleltest
for _, testCase := range testCases {
t.Run(testCase.typeName, func(t *testing.T) {
testNormalWorkflow(t, testCase.chanType)
})
}
}
// TestFundingManagerRejectCSV tests checking of local CSV values against our // TestFundingManagerRejectCSV tests checking of local CSV values against our
// local CSV limit for incoming and outgoing channels. // local CSV limit for incoming and outgoing channels.
func TestFundingManagerRejectCSV(t *testing.T) { func TestFundingManagerRejectCSV(t *testing.T) {
@@ -1617,6 +1742,7 @@ func TestFundingManagerRestartBehavior(t *testing.T) {
updateChan := make(chan *lnrpc.OpenStatusUpdate) updateChan := make(chan *lnrpc.OpenStatusUpdate)
fundingOutPoint, fundingTx := openChannel( fundingOutPoint, fundingTx := openChannel(
t, alice, bob, localAmt, pushAmt, 1, updateChan, true, t, alice, bob, localAmt, pushAmt, 1, updateChan, true,
nil,
) )
// After the funding transaction gets mined, both nodes will send the // After the funding transaction gets mined, both nodes will send the
@@ -1775,6 +1901,7 @@ func TestFundingManagerOfflinePeer(t *testing.T) {
updateChan := make(chan *lnrpc.OpenStatusUpdate) updateChan := make(chan *lnrpc.OpenStatusUpdate)
fundingOutPoint, fundingTx := openChannel( fundingOutPoint, fundingTx := openChannel(
t, alice, bob, localAmt, pushAmt, 1, updateChan, true, t, alice, bob, localAmt, pushAmt, 1, updateChan, true,
nil,
) )
// After the funding transaction gets mined, both nodes will send the // After the funding transaction gets mined, both nodes will send the
@@ -2160,7 +2287,7 @@ func TestFundingManagerFundingTimeout(t *testing.T) {
// Run through the process of opening the channel, up until the funding // Run through the process of opening the channel, up until the funding
// transaction is broadcasted. // transaction is broadcasted.
_, _ = openChannel(t, alice, bob, 500000, 0, 1, updateChan, true) _, _ = openChannel(t, alice, bob, 500000, 0, 1, updateChan, true, nil)
// Bob will at this point be waiting for the funding transaction to be // Bob will at this point be waiting for the funding transaction to be
// confirmed, so the channel should be considered pending. // confirmed, so the channel should be considered pending.
@@ -2208,7 +2335,7 @@ func TestFundingManagerFundingNotTimeoutInitiator(t *testing.T) {
// Run through the process of opening the channel, up until the funding // Run through the process of opening the channel, up until the funding
// transaction is broadcasted. // transaction is broadcasted.
_, _ = openChannel(t, alice, bob, 500000, 0, 1, updateChan, true) _, _ = openChannel(t, alice, bob, 500000, 0, 1, updateChan, true, nil)
// Alice will at this point be waiting for the funding transaction to be // Alice will at this point be waiting for the funding transaction to be
// confirmed, so the channel should be considered pending. // confirmed, so the channel should be considered pending.
@@ -2285,7 +2412,7 @@ func TestFundingManagerReceiveChannelReadyTwice(t *testing.T) {
pushAmt := btcutil.Amount(0) pushAmt := btcutil.Amount(0)
capacity := localAmt + pushAmt capacity := localAmt + pushAmt
fundingOutPoint, fundingTx := openChannel( fundingOutPoint, fundingTx := openChannel(
t, alice, bob, localAmt, pushAmt, 1, updateChan, true, t, alice, bob, localAmt, pushAmt, 1, updateChan, true, nil,
) )
// Notify that transaction was mined // Notify that transaction was mined
@@ -2398,7 +2525,7 @@ func TestFundingManagerRestartAfterChanAnn(t *testing.T) {
pushAmt := btcutil.Amount(0) pushAmt := btcutil.Amount(0)
capacity := localAmt + pushAmt capacity := localAmt + pushAmt
fundingOutPoint, fundingTx := openChannel( fundingOutPoint, fundingTx := openChannel(
t, alice, bob, localAmt, pushAmt, 1, updateChan, true, t, alice, bob, localAmt, pushAmt, 1, updateChan, true, nil,
) )
// Notify that transaction was mined // Notify that transaction was mined
@@ -2497,7 +2624,7 @@ func TestFundingManagerRestartAfterReceivingChannelReady(t *testing.T) {
pushAmt := btcutil.Amount(0) pushAmt := btcutil.Amount(0)
capacity := localAmt + pushAmt capacity := localAmt + pushAmt
fundingOutPoint, fundingTx := openChannel( fundingOutPoint, fundingTx := openChannel(
t, alice, bob, localAmt, pushAmt, 1, updateChan, true, t, alice, bob, localAmt, pushAmt, 1, updateChan, true, nil,
) )
// Notify that transaction was mined // Notify that transaction was mined
@@ -2592,7 +2719,7 @@ func TestFundingManagerPrivateChannel(t *testing.T) {
pushAmt := btcutil.Amount(0) pushAmt := btcutil.Amount(0)
capacity := localAmt + pushAmt capacity := localAmt + pushAmt
fundingOutPoint, fundingTx := openChannel( fundingOutPoint, fundingTx := openChannel(
t, alice, bob, localAmt, pushAmt, 1, updateChan, false, t, alice, bob, localAmt, pushAmt, 1, updateChan, false, nil,
) )
// Notify that transaction was mined // Notify that transaction was mined
@@ -2717,7 +2844,7 @@ func TestFundingManagerPrivateRestart(t *testing.T) {
pushAmt := btcutil.Amount(0) pushAmt := btcutil.Amount(0)
capacity := localAmt + pushAmt capacity := localAmt + pushAmt
fundingOutPoint, fundingTx := openChannel( fundingOutPoint, fundingTx := openChannel(
t, alice, bob, localAmt, pushAmt, 1, updateChan, false, t, alice, bob, localAmt, pushAmt, 1, updateChan, false, nil,
) )
// Notify that transaction was mined // Notify that transaction was mined
@@ -4320,11 +4447,7 @@ func testUpfrontFailure(t *testing.T, pkscript []byte, expectErr bool) {
} }
} }
// TestFundingManagerZeroConf tests that the fundingmanager properly handles func testZeroConf(t *testing.T, chanType *lnwire.ChannelType) {
// the whole flow for zero-conf channels.
func TestFundingManagerZeroConf(t *testing.T) {
t.Parallel()
alice, bob := setupFundingManagers(t) alice, bob := setupFundingManagers(t)
t.Cleanup(func() { t.Cleanup(func() {
tearDownFundingManagers(t, alice, bob) tearDownFundingManagers(t, alice, bob)
@@ -4337,6 +4460,7 @@ func TestFundingManagerZeroConf(t *testing.T) {
lnwire.ExplicitChannelTypeOptional, lnwire.ExplicitChannelTypeOptional,
lnwire.StaticRemoteKeyOptional, lnwire.StaticRemoteKeyOptional,
lnwire.AnchorsZeroFeeHtlcTxOptional, lnwire.AnchorsZeroFeeHtlcTxOptional,
lnwire.SimpleTaprootChannelsOptional,
} }
alice.localFeatures = featureBits alice.localFeatures = featureBits
alice.remoteFeatures = featureBits alice.remoteFeatures = featureBits
@@ -4348,11 +4472,20 @@ func TestFundingManagerZeroConf(t *testing.T) {
updateChan := make(chan *lnrpc.OpenStatusUpdate) updateChan := make(chan *lnrpc.OpenStatusUpdate)
// Construct the zero-conf ChannelType for use in open_channel. // Construct the zero-conf ChannelType for use in open_channel.
channelTypeBits := []lnwire.FeatureBit{ var channelTypeBits []lnwire.FeatureBit
lnwire.ZeroConfRequired, if isTaprootChanType(chanType) {
lnwire.StaticRemoteKeyRequired, channelTypeBits = []lnwire.FeatureBit{
lnwire.AnchorsZeroFeeHtlcTxRequired, lnwire.ZeroConfRequired,
lnwire.SimpleTaprootChannelsRequired,
}
} else {
channelTypeBits = []lnwire.FeatureBit{
lnwire.ZeroConfRequired,
lnwire.StaticRemoteKeyRequired,
lnwire.AnchorsZeroFeeHtlcTxRequired,
}
} }
channelType := lnwire.ChannelType( channelType := lnwire.ChannelType(
*lnwire.NewRawFeatureVector(channelTypeBits...), *lnwire.NewRawFeatureVector(channelTypeBits...),
) )
@@ -4425,9 +4558,12 @@ func TestFundingManagerZeroConf(t *testing.T) {
Tx: fundingTx, Tx: fundingTx,
} }
assertChannelAnnouncements( // For taproot channels, we don't expect them to be announced atm.
t, alice, bob, fundingAmt, nil, nil, nil, nil, if !isTaprootChanType(chanType) {
) assertChannelAnnouncements(
t, alice, bob, fundingAmt, nil, nil, nil, nil,
)
}
// Both Alice and Bob should send on reportScidChan. // Both Alice and Bob should send on reportScidChan.
select { select {
@@ -4451,7 +4587,20 @@ func TestFundingManagerZeroConf(t *testing.T) {
Tx: fundingTx, Tx: fundingTx,
} }
assertAnnouncementSignatures(t, alice, bob) switch {
// For taproot channels, we expect them to only send a node
// announcement message at this point. These channels aren't advertised
// so we don't expect the other messages.
case isTaprootChanType(chanType):
assertNodeAnnSent(t, alice, bob)
// For regular channels, we'll make sure the fundingManagers exchange
// announcement signatures.
case chanType == nil:
fallthrough
default:
assertAnnouncementSignatures(t, alice, bob)
}
// Assert that the channel state is deleted from the fundingmanager's // Assert that the channel state is deleted from the fundingmanager's
// datastore. // datastore.
@@ -4552,3 +4701,34 @@ func TestFundingManagerNoEchoChanType(t *testing.T) {
alice.fundingMgr.ProcessFundingMsg(acceptChanMsg, bob) alice.fundingMgr.ProcessFundingMsg(acceptChanMsg, bob)
assertFundingMsgSent(t, alice.msgChan, "Error") assertFundingMsgSent(t, alice.msgChan, "Error")
} }
// TestFundingManagerZeroConf tests that the fundingmanager properly handles
// the whole flow for zero-conf channels.
func TestFundingManagerZeroConf(t *testing.T) {
t.Parallel()
taprootChanType := lnwire.ChannelType(*lnwire.NewRawFeatureVector(
lnwire.SimpleTaprootChannelsRequired,
))
testCases := []struct {
typeName string
chanType *lnwire.ChannelType
}{
{
typeName: "normal",
chanType: nil,
},
{
typeName: "taproot",
chanType: &taprootChanType,
},
}
//nolint:paralleltest
for _, testCase := range testCases {
t.Run(testCase.typeName, func(t *testing.T) {
testZeroConf(t, testCase.chanType)
})
}
}