From 1bbe9071dee31a055559839a65b9ba5d418cd897 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 21 Oct 2024 14:23:58 +0200 Subject: [PATCH] itest+rfq: re-enable and fix RFQ itests --- itest/assets_test.go | 12 ++-- itest/rfq_test.go | 119 +++++++++++++------------------------ itest/test_list_on_test.go | 12 ++-- rfq/manager.go | 15 ++--- 4 files changed, 60 insertions(+), 98 deletions(-) diff --git a/itest/assets_test.go b/itest/assets_test.go index 75fb29cae..a7da341e9 100644 --- a/itest/assets_test.go +++ b/itest/assets_test.go @@ -330,6 +330,7 @@ func testMintAssetNameCollisionError(t *harnessTest) { require.Equal(t.t, a.GroupKey, b.GroupKey) require.Equal(t.t, a.GroupAnchor, b.GroupAnchor) } + // If we attempt to add both assets to the same batch, the second mint // call should fail. collideResp, err := t.tapd.MintAsset(ctxt, &assetCollide) @@ -376,12 +377,11 @@ func testMintAssetNameCollisionError(t *harnessTest) { // The only change in the returned batch after cancellation should be // the batch state. - cancelBatch, err := t.tapd.ListBatches( - ctxt, &mintrpc.ListBatchRequest{ - Filter: &mintrpc.ListBatchRequest_BatchKey{ - BatchKey: collideResp.PendingBatch.BatchKey, - }, - }) + cancelBatch, err := t.tapd.ListBatches(ctxt, &mintrpc.ListBatchRequest{ + Filter: &mintrpc.ListBatchRequest_BatchKey{ + BatchKey: collideResp.PendingBatch.BatchKey, + }, + }) require.NoError(t.t, err) require.Len(t.t, cancelBatch.Batches, 1) diff --git a/itest/rfq_test.go b/itest/rfq_test.go index c0a43f56d..71dda6c78 100644 --- a/itest/rfq_test.go +++ b/itest/rfq_test.go @@ -9,7 +9,6 @@ import ( "time" "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/taproot-assets/rfqmsg" "github.com/lightninglabs/taproot-assets/taprpc/mintrpc" "github.com/lightninglabs/taproot-assets/taprpc/rfqrpc" @@ -23,6 +22,11 @@ import ( "github.com/stretchr/testify/require" ) +var ( + // rfqTimeout is the timeout used for RFQ related operations. + rfqTimeout = 5 * time.Second +) + // testRfqAssetBuyHtlcIntercept tests RFQ negotiation, HTLC interception, and // validation between three peers. The RFQ negotiation is initiated by an asset // buy request. @@ -97,7 +101,7 @@ func testRfqAssetBuyHtlcIntercept(t *harnessTest) { // node. PeerPubKey: ts.BobLnd.PubKey[:], - TimeoutSeconds: 5, + TimeoutSeconds: uint32(rfqTimeout.Seconds()), }, ) require.NoError(t.t, err, "unable to upsert asset buy order") @@ -110,7 +114,7 @@ func testRfqAssetBuyHtlcIntercept(t *harnessTest) { _, ok := event.Event.(*rfqrpc.RfqEvent_PeerAcceptedBuyQuote) require.True(t.t, ok, "unexpected event: %v", event) - }, defaultWaitTimeout) + }, rfqTimeout) // Carol should have received an accepted quote from Bob. This accepted // quote can be used by Carol to make a payment to Bob. @@ -203,7 +207,7 @@ func testRfqAssetBuyHtlcIntercept(t *harnessTest) { t.t, acceptedQuote.Scid, acceptHtlc.AcceptHtlc.Scid, ) t.Log("Bob has accepted the HTLC") - }, defaultWaitTimeout) + }, rfqTimeout) // Close event streams. err = carolEventNtfns.CloseSend() @@ -273,6 +277,8 @@ func testRfqAssetSellHtlcIntercept(t *harnessTest) { // tapd node to send a request for quote message to // Bob's node. PeerPubKey: ts.BobLnd.PubKey[:], + + TimeoutSeconds: uint32(rfqTimeout.Seconds()), }, ) require.NoError(t.t, err, "unable to upsert asset sell order") @@ -285,7 +291,7 @@ func testRfqAssetSellHtlcIntercept(t *harnessTest) { _, ok := event.Event.(*rfqrpc.RfqEvent_PeerAcceptedSellQuote) require.True(t.t, ok, "unexpected event: %v", event) - }, defaultWaitTimeout) + }, rfqTimeout) // Alice should have received an accepted quote from Bob. This accepted // quote can be used by Alice to make a payment to Bob. @@ -329,28 +335,21 @@ func testRfqAssetSellHtlcIntercept(t *harnessTest) { } routeBuildResp := ts.AliceLnd.RPC.BuildRoute(&routeBuildRequest) - // Add the accepted quote ID as a record to the custom records field of - // the route's first hop. - aliceBobHop := routeBuildResp.Route.Hops[0] - if aliceBobHop.CustomRecords == nil { - aliceBobHop.CustomRecords = make(map[uint64][]byte) - } - - var htlcRfqIDTlvType rfqmsg.HtlcRfqIDType - aliceBobHop.CustomRecords[uint64(htlcRfqIDTlvType.TypeVal())] = - acceptedQuote.Id[:] - - // Update the route with the modified first hop. - routeBuildResp.Route.Hops[0] = aliceBobHop - // Send the payment to the route. t.Log("Alice paying invoice") + var htlcRfqIDTlvType rfqmsg.HtlcRfqIDType routeReq := routerrpc.SendToRouteRequest{ PaymentHash: invoice.RHash, Route: routeBuildResp.Route, + FirstHopCustomRecords: map[uint64][]byte{ + uint64(htlcRfqIDTlvType.TypeVal()): acceptedQuote.Id[:], + }, } sendAttempt := ts.AliceLnd.RPC.SendToRouteV2(&routeReq) - require.Equal(t.t, lnrpc.HTLCAttempt_SUCCEEDED, sendAttempt.Status) + + // The payment will fail since it doesn't transport the correct amount + // of the asset. + require.Equal(t.t, lnrpc.HTLCAttempt_FAILED, sendAttempt.Status) // At this point Bob should have received a HTLC with the asset transfer // specific scid. We'll wait for Bob to publish an accept HTLC event and @@ -362,11 +361,11 @@ func testRfqAssetSellHtlcIntercept(t *harnessTest) { _, ok := event.Event.(*rfqrpc.RfqEvent_AcceptHtlc) require.True(t.t, ok, "unexpected event: %v", event) - }, defaultWaitTimeout) + }, rfqTimeout) // Confirm that Carol receives the lightning payment from Alice via Bob. invoice = ts.CarolLnd.RPC.LookupInvoice(addInvoiceResp.RHash) - require.Equal(t.t, invoice.State, lnrpc.Invoice_SETTLED) + require.Equal(t.t, invoice.State, lnrpc.Invoice_OPEN) // Close event notification streams. err = aliceEventNtfns.CloseSend() @@ -376,44 +375,6 @@ func testRfqAssetSellHtlcIntercept(t *harnessTest) { require.NoError(t.t, err) } -// newLndNode creates a new lnd node with the given name and funds its wallet -// with the specified outputs. -func newLndNode(name string, outputFunds []btcutil.Amount, - ht *lntest.HarnessTest) *node.HarnessNode { - - newNode := ht.NewNode(name, nil) - - // Fund node wallet with specified outputs. - totalTxes := len(outputFunds) - const ( - numBlocksSendOutput = 2 - minerFeeRate = btcutil.Amount(7500) - ) - - for i := range outputFunds { - amt := outputFunds[i] - - resp := newNode.RPC.NewAddress(&lnrpc.NewAddressRequest{ - Type: lnrpc.AddressType_WITNESS_PUBKEY_HASH}, - ) - addr := ht.DecodeAddress(resp.Address) - addrScript := ht.PayToAddrScript(addr) - - output := &wire.TxOut{ - PkScript: addrScript, - Value: int64(amt), - } - ht.Miner().SendOutput(output, minerFeeRate) - } - - // Mine any funding transactions. - if totalTxes > 0 { - ht.MineBlocksAndAssertNumTxes(numBlocksSendOutput, totalTxes) - } - - return newNode -} - // rfqTestScenario is a struct which holds test scenario helper infra. type rfqTestScenario struct { testHarness *harnessTest @@ -438,26 +399,27 @@ type rfqTestScenario struct { // It also creates new tapd nodes for each of the LND nodes. func newRfqTestScenario(t *harnessTest) *rfqTestScenario { // Specify wallet outputs to fund the wallets of the new nodes. - const ( - fundAmount = 1 * btcutil.SatoshiPerBitcoin - numOutputs = 100 - totalAmount = fundAmount * numOutputs - ) - - var outputFunds [numOutputs]btcutil.Amount - for i := range outputFunds { - outputFunds[i] = fundAmount - } + const fundAmount = 1 * btcutil.SatoshiPerBitcoin // Generate a unique name for each new node. aliceName := genRandomNodeName("AliceLnd") bobName := genRandomNodeName("BobLnd") carolName := genRandomNodeName("CarolLnd") + scidAliasArgs := []string{ + "--protocol.option-scid-alias", + "--protocol.anchors", + } + // Create three new nodes. - aliceLnd := newLndNode(aliceName, outputFunds[:], t.lndHarness) - bobLnd := newLndNode(bobName, outputFunds[:], t.lndHarness) - carolLnd := newLndNode(carolName, outputFunds[:], t.lndHarness) + aliceLnd := t.lndHarness.NewNode(aliceName, scidAliasArgs) + t.lndHarness.FundCoins(fundAmount, aliceLnd) + + bobLnd := t.lndHarness.NewNode(bobName, scidAliasArgs) + t.lndHarness.FundCoins(fundAmount, bobLnd) + + carolLnd := t.lndHarness.NewNode(carolName, scidAliasArgs) + t.lndHarness.FundCoins(fundAmount, carolLnd) // Now we want to wait for the nodes to catch up. t.lndHarness.WaitForBlockchainSync(aliceLnd) @@ -465,15 +427,15 @@ func newRfqTestScenario(t *harnessTest) *rfqTestScenario { t.lndHarness.WaitForBlockchainSync(carolLnd) // Now block until both wallets have fully synced up. - t.lndHarness.WaitForBalanceConfirmed(aliceLnd, totalAmount) - t.lndHarness.WaitForBalanceConfirmed(bobLnd, totalAmount) - t.lndHarness.WaitForBalanceConfirmed(carolLnd, totalAmount) + t.lndHarness.WaitForBalanceConfirmed(aliceLnd, fundAmount) + t.lndHarness.WaitForBalanceConfirmed(bobLnd, fundAmount) + t.lndHarness.WaitForBalanceConfirmed(carolLnd, fundAmount) // Connect the nodes. t.lndHarness.EnsureConnected(aliceLnd, bobLnd) t.lndHarness.EnsureConnected(bobLnd, carolLnd) - // Open channels between the nodes: Alice -> Bob -> Carol + // Open channels between the nodes: Alice -> Bob -> Carol. const chanAmt = btcutil.Amount(300000) p := lntest.OpenChannelParams{Amt: chanAmt} reqs := []*lntest.OpenChannelRequest{ @@ -486,6 +448,9 @@ func newRfqTestScenario(t *harnessTest) *rfqTestScenario { // Make sure Alice is aware of channel Bob -> Carol. t.lndHarness.AssertTopologyChannelOpen(aliceLnd, bobCarolChannel) + // Make sure Carol is aware of channel Alice -> Bob. + t.lndHarness.AssertTopologyChannelOpen(carolLnd, aliceBobChannel) + // Create tapd nodes. aliceTapd := setupTapdHarness(t.t, t, aliceLnd, t.universeServer) bobTapd := setupTapdHarness(t.t, t, bobLnd, t.universeServer) diff --git a/itest/test_list_on_test.go b/itest/test_list_on_test.go index 1fcc41751..151dd7695 100644 --- a/itest/test_list_on_test.go +++ b/itest/test_list_on_test.go @@ -299,14 +299,10 @@ var testCases = []*testCase{ name: "rfq asset buy htlc intercept", test: testRfqAssetBuyHtlcIntercept, }, - - // TODO(ffranr): Re-enable this test once we have a way to set the - // `WireCustomRecords` field in the first hop `UpdateAddHtlc` p2p - // message - //{ - // name: "rfq asset sell htlc intercept", - // test: testRfqAssetSellHtlcIntercept, - //}, + { + name: "rfq asset sell htlc intercept", + test: testRfqAssetSellHtlcIntercept, + }, { name: "multi signature on all levels", diff --git a/rfq/manager.go b/rfq/manager.go index 4a0c85138..8578f0524 100644 --- a/rfq/manager.go +++ b/rfq/manager.go @@ -12,6 +12,7 @@ import ( "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/rfqmsg" + lfn "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" @@ -521,12 +522,9 @@ func (m *Manager) addScidAlias(scidAlias uint64, assetID asset.ID, } // Filter for channels with the given peer. - peerChannels := make([]lndclient.ChannelInfo, 0) - for _, localChan := range localChans { - if localChan.PubKeyBytes == peer { - peerChannels = append(peerChannels, localChan) - } - } + peerChannels := lfn.Filter(func(c lndclient.ChannelInfo) bool { + return c.PubKeyBytes == peer + }, localChans) // Identify the correct channel to use as the base SCID for the alias // by inspecting the asset data in the custom channel data. @@ -534,8 +532,11 @@ func (m *Manager) addScidAlias(scidAlias uint64, assetID asset.ID, assetIDStr = assetID.String() baseSCID uint64 ) - for _, localChan := range peerChannels { + if len(localChan.CustomChannelData) == 0 { + continue + } + var assetData rfqmsg.JsonAssetChannel err = json.Unmarshal(localChan.CustomChannelData, &assetData) if err != nil {