From 774d6afa6156fb6f8960e0d78b1cdf684575d9a2 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 27 Mar 2025 10:02:09 -0500 Subject: [PATCH 1/8] itest: speed up test by skipping Alice+bob --- itest/litd_custom_channels_test.go | 12 ---- itest/litd_test.go | 20 +------ itest/litd_test_list_on_test.go | 88 ++++++++++++++++++------------ itest/network_harness.go | 23 +++++++- itest/test_harness.go | 4 +- 5 files changed, 77 insertions(+), 70 deletions(-) diff --git a/itest/litd_custom_channels_test.go b/itest/litd_custom_channels_test.go index 80013c486..b72ae2ea0 100644 --- a/itest/litd_custom_channels_test.go +++ b/itest/litd_custom_channels_test.go @@ -4043,18 +4043,6 @@ func testCustomChannelsDecodeAssetInvoice(ctx context.Context, "rfqrpc://%s", oracleAddr, )) - // For this test, Zane will be our dedicated Universe server for all - // parties. - zane, err := net.NewNode( - t.t, "Zane", lndArgs, false, true, litdArgs..., - ) - require.NoError(t.t, err) - - litdArgs = append(litdArgs, fmt.Sprintf( - "--taproot-assets.proofcourieraddr=%s://%s", - proof.UniverseRpcCourierType, zane.Cfg.LitAddr(), - )) - // We'll just make a single node here, as this doesn't actually rely on // a set of active channels. alice, err := net.NewNode( diff --git a/itest/litd_test.go b/itest/litd_test.go index 30a69cd95..91a1b25ca 100644 --- a/itest/litd_test.go +++ b/itest/litd_test.go @@ -1,7 +1,6 @@ package itest import ( - "fmt" "os" "strings" "testing" @@ -42,8 +41,6 @@ func TestLightningTerminal(t *testing.T) { // Run the subset of the test cases selected in this tranche. for _, testCase := range allTestCases { - testCase := testCase - success := t.Run(testCase.name, func(t1 *testing.T) { cleanTestCaseName := strings.ReplaceAll( testCase.name, " ", "_", @@ -79,24 +76,9 @@ func TestLightningTerminal(t *testing.T) { err = litdHarness.SetUp( t1, cleanTestCaseName, aliceBobArgs, + testCase.noAliceBob, ) require.NoError(t1, err, "harness setup failed") - t1.Cleanup(func() { - require.NoError(t1, litdHarness.TearDown()) - litdHarness.Stop() - }) - - litdHarness.EnsureConnected( - t1, litdHarness.Alice, litdHarness.Bob, - ) - - logLine := fmt.Sprintf( - "STARTING ============ %v ============\n", - testCase.name, - ) - - litdHarness.Alice.AddToLog(logLine) - litdHarness.Bob.AddToLog(logLine) // Create a separate harness test for the testcase to // avoid overwriting the external harness test that is diff --git a/itest/litd_test_list_on_test.go b/itest/litd_test_list_on_test.go index 04c3f020c..a306794b1 100644 --- a/itest/litd_test_list_on_test.go +++ b/itest/litd_test_list_on_test.go @@ -5,11 +5,11 @@ package itest var allTestCases = []*testCase{ { - name: "test mode integrated", + name: "integrated mode", test: testModeIntegrated, }, { - name: "test mode remote", + name: "remote mode", test: testModeRemote, }, { @@ -17,75 +17,91 @@ var allTestCases = []*testCase{ test: testStatelessInitMode, }, { - name: "test firewall rules", + name: "firewall rules", test: testFirewallRules, }, { - name: "test large http header", + name: "large http header", test: testLargeHttpHeader, }, { - name: "test custom channels", - test: testCustomChannels, + name: "custom channels", + test: testCustomChannels, + noAliceBob: true, }, { - name: "test custom channels large", - test: testCustomChannelsLarge, + name: "custom channels large", + test: testCustomChannelsLarge, + noAliceBob: true, }, { - name: "test custom channels grouped asset", - test: testCustomChannelsGroupedAsset, + name: "custom channels grouped asset", + test: testCustomChannelsGroupedAsset, + noAliceBob: true, }, { - name: "test custom channels force close", - test: testCustomChannelsForceClose, + name: "custom channels force close", + test: testCustomChannelsForceClose, + noAliceBob: true, }, { - name: "test custom channels breach", - test: testCustomChannelsBreach, + name: "custom channels breach", + test: testCustomChannelsBreach, + noAliceBob: true, }, { - name: "test custom channels liquidity", - test: testCustomChannelsLiquidityEdgeCases, + name: "custom channels liquidity", + test: testCustomChannelsLiquidityEdgeCases, + noAliceBob: true, }, { - name: "test custom channels liquidity group", - test: testCustomChannelsLiquidityEdgeCasesGroup, + name: "custom channels liquidity group", + test: testCustomChannelsLiquidityEdgeCasesGroup, + noAliceBob: true, }, { - name: "test custom channels htlc force close", - test: testCustomChannelsHtlcForceClose, + name: "custom channels htlc force close", + test: testCustomChannelsHtlcForceClose, + noAliceBob: true, }, { - name: "test custom channels htlc force close MPP", - test: testCustomChannelsHtlcForceCloseMpp, + name: "custom channels htlc force close MPP", + test: testCustomChannelsHtlcForceCloseMpp, + noAliceBob: true, }, { - name: "test custom channels balance consistency", - test: testCustomChannelsBalanceConsistency, + name: "custom channels balance consistency", + test: testCustomChannelsBalanceConsistency, + noAliceBob: true, }, { - name: "test custom channels single asset multi input", - test: testCustomChannelsSingleAssetMultiInput, + name: "custom channels single asset multi input", + test: testCustomChannelsSingleAssetMultiInput, + noAliceBob: true, }, { - name: "test custom channels oracle pricing", - test: testCustomChannelsOraclePricing, + name: "custom channels oracle pricing", + test: testCustomChannelsOraclePricing, + noAliceBob: true, }, { - name: "test custom channels fee", - test: testCustomChannelsFee, + name: "custom channels fee", + test: testCustomChannelsFee, + noAliceBob: true, }, { - name: "test custom channels forward bandwidth", - test: testCustomChannelsForwardBandwidth, + name: "custom channels forward bandwidth", + test: testCustomChannelsForwardBandwidth, + noAliceBob: true, }, { - name: "test custom channels strict forwarding", - test: testCustomChannelsStrictForwarding, + name: "custom channels strict forwarding", + test: testCustomChannelsStrictForwarding, + noAliceBob: true, }, { - name: "test custom channels decode payreq", - test: testCustomChannelsDecodeAssetInvoice, + name: "custom channels decode payreq", + test: testCustomChannelsDecodeAssetInvoice, + noAliceBob: true, }, } diff --git a/itest/network_harness.go b/itest/network_harness.go index 86ab786aa..cb4a97d8f 100644 --- a/itest/network_harness.go +++ b/itest/network_harness.go @@ -122,8 +122,8 @@ func (n *NetworkHarness) ProcessErrors() <-chan error { // rpc clients capable of communicating with the initial seeder nodes are // created. Nodes are initialized with the given extra command line flags, which // should be formatted properly - "--arg=value". -func (n *NetworkHarness) SetUp(t *testing.T, - testCase string, lndArgs []string) error { +func (n *NetworkHarness) SetUp(t *testing.T, testCase string, lndArgs []string, + noAliceBob bool) error { // Swap out grpc's default logger with our fake logger which drops the // statements on the floor. @@ -143,6 +143,11 @@ func (n *NetworkHarness) SetUp(t *testing.T, n.autopilotServer = mock.NewServer() require.NoError(t, n.autopilotServer.Start()) + // Skip early if we don't need the default Alice and Bob nodes. + if noAliceBob { + return nil + } + // Start the initial seeder nodes within the test network, then connect // their respective RPC clients. eg := errgroup.Group{} @@ -252,6 +257,20 @@ out: } } + t.Cleanup(func() { + require.NoError(t, n.TearDown()) + n.Stop() + }) + + n.EnsureConnected(t, n.Alice, n.Bob) + + logLine := fmt.Sprintf( + "STARTING ============ %v ============\n", testCase, + ) + + n.Alice.AddToLog(logLine) + n.Bob.AddToLog(logLine) + return nil } diff --git a/itest/test_harness.go b/itest/test_harness.go index b3af045dc..a2a6bc36d 100644 --- a/itest/test_harness.go +++ b/itest/test_harness.go @@ -126,7 +126,9 @@ func getLitdBinary() string { type testCase struct { name string - test func(ctx context.Context, net *NetworkHarness, t *harnessTest) + test func(ctx context.Context, net *NetworkHarness, + t *harnessTest) + noAliceBob bool } // waitForNTxsInMempool polls until finding the desired number of transactions From 910c806b82bada0d33ac3979f3788685609c9032 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 27 Mar 2025 10:18:51 -0500 Subject: [PATCH 2/8] itest: use Charlie as proof courier, remove Zane We remove the Zane node from all tests where its only role was being the universe server. Starting a node takes multiple seconds, so if we can spin up fewer nodes, we can save some time. And functionality wise Charlie can easily be the universe, we just need to define its port upfront so we can configure it to be its own proof courier. The topology for all tests is at least the following: Charlie --[assets]--> Dave --[sats]--> Erin --[assets]--> Fabia Which means, we the case where one of the channel peers is its own universe (Charlie in Charlie<->Dave) as well as the case where the universe server is a remote node (Charlie in the Erin<->Fabia channel). --- itest/litd_custom_channels_test.go | 227 +++++++++++------------------ itest/litd_node.go | 3 + itest/network_harness.go | 23 ++- 3 files changed, 111 insertions(+), 142 deletions(-) diff --git a/itest/litd_custom_channels_test.go b/itest/litd_custom_channels_test.go index b72ae2ea0..256275f56 100644 --- a/itest/litd_custom_channels_test.go +++ b/itest/litd_custom_channels_test.go @@ -32,6 +32,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/port" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -109,18 +110,13 @@ func testCustomChannelsLarge(_ context.Context, net *NetworkHarness, lndArgs := slices.Clone(lndArgsTemplate) litdArgs := slices.Clone(litdArgsTemplate) - // Explicitly set the proof courier as Zane (now has no other role - // other than proof shuffling), otherwise a hashmail courier will be - // used. For the funding transaction, we're just posting it and don't - // expect a true receiver. - zane, err := net.NewNode( - t.t, "Zane", lndArgs, false, true, litdArgs..., - ) - require.NoError(t.t, err) - + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() litdArgs = append(litdArgs, fmt.Sprintf( "--taproot-assets.proofcourieraddr=%s://%s", - proof.UniverseRpcCourierType, zane.Cfg.LitAddr(), + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), )) // The topology we are going for looks like the following: @@ -139,8 +135,8 @@ func testCustomChannelsLarge(_ context.Context, net *NetworkHarness, // with tapd included. We also need specific flags to be enabled, so we // create 5 completely new nodes, ignoring the two default nodes that // are created by the harness. - charlie, err := net.NewNode( - t.t, "Charlie", lndArgs, false, true, litdArgs..., + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", lndArgs, false, true, charliePort, litdArgs..., ) require.NoError(t.t, err) @@ -175,7 +171,7 @@ func testCustomChannelsLarge(_ context.Context, net *NetworkHarness, assertChannelKnown(t.t, charlie, channelOp) assertChannelKnown(t.t, fabia, channelOp) - universeTap := newTapClient(t.t, zane) + universeTap := newTapClient(t.t, charlie) charlieTap := newTapClient(t.t, charlie) daveTap := newTapClient(t.t, dave) erinTap := newTapClient(t.t, erin) @@ -296,18 +292,13 @@ func testCustomChannels(ctx context.Context, net *NetworkHarness, lndArgs := slices.Clone(lndArgsTemplate) litdArgs := slices.Clone(litdArgsTemplate) - // Explicitly set the proof courier as Zane (now has no other role - // other than proof shuffling), otherwise a hashmail courier will be - // used. For the funding transaction, we're just posting it and don't - // expect a true receiver. - zane, err := net.NewNode( - t.t, "Zane", lndArgs, false, true, litdArgs..., - ) - require.NoError(t.t, err) - + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() litdArgs = append(litdArgs, fmt.Sprintf( "--taproot-assets.proofcourieraddr=%s://%s", - proof.UniverseRpcCourierType, zane.Cfg.LitAddr(), + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), )) // The topology we are going for looks like the following: @@ -326,8 +317,8 @@ func testCustomChannels(ctx context.Context, net *NetworkHarness, // with tapd included. We also need specific flags to be enabled, so we // create 5 completely new nodes, ignoring the two default nodes that // are created by the harness. - charlie, err := net.NewNode( - t.t, "Charlie", lndArgs, false, true, litdArgs..., + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", lndArgs, false, true, charliePort, litdArgs..., ) require.NoError(t.t, err) @@ -339,9 +330,7 @@ func testCustomChannels(ctx context.Context, net *NetworkHarness, t.t, "Fabia", lndArgs, false, true, litdArgs..., ) require.NoError(t.t, err) - yara, err := net.NewNode( - t.t, "Yara", lndArgs, false, true, litdArgs..., - ) + yara, err := net.NewNode(t.t, "Yara", lndArgs, false, true, litdArgs...) require.NoError(t.t, err) nodes := []*HarnessNode{charlie, dave, erin, fabia, yara} @@ -362,7 +351,7 @@ func testCustomChannels(ctx context.Context, net *NetworkHarness, assertChannelKnown(t.t, charlie, channelOp) assertChannelKnown(t.t, fabia, channelOp) - universeTap := newTapClient(t.t, zane) + universeTap := newTapClient(t.t, charlie) charlieTap := newTapClient(t.t, charlie) daveTap := newTapClient(t.t, dave) erinTap := newTapClient(t.t, erin) @@ -761,18 +750,13 @@ func testCustomChannelsGroupedAsset(ctx context.Context, net *NetworkHarness, lndArgs := slices.Clone(lndArgsTemplate) litdArgs := slices.Clone(litdArgsTemplate) - // Explicitly set the proof courier as Zane (now has no other role - // other than proof shuffling), otherwise a hashmail courier will be - // used. For the funding transaction, we're just posting it and don't - // expect a true receiver. - zane, err := net.NewNode( - t.t, "Zane", lndArgs, false, true, litdArgs..., - ) - require.NoError(t.t, err) - + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() litdArgs = append(litdArgs, fmt.Sprintf( "--taproot-assets.proofcourieraddr=%s://%s", - proof.UniverseRpcCourierType, zane.Cfg.LitAddr(), + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), )) // The topology we are going for looks like the following: @@ -791,8 +775,8 @@ func testCustomChannelsGroupedAsset(ctx context.Context, net *NetworkHarness, // with tapd included. We also need specific flags to be enabled, so we // create 5 completely new nodes, ignoring the two default nodes that // are created by the harness. - charlie, err := net.NewNode( - t.t, "Charlie", lndArgs, false, true, litdArgs..., + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", lndArgs, false, true, charliePort, litdArgs..., ) require.NoError(t.t, err) @@ -827,7 +811,7 @@ func testCustomChannelsGroupedAsset(ctx context.Context, net *NetworkHarness, assertChannelKnown(t.t, charlie, channelOp) assertChannelKnown(t.t, fabia, channelOp) - universeTap := newTapClient(t.t, zane) + universeTap := newTapClient(t.t, charlie) charlieTap := newTapClient(t.t, charlie) daveTap := newTapClient(t.t, dave) erinTap := newTapClient(t.t, erin) @@ -1518,20 +1502,13 @@ func testCustomChannelsBreach(ctx context.Context, net *NetworkHarness, lndArgs := slices.Clone(lndArgsTemplate) litdArgs := slices.Clone(litdArgsTemplate) - // Explicitly set the proof courier as Zane (now has no other role - // other than proof shuffling), otherwise a hashmail courier will be - // used. For the funding transaction, we're just posting it and don't - // expect a true receiver. - zane, err := net.NewNode( - t.t, "Zane", lndArgs, false, true, litdArgs..., - ) - require.NoError(t.t, err) - - // For our litd args, make sure that they all seen Zane as the main - // Universe server. + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() litdArgs = append(litdArgs, fmt.Sprintf( "--taproot-assets.proofcourieraddr=%s://%s", - proof.UniverseRpcCourierType, zane.Cfg.LitAddr(), + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), )) // Charlie will be the breached party. We set --nolisten to ensure Dave @@ -1545,8 +1522,9 @@ func testCustomChannelsBreach(ctx context.Context, net *NetworkHarness, // For this simple test, we'll just have Carol -> Dave as an assets // channel. - charlie, err := net.NewNode( - t.t, "Charlie", charlieFlags, false, true, litdArgs..., + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", charlieFlags, false, true, charliePort, + litdArgs..., ) require.NoError(t.t, err) @@ -1558,7 +1536,7 @@ func testCustomChannelsBreach(ctx context.Context, net *NetworkHarness, connectAllNodes(t.t, net, nodes) fundAllNodes(t.t, net, nodes) - universeTap := newTapClient(t.t, zane) + universeTap := newTapClient(t.t, charlie) charlieTap := newTapClient(t.t, charlie) daveTap := newTapClient(t.t, dave) @@ -1740,18 +1718,13 @@ func testCustomChannelsLiquidtyEdgeCasesCore(ctx context.Context, lndArgs := slices.Clone(lndArgsTemplate) litdArgs := slices.Clone(litdArgsTemplate) - // Explicitly set the proof courier as Zane (now has no other role - // other than proof shuffling), otherwise a hashmail courier will be - // used. For the funding transaction, we're just posting it and don't - // expect a true receiver. - zane, err := net.NewNode( - t.t, "Zane", lndArgs, false, true, litdArgs..., - ) - require.NoError(t.t, err) - + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() litdArgs = append(litdArgs, fmt.Sprintf( "--taproot-assets.proofcourieraddr=%s://%s", - proof.UniverseRpcCourierType, zane.Cfg.LitAddr(), + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), )) // The topology we are going for looks like the following: @@ -1770,8 +1743,8 @@ func testCustomChannelsLiquidtyEdgeCasesCore(ctx context.Context, // with tapd included. We also need specific flags to be enabled, so we // create 5 completely new nodes, ignoring the two default nodes that // are created by the harness. - charlie, err := net.NewNode( - t.t, "Charlie", lndArgs, false, true, litdArgs..., + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", lndArgs, false, true, charliePort, litdArgs..., ) require.NoError(t.t, err) @@ -1808,7 +1781,7 @@ func testCustomChannelsLiquidtyEdgeCasesCore(ctx context.Context, assertChannelKnown(t.t, charlie, channelOp) assertChannelKnown(t.t, fabia, channelOp) - universeTap := newTapClient(t.t, zane) + universeTap := newTapClient(t.t, charlie) charlieTap := newTapClient(t.t, charlie) daveTap := newTapClient(t.t, dave) erinTap := newTapClient(t.t, erin) @@ -2268,18 +2241,13 @@ func testCustomChannelsStrictForwarding(ctx context.Context, lndArgs := slices.Clone(lndArgsTemplate) litdArgs := slices.Clone(litdArgsTemplate) - // Explicitly set the proof courier as Zane (now has no other role - // other than proof shuffling), otherwise a hashmail courier will be - // used. For the funding transaction, we're just posting it and don't - // expect a true receiver. - zane, err := net.NewNode( - t.t, "Zane", lndArgs, false, true, litdArgs..., - ) - require.NoError(t.t, err) - + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() litdArgs = append(litdArgs, fmt.Sprintf( "--taproot-assets.proofcourieraddr=%s://%s", - proof.UniverseRpcCourierType, zane.Cfg.LitAddr(), + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), )) // The topology we are going for looks like the following: @@ -2298,8 +2266,8 @@ func testCustomChannelsStrictForwarding(ctx context.Context, // with tapd included. We also need specific flags to be enabled, so we // create 5 completely new nodes, ignoring the two default nodes that // are created by the harness. - charlie, err := net.NewNode( - t.t, "Charlie", lndArgs, false, true, litdArgs..., + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", lndArgs, false, true, charliePort, litdArgs..., ) require.NoError(t.t, err) @@ -2334,7 +2302,7 @@ func testCustomChannelsStrictForwarding(ctx context.Context, assertChannelKnown(t.t, charlie, channelOp) assertChannelKnown(t.t, fabia, channelOp) - universeTap := newTapClient(t.t, zane) + universeTap := newTapClient(t.t, charlie) charlieTap := newTapClient(t.t, charlie) daveTap := newTapClient(t.t, dave) erinTap := newTapClient(t.t, erin) @@ -2480,22 +2448,17 @@ func testCustomChannelsBalanceConsistency(ctx context.Context, lndArgs := slices.Clone(lndArgsTemplate) litdArgs := slices.Clone(litdArgsTemplate) - // Explicitly set the proof courier as Zane (now has no other role - // other than proof shuffling), otherwise a hashmail courier will be - // used. For the funding transaction, we're just posting it and don't - // expect a true receiver. - zane, err := net.NewNode( - t.t, "Zane", lndArgs, false, true, litdArgs..., - ) - require.NoError(t.t, err) - + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() litdArgs = append(litdArgs, fmt.Sprintf( "--taproot-assets.proofcourieraddr=%s://%s", - proof.UniverseRpcCourierType, zane.Cfg.LitAddr(), + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), )) - charlie, err := net.NewNode( - t.t, "Charlie", lndArgs, false, true, litdArgs..., + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", lndArgs, false, true, charliePort, litdArgs..., ) require.NoError(t.t, err) dave, err := net.NewNode(t.t, "Dave", lndArgs, false, true, litdArgs...) @@ -2507,7 +2470,7 @@ func testCustomChannelsBalanceConsistency(ctx context.Context, charlieTap := newTapClient(t.t, charlie) daveTap := newTapClient(t.t, dave) - universeTap := newTapClient(t.t, zane) + universeTap := newTapClient(t.t, charlie) // Mint an asset on Charlie and sync Dave to Charlie as the universe. mintedAssets := itest.MintAssetsConfirmBatch( @@ -2679,18 +2642,17 @@ func testCustomChannelsSingleAssetMultiInput(ctx context.Context, lndArgs := slices.Clone(lndArgsTemplate) litdArgs := slices.Clone(litdArgsTemplate) - zane, err := net.NewNode( - t.t, "Zane", lndArgs, false, true, litdArgs..., - ) - require.NoError(t.t, err) - + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() litdArgs = append(litdArgs, fmt.Sprintf( "--taproot-assets.proofcourieraddr=%s://%s", - proof.UniverseRpcCourierType, zane.Cfg.LitAddr(), + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), )) - charlie, err := net.NewNode( - t.t, "Charlie", lndArgs, false, true, litdArgs..., + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", lndArgs, false, true, charliePort, litdArgs..., ) require.NoError(t.t, err) dave, err := net.NewNode(t.t, "Dave", lndArgs, false, true, litdArgs...) @@ -2825,18 +2787,13 @@ func testCustomChannelsOraclePricing(ctx context.Context, net *NetworkHarness, "rfqrpc://%s", oracleAddr, )) - // Explicitly set the proof courier as Zane (now has no other role - // other than proof shuffling), otherwise a hashmail courier will be - // used. For the funding transaction, we're just posting it and don't - // expect a true receiver. - zane, err := net.NewNode( - t.t, "Zane", lndArgs, false, true, litdArgs..., - ) - require.NoError(t.t, err) - + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() litdArgs = append(litdArgs, fmt.Sprintf( "--taproot-assets.proofcourieraddr=%s://%s", - proof.UniverseRpcCourierType, zane.Cfg.LitAddr(), + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), )) // The topology we are going for looks like the following: @@ -2855,8 +2812,8 @@ func testCustomChannelsOraclePricing(ctx context.Context, net *NetworkHarness, // with tapd included. We also need specific flags to be enabled, so we // create 5 completely new nodes, ignoring the two default nodes that // are created by the harness. - charlie, err := net.NewNode( - t.t, "Charlie", lndArgs, false, true, litdArgs..., + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", lndArgs, false, true, charliePort, litdArgs..., ) require.NoError(t.t, err) @@ -2892,7 +2849,7 @@ func testCustomChannelsOraclePricing(ctx context.Context, net *NetworkHarness, assertChannelKnown(t.t, charlie, chanPointDE) assertChannelKnown(t.t, fabia, chanPointDE) - universeTap := newTapClient(t.t, zane) + universeTap := newTapClient(t.t, charlie) charlieTap := newTapClient(t.t, charlie) daveTap := newTapClient(t.t, dave) erinTap := newTapClient(t.t, erin) @@ -3111,18 +3068,17 @@ func testCustomChannelsFee(ctx context.Context, net *NetworkHarness, lndArgs := slices.Clone(lndArgsTemplate) litdArgs := slices.Clone(litdArgsTemplate) - zane, err := net.NewNode( - t.t, "Zane", lndArgs, false, true, litdArgs..., - ) - require.NoError(t.t, err) - + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() litdArgs = append(litdArgs, fmt.Sprintf( "--taproot-assets.proofcourieraddr=%s://%s", - proof.UniverseRpcCourierType, zane.Cfg.LitAddr(), + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), )) - charlie, err := net.NewNode( - t.t, "Charlie", lndArgs, false, true, litdArgs..., + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", lndArgs, false, true, charliePort, litdArgs..., ) require.NoError(t.t, err) dave, err := net.NewNode(t.t, "Dave", lndArgs, false, true, litdArgs...) @@ -3813,18 +3769,13 @@ func testCustomChannelsForwardBandwidth(ctx context.Context, lndArgs := slices.Clone(lndArgsTemplate) litdArgs := slices.Clone(litdArgsTemplate) - // Explicitly set the proof courier as Zane (now has no other role - // other than proof shuffling), otherwise a hashmail courier will be - // used. For the funding transaction, we're just posting it and don't - // expect a true receiver. - zane, err := net.NewNode( - t.t, "Zane", lndArgs, false, true, litdArgs..., - ) - require.NoError(t.t, err) - + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() litdArgs = append(litdArgs, fmt.Sprintf( "--taproot-assets.proofcourieraddr=%s://%s", - proof.UniverseRpcCourierType, zane.Cfg.LitAddr(), + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), )) // The topology we are going for looks like the following: @@ -3843,8 +3794,8 @@ func testCustomChannelsForwardBandwidth(ctx context.Context, // with tapd included. We also need specific flags to be enabled, so we // create 5 completely new nodes, ignoring the two default nodes that // are created by the harness. - charlie, err := net.NewNode( - t.t, "Charlie", lndArgs, false, true, litdArgs..., + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", lndArgs, false, true, charliePort, litdArgs..., ) require.NoError(t.t, err) @@ -3879,7 +3830,7 @@ func testCustomChannelsForwardBandwidth(ctx context.Context, assertChannelKnown(t.t, charlie, channelOp) assertChannelKnown(t.t, fabia, channelOp) - universeTap := newTapClient(t.t, zane) + universeTap := newTapClient(t.t, charlie) charlieTap := newTapClient(t.t, charlie) daveTap := newTapClient(t.t, dave) erinTap := newTapClient(t.t, erin) diff --git a/itest/litd_node.go b/itest/litd_node.go index 48cd6353a..bbd822f85 100644 --- a/itest/litd_node.go +++ b/itest/litd_node.go @@ -70,6 +70,9 @@ var ( ) ) +// Option is a function for updating a node's configuration. +type Option func(config *LitNodeConfig) + type LitNodeConfig struct { *node.BaseNodeConfig diff --git a/itest/network_harness.go b/itest/network_harness.go index cb4a97d8f..52c94d073 100644 --- a/itest/network_harness.go +++ b/itest/network_harness.go @@ -319,13 +319,28 @@ func (n *NetworkHarness) NewNode(t *testing.T, name string, extraArgs []string, ) } +// NewNodeWithPort initializes a new HarnessNode with a custom litd port. +func (n *NetworkHarness) NewNodeWithPort(t *testing.T, name string, + extraArgs []string, remoteMode bool, wait bool, litPort int, + additionalLitArgs ...string) (*HarnessNode, error) { + + allLitArgs := append(n.litArgs(), additionalLitArgs...) + + return n.newNode( + t, name, extraArgs, allLitArgs, false, remoteMode, nil, wait, + false, func(config *LitNodeConfig) { + config.LitPort = litPort + }, + ) +} + // newNode initializes a new HarnessNode, supporting the ability to initialize a // wallet with or without a seed. If hasSeed is false, the returned harness node // can be used immediately. Otherwise, the node will require an additional // initialization phase where the wallet is either created or restored. func (n *NetworkHarness) newNode(t *testing.T, name string, extraArgs, litArgs []string, hasSeed, remoteMode bool, password []byte, wait, - skipUnlock bool, opts ...node.Option) (*HarnessNode, error) { + skipUnlock bool, opts ...Option) (*HarnessNode, error) { baseCfg := &node.BaseNodeConfig{ Name: name, @@ -337,15 +352,15 @@ func (n *NetworkHarness) newNode(t *testing.T, name string, extraArgs, SkipUnlock: skipUnlock, FeeURL: n.feeService.URL(), } - for _, opt := range opts { - opt(baseCfg) - } cfg := &LitNodeConfig{ BaseNodeConfig: baseCfg, HasSeed: hasSeed, LitArgs: litArgs, RemoteMode: remoteMode, } + for _, opt := range opts { + opt(cfg) + } node, err := NewNode(t, cfg, n.LNDHarness) if err != nil { From e995fcbeb77492eed0174b636656447fd6834b3d Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 26 Feb 2025 16:34:36 +0100 Subject: [PATCH 3/8] mod: bump to latest version of tapd, lnd, loop --- app/src/types/generated/lnd_pb.d.ts | 31 + app/src/types/generated/lnd_pb.js | 270 +++- app/src/types/generated/loop_pb.d.ts | 227 ++- app/src/types/generated/loop_pb.js | 1981 ++++++++++++++++++++++++-- app/src/util/tests/sampleData.ts | 2 + go.mod | 49 +- go.sum | 103 +- itest/assets_test.go | 18 +- proto/lnd.proto | 20 + proto/loop.proto | 195 ++- rules/channel_restrictions_test.go | 4 +- 11 files changed, 2692 insertions(+), 208 deletions(-) diff --git a/app/src/types/generated/lnd_pb.d.ts b/app/src/types/generated/lnd_pb.d.ts index 4bdbde587..347abb79c 100644 --- a/app/src/types/generated/lnd_pb.d.ts +++ b/app/src/types/generated/lnd_pb.d.ts @@ -1773,6 +1773,11 @@ export class ChannelCloseSummary extends jspb.Message { getZeroConfConfirmedScid(): string; setZeroConfConfirmedScid(value: string): void; + getCustomChannelData(): Uint8Array | string; + getCustomChannelData_asU8(): Uint8Array; + getCustomChannelData_asB64(): string; + setCustomChannelData(value: Uint8Array | string): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): ChannelCloseSummary.AsObject; static toObject(includeInstance: boolean, msg: ChannelCloseSummary): ChannelCloseSummary.AsObject; @@ -1800,6 +1805,7 @@ export namespace ChannelCloseSummary { resolutionsList: Array, aliasScidsList: Array, zeroConfConfirmedScid: string, + customChannelData: Uint8Array | string, } export interface ClosureTypeMap { @@ -7376,6 +7382,8 @@ export class RPCMiddlewareRequest extends jspb.Message { getMsgId(): string; setMsgId(value: string): void; + getMetadataPairsMap(): jspb.Map; + clearMetadataPairsMap(): void; getInterceptTypeCase(): RPCMiddlewareRequest.InterceptTypeCase; serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): RPCMiddlewareRequest.AsObject; @@ -7397,6 +7405,7 @@ export namespace RPCMiddlewareRequest { response?: RPCMessage.AsObject, regComplete: boolean, msgId: string, + metadataPairsMap: Array<[string, MetadataValues.AsObject]>, } export enum InterceptTypeCase { @@ -7408,6 +7417,28 @@ export namespace RPCMiddlewareRequest { } } +export class MetadataValues extends jspb.Message { + clearValuesList(): void; + getValuesList(): Array; + setValuesList(value: Array): void; + addValues(value: string, index?: number): string; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): MetadataValues.AsObject; + static toObject(includeInstance: boolean, msg: MetadataValues): MetadataValues.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: MetadataValues, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): MetadataValues; + static deserializeBinaryFromReader(message: MetadataValues, reader: jspb.BinaryReader): MetadataValues; +} + +export namespace MetadataValues { + export type AsObject = { + valuesList: Array, + } +} + export class StreamAuth extends jspb.Message { getMethodFullUri(): string; setMethodFullUri(value: string): void; diff --git a/app/src/types/generated/lnd_pb.js b/app/src/types/generated/lnd_pb.js index 858bfa42b..5202df5b9 100644 --- a/app/src/types/generated/lnd_pb.js +++ b/app/src/types/generated/lnd_pb.js @@ -171,6 +171,7 @@ goog.exportSymbol('proto.lnrpc.MPPRecord', null, global); goog.exportSymbol('proto.lnrpc.MacaroonId', null, global); goog.exportSymbol('proto.lnrpc.MacaroonPermission', null, global); goog.exportSymbol('proto.lnrpc.MacaroonPermissionList', null, global); +goog.exportSymbol('proto.lnrpc.MetadataValues', null, global); goog.exportSymbol('proto.lnrpc.MiddlewareRegistration', null, global); goog.exportSymbol('proto.lnrpc.MultiChanBackup', null, global); goog.exportSymbol('proto.lnrpc.NetworkInfo', null, global); @@ -4505,6 +4506,27 @@ if (goog.DEBUG && !COMPILED) { */ proto.lnrpc.RPCMiddlewareRequest.displayName = 'proto.lnrpc.RPCMiddlewareRequest'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.lnrpc.MetadataValues = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.lnrpc.MetadataValues.repeatedFields_, null); +}; +goog.inherits(proto.lnrpc.MetadataValues, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.lnrpc.MetadataValues.displayName = 'proto.lnrpc.MetadataValues'; +} /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a @@ -17005,7 +17027,8 @@ proto.lnrpc.ChannelCloseSummary.toObject = function(includeInstance, msg) { resolutionsList: jspb.Message.toObjectList(msg.getResolutionsList(), proto.lnrpc.Resolution.toObject, includeInstance), aliasScidsList: (f = jspb.Message.getRepeatedField(msg, 14)) == null ? undefined : f, - zeroConfConfirmedScid: jspb.Message.getFieldWithDefault(msg, 15, "0") + zeroConfConfirmedScid: jspb.Message.getFieldWithDefault(msg, 15, "0"), + customChannelData: msg.getCustomChannelData_asB64() }; if (includeInstance) { @@ -17105,6 +17128,10 @@ proto.lnrpc.ChannelCloseSummary.deserializeBinaryFromReader = function(msg, read var value = /** @type {string} */ (reader.readUint64String()); msg.setZeroConfConfirmedScid(value); break; + case 16: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setCustomChannelData(value); + break; default: reader.skipField(); break; @@ -17240,6 +17267,13 @@ proto.lnrpc.ChannelCloseSummary.serializeBinaryToWriter = function(message, writ f ); } + f = message.getCustomChannelData_asU8(); + if (f.length > 0) { + writer.writeBytes( + 16, + f + ); + } }; @@ -17564,6 +17598,48 @@ proto.lnrpc.ChannelCloseSummary.prototype.setZeroConfConfirmedScid = function(va }; +/** + * optional bytes custom_channel_data = 16; + * @return {!(string|Uint8Array)} + */ +proto.lnrpc.ChannelCloseSummary.prototype.getCustomChannelData = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 16, "")); +}; + + +/** + * optional bytes custom_channel_data = 16; + * This is a type-conversion wrapper around `getCustomChannelData()` + * @return {string} + */ +proto.lnrpc.ChannelCloseSummary.prototype.getCustomChannelData_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getCustomChannelData())); +}; + + +/** + * optional bytes custom_channel_data = 16; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getCustomChannelData()` + * @return {!Uint8Array} + */ +proto.lnrpc.ChannelCloseSummary.prototype.getCustomChannelData_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getCustomChannelData())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.lnrpc.ChannelCloseSummary} returns this + */ +proto.lnrpc.ChannelCloseSummary.prototype.setCustomChannelData = function(value) { + return jspb.Message.setProto3BytesField(this, 16, value); +}; + + @@ -57081,7 +57157,8 @@ proto.lnrpc.RPCMiddlewareRequest.toObject = function(includeInstance, msg) { request: (f = msg.getRequest()) && proto.lnrpc.RPCMessage.toObject(includeInstance, f), response: (f = msg.getResponse()) && proto.lnrpc.RPCMessage.toObject(includeInstance, f), regComplete: jspb.Message.getBooleanFieldWithDefault(msg, 8, false), - msgId: jspb.Message.getFieldWithDefault(msg, 7, "0") + msgId: jspb.Message.getFieldWithDefault(msg, 7, "0"), + metadataPairsMap: (f = msg.getMetadataPairsMap()) ? f.toObject(includeInstance, proto.lnrpc.MetadataValues.toObject) : [] }; if (includeInstance) { @@ -57153,6 +57230,12 @@ proto.lnrpc.RPCMiddlewareRequest.deserializeBinaryFromReader = function(msg, rea var value = /** @type {string} */ (reader.readUint64String()); msg.setMsgId(value); break; + case 9: + var value = msg.getMetadataPairsMap(); + reader.readMessage(value, function(message, reader) { + jspb.Map.deserializeBinary(message, reader, jspb.BinaryReader.prototype.readString, jspb.BinaryReader.prototype.readMessage, proto.lnrpc.MetadataValues.deserializeBinaryFromReader, "", new proto.lnrpc.MetadataValues()); + }); + break; default: reader.skipField(); break; @@ -57241,6 +57324,10 @@ proto.lnrpc.RPCMiddlewareRequest.serializeBinaryToWriter = function(message, wri f ); } + f = message.getMetadataPairsMap(true); + if (f && f.getLength() > 0) { + f.serializeBinary(9, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeMessage, proto.lnrpc.MetadataValues.serializeBinaryToWriter); + } }; @@ -57487,6 +57574,185 @@ proto.lnrpc.RPCMiddlewareRequest.prototype.setMsgId = function(value) { }; +/** + * map metadata_pairs = 9; + * @param {boolean=} opt_noLazyCreate Do not create the map if + * empty, instead returning `undefined` + * @return {!jspb.Map} + */ +proto.lnrpc.RPCMiddlewareRequest.prototype.getMetadataPairsMap = function(opt_noLazyCreate) { + return /** @type {!jspb.Map} */ ( + jspb.Message.getMapField(this, 9, opt_noLazyCreate, + proto.lnrpc.MetadataValues)); +}; + + +/** + * Clears values from the map. The map will be non-null. + * @return {!proto.lnrpc.RPCMiddlewareRequest} returns this + */ +proto.lnrpc.RPCMiddlewareRequest.prototype.clearMetadataPairsMap = function() { + this.getMetadataPairsMap().clear(); + return this; +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.lnrpc.MetadataValues.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.lnrpc.MetadataValues.prototype.toObject = function(opt_includeInstance) { + return proto.lnrpc.MetadataValues.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.lnrpc.MetadataValues} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.lnrpc.MetadataValues.toObject = function(includeInstance, msg) { + var f, obj = { + valuesList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.lnrpc.MetadataValues} + */ +proto.lnrpc.MetadataValues.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.lnrpc.MetadataValues; + return proto.lnrpc.MetadataValues.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.lnrpc.MetadataValues} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.lnrpc.MetadataValues} + */ +proto.lnrpc.MetadataValues.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.addValues(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.lnrpc.MetadataValues.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.lnrpc.MetadataValues.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.lnrpc.MetadataValues} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.lnrpc.MetadataValues.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getValuesList(); + if (f.length > 0) { + writer.writeRepeatedString( + 1, + f + ); + } +}; + + +/** + * repeated string values = 1; + * @return {!Array} + */ +proto.lnrpc.MetadataValues.prototype.getValuesList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * @param {!Array} value + * @return {!proto.lnrpc.MetadataValues} returns this + */ +proto.lnrpc.MetadataValues.prototype.setValuesList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {string} value + * @param {number=} opt_index + * @return {!proto.lnrpc.MetadataValues} returns this + */ +proto.lnrpc.MetadataValues.prototype.addValues = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.lnrpc.MetadataValues} returns this + */ +proto.lnrpc.MetadataValues.prototype.clearValuesList = function() { + return this.setValuesList([]); +}; + + diff --git a/app/src/types/generated/loop_pb.d.ts b/app/src/types/generated/loop_pb.d.ts index 903e1f70c..b18d8a6c0 100644 --- a/app/src/types/generated/loop_pb.d.ts +++ b/app/src/types/generated/loop_pb.d.ts @@ -68,6 +68,16 @@ export class LoopOutRequest extends jspb.Message { getPaymentTimeout(): number; setPaymentTimeout(value: number): void; + hasAssetInfo(): boolean; + clearAssetInfo(): void; + getAssetInfo(): AssetLoopOutRequest | undefined; + setAssetInfo(value?: AssetLoopOutRequest): void; + + hasAssetRfqInfo(): boolean; + clearAssetRfqInfo(): void; + getAssetRfqInfo(): AssetRfqInfo | undefined; + setAssetRfqInfo(value?: AssetRfqInfo): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): LoopOutRequest.AsObject; static toObject(includeInstance: boolean, msg: LoopOutRequest): LoopOutRequest.AsObject; @@ -99,6 +109,8 @@ export namespace LoopOutRequest { isExternalAddr: boolean, reservationIdsList: Array, paymentTimeout: number, + assetInfo?: AssetLoopOutRequest.AsObject, + assetRfqInfo?: AssetRfqInfo.AsObject, } } @@ -278,6 +290,11 @@ export class SwapStatus extends jspb.Message { getLabel(): string; setLabel(value: string): void; + hasAssetInfo(): boolean; + clearAssetInfo(): void; + getAssetInfo(): AssetLoopOutInfo | undefined; + setAssetInfo(value?: AssetLoopOutInfo): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): SwapStatus.AsObject; static toObject(includeInstance: boolean, msg: SwapStatus): SwapStatus.AsObject; @@ -307,6 +324,7 @@ export namespace SwapStatus { lastHop: Uint8Array | string, outgoingChanSetList: Array, label: string, + assetInfo?: AssetLoopOutInfo.AsObject, } } @@ -316,6 +334,9 @@ export class ListSwapsRequest extends jspb.Message { getListSwapFilter(): ListSwapsFilter | undefined; setListSwapFilter(value?: ListSwapsFilter): void; + getMaxSwaps(): string; + setMaxSwaps(value: string): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): ListSwapsRequest.AsObject; static toObject(includeInstance: boolean, msg: ListSwapsRequest): ListSwapsRequest.AsObject; @@ -329,6 +350,7 @@ export class ListSwapsRequest extends jspb.Message { export namespace ListSwapsRequest { export type AsObject = { listSwapFilter?: ListSwapsFilter.AsObject, + maxSwaps: string, } } @@ -352,6 +374,12 @@ export class ListSwapsFilter extends jspb.Message { getLoopInLastHop_asB64(): string; setLoopInLastHop(value: Uint8Array | string): void; + getAssetSwapOnly(): boolean; + setAssetSwapOnly(value: boolean): void; + + getStartTimestampNs(): string; + setStartTimestampNs(value: string): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): ListSwapsFilter.AsObject; static toObject(includeInstance: boolean, msg: ListSwapsFilter): ListSwapsFilter.AsObject; @@ -369,6 +397,8 @@ export namespace ListSwapsFilter { outgoingChanSetList: Array, label: string, loopInLastHop: Uint8Array | string, + assetSwapOnly: boolean, + startTimestampNs: string, } export interface SwapTypeFilterMap { @@ -386,6 +416,9 @@ export class ListSwapsResponse extends jspb.Message { setSwapsList(value: Array): void; addSwaps(value?: SwapStatus, index?: number): SwapStatus; + getNextStartTime(): string; + setNextStartTime(value: string): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): ListSwapsResponse.AsObject; static toObject(includeInstance: boolean, msg: ListSwapsResponse): ListSwapsResponse.AsObject; @@ -399,6 +432,7 @@ export class ListSwapsResponse extends jspb.Message { export namespace ListSwapsResponse { export type AsObject = { swapsList: Array, + nextStartTime: string, } } @@ -527,6 +561,11 @@ export class QuoteRequest extends jspb.Message { setDepositOutpointsList(value: Array): void; addDepositOutpoints(value: string, index?: number): string; + hasAssetInfo(): boolean; + clearAssetInfo(): void; + getAssetInfo(): AssetLoopOutRequest | undefined; + setAssetInfo(value?: AssetLoopOutRequest): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): QuoteRequest.AsObject; static toObject(includeInstance: boolean, msg: QuoteRequest): QuoteRequest.AsObject; @@ -547,6 +586,7 @@ export namespace QuoteRequest { loopInRouteHintsList: Array, pb_private: boolean, depositOutpointsList: Array, + assetInfo?: AssetLoopOutRequest.AsObject, } } @@ -603,6 +643,11 @@ export class OutQuoteResponse extends jspb.Message { getConfTarget(): number; setConfTarget(value: number): void; + hasAssetRfqInfo(): boolean; + clearAssetRfqInfo(): void; + getAssetRfqInfo(): AssetRfqInfo | undefined; + setAssetRfqInfo(value?: AssetRfqInfo): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): OutQuoteResponse.AsObject; static toObject(includeInstance: boolean, msg: OutQuoteResponse): OutQuoteResponse.AsObject; @@ -621,6 +666,7 @@ export namespace OutQuoteResponse { swapPaymentDest: Uint8Array | string, cltvDelta: number, confTarget: number, + assetRfqInfo?: AssetRfqInfo.AsObject, } } @@ -881,6 +927,9 @@ export class GetInfoResponse extends jspb.Message { getLoopInStats(): LoopStats | undefined; setLoopInStats(value?: LoopStats): void; + getCommitHash(): string; + setCommitHash(value: string): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): GetInfoResponse.AsObject; static toObject(includeInstance: boolean, msg: GetInfoResponse): GetInfoResponse.AsObject; @@ -901,6 +950,7 @@ export namespace GetInfoResponse { tlsCertPath: string, loopOutStats?: LoopStats.AsObject, loopInStats?: LoopStats.AsObject, + commitHash: string, } } @@ -995,6 +1045,8 @@ export class LiquidityParameters extends jspb.Message { getAccountAddrType(): AddressTypeMap[keyof AddressTypeMap]; setAccountAddrType(value: AddressTypeMap[keyof AddressTypeMap]): void; + getEasyAssetParamsMap(): jspb.Map; + clearEasyAssetParamsMap(): void; serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): LiquidityParameters.AsObject; static toObject(includeInstance: boolean, msg: LiquidityParameters): LiquidityParameters.AsObject; @@ -1031,6 +1083,31 @@ export namespace LiquidityParameters { easyAutoloopLocalTargetSat: string, account: string, accountAddrType: AddressTypeMap[keyof AddressTypeMap], + easyAssetParamsMap: Array<[string, EasyAssetAutoloopParams.AsObject]>, + } +} + +export class EasyAssetAutoloopParams extends jspb.Message { + getEnabled(): boolean; + setEnabled(value: boolean): void; + + getLocalTargetAssetAmt(): string; + setLocalTargetAssetAmt(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): EasyAssetAutoloopParams.AsObject; + static toObject(includeInstance: boolean, msg: EasyAssetAutoloopParams): EasyAssetAutoloopParams.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: EasyAssetAutoloopParams, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): EasyAssetAutoloopParams; + static deserializeBinaryFromReader(message: EasyAssetAutoloopParams, reader: jspb.BinaryReader): EasyAssetAutoloopParams; +} + +export namespace EasyAssetAutoloopParams { + export type AsObject = { + enabled: boolean, + localTargetAssetAmt: string, } } @@ -1647,6 +1724,9 @@ export class WithdrawDepositsRequest extends jspb.Message { getSatPerVbyte(): string; setSatPerVbyte(value: string): void; + getAmount(): string; + setAmount(value: string): void; + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): WithdrawDepositsRequest.AsObject; static toObject(includeInstance: boolean, msg: WithdrawDepositsRequest): WithdrawDepositsRequest.AsObject; @@ -1663,6 +1743,7 @@ export namespace WithdrawDepositsRequest { all: boolean, destAddr: string, satPerVbyte: string, + amount: string, } } @@ -1670,8 +1751,8 @@ export class WithdrawDepositsResponse extends jspb.Message { getWithdrawalTxHash(): string; setWithdrawalTxHash(value: string): void; - getPkScript(): string; - setPkScript(value: string): void; + getAddress(): string; + setAddress(value: string): void; serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): WithdrawDepositsResponse.AsObject; @@ -1686,7 +1767,7 @@ export class WithdrawDepositsResponse extends jspb.Message { export namespace WithdrawDepositsResponse { export type AsObject = { withdrawalTxHash: string, - pkScript: string, + address: string, } } @@ -2072,6 +2153,146 @@ export namespace StaticAddressLoopInResponse { } } +export class AssetLoopOutRequest extends jspb.Message { + getAssetId(): Uint8Array | string; + getAssetId_asU8(): Uint8Array; + getAssetId_asB64(): string; + setAssetId(value: Uint8Array | string): void; + + getAssetEdgeNode(): Uint8Array | string; + getAssetEdgeNode_asU8(): Uint8Array; + getAssetEdgeNode_asB64(): string; + setAssetEdgeNode(value: Uint8Array | string): void; + + getMaxLimitMultiplier(): number; + setMaxLimitMultiplier(value: number): void; + + getExpiry(): string; + setExpiry(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): AssetLoopOutRequest.AsObject; + static toObject(includeInstance: boolean, msg: AssetLoopOutRequest): AssetLoopOutRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: AssetLoopOutRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): AssetLoopOutRequest; + static deserializeBinaryFromReader(message: AssetLoopOutRequest, reader: jspb.BinaryReader): AssetLoopOutRequest; +} + +export namespace AssetLoopOutRequest { + export type AsObject = { + assetId: Uint8Array | string, + assetEdgeNode: Uint8Array | string, + maxLimitMultiplier: number, + expiry: string, + } +} + +export class AssetRfqInfo extends jspb.Message { + getPrepayRfqId(): Uint8Array | string; + getPrepayRfqId_asU8(): Uint8Array; + getPrepayRfqId_asB64(): string; + setPrepayRfqId(value: Uint8Array | string): void; + + getMaxPrepayAssetAmt(): string; + setMaxPrepayAssetAmt(value: string): void; + + hasPrepayAssetRate(): boolean; + clearPrepayAssetRate(): void; + getPrepayAssetRate(): FixedPoint | undefined; + setPrepayAssetRate(value?: FixedPoint): void; + + getSwapRfqId(): Uint8Array | string; + getSwapRfqId_asU8(): Uint8Array; + getSwapRfqId_asB64(): string; + setSwapRfqId(value: Uint8Array | string): void; + + getMaxSwapAssetAmt(): string; + setMaxSwapAssetAmt(value: string): void; + + hasSwapAssetRate(): boolean; + clearSwapAssetRate(): void; + getSwapAssetRate(): FixedPoint | undefined; + setSwapAssetRate(value?: FixedPoint): void; + + getAssetName(): string; + setAssetName(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): AssetRfqInfo.AsObject; + static toObject(includeInstance: boolean, msg: AssetRfqInfo): AssetRfqInfo.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: AssetRfqInfo, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): AssetRfqInfo; + static deserializeBinaryFromReader(message: AssetRfqInfo, reader: jspb.BinaryReader): AssetRfqInfo; +} + +export namespace AssetRfqInfo { + export type AsObject = { + prepayRfqId: Uint8Array | string, + maxPrepayAssetAmt: string, + prepayAssetRate?: FixedPoint.AsObject, + swapRfqId: Uint8Array | string, + maxSwapAssetAmt: string, + swapAssetRate?: FixedPoint.AsObject, + assetName: string, + } +} + +export class FixedPoint extends jspb.Message { + getCoefficient(): string; + setCoefficient(value: string): void; + + getScale(): number; + setScale(value: number): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): FixedPoint.AsObject; + static toObject(includeInstance: boolean, msg: FixedPoint): FixedPoint.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: FixedPoint, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): FixedPoint; + static deserializeBinaryFromReader(message: FixedPoint, reader: jspb.BinaryReader): FixedPoint; +} + +export namespace FixedPoint { + export type AsObject = { + coefficient: string, + scale: number, + } +} + +export class AssetLoopOutInfo extends jspb.Message { + getAssetId(): string; + setAssetId(value: string): void; + + getAssetName(): string; + setAssetName(value: string): void; + + getAssetCostOffchain(): string; + setAssetCostOffchain(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): AssetLoopOutInfo.AsObject; + static toObject(includeInstance: boolean, msg: AssetLoopOutInfo): AssetLoopOutInfo.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: AssetLoopOutInfo, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): AssetLoopOutInfo; + static deserializeBinaryFromReader(message: AssetLoopOutInfo, reader: jspb.BinaryReader): AssetLoopOutInfo; +} + +export namespace AssetLoopOutInfo { + export type AsObject = { + assetId: string, + assetName: string, + assetCostOffchain: string, + } +} + export interface AddressTypeMap { ADDRESS_TYPE_UNKNOWN: 0; TAPROOT_PUBKEY: 1; diff --git a/app/src/types/generated/loop_pb.js b/app/src/types/generated/loop_pb.js index 37d7766be..bc97b330c 100644 --- a/app/src/types/generated/loop_pb.js +++ b/app/src/types/generated/loop_pb.js @@ -29,14 +29,19 @@ goog.object.extend(proto, swapserverrpc_common_pb); goog.exportSymbol('proto.looprpc.AbandonSwapRequest', null, global); goog.exportSymbol('proto.looprpc.AbandonSwapResponse', null, global); goog.exportSymbol('proto.looprpc.AddressType', null, global); +goog.exportSymbol('proto.looprpc.AssetLoopOutInfo', null, global); +goog.exportSymbol('proto.looprpc.AssetLoopOutRequest', null, global); +goog.exportSymbol('proto.looprpc.AssetRfqInfo', null, global); goog.exportSymbol('proto.looprpc.AutoReason', null, global); goog.exportSymbol('proto.looprpc.ClientReservation', null, global); goog.exportSymbol('proto.looprpc.Deposit', null, global); goog.exportSymbol('proto.looprpc.DepositState', null, global); goog.exportSymbol('proto.looprpc.Disqualified', null, global); +goog.exportSymbol('proto.looprpc.EasyAssetAutoloopParams', null, global); goog.exportSymbol('proto.looprpc.FailureReason', null, global); goog.exportSymbol('proto.looprpc.FetchL402TokenRequest', null, global); goog.exportSymbol('proto.looprpc.FetchL402TokenResponse', null, global); +goog.exportSymbol('proto.looprpc.FixedPoint', null, global); goog.exportSymbol('proto.looprpc.GetInfoRequest', null, global); goog.exportSymbol('proto.looprpc.GetInfoResponse', null, global); goog.exportSymbol('proto.looprpc.GetLiquidityParamsRequest', null, global); @@ -665,6 +670,27 @@ if (goog.DEBUG && !COMPILED) { */ proto.looprpc.LiquidityParameters.displayName = 'proto.looprpc.LiquidityParameters'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.looprpc.EasyAssetAutoloopParams = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.looprpc.EasyAssetAutoloopParams, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.looprpc.EasyAssetAutoloopParams.displayName = 'proto.looprpc.EasyAssetAutoloopParams'; +} /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a @@ -1421,6 +1447,90 @@ if (goog.DEBUG && !COMPILED) { */ proto.looprpc.StaticAddressLoopInResponse.displayName = 'proto.looprpc.StaticAddressLoopInResponse'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.looprpc.AssetLoopOutRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.looprpc.AssetLoopOutRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.looprpc.AssetLoopOutRequest.displayName = 'proto.looprpc.AssetLoopOutRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.looprpc.AssetRfqInfo = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.looprpc.AssetRfqInfo, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.looprpc.AssetRfqInfo.displayName = 'proto.looprpc.AssetRfqInfo'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.looprpc.FixedPoint = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.looprpc.FixedPoint, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.looprpc.FixedPoint.displayName = 'proto.looprpc.FixedPoint'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.looprpc.AssetLoopOutInfo = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.looprpc.AssetLoopOutInfo, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.looprpc.AssetLoopOutInfo.displayName = 'proto.looprpc.AssetLoopOutInfo'; +} /** * List of repeated fields within this message type. @@ -1478,7 +1588,9 @@ proto.looprpc.LoopOutRequest.toObject = function(includeInstance, msg) { accountAddrType: jspb.Message.getFieldWithDefault(msg, 16, 0), isExternalAddr: jspb.Message.getBooleanFieldWithDefault(msg, 17, false), reservationIdsList: msg.getReservationIdsList_asB64(), - paymentTimeout: jspb.Message.getFieldWithDefault(msg, 19, 0) + paymentTimeout: jspb.Message.getFieldWithDefault(msg, 19, 0), + assetInfo: (f = msg.getAssetInfo()) && proto.looprpc.AssetLoopOutRequest.toObject(includeInstance, f), + assetRfqInfo: (f = msg.getAssetRfqInfo()) && proto.looprpc.AssetRfqInfo.toObject(includeInstance, f) }; if (includeInstance) { @@ -1593,6 +1705,16 @@ proto.looprpc.LoopOutRequest.deserializeBinaryFromReader = function(msg, reader) var value = /** @type {number} */ (reader.readUint32()); msg.setPaymentTimeout(value); break; + case 20: + var value = new proto.looprpc.AssetLoopOutRequest; + reader.readMessage(value,proto.looprpc.AssetLoopOutRequest.deserializeBinaryFromReader); + msg.setAssetInfo(value); + break; + case 21: + var value = new proto.looprpc.AssetRfqInfo; + reader.readMessage(value,proto.looprpc.AssetRfqInfo.deserializeBinaryFromReader); + msg.setAssetRfqInfo(value); + break; default: reader.skipField(); break; @@ -1755,6 +1877,22 @@ proto.looprpc.LoopOutRequest.serializeBinaryToWriter = function(message, writer) f ); } + f = message.getAssetInfo(); + if (f != null) { + writer.writeMessage( + 20, + f, + proto.looprpc.AssetLoopOutRequest.serializeBinaryToWriter + ); + } + f = message.getAssetRfqInfo(); + if (f != null) { + writer.writeMessage( + 21, + f, + proto.looprpc.AssetRfqInfo.serializeBinaryToWriter + ); + } }; @@ -2162,6 +2300,80 @@ proto.looprpc.LoopOutRequest.prototype.setPaymentTimeout = function(value) { }; +/** + * optional AssetLoopOutRequest asset_info = 20; + * @return {?proto.looprpc.AssetLoopOutRequest} + */ +proto.looprpc.LoopOutRequest.prototype.getAssetInfo = function() { + return /** @type{?proto.looprpc.AssetLoopOutRequest} */ ( + jspb.Message.getWrapperField(this, proto.looprpc.AssetLoopOutRequest, 20)); +}; + + +/** + * @param {?proto.looprpc.AssetLoopOutRequest|undefined} value + * @return {!proto.looprpc.LoopOutRequest} returns this +*/ +proto.looprpc.LoopOutRequest.prototype.setAssetInfo = function(value) { + return jspb.Message.setWrapperField(this, 20, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.looprpc.LoopOutRequest} returns this + */ +proto.looprpc.LoopOutRequest.prototype.clearAssetInfo = function() { + return this.setAssetInfo(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.looprpc.LoopOutRequest.prototype.hasAssetInfo = function() { + return jspb.Message.getField(this, 20) != null; +}; + + +/** + * optional AssetRfqInfo asset_rfq_info = 21; + * @return {?proto.looprpc.AssetRfqInfo} + */ +proto.looprpc.LoopOutRequest.prototype.getAssetRfqInfo = function() { + return /** @type{?proto.looprpc.AssetRfqInfo} */ ( + jspb.Message.getWrapperField(this, proto.looprpc.AssetRfqInfo, 21)); +}; + + +/** + * @param {?proto.looprpc.AssetRfqInfo|undefined} value + * @return {!proto.looprpc.LoopOutRequest} returns this +*/ +proto.looprpc.LoopOutRequest.prototype.setAssetRfqInfo = function(value) { + return jspb.Message.setWrapperField(this, 21, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.looprpc.LoopOutRequest} returns this + */ +proto.looprpc.LoopOutRequest.prototype.clearAssetRfqInfo = function() { + return this.setAssetRfqInfo(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.looprpc.LoopOutRequest.prototype.hasAssetRfqInfo = function() { + return jspb.Message.getField(this, 21) != null; +}; + + /** * List of repeated fields within this message type. @@ -3076,7 +3288,8 @@ proto.looprpc.SwapStatus.toObject = function(includeInstance, msg) { costOffchain: jspb.Message.getFieldWithDefault(msg, 10, "0"), lastHop: msg.getLastHop_asB64(), outgoingChanSetList: (f = jspb.Message.getRepeatedField(msg, 17)) == null ? undefined : f, - label: jspb.Message.getFieldWithDefault(msg, 15, "") + label: jspb.Message.getFieldWithDefault(msg, 15, ""), + assetInfo: (f = msg.getAssetInfo()) && proto.looprpc.AssetLoopOutInfo.toObject(includeInstance, f) }; if (includeInstance) { @@ -3183,6 +3396,11 @@ proto.looprpc.SwapStatus.deserializeBinaryFromReader = function(msg, reader) { var value = /** @type {string} */ (reader.readString()); msg.setLabel(value); break; + case 19: + var value = new proto.looprpc.AssetLoopOutInfo; + reader.readMessage(value,proto.looprpc.AssetLoopOutInfo.deserializeBinaryFromReader); + msg.setAssetInfo(value); + break; default: reader.skipField(); break; @@ -3331,6 +3549,14 @@ proto.looprpc.SwapStatus.serializeBinaryToWriter = function(message, writer) { f ); } + f = message.getAssetInfo(); + if (f != null) { + writer.writeMessage( + 19, + f, + proto.looprpc.AssetLoopOutInfo.serializeBinaryToWriter + ); + } }; @@ -3707,6 +3933,43 @@ proto.looprpc.SwapStatus.prototype.setLabel = function(value) { }; +/** + * optional AssetLoopOutInfo asset_info = 19; + * @return {?proto.looprpc.AssetLoopOutInfo} + */ +proto.looprpc.SwapStatus.prototype.getAssetInfo = function() { + return /** @type{?proto.looprpc.AssetLoopOutInfo} */ ( + jspb.Message.getWrapperField(this, proto.looprpc.AssetLoopOutInfo, 19)); +}; + + +/** + * @param {?proto.looprpc.AssetLoopOutInfo|undefined} value + * @return {!proto.looprpc.SwapStatus} returns this +*/ +proto.looprpc.SwapStatus.prototype.setAssetInfo = function(value) { + return jspb.Message.setWrapperField(this, 19, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.looprpc.SwapStatus} returns this + */ +proto.looprpc.SwapStatus.prototype.clearAssetInfo = function() { + return this.setAssetInfo(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.looprpc.SwapStatus.prototype.hasAssetInfo = function() { + return jspb.Message.getField(this, 19) != null; +}; + + @@ -3739,7 +4002,8 @@ proto.looprpc.ListSwapsRequest.prototype.toObject = function(opt_includeInstance */ proto.looprpc.ListSwapsRequest.toObject = function(includeInstance, msg) { var f, obj = { - listSwapFilter: (f = msg.getListSwapFilter()) && proto.looprpc.ListSwapsFilter.toObject(includeInstance, f) + listSwapFilter: (f = msg.getListSwapFilter()) && proto.looprpc.ListSwapsFilter.toObject(includeInstance, f), + maxSwaps: jspb.Message.getFieldWithDefault(msg, 2, "0") }; if (includeInstance) { @@ -3781,6 +4045,10 @@ proto.looprpc.ListSwapsRequest.deserializeBinaryFromReader = function(msg, reade reader.readMessage(value,proto.looprpc.ListSwapsFilter.deserializeBinaryFromReader); msg.setListSwapFilter(value); break; + case 2: + var value = /** @type {string} */ (reader.readUint64String()); + msg.setMaxSwaps(value); + break; default: reader.skipField(); break; @@ -3818,6 +4086,13 @@ proto.looprpc.ListSwapsRequest.serializeBinaryToWriter = function(message, write proto.looprpc.ListSwapsFilter.serializeBinaryToWriter ); } + f = message.getMaxSwaps(); + if (parseInt(f, 10) !== 0) { + writer.writeUint64String( + 2, + f + ); + } }; @@ -3858,6 +4133,24 @@ proto.looprpc.ListSwapsRequest.prototype.hasListSwapFilter = function() { }; +/** + * optional uint64 max_swaps = 2; + * @return {string} + */ +proto.looprpc.ListSwapsRequest.prototype.getMaxSwaps = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "0")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.ListSwapsRequest} returns this + */ +proto.looprpc.ListSwapsRequest.prototype.setMaxSwaps = function(value) { + return jspb.Message.setProto3StringIntField(this, 2, value); +}; + + /** * List of repeated fields within this message type. @@ -3901,7 +4194,9 @@ proto.looprpc.ListSwapsFilter.toObject = function(includeInstance, msg) { pendingOnly: jspb.Message.getBooleanFieldWithDefault(msg, 2, false), outgoingChanSetList: (f = jspb.Message.getRepeatedField(msg, 3)) == null ? undefined : f, label: jspb.Message.getFieldWithDefault(msg, 4, ""), - loopInLastHop: msg.getLoopInLastHop_asB64() + loopInLastHop: msg.getLoopInLastHop_asB64(), + assetSwapOnly: jspb.Message.getBooleanFieldWithDefault(msg, 6, false), + startTimestampNs: jspb.Message.getFieldWithDefault(msg, 7, "0") }; if (includeInstance) { @@ -3960,6 +4255,14 @@ proto.looprpc.ListSwapsFilter.deserializeBinaryFromReader = function(msg, reader var value = /** @type {!Uint8Array} */ (reader.readBytes()); msg.setLoopInLastHop(value); break; + case 6: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setAssetSwapOnly(value); + break; + case 7: + var value = /** @type {string} */ (reader.readInt64String()); + msg.setStartTimestampNs(value); + break; default: reader.skipField(); break; @@ -4024,6 +4327,20 @@ proto.looprpc.ListSwapsFilter.serializeBinaryToWriter = function(message, writer f ); } + f = message.getAssetSwapOnly(); + if (f) { + writer.writeBool( + 6, + f + ); + } + f = message.getStartTimestampNs(); + if (parseInt(f, 10) !== 0) { + writer.writeInt64String( + 7, + f + ); + } }; @@ -4169,36 +4486,72 @@ proto.looprpc.ListSwapsFilter.prototype.setLoopInLastHop = function(value) { }; - /** - * List of repeated fields within this message type. - * @private {!Array} - * @const + * optional bool asset_swap_only = 6; + * @return {boolean} */ -proto.looprpc.ListSwapsResponse.repeatedFields_ = [1]; - +proto.looprpc.ListSwapsFilter.prototype.getAssetSwapOnly = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 6, false)); +}; -if (jspb.Message.GENERATE_TO_OBJECT) { /** - * Creates an object representation of this proto. - * Field names that are reserved in JavaScript and will be renamed to pb_name. - * Optional fields that are not set will be set to undefined. - * To access a reserved field use, foo.pb_, eg, foo.pb_default. - * For the list of reserved names please see: - * net/proto2/compiler/js/internal/generator.cc#kKeyword. - * @param {boolean=} opt_includeInstance Deprecated. whether to include the - * JSPB instance for transitional soy proto support: - * http://goto/soy-param-migration - * @return {!Object} + * @param {boolean} value + * @return {!proto.looprpc.ListSwapsFilter} returns this */ -proto.looprpc.ListSwapsResponse.prototype.toObject = function(opt_includeInstance) { - return proto.looprpc.ListSwapsResponse.toObject(opt_includeInstance, this); +proto.looprpc.ListSwapsFilter.prototype.setAssetSwapOnly = function(value) { + return jspb.Message.setProto3BooleanField(this, 6, value); }; /** - * Static version of the {@see toObject} method. + * optional int64 start_timestamp_ns = 7; + * @return {string} + */ +proto.looprpc.ListSwapsFilter.prototype.getStartTimestampNs = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, "0")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.ListSwapsFilter} returns this + */ +proto.looprpc.ListSwapsFilter.prototype.setStartTimestampNs = function(value) { + return jspb.Message.setProto3StringIntField(this, 7, value); +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.looprpc.ListSwapsResponse.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.looprpc.ListSwapsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.looprpc.ListSwapsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. * @param {boolean|undefined} includeInstance Deprecated. Whether to include * the JSPB instance for transitional soy proto support: * http://goto/soy-param-migration @@ -4209,7 +4562,8 @@ proto.looprpc.ListSwapsResponse.prototype.toObject = function(opt_includeInstanc proto.looprpc.ListSwapsResponse.toObject = function(includeInstance, msg) { var f, obj = { swapsList: jspb.Message.toObjectList(msg.getSwapsList(), - proto.looprpc.SwapStatus.toObject, includeInstance) + proto.looprpc.SwapStatus.toObject, includeInstance), + nextStartTime: jspb.Message.getFieldWithDefault(msg, 2, "0") }; if (includeInstance) { @@ -4251,6 +4605,10 @@ proto.looprpc.ListSwapsResponse.deserializeBinaryFromReader = function(msg, read reader.readMessage(value,proto.looprpc.SwapStatus.deserializeBinaryFromReader); msg.addSwaps(value); break; + case 2: + var value = /** @type {string} */ (reader.readInt64String()); + msg.setNextStartTime(value); + break; default: reader.skipField(); break; @@ -4288,6 +4646,13 @@ proto.looprpc.ListSwapsResponse.serializeBinaryToWriter = function(message, writ proto.looprpc.SwapStatus.serializeBinaryToWriter ); } + f = message.getNextStartTime(); + if (parseInt(f, 10) !== 0) { + writer.writeInt64String( + 2, + f + ); + } }; @@ -4329,6 +4694,24 @@ proto.looprpc.ListSwapsResponse.prototype.clearSwapsList = function() { }; +/** + * optional int64 next_start_time = 2; + * @return {string} + */ +proto.looprpc.ListSwapsResponse.prototype.getNextStartTime = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "0")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.ListSwapsResponse} returns this + */ +proto.looprpc.ListSwapsResponse.prototype.setNextStartTime = function(value) { + return jspb.Message.setProto3StringIntField(this, 2, value); +}; + + @@ -5011,7 +5394,8 @@ proto.looprpc.QuoteRequest.toObject = function(includeInstance, msg) { loopInRouteHintsList: jspb.Message.toObjectList(msg.getLoopInRouteHintsList(), swapserverrpc_common_pb.RouteHint.toObject, includeInstance), pb_private: jspb.Message.getBooleanFieldWithDefault(msg, 7, false), - depositOutpointsList: (f = jspb.Message.getRepeatedField(msg, 8)) == null ? undefined : f + depositOutpointsList: (f = jspb.Message.getRepeatedField(msg, 8)) == null ? undefined : f, + assetInfo: (f = msg.getAssetInfo()) && proto.looprpc.AssetLoopOutRequest.toObject(includeInstance, f) }; if (includeInstance) { @@ -5081,6 +5465,11 @@ proto.looprpc.QuoteRequest.deserializeBinaryFromReader = function(msg, reader) { var value = /** @type {string} */ (reader.readString()); msg.addDepositOutpoints(value); break; + case 9: + var value = new proto.looprpc.AssetLoopOutRequest; + reader.readMessage(value,proto.looprpc.AssetLoopOutRequest.deserializeBinaryFromReader); + msg.setAssetInfo(value); + break; default: reader.skipField(); break; @@ -5167,6 +5556,14 @@ proto.looprpc.QuoteRequest.serializeBinaryToWriter = function(message, writer) { f ); } + f = message.getAssetInfo(); + if (f != null) { + writer.writeMessage( + 9, + f, + proto.looprpc.AssetLoopOutRequest.serializeBinaryToWriter + ); + } }; @@ -5377,6 +5774,43 @@ proto.looprpc.QuoteRequest.prototype.clearDepositOutpointsList = function() { }; +/** + * optional AssetLoopOutRequest asset_info = 9; + * @return {?proto.looprpc.AssetLoopOutRequest} + */ +proto.looprpc.QuoteRequest.prototype.getAssetInfo = function() { + return /** @type{?proto.looprpc.AssetLoopOutRequest} */ ( + jspb.Message.getWrapperField(this, proto.looprpc.AssetLoopOutRequest, 9)); +}; + + +/** + * @param {?proto.looprpc.AssetLoopOutRequest|undefined} value + * @return {!proto.looprpc.QuoteRequest} returns this +*/ +proto.looprpc.QuoteRequest.prototype.setAssetInfo = function(value) { + return jspb.Message.setWrapperField(this, 9, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.looprpc.QuoteRequest} returns this + */ +proto.looprpc.QuoteRequest.prototype.clearAssetInfo = function() { + return this.setAssetInfo(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.looprpc.QuoteRequest.prototype.hasAssetInfo = function() { + return jspb.Message.getField(this, 9) != null; +}; + + @@ -5634,7 +6068,8 @@ proto.looprpc.OutQuoteResponse.toObject = function(includeInstance, msg) { htlcSweepFeeSat: jspb.Message.getFieldWithDefault(msg, 3, "0"), swapPaymentDest: msg.getSwapPaymentDest_asB64(), cltvDelta: jspb.Message.getFieldWithDefault(msg, 5, 0), - confTarget: jspb.Message.getFieldWithDefault(msg, 6, 0) + confTarget: jspb.Message.getFieldWithDefault(msg, 6, 0), + assetRfqInfo: (f = msg.getAssetRfqInfo()) && proto.looprpc.AssetRfqInfo.toObject(includeInstance, f) }; if (includeInstance) { @@ -5695,6 +6130,11 @@ proto.looprpc.OutQuoteResponse.deserializeBinaryFromReader = function(msg, reade var value = /** @type {number} */ (reader.readInt32()); msg.setConfTarget(value); break; + case 7: + var value = new proto.looprpc.AssetRfqInfo; + reader.readMessage(value,proto.looprpc.AssetRfqInfo.deserializeBinaryFromReader); + msg.setAssetRfqInfo(value); + break; default: reader.skipField(); break; @@ -5766,6 +6206,14 @@ proto.looprpc.OutQuoteResponse.serializeBinaryToWriter = function(message, write f ); } + f = message.getAssetRfqInfo(); + if (f != null) { + writer.writeMessage( + 7, + f, + proto.looprpc.AssetRfqInfo.serializeBinaryToWriter + ); + } }; @@ -5901,6 +6349,43 @@ proto.looprpc.OutQuoteResponse.prototype.setConfTarget = function(value) { }; +/** + * optional AssetRfqInfo asset_rfq_info = 7; + * @return {?proto.looprpc.AssetRfqInfo} + */ +proto.looprpc.OutQuoteResponse.prototype.getAssetRfqInfo = function() { + return /** @type{?proto.looprpc.AssetRfqInfo} */ ( + jspb.Message.getWrapperField(this, proto.looprpc.AssetRfqInfo, 7)); +}; + + +/** + * @param {?proto.looprpc.AssetRfqInfo|undefined} value + * @return {!proto.looprpc.OutQuoteResponse} returns this +*/ +proto.looprpc.OutQuoteResponse.prototype.setAssetRfqInfo = function(value) { + return jspb.Message.setWrapperField(this, 7, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.looprpc.OutQuoteResponse} returns this + */ +proto.looprpc.OutQuoteResponse.prototype.clearAssetRfqInfo = function() { + return this.setAssetRfqInfo(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.looprpc.OutQuoteResponse.prototype.hasAssetRfqInfo = function() { + return jspb.Message.getField(this, 7) != null; +}; + + /** * List of repeated fields within this message type. @@ -7541,7 +8026,8 @@ proto.looprpc.GetInfoResponse.toObject = function(includeInstance, msg) { macaroonPath: jspb.Message.getFieldWithDefault(msg, 5, ""), tlsCertPath: jspb.Message.getFieldWithDefault(msg, 6, ""), loopOutStats: (f = msg.getLoopOutStats()) && proto.looprpc.LoopStats.toObject(includeInstance, f), - loopInStats: (f = msg.getLoopInStats()) && proto.looprpc.LoopStats.toObject(includeInstance, f) + loopInStats: (f = msg.getLoopInStats()) && proto.looprpc.LoopStats.toObject(includeInstance, f), + commitHash: jspb.Message.getFieldWithDefault(msg, 9, "") }; if (includeInstance) { @@ -7612,6 +8098,10 @@ proto.looprpc.GetInfoResponse.deserializeBinaryFromReader = function(msg, reader reader.readMessage(value,proto.looprpc.LoopStats.deserializeBinaryFromReader); msg.setLoopInStats(value); break; + case 9: + var value = /** @type {string} */ (reader.readString()); + msg.setCommitHash(value); + break; default: reader.skipField(); break; @@ -7699,6 +8189,13 @@ proto.looprpc.GetInfoResponse.serializeBinaryToWriter = function(message, writer proto.looprpc.LoopStats.serializeBinaryToWriter ); } + f = message.getCommitHash(); + if (f.length > 0) { + writer.writeString( + 9, + f + ); + } }; @@ -7884,6 +8381,24 @@ proto.looprpc.GetInfoResponse.prototype.hasLoopInStats = function() { }; +/** + * optional string commit_hash = 9; + * @return {string} + */ +proto.looprpc.GetInfoResponse.prototype.getCommitHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 9, "")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.GetInfoResponse} returns this + */ +proto.looprpc.GetInfoResponse.prototype.setCommitHash = function(value) { + return jspb.Message.setProto3StringField(this, 9, value); +}; + + @@ -8048,7 +8563,8 @@ proto.looprpc.LiquidityParameters.toObject = function(includeInstance, msg) { easyAutoloop: jspb.Message.getBooleanFieldWithDefault(msg, 21, false), easyAutoloopLocalTargetSat: jspb.Message.getFieldWithDefault(msg, 22, "0"), account: jspb.Message.getFieldWithDefault(msg, 23, ""), - accountAddrType: jspb.Message.getFieldWithDefault(msg, 24, 0) + accountAddrType: jspb.Message.getFieldWithDefault(msg, 24, 0), + easyAssetParamsMap: (f = msg.getEasyAssetParamsMap()) ? f.toObject(includeInstance, proto.looprpc.EasyAssetAutoloopParams.toObject) : [] }; if (includeInstance) { @@ -8182,6 +8698,12 @@ proto.looprpc.LiquidityParameters.deserializeBinaryFromReader = function(msg, re var value = /** @type {!proto.looprpc.AddressType} */ (reader.readEnum()); msg.setAccountAddrType(value); break; + case 25: + var value = msg.getEasyAssetParamsMap(); + reader.readMessage(value, function(message, reader) { + jspb.Map.deserializeBinary(message, reader, jspb.BinaryReader.prototype.readString, jspb.BinaryReader.prototype.readMessage, proto.looprpc.EasyAssetAutoloopParams.deserializeBinaryFromReader, "", new proto.looprpc.EasyAssetAutoloopParams()); + }); + break; default: reader.skipField(); break; @@ -8380,6 +8902,10 @@ proto.looprpc.LiquidityParameters.serializeBinaryToWriter = function(message, wr f ); } + f = message.getEasyAssetParamsMap(true); + if (f && f.getLength() > 0) { + f.serializeBinary(25, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeMessage, proto.looprpc.EasyAssetAutoloopParams.serializeBinaryToWriter); + } }; @@ -8835,6 +9361,29 @@ proto.looprpc.LiquidityParameters.prototype.setAccountAddrType = function(value) }; +/** + * map easy_asset_params = 25; + * @param {boolean=} opt_noLazyCreate Do not create the map if + * empty, instead returning `undefined` + * @return {!jspb.Map} + */ +proto.looprpc.LiquidityParameters.prototype.getEasyAssetParamsMap = function(opt_noLazyCreate) { + return /** @type {!jspb.Map} */ ( + jspb.Message.getMapField(this, 25, opt_noLazyCreate, + proto.looprpc.EasyAssetAutoloopParams)); +}; + + +/** + * Clears values from the map. The map will be non-null. + * @return {!proto.looprpc.LiquidityParameters} returns this + */ +proto.looprpc.LiquidityParameters.prototype.clearEasyAssetParamsMap = function() { + this.getEasyAssetParamsMap().clear(); + return this; +}; + + @@ -8851,8 +9400,8 @@ if (jspb.Message.GENERATE_TO_OBJECT) { * http://goto/soy-param-migration * @return {!Object} */ -proto.looprpc.LiquidityRule.prototype.toObject = function(opt_includeInstance) { - return proto.looprpc.LiquidityRule.toObject(opt_includeInstance, this); +proto.looprpc.EasyAssetAutoloopParams.prototype.toObject = function(opt_includeInstance) { + return proto.looprpc.EasyAssetAutoloopParams.toObject(opt_includeInstance, this); }; @@ -8861,18 +9410,14 @@ proto.looprpc.LiquidityRule.prototype.toObject = function(opt_includeInstance) { * @param {boolean|undefined} includeInstance Deprecated. Whether to include * the JSPB instance for transitional soy proto support: * http://goto/soy-param-migration - * @param {!proto.looprpc.LiquidityRule} msg The msg instance to transform. + * @param {!proto.looprpc.EasyAssetAutoloopParams} msg The msg instance to transform. * @return {!Object} * @suppress {unusedLocalVariables} f is only used for nested messages */ -proto.looprpc.LiquidityRule.toObject = function(includeInstance, msg) { +proto.looprpc.EasyAssetAutoloopParams.toObject = function(includeInstance, msg) { var f, obj = { - channelId: jspb.Message.getFieldWithDefault(msg, 1, "0"), - swapType: jspb.Message.getFieldWithDefault(msg, 6, 0), - pubkey: msg.getPubkey_asB64(), - type: jspb.Message.getFieldWithDefault(msg, 2, 0), - incomingThreshold: jspb.Message.getFieldWithDefault(msg, 3, 0), - outgoingThreshold: jspb.Message.getFieldWithDefault(msg, 4, 0) + enabled: jspb.Message.getBooleanFieldWithDefault(msg, 1, false), + localTargetAssetAmt: jspb.Message.getFieldWithDefault(msg, 2, "0") }; if (includeInstance) { @@ -8886,23 +9431,23 @@ proto.looprpc.LiquidityRule.toObject = function(includeInstance, msg) { /** * Deserializes binary data (in protobuf wire format). * @param {jspb.ByteSource} bytes The bytes to deserialize. - * @return {!proto.looprpc.LiquidityRule} + * @return {!proto.looprpc.EasyAssetAutoloopParams} */ -proto.looprpc.LiquidityRule.deserializeBinary = function(bytes) { +proto.looprpc.EasyAssetAutoloopParams.deserializeBinary = function(bytes) { var reader = new jspb.BinaryReader(bytes); - var msg = new proto.looprpc.LiquidityRule; - return proto.looprpc.LiquidityRule.deserializeBinaryFromReader(msg, reader); + var msg = new proto.looprpc.EasyAssetAutoloopParams; + return proto.looprpc.EasyAssetAutoloopParams.deserializeBinaryFromReader(msg, reader); }; /** * Deserializes binary data (in protobuf wire format) from the * given reader into the given message object. - * @param {!proto.looprpc.LiquidityRule} msg The message object to deserialize into. + * @param {!proto.looprpc.EasyAssetAutoloopParams} msg The message object to deserialize into. * @param {!jspb.BinaryReader} reader The BinaryReader to use. - * @return {!proto.looprpc.LiquidityRule} + * @return {!proto.looprpc.EasyAssetAutoloopParams} */ -proto.looprpc.LiquidityRule.deserializeBinaryFromReader = function(msg, reader) { +proto.looprpc.EasyAssetAutoloopParams.deserializeBinaryFromReader = function(msg, reader) { while (reader.nextField()) { if (reader.isEndGroup()) { break; @@ -8910,28 +9455,12 @@ proto.looprpc.LiquidityRule.deserializeBinaryFromReader = function(msg, reader) var field = reader.getFieldNumber(); switch (field) { case 1: - var value = /** @type {string} */ (reader.readUint64String()); - msg.setChannelId(value); - break; - case 6: - var value = /** @type {!proto.looprpc.SwapType} */ (reader.readEnum()); - msg.setSwapType(value); - break; - case 5: - var value = /** @type {!Uint8Array} */ (reader.readBytes()); - msg.setPubkey(value); + var value = /** @type {boolean} */ (reader.readBool()); + msg.setEnabled(value); break; case 2: - var value = /** @type {!proto.looprpc.LiquidityRuleType} */ (reader.readEnum()); - msg.setType(value); - break; - case 3: - var value = /** @type {number} */ (reader.readUint32()); - msg.setIncomingThreshold(value); - break; - case 4: - var value = /** @type {number} */ (reader.readUint32()); - msg.setOutgoingThreshold(value); + var value = /** @type {string} */ (reader.readUint64String()); + msg.setLocalTargetAssetAmt(value); break; default: reader.skipField(); @@ -8946,9 +9475,9 @@ proto.looprpc.LiquidityRule.deserializeBinaryFromReader = function(msg, reader) * Serializes the message to binary data (in protobuf wire format). * @return {!Uint8Array} */ -proto.looprpc.LiquidityRule.prototype.serializeBinary = function() { +proto.looprpc.EasyAssetAutoloopParams.prototype.serializeBinary = function() { var writer = new jspb.BinaryWriter(); - proto.looprpc.LiquidityRule.serializeBinaryToWriter(this, writer); + proto.looprpc.EasyAssetAutoloopParams.serializeBinaryToWriter(this, writer); return writer.getResultBuffer(); }; @@ -8956,59 +9485,239 @@ proto.looprpc.LiquidityRule.prototype.serializeBinary = function() { /** * Serializes the given message to binary data (in protobuf wire * format), writing to the given BinaryWriter. - * @param {!proto.looprpc.LiquidityRule} message + * @param {!proto.looprpc.EasyAssetAutoloopParams} message * @param {!jspb.BinaryWriter} writer * @suppress {unusedLocalVariables} f is only used for nested messages */ -proto.looprpc.LiquidityRule.serializeBinaryToWriter = function(message, writer) { +proto.looprpc.EasyAssetAutoloopParams.serializeBinaryToWriter = function(message, writer) { var f = undefined; - f = message.getChannelId(); - if (parseInt(f, 10) !== 0) { - writer.writeUint64String( + f = message.getEnabled(); + if (f) { + writer.writeBool( 1, f ); } - f = message.getSwapType(); - if (f !== 0.0) { - writer.writeEnum( - 6, - f - ); - } - f = message.getPubkey_asU8(); - if (f.length > 0) { - writer.writeBytes( - 5, - f - ); - } - f = message.getType(); - if (f !== 0.0) { - writer.writeEnum( + f = message.getLocalTargetAssetAmt(); + if (parseInt(f, 10) !== 0) { + writer.writeUint64String( 2, f ); } - f = message.getIncomingThreshold(); - if (f !== 0) { - writer.writeUint32( - 3, - f - ); - } - f = message.getOutgoingThreshold(); - if (f !== 0) { - writer.writeUint32( - 4, - f - ); - } }; /** - * optional uint64 channel_id = 1; + * optional bool enabled = 1; + * @return {boolean} + */ +proto.looprpc.EasyAssetAutoloopParams.prototype.getEnabled = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.looprpc.EasyAssetAutoloopParams} returns this + */ +proto.looprpc.EasyAssetAutoloopParams.prototype.setEnabled = function(value) { + return jspb.Message.setProto3BooleanField(this, 1, value); +}; + + +/** + * optional uint64 local_target_asset_amt = 2; + * @return {string} + */ +proto.looprpc.EasyAssetAutoloopParams.prototype.getLocalTargetAssetAmt = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "0")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.EasyAssetAutoloopParams} returns this + */ +proto.looprpc.EasyAssetAutoloopParams.prototype.setLocalTargetAssetAmt = function(value) { + return jspb.Message.setProto3StringIntField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.looprpc.LiquidityRule.prototype.toObject = function(opt_includeInstance) { + return proto.looprpc.LiquidityRule.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.looprpc.LiquidityRule} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.looprpc.LiquidityRule.toObject = function(includeInstance, msg) { + var f, obj = { + channelId: jspb.Message.getFieldWithDefault(msg, 1, "0"), + swapType: jspb.Message.getFieldWithDefault(msg, 6, 0), + pubkey: msg.getPubkey_asB64(), + type: jspb.Message.getFieldWithDefault(msg, 2, 0), + incomingThreshold: jspb.Message.getFieldWithDefault(msg, 3, 0), + outgoingThreshold: jspb.Message.getFieldWithDefault(msg, 4, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.looprpc.LiquidityRule} + */ +proto.looprpc.LiquidityRule.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.looprpc.LiquidityRule; + return proto.looprpc.LiquidityRule.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.looprpc.LiquidityRule} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.looprpc.LiquidityRule} + */ +proto.looprpc.LiquidityRule.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readUint64String()); + msg.setChannelId(value); + break; + case 6: + var value = /** @type {!proto.looprpc.SwapType} */ (reader.readEnum()); + msg.setSwapType(value); + break; + case 5: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setPubkey(value); + break; + case 2: + var value = /** @type {!proto.looprpc.LiquidityRuleType} */ (reader.readEnum()); + msg.setType(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setIncomingThreshold(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint32()); + msg.setOutgoingThreshold(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.looprpc.LiquidityRule.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.looprpc.LiquidityRule.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.looprpc.LiquidityRule} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.looprpc.LiquidityRule.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getChannelId(); + if (parseInt(f, 10) !== 0) { + writer.writeUint64String( + 1, + f + ); + } + f = message.getSwapType(); + if (f !== 0.0) { + writer.writeEnum( + 6, + f + ); + } + f = message.getPubkey_asU8(); + if (f.length > 0) { + writer.writeBytes( + 5, + f + ); + } + f = message.getType(); + if (f !== 0.0) { + writer.writeEnum( + 2, + f + ); + } + f = message.getIncomingThreshold(); + if (f !== 0) { + writer.writeUint32( + 3, + f + ); + } + f = message.getOutgoingThreshold(); + if (f !== 0) { + writer.writeUint32( + 4, + f + ); + } +}; + + +/** + * optional uint64 channel_id = 1; * @return {string} */ proto.looprpc.LiquidityRule.prototype.getChannelId = function() { @@ -13099,7 +13808,8 @@ proto.looprpc.WithdrawDepositsRequest.toObject = function(includeInstance, msg) proto.looprpc.OutPoint.toObject, includeInstance), all: jspb.Message.getBooleanFieldWithDefault(msg, 2, false), destAddr: jspb.Message.getFieldWithDefault(msg, 3, ""), - satPerVbyte: jspb.Message.getFieldWithDefault(msg, 4, "0") + satPerVbyte: jspb.Message.getFieldWithDefault(msg, 4, "0"), + amount: jspb.Message.getFieldWithDefault(msg, 5, "0") }; if (includeInstance) { @@ -13153,6 +13863,10 @@ proto.looprpc.WithdrawDepositsRequest.deserializeBinaryFromReader = function(msg var value = /** @type {string} */ (reader.readInt64String()); msg.setSatPerVbyte(value); break; + case 5: + var value = /** @type {string} */ (reader.readInt64String()); + msg.setAmount(value); + break; default: reader.skipField(); break; @@ -13211,6 +13925,13 @@ proto.looprpc.WithdrawDepositsRequest.serializeBinaryToWriter = function(message f ); } + f = message.getAmount(); + if (parseInt(f, 10) !== 0) { + writer.writeInt64String( + 5, + f + ); + } }; @@ -13306,6 +14027,24 @@ proto.looprpc.WithdrawDepositsRequest.prototype.setSatPerVbyte = function(value) }; +/** + * optional int64 amount = 5; + * @return {string} + */ +proto.looprpc.WithdrawDepositsRequest.prototype.getAmount = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, "0")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.WithdrawDepositsRequest} returns this + */ +proto.looprpc.WithdrawDepositsRequest.prototype.setAmount = function(value) { + return jspb.Message.setProto3StringIntField(this, 5, value); +}; + + @@ -13339,7 +14078,7 @@ proto.looprpc.WithdrawDepositsResponse.prototype.toObject = function(opt_include proto.looprpc.WithdrawDepositsResponse.toObject = function(includeInstance, msg) { var f, obj = { withdrawalTxHash: jspb.Message.getFieldWithDefault(msg, 1, ""), - pkScript: jspb.Message.getFieldWithDefault(msg, 2, "") + address: jspb.Message.getFieldWithDefault(msg, 2, "") }; if (includeInstance) { @@ -13382,7 +14121,7 @@ proto.looprpc.WithdrawDepositsResponse.deserializeBinaryFromReader = function(ms break; case 2: var value = /** @type {string} */ (reader.readString()); - msg.setPkScript(value); + msg.setAddress(value); break; default: reader.skipField(); @@ -13420,7 +14159,7 @@ proto.looprpc.WithdrawDepositsResponse.serializeBinaryToWriter = function(messag f ); } - f = message.getPkScript(); + f = message.getAddress(); if (f.length > 0) { writer.writeString( 2, @@ -13449,10 +14188,10 @@ proto.looprpc.WithdrawDepositsResponse.prototype.setWithdrawalTxHash = function( /** - * optional string pk_script = 2; + * optional string address = 2; * @return {string} */ -proto.looprpc.WithdrawDepositsResponse.prototype.getPkScript = function() { +proto.looprpc.WithdrawDepositsResponse.prototype.getAddress = function() { return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); }; @@ -13461,7 +14200,7 @@ proto.looprpc.WithdrawDepositsResponse.prototype.getPkScript = function() { * @param {string} value * @return {!proto.looprpc.WithdrawDepositsResponse} returns this */ -proto.looprpc.WithdrawDepositsResponse.prototype.setPkScript = function(value) { +proto.looprpc.WithdrawDepositsResponse.prototype.setAddress = function(value) { return jspb.Message.setProto3StringField(this, 2, value); }; @@ -16229,6 +16968,1024 @@ proto.looprpc.StaticAddressLoopInResponse.prototype.setPaymentTimeoutSeconds = f }; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.looprpc.AssetLoopOutRequest.prototype.toObject = function(opt_includeInstance) { + return proto.looprpc.AssetLoopOutRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.looprpc.AssetLoopOutRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.looprpc.AssetLoopOutRequest.toObject = function(includeInstance, msg) { + var f, obj = { + assetId: msg.getAssetId_asB64(), + assetEdgeNode: msg.getAssetEdgeNode_asB64(), + maxLimitMultiplier: jspb.Message.getFloatingPointFieldWithDefault(msg, 3, 0.0), + expiry: jspb.Message.getFieldWithDefault(msg, 4, "0") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.looprpc.AssetLoopOutRequest} + */ +proto.looprpc.AssetLoopOutRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.looprpc.AssetLoopOutRequest; + return proto.looprpc.AssetLoopOutRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.looprpc.AssetLoopOutRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.looprpc.AssetLoopOutRequest} + */ +proto.looprpc.AssetLoopOutRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setAssetId(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setAssetEdgeNode(value); + break; + case 3: + var value = /** @type {number} */ (reader.readDouble()); + msg.setMaxLimitMultiplier(value); + break; + case 4: + var value = /** @type {string} */ (reader.readInt64String()); + msg.setExpiry(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.looprpc.AssetLoopOutRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.looprpc.AssetLoopOutRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.looprpc.AssetLoopOutRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.looprpc.AssetLoopOutRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getAssetId_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getAssetEdgeNode_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getMaxLimitMultiplier(); + if (f !== 0.0) { + writer.writeDouble( + 3, + f + ); + } + f = message.getExpiry(); + if (parseInt(f, 10) !== 0) { + writer.writeInt64String( + 4, + f + ); + } +}; + + +/** + * optional bytes asset_id = 1; + * @return {!(string|Uint8Array)} + */ +proto.looprpc.AssetLoopOutRequest.prototype.getAssetId = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes asset_id = 1; + * This is a type-conversion wrapper around `getAssetId()` + * @return {string} + */ +proto.looprpc.AssetLoopOutRequest.prototype.getAssetId_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getAssetId())); +}; + + +/** + * optional bytes asset_id = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getAssetId()` + * @return {!Uint8Array} + */ +proto.looprpc.AssetLoopOutRequest.prototype.getAssetId_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getAssetId())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.looprpc.AssetLoopOutRequest} returns this + */ +proto.looprpc.AssetLoopOutRequest.prototype.setAssetId = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bytes asset_edge_node = 2; + * @return {!(string|Uint8Array)} + */ +proto.looprpc.AssetLoopOutRequest.prototype.getAssetEdgeNode = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes asset_edge_node = 2; + * This is a type-conversion wrapper around `getAssetEdgeNode()` + * @return {string} + */ +proto.looprpc.AssetLoopOutRequest.prototype.getAssetEdgeNode_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getAssetEdgeNode())); +}; + + +/** + * optional bytes asset_edge_node = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getAssetEdgeNode()` + * @return {!Uint8Array} + */ +proto.looprpc.AssetLoopOutRequest.prototype.getAssetEdgeNode_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getAssetEdgeNode())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.looprpc.AssetLoopOutRequest} returns this + */ +proto.looprpc.AssetLoopOutRequest.prototype.setAssetEdgeNode = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional double max_limit_multiplier = 3; + * @return {number} + */ +proto.looprpc.AssetLoopOutRequest.prototype.getMaxLimitMultiplier = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 3, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.looprpc.AssetLoopOutRequest} returns this + */ +proto.looprpc.AssetLoopOutRequest.prototype.setMaxLimitMultiplier = function(value) { + return jspb.Message.setProto3FloatField(this, 3, value); +}; + + +/** + * optional int64 expiry = 4; + * @return {string} + */ +proto.looprpc.AssetLoopOutRequest.prototype.getExpiry = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "0")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.AssetLoopOutRequest} returns this + */ +proto.looprpc.AssetLoopOutRequest.prototype.setExpiry = function(value) { + return jspb.Message.setProto3StringIntField(this, 4, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.looprpc.AssetRfqInfo.prototype.toObject = function(opt_includeInstance) { + return proto.looprpc.AssetRfqInfo.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.looprpc.AssetRfqInfo} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.looprpc.AssetRfqInfo.toObject = function(includeInstance, msg) { + var f, obj = { + prepayRfqId: msg.getPrepayRfqId_asB64(), + maxPrepayAssetAmt: jspb.Message.getFieldWithDefault(msg, 2, "0"), + prepayAssetRate: (f = msg.getPrepayAssetRate()) && proto.looprpc.FixedPoint.toObject(includeInstance, f), + swapRfqId: msg.getSwapRfqId_asB64(), + maxSwapAssetAmt: jspb.Message.getFieldWithDefault(msg, 4, "0"), + swapAssetRate: (f = msg.getSwapAssetRate()) && proto.looprpc.FixedPoint.toObject(includeInstance, f), + assetName: jspb.Message.getFieldWithDefault(msg, 5, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.looprpc.AssetRfqInfo} + */ +proto.looprpc.AssetRfqInfo.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.looprpc.AssetRfqInfo; + return proto.looprpc.AssetRfqInfo.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.looprpc.AssetRfqInfo} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.looprpc.AssetRfqInfo} + */ +proto.looprpc.AssetRfqInfo.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setPrepayRfqId(value); + break; + case 2: + var value = /** @type {string} */ (reader.readUint64String()); + msg.setMaxPrepayAssetAmt(value); + break; + case 6: + var value = new proto.looprpc.FixedPoint; + reader.readMessage(value,proto.looprpc.FixedPoint.deserializeBinaryFromReader); + msg.setPrepayAssetRate(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSwapRfqId(value); + break; + case 4: + var value = /** @type {string} */ (reader.readUint64String()); + msg.setMaxSwapAssetAmt(value); + break; + case 7: + var value = new proto.looprpc.FixedPoint; + reader.readMessage(value,proto.looprpc.FixedPoint.deserializeBinaryFromReader); + msg.setSwapAssetRate(value); + break; + case 5: + var value = /** @type {string} */ (reader.readString()); + msg.setAssetName(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.looprpc.AssetRfqInfo.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.looprpc.AssetRfqInfo.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.looprpc.AssetRfqInfo} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.looprpc.AssetRfqInfo.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getPrepayRfqId_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getMaxPrepayAssetAmt(); + if (parseInt(f, 10) !== 0) { + writer.writeUint64String( + 2, + f + ); + } + f = message.getPrepayAssetRate(); + if (f != null) { + writer.writeMessage( + 6, + f, + proto.looprpc.FixedPoint.serializeBinaryToWriter + ); + } + f = message.getSwapRfqId_asU8(); + if (f.length > 0) { + writer.writeBytes( + 3, + f + ); + } + f = message.getMaxSwapAssetAmt(); + if (parseInt(f, 10) !== 0) { + writer.writeUint64String( + 4, + f + ); + } + f = message.getSwapAssetRate(); + if (f != null) { + writer.writeMessage( + 7, + f, + proto.looprpc.FixedPoint.serializeBinaryToWriter + ); + } + f = message.getAssetName(); + if (f.length > 0) { + writer.writeString( + 5, + f + ); + } +}; + + +/** + * optional bytes prepay_rfq_id = 1; + * @return {!(string|Uint8Array)} + */ +proto.looprpc.AssetRfqInfo.prototype.getPrepayRfqId = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes prepay_rfq_id = 1; + * This is a type-conversion wrapper around `getPrepayRfqId()` + * @return {string} + */ +proto.looprpc.AssetRfqInfo.prototype.getPrepayRfqId_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getPrepayRfqId())); +}; + + +/** + * optional bytes prepay_rfq_id = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getPrepayRfqId()` + * @return {!Uint8Array} + */ +proto.looprpc.AssetRfqInfo.prototype.getPrepayRfqId_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getPrepayRfqId())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.looprpc.AssetRfqInfo} returns this + */ +proto.looprpc.AssetRfqInfo.prototype.setPrepayRfqId = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional uint64 max_prepay_asset_amt = 2; + * @return {string} + */ +proto.looprpc.AssetRfqInfo.prototype.getMaxPrepayAssetAmt = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "0")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.AssetRfqInfo} returns this + */ +proto.looprpc.AssetRfqInfo.prototype.setMaxPrepayAssetAmt = function(value) { + return jspb.Message.setProto3StringIntField(this, 2, value); +}; + + +/** + * optional FixedPoint prepay_asset_rate = 6; + * @return {?proto.looprpc.FixedPoint} + */ +proto.looprpc.AssetRfqInfo.prototype.getPrepayAssetRate = function() { + return /** @type{?proto.looprpc.FixedPoint} */ ( + jspb.Message.getWrapperField(this, proto.looprpc.FixedPoint, 6)); +}; + + +/** + * @param {?proto.looprpc.FixedPoint|undefined} value + * @return {!proto.looprpc.AssetRfqInfo} returns this +*/ +proto.looprpc.AssetRfqInfo.prototype.setPrepayAssetRate = function(value) { + return jspb.Message.setWrapperField(this, 6, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.looprpc.AssetRfqInfo} returns this + */ +proto.looprpc.AssetRfqInfo.prototype.clearPrepayAssetRate = function() { + return this.setPrepayAssetRate(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.looprpc.AssetRfqInfo.prototype.hasPrepayAssetRate = function() { + return jspb.Message.getField(this, 6) != null; +}; + + +/** + * optional bytes swap_rfq_id = 3; + * @return {!(string|Uint8Array)} + */ +proto.looprpc.AssetRfqInfo.prototype.getSwapRfqId = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes swap_rfq_id = 3; + * This is a type-conversion wrapper around `getSwapRfqId()` + * @return {string} + */ +proto.looprpc.AssetRfqInfo.prototype.getSwapRfqId_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSwapRfqId())); +}; + + +/** + * optional bytes swap_rfq_id = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSwapRfqId()` + * @return {!Uint8Array} + */ +proto.looprpc.AssetRfqInfo.prototype.getSwapRfqId_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSwapRfqId())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.looprpc.AssetRfqInfo} returns this + */ +proto.looprpc.AssetRfqInfo.prototype.setSwapRfqId = function(value) { + return jspb.Message.setProto3BytesField(this, 3, value); +}; + + +/** + * optional uint64 max_swap_asset_amt = 4; + * @return {string} + */ +proto.looprpc.AssetRfqInfo.prototype.getMaxSwapAssetAmt = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "0")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.AssetRfqInfo} returns this + */ +proto.looprpc.AssetRfqInfo.prototype.setMaxSwapAssetAmt = function(value) { + return jspb.Message.setProto3StringIntField(this, 4, value); +}; + + +/** + * optional FixedPoint swap_asset_rate = 7; + * @return {?proto.looprpc.FixedPoint} + */ +proto.looprpc.AssetRfqInfo.prototype.getSwapAssetRate = function() { + return /** @type{?proto.looprpc.FixedPoint} */ ( + jspb.Message.getWrapperField(this, proto.looprpc.FixedPoint, 7)); +}; + + +/** + * @param {?proto.looprpc.FixedPoint|undefined} value + * @return {!proto.looprpc.AssetRfqInfo} returns this +*/ +proto.looprpc.AssetRfqInfo.prototype.setSwapAssetRate = function(value) { + return jspb.Message.setWrapperField(this, 7, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.looprpc.AssetRfqInfo} returns this + */ +proto.looprpc.AssetRfqInfo.prototype.clearSwapAssetRate = function() { + return this.setSwapAssetRate(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.looprpc.AssetRfqInfo.prototype.hasSwapAssetRate = function() { + return jspb.Message.getField(this, 7) != null; +}; + + +/** + * optional string asset_name = 5; + * @return {string} + */ +proto.looprpc.AssetRfqInfo.prototype.getAssetName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, "")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.AssetRfqInfo} returns this + */ +proto.looprpc.AssetRfqInfo.prototype.setAssetName = function(value) { + return jspb.Message.setProto3StringField(this, 5, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.looprpc.FixedPoint.prototype.toObject = function(opt_includeInstance) { + return proto.looprpc.FixedPoint.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.looprpc.FixedPoint} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.looprpc.FixedPoint.toObject = function(includeInstance, msg) { + var f, obj = { + coefficient: jspb.Message.getFieldWithDefault(msg, 1, ""), + scale: jspb.Message.getFieldWithDefault(msg, 2, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.looprpc.FixedPoint} + */ +proto.looprpc.FixedPoint.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.looprpc.FixedPoint; + return proto.looprpc.FixedPoint.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.looprpc.FixedPoint} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.looprpc.FixedPoint} + */ +proto.looprpc.FixedPoint.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setCoefficient(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint32()); + msg.setScale(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.looprpc.FixedPoint.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.looprpc.FixedPoint.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.looprpc.FixedPoint} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.looprpc.FixedPoint.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getCoefficient(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getScale(); + if (f !== 0) { + writer.writeUint32( + 2, + f + ); + } +}; + + +/** + * optional string coefficient = 1; + * @return {string} + */ +proto.looprpc.FixedPoint.prototype.getCoefficient = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.FixedPoint} returns this + */ +proto.looprpc.FixedPoint.prototype.setCoefficient = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional uint32 scale = 2; + * @return {number} + */ +proto.looprpc.FixedPoint.prototype.getScale = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.looprpc.FixedPoint} returns this + */ +proto.looprpc.FixedPoint.prototype.setScale = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.looprpc.AssetLoopOutInfo.prototype.toObject = function(opt_includeInstance) { + return proto.looprpc.AssetLoopOutInfo.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.looprpc.AssetLoopOutInfo} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.looprpc.AssetLoopOutInfo.toObject = function(includeInstance, msg) { + var f, obj = { + assetId: jspb.Message.getFieldWithDefault(msg, 1, ""), + assetName: jspb.Message.getFieldWithDefault(msg, 2, ""), + assetCostOffchain: jspb.Message.getFieldWithDefault(msg, 3, "0") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.looprpc.AssetLoopOutInfo} + */ +proto.looprpc.AssetLoopOutInfo.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.looprpc.AssetLoopOutInfo; + return proto.looprpc.AssetLoopOutInfo.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.looprpc.AssetLoopOutInfo} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.looprpc.AssetLoopOutInfo} + */ +proto.looprpc.AssetLoopOutInfo.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setAssetId(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setAssetName(value); + break; + case 3: + var value = /** @type {string} */ (reader.readUint64String()); + msg.setAssetCostOffchain(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.looprpc.AssetLoopOutInfo.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.looprpc.AssetLoopOutInfo.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.looprpc.AssetLoopOutInfo} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.looprpc.AssetLoopOutInfo.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getAssetId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getAssetName(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getAssetCostOffchain(); + if (parseInt(f, 10) !== 0) { + writer.writeUint64String( + 3, + f + ); + } +}; + + +/** + * optional string asset_id = 1; + * @return {string} + */ +proto.looprpc.AssetLoopOutInfo.prototype.getAssetId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.AssetLoopOutInfo} returns this + */ +proto.looprpc.AssetLoopOutInfo.prototype.setAssetId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string asset_name = 2; + * @return {string} + */ +proto.looprpc.AssetLoopOutInfo.prototype.getAssetName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.AssetLoopOutInfo} returns this + */ +proto.looprpc.AssetLoopOutInfo.prototype.setAssetName = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional uint64 asset_cost_offchain = 3; + * @return {string} + */ +proto.looprpc.AssetLoopOutInfo.prototype.getAssetCostOffchain = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "0")); +}; + + +/** + * @param {string} value + * @return {!proto.looprpc.AssetLoopOutInfo} returns this + */ +proto.looprpc.AssetLoopOutInfo.prototype.setAssetCostOffchain = function(value) { + return jspb.Message.setProto3StringIntField(this, 3, value); +}; + + /** * @enum {number} */ diff --git a/app/src/util/tests/sampleData.ts b/app/src/util/tests/sampleData.ts index a1b3505c7..4521d35b9 100644 --- a/app/src/util/tests/sampleData.ts +++ b/app/src/util/tests/sampleData.ts @@ -258,6 +258,7 @@ export const lndChannelEvent: Required = { resolutionsList: [], aliasScidsList: [], zeroConfConfirmedScid: '', + customChannelData: '', }, activeChannel: { fundingTxidBytes: txIdBytes, @@ -360,6 +361,7 @@ export const loopListSwaps: LOOP.ListSwapsResponse.AsObject = { outgoingChanSetList: ['123456789'], htlcAddressP2tr: '', })), + nextStartTime: '', }; export const loopOutTerms: LOOP.OutTermsResponse.AsObject = { diff --git a/go.mod b/go.mod index 290263012..19987e8a7 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/btcsuite/btcd/btcutil v1.1.5 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/btcsuite/btclog/v2 v2.0.1-0.20250110154127-3ae4bf1cb318 - github.com/btcsuite/btcwallet/walletdb v1.4.4 + github.com/btcsuite/btcwallet/walletdb v1.5.1 github.com/davecgh/go-spew v1.1.1 github.com/go-errors/errors v1.0.1 github.com/golang-migrate/migrate/v4 v4.17.0 @@ -21,20 +21,20 @@ require ( github.com/lightninglabs/lightning-node-connect v0.3.3-alpha.0.20250306111457-cad4234830cc github.com/lightninglabs/lightning-terminal/autopilotserverrpc v0.0.2 github.com/lightninglabs/lightning-terminal/litrpc v1.0.1 - github.com/lightninglabs/lndclient v0.19.0-3 - github.com/lightninglabs/loop v0.29.0-beta.rc2.0.20250306160707-1091a628755c - github.com/lightninglabs/loop/looprpc v1.0.4-0.20250306160707-1091a628755c - github.com/lightninglabs/loop/swapserverrpc v1.0.13-0.20250306160707-1091a628755c + github.com/lightninglabs/lndclient v0.19.0-4 + github.com/lightninglabs/loop v0.31.0-beta.0.20250425065236-7e81916829d9 + github.com/lightninglabs/loop/looprpc v1.0.6 + github.com/lightninglabs/loop/swapserverrpc v1.0.13 github.com/lightninglabs/pool v0.6.5-beta.0.20250305125211-4e860ec4e77f github.com/lightninglabs/pool/auctioneerrpc v1.1.3-0.20250305125211-4e860ec4e77f github.com/lightninglabs/pool/poolrpc v1.0.1-0.20250305125211-4e860ec4e77f - github.com/lightninglabs/taproot-assets v0.5.2-0.20250416114205-2da076df4b4e - github.com/lightningnetwork/lnd v0.19.0-beta.rc1.0.20250327183348-eb822a5e117f + github.com/lightninglabs/taproot-assets v0.5.2-0.20250424162728-b6000498210d + github.com/lightningnetwork/lnd v0.19.0-beta.rc3 github.com/lightningnetwork/lnd/cert v1.2.2 github.com/lightningnetwork/lnd/clock v1.1.1 github.com/lightningnetwork/lnd/fn v1.2.3 github.com/lightningnetwork/lnd/fn/v2 v2.0.8 - github.com/lightningnetwork/lnd/kvdb v1.4.13 + github.com/lightningnetwork/lnd/kvdb v1.4.16 github.com/lightningnetwork/lnd/tlv v1.3.0 github.com/lightningnetwork/lnd/tor v1.1.6 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f @@ -57,7 +57,7 @@ require ( require ( dario.cat/mergo v1.0.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e // indirect github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect @@ -68,11 +68,11 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/btcsuite/btcd/btcutil/psbt v1.1.10 // indirect github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c // indirect - github.com/btcsuite/btcwallet v0.16.12 // indirect + github.com/btcsuite/btcwallet v0.16.13 // indirect github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 // indirect github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 // indirect github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5 // indirect - github.com/btcsuite/btcwallet/wtxmgr v1.5.4 // indirect + github.com/btcsuite/btcwallet/wtxmgr v1.5.6 // indirect github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect github.com/btcsuite/winsvc v1.0.0 // indirect @@ -80,7 +80,6 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/continuity v0.3.0 // indirect - github.com/coreos/bbolt v1.3.3 // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect @@ -91,7 +90,7 @@ require ( github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/docker/cli v28.0.1+incompatible // indirect github.com/docker/docker v28.0.1+incompatible // indirect - github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fergusstrange/embedded-postgres v1.25.0 // indirect @@ -144,7 +143,7 @@ require ( github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb // indirect github.com/lightningnetwork/lnd/healthcheck v1.2.6 // indirect github.com/lightningnetwork/lnd/queue v1.1.1 // indirect - github.com/lightningnetwork/lnd/sqldb v1.0.7 // indirect + github.com/lightningnetwork/lnd/sqldb v1.0.9 // indirect github.com/lightningnetwork/lnd/ticker v1.1.1 // indirect github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -159,7 +158,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/runc v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -194,25 +193,25 @@ require ( go.etcd.io/etcd/raft/v3 v3.5.12 // indirect go.etcd.io/etcd/server/v3 v3.5.12 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 // indirect go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/sdk v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.23.0 // indirect - golang.org/x/mod v0.17.0 // indirect + golang.org/x/mod v0.21.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/term v0.30.0 // indirect golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.24.0 // indirect + google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect gopkg.in/errgo.v1 v1.0.1 // indirect @@ -237,4 +236,10 @@ replace ( // taproot-assets dependency to function properly. replace google.golang.org/protobuf => github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display +// We are using a fork of the migration library in tapd with custom +// functionality that did not yet make it into the upstream repository. Because +// it is a replace in the tapd repository, it doesn't get propagated here +// automatically, so we need to add it manually. +replace github.com/golang-migrate/migrate/v4 => github.com/lightninglabs/migrate/v4 v4.18.2-9023d66a-fork-pr-2 + go 1.23.6 diff --git a/go.sum b/go.sum index 22dce9014..3443d3720 100644 --- a/go.sum +++ b/go.sum @@ -36,7 +36,6 @@ cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRY cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= @@ -173,13 +172,10 @@ cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63 cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= @@ -611,8 +607,8 @@ github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0 github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e h1:n+DcnTNkQnHlwpsrHoQtkrJIO7CBx029fw6oR4vIob4= github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ= github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82 h1:MG93+PZYs9PyEsj/n5/haQu2gK0h4tUtSy9ejtMwWa0= @@ -674,18 +670,18 @@ github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c/go.mod h1:w7xnGOhw github.com/btcsuite/btclog/v2 v2.0.1-0.20250110154127-3ae4bf1cb318 h1:oCjIcinPt7XQ644MP/22JcjYEC84qRc3bRBH0d7Hhd4= github.com/btcsuite/btclog/v2 v2.0.1-0.20250110154127-3ae4bf1cb318/go.mod h1:XItGUfVOxotJL8kkuk2Hj3EVow5KCugXl3wWfQ6K0AE= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcwallet v0.16.12 h1:9SREKY892i1xTGlGLcu6x7O+WSQFn6+uQrSuskAOqh0= -github.com/btcsuite/btcwallet v0.16.12/go.mod h1:jBn+ThFrx/QqW0nXiGvXtJytju4aVoW7C0hY4s/+9vo= +github.com/btcsuite/btcwallet v0.16.13 h1:JGu+wrihQ0I00ODb3w92JtBPbrHxZhbcvU01O+e+lKw= +github.com/btcsuite/btcwallet v0.16.13/go.mod h1:H6dfoZcWPonM2wbVsR2ZBY0PKNZKdQyLAmnX8vL9JFA= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 h1:Rr0njWI3r341nhSPesKQ2JF+ugDSzdPoeckS75SeDZk= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5/go.mod h1:+tXJ3Ym0nlQc/iHSwW1qzjmPs3ev+UVWMbGgfV1OZqU= github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 h1:YEO+Lx1ZJJAtdRrjuhXjWrYsmAk26wLTlNzxt2q0lhk= github.com/btcsuite/btcwallet/wallet/txrules v1.2.2/go.mod h1:4v+grppsDpVn91SJv+mZT7B8hEV4nSmpREM4I8Uohws= github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5 h1:93o5Xz9dYepBP4RMFUc9RGIFXwqP2volSWRkYJFrNtI= github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5/go.mod h1:lQ+e9HxZ85QP7r3kdxItkiMSloSLg1PEGis5o5CXUQw= -github.com/btcsuite/btcwallet/walletdb v1.4.4 h1:BDel6iT/ltYSIYKs0YbjwnEDi7xR3yzABIsQxN2F1L8= -github.com/btcsuite/btcwallet/walletdb v1.4.4/go.mod h1:jk/hvpLFINF0C1kfTn0bfx2GbnFT+Nvnj6eblZALfjs= -github.com/btcsuite/btcwallet/wtxmgr v1.5.4 h1:hJjHy1h/dJwSfD9uDsCwcH21D1iOrus6OrI5gR9E/O0= -github.com/btcsuite/btcwallet/wtxmgr v1.5.4/go.mod h1:lAv0b1Vj9Ig5U8QFm0yiJ9WqPl8yGO/6l7JxdHY1PKE= +github.com/btcsuite/btcwallet/walletdb v1.5.1 h1:HgMhDNCrtEFPC+8q0ei5DQ5U9Tl4RCspA22DEKXlopI= +github.com/btcsuite/btcwallet/walletdb v1.5.1/go.mod h1:jk/hvpLFINF0C1kfTn0bfx2GbnFT+Nvnj6eblZALfjs= +github.com/btcsuite/btcwallet/wtxmgr v1.5.6 h1:Zwvr/rrJYdOLqdBCSr4eICEstnEA+NBUvjIWLkrXaYI= +github.com/btcsuite/btcwallet/wtxmgr v1.5.6/go.mod h1:lzVbDkk/jRao2ib5kge46aLZW1yFc8RFNycdYpnsmZA= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc= @@ -727,16 +723,12 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= -github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY= -github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -766,16 +758,16 @@ github.com/decred/dcrd/lru v1.1.2 h1:KdCzlkxppuoIDGEvCGah1fZRicrDH36IipvlB1ROkFY github.com/decred/dcrd/lru v1.1.2/go.mod h1:gEdCVgXs1/YoBvFWt7Scgknbhwik3FgVSzlnCcXL2N8= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= -github.com/dhui/dktest v0.4.0 h1:z05UmuXZHO/bgj/ds2bGMBu8FI4WA+Ag/m3ghL+om7M= -github.com/dhui/dktest v0.4.0/go.mod h1:v/Dbz1LgCBOi2Uki2nUqLBGa83hWBGFMu5MrgMDCc78= +github.com/dhui/dktest v0.4.5 h1:uUfYBIVREmj/Rw6MvgmqNAYzTiKOHJak+enB5Di73MM= +github.com/dhui/dktest v0.4.5/go.mod h1:tmcyeHDKagvlDrz7gDKq4UAJOLIfVZYkfD5OnHDwcCo= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs= github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= @@ -797,8 +789,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fergusstrange/embedded-postgres v1.25.0 h1:sa+k2Ycrtz40eCRPOzI7Ry7TtkWXXJ+YRsxpKMDhxK0= @@ -877,8 +867,6 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYND2gB4p7xcaU= -github.com/golang-migrate/migrate/v4 v4.17.0/go.mod h1:+Cp2mtLP4/aXDTKb9wmXYitdrNx2HGs45rbWAo6OsKM= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -1161,14 +1149,16 @@ github.com/lightninglabs/lightning-node-connect v0.3.3-alpha.0.20250306111457-ca github.com/lightninglabs/lightning-node-connect v0.3.3-alpha.0.20250306111457-cad4234830cc/go.mod h1:yrfNoMrGcWljHoQ31+dCSc0R7mBdYqISQeZABlrdkz4= github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2 h1:Er1miPZD2XZwcfE4xoS5AILqP1mj7kqnhbBSxW9BDxY= github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2/go.mod h1:antQGRDRJiuyQF6l+k6NECCSImgCpwaZapATth2Chv4= -github.com/lightninglabs/lndclient v0.19.0-3 h1:PGGlDaz8x1dXGowDfAWhbuDqXTKNaJyb7SOTrRdG1es= -github.com/lightninglabs/lndclient v0.19.0-3/go.mod h1:5YMrFx00NvcmUHGZRxT4Qw/gOfR5x50/ReJmJ6w0yVk= -github.com/lightninglabs/loop v0.29.0-beta.rc2.0.20250306160707-1091a628755c h1:Ox7SfusBRizc7tOKy9HKXodR7rcvpko08EH9aP/+euQ= -github.com/lightninglabs/loop v0.29.0-beta.rc2.0.20250306160707-1091a628755c/go.mod h1:IzzOw/v4VwKmotJrmPyM8P+FPZ/XBbxa4u2JuDYrtAU= -github.com/lightninglabs/loop/looprpc v1.0.4-0.20250306160707-1091a628755c h1:ueEVZjeUKI3CoTJAdpDbXKHTSd9OfY8OPqTDBjeHhfs= -github.com/lightninglabs/loop/looprpc v1.0.4-0.20250306160707-1091a628755c/go.mod h1:gO5c42iHaY6O7kXmHMqEK0ZZkRXrVDisGP22LkHMDmA= -github.com/lightninglabs/loop/swapserverrpc v1.0.13-0.20250306160707-1091a628755c h1:41URVu1xE88R3kUE1VZGJS50RDWX7wBwVmICBb/0gnw= -github.com/lightninglabs/loop/swapserverrpc v1.0.13-0.20250306160707-1091a628755c/go.mod h1:Ml3gMwe/iTRLvu1QGGZzXcr0DYSa9sJGwKPktLaWtwE= +github.com/lightninglabs/lndclient v0.19.0-4 h1:U+koisg716/i51kf5ENI5+9a1joXcPXeJYl3q0s4/co= +github.com/lightninglabs/lndclient v0.19.0-4/go.mod h1:LP3FM3JGBdvOX8Lum9x1r7q54oiftoqaq4EYhtpp/fk= +github.com/lightninglabs/loop v0.31.0-beta.0.20250425065236-7e81916829d9 h1:wVOSaMFH/1rrX8vYd7MFkv4TjIqPZyYXYWhV5WwVdnA= +github.com/lightninglabs/loop v0.31.0-beta.0.20250425065236-7e81916829d9/go.mod h1:+t9d/gnfPcdHFaOPEdrtJx1Deb+drhlD5EYQ5kpH5hk= +github.com/lightninglabs/loop/looprpc v1.0.6 h1:lvxGIOB2jL67vL9qY5FH1xIVyMZJvqLy1gS0J9dQXwA= +github.com/lightninglabs/loop/looprpc v1.0.6/go.mod h1:VJSzz1Ug/cVCNjE+1jYZ/51DuLnCjbJdF4BtrxTTM8c= +github.com/lightninglabs/loop/swapserverrpc v1.0.13 h1:Qf4L8QBJKzhKRcC8dpvfrrBEXJMF3+XbpomAHEJsRDY= +github.com/lightninglabs/loop/swapserverrpc v1.0.13/go.mod h1:Ml3gMwe/iTRLvu1QGGZzXcr0DYSa9sJGwKPktLaWtwE= +github.com/lightninglabs/migrate/v4 v4.18.2-9023d66a-fork-pr-2 h1:eFjp1dIB2BhhQp/THKrjLdlYuPugO9UU4kDqu91OX/Q= +github.com/lightninglabs/migrate/v4 v4.18.2-9023d66a-fork-pr-2/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY= github.com/lightninglabs/neutrino v0.16.1 h1:5Kz4ToxncEVkpKC6fwUjXKtFKJhuxlG3sBB3MdJTJjs= github.com/lightninglabs/neutrino v0.16.1/go.mod h1:L+5UAccpUdyM7yDgmQySgixf7xmwBgJtOfs/IP26jCs= github.com/lightninglabs/neutrino/cache v1.1.2 h1:C9DY/DAPaPxbFC+xNNEI/z1SJY9GS3shmlu5hIQ798g= @@ -1181,12 +1171,12 @@ github.com/lightninglabs/pool/poolrpc v1.0.1-0.20250305125211-4e860ec4e77f h1:5p github.com/lightninglabs/pool/poolrpc v1.0.1-0.20250305125211-4e860ec4e77f/go.mod h1:lGs2hSVZ+GFpdv3btaIl9icG5/gz7BBRfvmD2iqqNl0= github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display h1:w7FM5LH9Z6CpKxl13mS48idsu6F+cEZf0lkyiV+Dq9g= github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -github.com/lightninglabs/taproot-assets v0.5.2-0.20250416114205-2da076df4b4e h1:37sk9Wmkh9QFjnqR8eHIhCi8x0uIQL0F2fpcQI25I9g= -github.com/lightninglabs/taproot-assets v0.5.2-0.20250416114205-2da076df4b4e/go.mod h1:e3SjXbbi4xKhOzq54c672Z/j9UTRq5DLJGx/URgVTJo= +github.com/lightninglabs/taproot-assets v0.5.2-0.20250424162728-b6000498210d h1:8WcOFToO9iY62c6ghbAaJfVP8qudzdrK89OXiOri9K4= +github.com/lightninglabs/taproot-assets v0.5.2-0.20250424162728-b6000498210d/go.mod h1:6kQm7VC4yWAwczJaxfOWlCOQ4TuzfCLUkGKBwVONN7k= github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb h1:yfM05S8DXKhuCBp5qSMZdtSwvJ+GFzl94KbXMNB1JDY= github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI= -github.com/lightningnetwork/lnd v0.19.0-beta.rc1.0.20250327183348-eb822a5e117f h1:+Bejv2Ij/ryUjLacBd5au0acMH0AYs0lhb7ki5rx9ms= -github.com/lightningnetwork/lnd v0.19.0-beta.rc1.0.20250327183348-eb822a5e117f/go.mod h1:BP+neeFpmeAA7o5hu3zp3FwOEl26idSyPV9zBOavp6E= +github.com/lightningnetwork/lnd v0.19.0-beta.rc3 h1:XWMlyZvBrp69AnQqrshLOOPZjOl4hFWR1RD4ceCXt9k= +github.com/lightningnetwork/lnd v0.19.0-beta.rc3/go.mod h1:bASjjAiZsoEtHFo0imi5m4z/8m9afdIpu+Wz7rUXink= github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf0d0Uy4qBjI= github.com/lightningnetwork/lnd/cert v1.2.2/go.mod h1:jQmFn/Ez4zhDgq2hnYSw8r35bqGVxViXhX6Cd7HXM6U= github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0= @@ -1197,12 +1187,12 @@ github.com/lightningnetwork/lnd/fn/v2 v2.0.8 h1:r2SLz7gZYQPVc3IZhU82M66guz3Zk2oY github.com/lightningnetwork/lnd/fn/v2 v2.0.8/go.mod h1:TOzwrhjB/Azw1V7aa8t21ufcQmdsQOQMDtxVOQWNl8s= github.com/lightningnetwork/lnd/healthcheck v1.2.6 h1:1sWhqr93GdkWy4+6U7JxBfcyZIE78MhIHTJZfPx7qqI= github.com/lightningnetwork/lnd/healthcheck v1.2.6/go.mod h1:Mu02um4CWY/zdTOvFje7WJgJcHyX2zq/FG3MhOAiGaQ= -github.com/lightningnetwork/lnd/kvdb v1.4.13 h1:fe3sFBxsgcXl16G1zj6O/wZf0hbBHOxFe8pCgmnHZxM= -github.com/lightningnetwork/lnd/kvdb v1.4.13/go.mod h1:1y0Z81CGQu4SMpcnAie/oK4tzgEqFQqFdj6k3fz2s8s= +github.com/lightningnetwork/lnd/kvdb v1.4.16 h1:9BZgWdDfjmHRHLS97cz39bVuBAqMc4/p3HX1xtUdbDI= +github.com/lightningnetwork/lnd/kvdb v1.4.16/go.mod h1:HW+bvwkxNaopkz3oIgBV6NEnV4jCEZCACFUcNg4xSjM= github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQZznVb18iNMI= github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4= -github.com/lightningnetwork/lnd/sqldb v1.0.7 h1:wQ4DdHY++uwxwth2CHL7s+duGqmMLaoIRBOQCa9HPTk= -github.com/lightningnetwork/lnd/sqldb v1.0.7/go.mod h1:OG09zL/PHPaBJefp4HsPz2YLUJ+zIQHbpgCtLnOx8I4= +github.com/lightningnetwork/lnd/sqldb v1.0.9 h1:7OHi+Hui823mB/U9NzCdlZTAGSVdDCbjp33+6d/Q+G0= +github.com/lightningnetwork/lnd/sqldb v1.0.9/go.mod h1:OG09zL/PHPaBJefp4HsPz2YLUJ+zIQHbpgCtLnOx8I4= github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM= github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA= github.com/lightningnetwork/lnd/tlv v1.3.0 h1:exS/KCPEgpOgviIttfiXAPaUqw2rHQrnUOpP7HPBPiY= @@ -1277,8 +1267,8 @@ github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/runc v1.2.0 h1:qke7ZVCmJcKrJVY2iHJVC+0kql9uYdkusOPsQOOeBw4= github.com/opencontainers/runc v1.2.0/go.mod h1:/PXzF0h531HTMsYQnmxXkBD7YaGShm/2zcRB79dksUc= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -1445,14 +1435,14 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 h1:DeFD0VgTZ+Cj6hxravYYZE2W4GlneVH81iAOPjZkzk8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= @@ -1464,8 +1454,8 @@ go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1569,8 +1559,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1674,8 +1664,6 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1831,8 +1819,9 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1903,8 +1892,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2123,8 +2112,8 @@ google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOl google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= diff --git a/itest/assets_test.go b/itest/assets_test.go index 63d59335c..fc3cd34e6 100644 --- a/itest/assets_test.go +++ b/itest/assets_test.go @@ -390,8 +390,8 @@ func syncUniverses(t *testing.T, universe *tapClient, nodes ...*HarnessNode) { func assertUniverseProofExists(t *testing.T, universe *tapClient, assetID, groupKey, scriptKey []byte, outpoint string) *taprpc.Asset { - t.Logf("Asserting proof outpoint=%v, script_key=%x", outpoint, - scriptKey) + t.Logf("Asserting proof outpoint=%v, script_key=%x, asset_id=%x, "+ + "group_key=%x", outpoint, scriptKey, assetID, groupKey) req := &universerpc.UniverseKey{ Id: &universerpc.ID{ @@ -517,7 +517,7 @@ func haveFundingAsset(assetChannel *rfqmsg.JsonAssetChannel, } func assertAssetChan(t *testing.T, src, dst *HarnessNode, fundingAmount uint64, - mintedAsset *taprpc.Asset) { + channelAsset *taprpc.Asset) { err := wait.NoError(func() error { a, err := getChannelCustomData(src, dst) @@ -525,7 +525,7 @@ func assertAssetChan(t *testing.T, src, dst *HarnessNode, fundingAmount uint64, return err } - assetID := mintedAsset.AssetGenesis.AssetId + assetID := channelAsset.AssetGenesis.AssetId if !haveFundingAsset(a, assetID) { return fmt.Errorf("expected asset ID %x, to "+ "be in channel", assetID) @@ -538,10 +538,12 @@ func assertAssetChan(t *testing.T, src, dst *HarnessNode, fundingAmount uint64, // Check the decimal display of the channel funding blob. If no // explicit value was set, we assume and expect the value of 0. + // We only need to check the first funding asset, since we + // enforce them to be the same. var expectedDecimalDisplay uint8 - if mintedAsset.DecimalDisplay != nil { + if channelAsset.DecimalDisplay != nil { expectedDecimalDisplay = uint8( - mintedAsset.DecimalDisplay.DecimalDisplay, + channelAsset.DecimalDisplay.DecimalDisplay, ) } @@ -629,8 +631,8 @@ func getChannelCustomData(src, dst *HarnessNode) (*rfqmsg.JsonAssetChannel, err) } - if len(assetData.FundingAssets) != 1 { - return nil, fmt.Errorf("expected 1 asset, got %d", + if len(assetData.FundingAssets) == 0 { + return nil, fmt.Errorf("expected at least 1 asset, got %d", len(assetData.FundingAssets)) } diff --git a/proto/lnd.proto b/proto/lnd.proto index aef90a6f7..2254a5a3f 100644 --- a/proto/lnd.proto +++ b/proto/lnd.proto @@ -1758,6 +1758,10 @@ message ChannelCloseSummary { // The confirmed SCID for a zero-conf channel. uint64 zero_conf_confirmed_scid = 15 [jstype = JS_STRING]; + + // The TLV encoded custom channel data records for this output, which might + // be set for custom channels. + bytes custom_channel_data = 16; } enum ResolutionType { @@ -5124,6 +5128,22 @@ message RPCMiddlewareRequest { intercept message. */ uint64 msg_id = 7 [jstype = JS_STRING]; + + /* + The metadata pairs that were sent along with the original gRPC request via + the golang context.Context using explicit [gRPC + metadata](https://grpc.io/docs/guides/metadata/). Context values are not + propagated via gRPC and so we send any pairs along explicitly here so that + the interceptor can access them. + */ + map metadata_pairs = 9; +} + +message MetadataValues { + /* + The set of metadata values that correspond to the metadata key. + */ + repeated string values = 1; } message StreamAuth { diff --git a/proto/loop.proto b/proto/loop.proto index c1c7cdd9c..0748755e1 100644 --- a/proto/loop.proto +++ b/proto/loop.proto @@ -336,6 +336,19 @@ message LoopOutRequest { as the timeout for the payment. */ uint32 payment_timeout = 19; + + /* + The optional asset information to use for the swap. If set, the swap will + be paid in the specified asset using the provided edge node. An Asset client + must be connected to the loop client to use this feature. + */ + AssetLoopOutRequest asset_info = 20; + + /* + The optional RFQ information to use for the swap. If set, the swap will + use the provided RFQs to pay for the swap invoice. + */ + AssetRfqInfo asset_rfq_info = 21; } /* @@ -539,6 +552,9 @@ message SwapStatus { // An optional label given to the swap on creation. string label = 15; + + // If the swap was an asset swap, the asset information will be returned. + AssetLoopOutInfo asset_info = 19; } enum SwapType { @@ -659,6 +675,9 @@ enum FailureReason { message ListSwapsRequest { // Optional filter to only return swaps that match the filter. ListSwapsFilter list_swap_filter = 1; + + // Set a maximum number of swaps to return in the response. + uint64 max_swaps = 2 [jstype = JS_STRING]; } message ListSwapsFilter { @@ -685,6 +704,12 @@ message ListSwapsFilter { // If specified on creation, the last hop of the swap. bytes loop_in_last_hop = 5; + + // If specified, only returns asset swaps. + bool asset_swap_only = 6; + + // If specified, returns swaps initiated after this Unix (ns) timestamp. + int64 start_timestamp_ns = 7 [jstype = JS_STRING]; } message ListSwapsResponse { @@ -692,6 +717,9 @@ message ListSwapsResponse { The list of all currently known swaps and their status. */ repeated SwapStatus swaps = 1; + + // Timestamp to use for paging start_timestamp_ns. + int64 next_start_time = 2 [jstype = JS_STRING]; } message SwapInfoRequest { @@ -793,6 +821,12 @@ message QuoteRequest { the same time. */ repeated string deposit_outpoints = 8; + + /* + The optional asset information to use for the swap. If set, the quote will + be returned in the specified asset. + */ + AssetLoopOutRequest asset_info = 9; } message InQuoteResponse { @@ -856,6 +890,12 @@ message OutQuoteResponse { The confirmation target to be used for the sweep of the on-chain HTLC. */ int32 conf_target = 6; + + /* + If the request was for an asset swap, the quote will return the rfq ids + that will be used to pay for the swap and prepay invoices. + */ + AssetRfqInfo asset_rfq_info = 7; } message ProbeRequest { @@ -1014,6 +1054,11 @@ message GetInfoResponse { Statistics about loop ins. */ LoopStats loop_in_stats = 8; + + /* + The git commit hash of the loopd binary. + */ + string commit_hash = 9; } message GetLiquidityParamsRequest { @@ -1176,6 +1221,29 @@ message LiquidityParameters { The address type of the account specified in the account field. */ AddressType account_addr_type = 24; + + /* + A map of asset parameters to use for swaps. The key is the asset id and the + value is the parameters to use for swaps in that asset. + */ + map easy_asset_params = 25; +} + +message EasyAssetAutoloopParams { + /* + Set to true to enable easy autoloop for this asset. If set the client will + automatically dispatch swaps in order to meet the configured local balance + target size. Currently only loop out is supported, meaning that easy + autoloop can only reduce the funds that are held as balance in channels. + */ + bool enabled = 1; + + /* + The local balance target size, expressed in the asset's base units. This is + used by easy autoloop to determine how much liquidity should be maintained + in channels. + */ + uint64 local_target_asset_amt = 2 [jstype = JS_STRING]; } enum LiquidityRuleType { @@ -1592,6 +1660,15 @@ message WithdrawDepositsRequest { The fee rate in sat/vbyte to use for the withdrawal transaction. */ int64 sat_per_vbyte = 4 [jstype = JS_STRING]; + + /* + The amount in satoshis that should be withdrawn from the selected deposits. + If there is change, it will be sent back to the static address. The fees for + the transaction are taken from the change output. If the change is below + the dust limit, there won't be a change output and the dust goes towards + fees. + */ + int64 amount = 5 [jstype = JS_STRING]; } message WithdrawDepositsResponse { @@ -1601,9 +1678,9 @@ message WithdrawDepositsResponse { string withdrawal_tx_hash = 1; /* - The pkscript of the withdrawal transaction. + The destination address of the withdrawal transaction. */ - string pk_script = 2; + string address = 2; } message OutPoint { @@ -2000,3 +2077,117 @@ message StaticAddressLoopInResponse { */ uint32 payment_timeout_seconds = 11; } + +message AssetLoopOutRequest { + /* + The asset id to use to pay for the swap invoice. If set an + asset client is needed to set to be able to pay the invoice. + */ + bytes asset_id = 1; + + /* + The node identity public key of the peer to ask for a quote for sending out + the assets and converting them to satoshis. This must be specified if + an asset id is set. + */ + bytes asset_edge_node = 2; + + /* + An optional maximum multiplier for the rfq rate. If not set, the default + will be 1.1. This means if we request a loop out quote for 1 BTC, the off + chain cost will be at most 1.1 BTC. + */ + double max_limit_multiplier = 3; + + /* + An optional expiry unix timestamp for when the rfq quote should expire. + */ + int64 expiry = 4 [jstype = JS_STRING]; +} + +message AssetRfqInfo { + /* + The Prepay RFQ ID to use to pay for the prepay invoice. + */ + bytes prepay_rfq_id = 1; + + /* + The maximum asset amt we'll pay for the prepay payment. This includes the + max limit multiplier that was set in the request. + */ + uint64 max_prepay_asset_amt = 2 [jstype = JS_STRING]; + + /* + The asset to BTC conversion rate for the prepay invoice. + */ + FixedPoint prepay_asset_rate = 6; + + /* + The Swap RFQ ID to use to pay for the swap invoice. + */ + bytes swap_rfq_id = 3; + + /* + The maximum asset amt we'll pay for the swap payment. This includes the + max limit multiplier that was set in the request. + */ + uint64 max_swap_asset_amt = 4 [jstype = JS_STRING]; + + /* + The asset to BTC conversion rate for the swap invoice. + */ + FixedPoint swap_asset_rate = 7; + + /* + The name of the asset to swap. + */ + string asset_name = 5; +} + +// FixedPoint is a scaled integer representation of a fractional number. +// +// This type consists of two integer fields: a coefficient and a scale. +// Using this format enables precise and consistent representation of fractional +// numbers while avoiding floating-point data types, which are prone to +// precision errors. +// +// The relationship between the fractional representation and its fixed-point +// representation is expressed as: +// ``` +// V = F_c / (10^F_s) +// ``` +// where: +// +// * `V` is the fractional value. +// +// * `F_c` is the coefficient component of the fixed-point representation. It is +// the scaled-up fractional value represented as an integer. +// +// * `F_s` is the scale component. It is an integer specifying how +// many decimal places `F_c` should be divided by to obtain the fractional +// representation. +message FixedPoint { + // The coefficient is the fractional value scaled-up as an integer. This + // integer is represented as a string as it may be too large to fit in a + // uint64. + string coefficient = 1; + + // The scale is the component that determines how many decimal places + // the coefficient should be divided by to obtain the fractional value. + uint32 scale = 2; +} + +message AssetLoopOutInfo { + /* + The asset id that was used to pay for the swap invoice. + */ + string asset_id = 1; + /* + The human readable name of the asset. + */ + string asset_name = 2; + /* + The total asset offchain cost of the swap. + */ + uint64 asset_cost_offchain = 3 [jstype = JS_STRING]; +} diff --git a/rules/channel_restrictions_test.go b/rules/channel_restrictions_test.go index b2d6200c3..6102851c4 100644 --- a/rules/channel_restrictions_test.go +++ b/rules/channel_restrictions_test.go @@ -157,8 +157,8 @@ func (m *mockLndClient) GetLndClient() lndclient.LightningClient { return m } -func (m *mockLndClient) ListChannels(_ context.Context, _, _ bool) ( - []lndclient.ChannelInfo, error) { +func (m *mockLndClient) ListChannels(_ context.Context, _, _ bool, + _ ...lndclient.ListChannelsOption) ([]lndclient.ChannelInfo, error) { return m.channels, nil } From b2f9e6e7edfde4da9fce966881b8bd7fc4eb5608 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 27 Mar 2025 12:22:33 -0500 Subject: [PATCH 4/8] itest: refactor to prepare for grouped assets This commit prepares some of the helper functions to be able to handle channels that have multiple asset pieces in them. --- itest/assets_test.go | 337 +++++++++++++++++++---------- itest/litd_custom_channels_test.go | 104 ++++----- 2 files changed, 264 insertions(+), 177 deletions(-) diff --git a/itest/assets_test.go b/itest/assets_test.go index fc3cd34e6..690fbb116 100644 --- a/itest/assets_test.go +++ b/itest/assets_test.go @@ -258,14 +258,15 @@ func createTestAssetNetwork(t *harnessTest, net *NetworkHarness, charlieTap, // Make sure the channel shows the correct asset information. assertAssetChan( t.t, charlieTap.node, daveTap.node, charlieFundingAmount, - mintedAsset, + []*taprpc.Asset{mintedAsset}, ) assertAssetChan( - t.t, daveTap.node, yaraTap.node, daveFundingAmount, mintedAsset, + t.t, daveTap.node, yaraTap.node, daveFundingAmount, + []*taprpc.Asset{mintedAsset}, ) assertAssetChan( t.t, erinTap.node, fabiaTap.node, erinFundingAmount, - mintedAsset, + []*taprpc.Asset{mintedAsset}, ) chanPointCD := &lnrpc.ChannelPoint{ @@ -473,7 +474,7 @@ func assertPendingChannels(t *testing.T, node *HarnessNode, pendingChan.Channel.CustomChannelData, &pendingJSON, ) require.NoError(t, err) - require.Len(t, pendingJSON.FundingAssets, 1) + require.GreaterOrEqual(t, len(pendingJSON.FundingAssets), 1) require.NotZero(t, pendingJSON.Capacity) @@ -494,9 +495,7 @@ func assertPendingChannels(t *testing.T, node *HarnessNode, // Check the balance of the pending channel. assetID := mintedAsset.AssetGenesis.AssetId pendingLocalBalance, pendingRemoteBalance, _, _ := - getAssetChannelBalance( - t, node, assetID, true, - ) + getAssetChannelBalance(t, node, [][]byte{assetID}, true) require.EqualValues(t, localSum, pendingLocalBalance) require.EqualValues(t, remoteSum, pendingRemoteBalance) } @@ -517,7 +516,7 @@ func haveFundingAsset(assetChannel *rfqmsg.JsonAssetChannel, } func assertAssetChan(t *testing.T, src, dst *HarnessNode, fundingAmount uint64, - channelAsset *taprpc.Asset) { + channelAssets []*taprpc.Asset) { err := wait.NoError(func() error { a, err := getChannelCustomData(src, dst) @@ -525,10 +524,12 @@ func assertAssetChan(t *testing.T, src, dst *HarnessNode, fundingAmount uint64, return err } - assetID := channelAsset.AssetGenesis.AssetId - if !haveFundingAsset(a, assetID) { - return fmt.Errorf("expected asset ID %x, to "+ - "be in channel", assetID) + for _, channelAsset := range channelAssets { + assetID := channelAsset.AssetGenesis.AssetId + if !haveFundingAsset(a, assetID) { + return fmt.Errorf("expected asset ID %x, to "+ + "be in channel", assetID) + } } if a.Capacity != fundingAmount { @@ -541,9 +542,9 @@ func assertAssetChan(t *testing.T, src, dst *HarnessNode, fundingAmount uint64, // We only need to check the first funding asset, since we // enforce them to be the same. var expectedDecimalDisplay uint8 - if channelAsset.DecimalDisplay != nil { + if channelAssets[0].DecimalDisplay != nil { expectedDecimalDisplay = uint8( - channelAsset.DecimalDisplay.DecimalDisplay, + channelAssets[0].DecimalDisplay.DecimalDisplay, ) } @@ -639,7 +640,7 @@ func getChannelCustomData(src, dst *HarnessNode) (*rfqmsg.JsonAssetChannel, return &assetData, nil } -func getAssetChannelBalance(t *testing.T, node *HarnessNode, assetID []byte, +func getAssetChannelBalance(t *testing.T, node *HarnessNode, assetIDs [][]byte, pending bool) (uint64, uint64, uint64, uint64) { ctxb := context.Background() @@ -651,18 +652,34 @@ func getAssetChannelBalance(t *testing.T, node *HarnessNode, assetID []byte, ) require.NoError(t, err) + // In case there are no channels, the custom channel data will just be + // empty. Which means the total asset balance is zero. + if len(balance.CustomChannelData) == 0 { + return 0, 0, 0, 0 + } + var assetBalance rfqmsg.JsonAssetChannelBalances err = json.Unmarshal(balance.CustomChannelData, &assetBalance) - require.NoError(t, err) + require.NoErrorf(t, err, "json: '%x'", balance.CustomChannelData) balances := assetBalance.OpenChannels if pending { balances = assetBalance.PendingChannels } + idMatch := func(assetIDString string) bool { + for _, groupedID := range assetIDs { + if assetIDString == hex.EncodeToString(groupedID) { + return true + } + } + + return false + } + var localSum, remoteSum uint64 for assetIDString := range balances { - if assetIDString != hex.EncodeToString(assetID) { + if !idMatch(assetIDString) { continue } @@ -743,12 +760,6 @@ func sendAssetKeySendPayment(t *testing.T, src, dst *HarnessNode, amt uint64, opt(cfg) } - // Nullify assetID if group key is set. RPC methods won't accept both so - // let's prioritize the group key if set. - if len(cfg.groupKey) > 0 { - assetID = nil - } - ctxb := context.Background() ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) defer cancel() @@ -775,12 +786,20 @@ func sendAssetKeySendPayment(t *testing.T, src, dst *HarnessNode, amt uint64, TimeoutSeconds: int32(PaymentTimeout.Seconds()), } - stream, err := srcTapd.SendPayment(ctxt, &tchrpc.SendPaymentRequest{ - AssetId: assetID, + request := &tchrpc.SendPaymentRequest{ AssetAmount: amt, - GroupKey: cfg.groupKey, PaymentRequest: sendReq, - }) + } + + switch { + case len(cfg.groupKey) > 0: + request.GroupKey = cfg.groupKey + + default: + request.AssetId = assetID + } + + stream, err := srcTapd.SendPayment(ctxt, request) require.NoError(t, err) result, err := getAssetPaymentResult(stream, false) @@ -966,12 +985,6 @@ func defaultPayConfig() *payConfig { type payOpt func(*payConfig) -func withGroupKey(groupKey []byte) payOpt { - return func(c *payConfig) { - c.groupKey = groupKey - } -} - func withSmallShards() payOpt { return func(c *payConfig) { c.smallShards = true @@ -1017,6 +1030,12 @@ func withAllowOverpay() payOpt { } } +func withGroupKey(groupKey []byte) payOpt { + return func(c *payConfig) { + c.groupKey = groupKey + } +} + func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode, payReq string, assetID []byte, opts ...payOpt) (uint64, rfqmath.BigIntFixedPoint) { @@ -1026,12 +1045,6 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode, opt(cfg) } - // Nullify assetID if group key is set. RPC methods won't accept both so - // let's prioritize the group key if set. - if len(cfg.groupKey) > 0 { - assetID = []byte{} - } - ctxb := context.Background() ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) defer cancel() @@ -1060,14 +1073,22 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode, copy(rfqBytes, i[:]) }) - stream, err := payerTapd.SendPayment(ctxt, &tchrpc.SendPaymentRequest{ - AssetId: assetID, + request := &tchrpc.SendPaymentRequest{ PeerPubkey: rfqPeer.PubKey[:], - GroupKey: cfg.groupKey, PaymentRequest: sendReq, RfqId: rfqBytes, AllowOverpay: cfg.allowOverpay, - }) + } + + switch { + case len(cfg.groupKey) > 0: + request.GroupKey = cfg.groupKey + + default: + request.AssetId = assetID + } + + stream, err := payerTapd.SendPayment(ctxt, request) require.NoError(t, err) // If an error is returned by the RPC method (meaning the stream itself @@ -1157,12 +1178,6 @@ func createAssetInvoice(t *testing.T, dstRfqPeer, dst *HarnessNode, opt(cfg) } - // Nullify assetID if group key is set. RPC methods won't accept both so - // let's prioritize the group key if set. - if len(cfg.groupKey) > 0 { - assetID = []byte{} - } - ctxb := context.Background() ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) defer cancel() @@ -1175,8 +1190,7 @@ func createAssetInvoice(t *testing.T, dstRfqPeer, dst *HarnessNode, dstTapd := newTapClient(t, dst) - resp, err := dstTapd.AddInvoice(ctxt, &tchrpc.AddInvoiceRequest{ - AssetId: assetID, + request := &tchrpc.AddInvoiceRequest{ GroupKey: cfg.groupKey, AssetAmount: assetAmount, PeerPubkey: dstRfqPeer.PubKey[:], @@ -1185,7 +1199,17 @@ func createAssetInvoice(t *testing.T, dstRfqPeer, dst *HarnessNode, "%d units", assetAmount), Expiry: timeoutSeconds, }, - }) + } + + switch { + case len(cfg.groupKey) > 0: + request.GroupKey = cfg.groupKey + + default: + request.AssetId = assetID + } + + resp, err := dstTapd.AddInvoice(ctxt, request) if cfg.errSubStr != "" { require.ErrorContains(t, err, cfg.errSubStr) @@ -1341,12 +1365,6 @@ func createAssetHodlInvoice(t *testing.T, dstRfqPeer, dst *HarnessNode, opt(cfg) } - // Nullify assetID if group key is set. RPC methods won't accept both so - // let's prioritize the group key if set. - if len(cfg.groupKey) > 0 { - assetID = []byte{} - } - ctxb := context.Background() ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) defer cancel() @@ -1366,10 +1384,7 @@ func createAssetHodlInvoice(t *testing.T, dstRfqPeer, dst *HarnessNode, require.NoError(t, err) payHash := preimage.Hash() - - resp, err := dstTapd.AddInvoice(ctxt, &tchrpc.AddInvoiceRequest{ - AssetId: assetID, - GroupKey: cfg.groupKey, + request := &tchrpc.AddInvoiceRequest{ AssetAmount: assetAmount, PeerPubkey: dstRfqPeer.PubKey[:], InvoiceRequest: &lnrpc.Invoice{ @@ -1380,7 +1395,17 @@ func createAssetHodlInvoice(t *testing.T, dstRfqPeer, dst *HarnessNode, HodlInvoice: &tchrpc.HodlInvoice{ PaymentHash: payHash[:], }, - }) + } + + switch { + case len(cfg.groupKey) > 0: + request.GroupKey = cfg.groupKey + + default: + request.AssetId = assetID + } + + resp, err := dstTapd.AddInvoice(ctxt, request) require.NoError(t, err) decodedInvoice, err := dst.DecodePayReq(ctxt, &lnrpc.PayReqString{ @@ -1398,7 +1423,7 @@ func createAssetHodlInvoice(t *testing.T, dstRfqPeer, dst *HarnessNode, require.EqualValues(t, uint64(numMSats), uint64(decodedInvoice.NumMsat)) - t.Logf("Got quote for %d sats at %v msat/unit from peer %x with SCID "+ + t.Logf("Got quote for %d msat at %v msat/unit from peer %x with SCID "+ "%d", decodedInvoice.NumMsat, mSatPerUnit, dstRfqPeer.PubKey[:], resp.AcceptedBuyQuote.Scid) @@ -1430,12 +1455,12 @@ func waitForSendEvent(t *testing.T, // transaction. type coOpCloseBalanceCheck func(t *testing.T, local, remote *HarnessNode, closeTx *wire.MsgTx, closeUpdate *lnrpc.ChannelCloseUpdate, - assetID, groupKey []byte, universeTap *tapClient) + assetIDs [][]byte, groupKey []byte, universeTap *tapClient) // noOpCoOpCloseBalanceCheck is a no-op implementation of the co-op close // balance check that can be used in tests. func noOpCoOpCloseBalanceCheck(_ *testing.T, _, _ *HarnessNode, _ *wire.MsgTx, - _ *lnrpc.ChannelCloseUpdate, _, _ []byte, _ *tapClient) { + _ *lnrpc.ChannelCloseUpdate, _ [][]byte, _ []byte, _ *tapClient) { // This is a no-op function. } @@ -1444,7 +1469,7 @@ func noOpCoOpCloseBalanceCheck(_ *testing.T, _, _ *HarnessNode, _ *wire.MsgTx, // node and asserts the final balances of the closing transaction. func closeAssetChannelAndAssert(t *harnessTest, net *NetworkHarness, local, remote *HarnessNode, chanPoint *lnrpc.ChannelPoint, - assetID, groupKey []byte, universeTap *tapClient, + assetIDs [][]byte, groupKey []byte, universeTap *tapClient, balanceCheck coOpCloseBalanceCheck) { t.t.Helper() @@ -1484,7 +1509,7 @@ func closeAssetChannelAndAssert(t *harnessTest, net *NetworkHarness, // Check the final balance of the closing transaction. balanceCheck( - t.t, local, remote, closeTx, closeUpdate, assetID, groupKey, + t.t, local, remote, closeTx, closeUpdate, assetIDs, groupKey, universeTap, ) @@ -1501,10 +1526,10 @@ func assertDefaultCoOpCloseBalance(remoteBtcBalance, return func(t *testing.T, local, remote *HarnessNode, closeTx *wire.MsgTx, closeUpdate *lnrpc.ChannelCloseUpdate, - assetID, groupKey []byte, universeTap *tapClient) { + assetIDs [][]byte, groupKey []byte, universeTap *tapClient) { defaultCoOpCloseBalanceCheck( - t, local, remote, closeTx, closeUpdate, assetID, + t, local, remote, closeTx, closeUpdate, assetIDs, groupKey, universeTap, remoteBtcBalance, remoteAssetBalance, ) @@ -1517,8 +1542,8 @@ func assertDefaultCoOpCloseBalance(remoteBtcBalance, // with the boolean variables. func defaultCoOpCloseBalanceCheck(t *testing.T, local, remote *HarnessNode, closeTx *wire.MsgTx, closeUpdate *lnrpc.ChannelCloseUpdate, - assetID, groupKey []byte, universeTap *tapClient, remoteBtcBalance, - remoteAssetBalance bool) { + assetIDs [][]byte, groupKey []byte, universeTap *tapClient, + remoteBtcBalance, remoteAssetBalance bool) { // With the channel closed, we'll now assert that the co-op close // transaction was inserted into the local universe. @@ -1625,11 +1650,15 @@ func defaultCoOpCloseBalanceCheck(t *testing.T, local, remote *HarnessNode, ) require.NoError(t, err) + assetIDStrings := fn.Map(hex.EncodeToString, assetIDs) for assetIDStr, scriptKeyStr := range localAssetCloseOut.ScriptKeys { scriptKeyBytes, err := hex.DecodeString(scriptKeyStr) require.NoError(t, err) - require.Equal(t, hex.EncodeToString(assetID), assetIDStr) + require.Contains(t, assetIDStrings, assetIDStr) + + assetID, err := hex.DecodeString(assetIDStr) + require.NoError(t, err) a := assertUniverseProofExists( t, universeTap, assetID, groupKey, scriptKeyBytes, @@ -1667,7 +1696,10 @@ func defaultCoOpCloseBalanceCheck(t *testing.T, local, remote *HarnessNode, scriptKeyBytes, err := hex.DecodeString(scriptKeyStr) require.NoError(t, err) - require.Equal(t, hex.EncodeToString(assetID), assetIDStr) + require.Contains(t, assetIDStrings, assetIDStr) + + assetID, err := hex.DecodeString(assetIDStr) + require.NoError(t, err) a := assertUniverseProofExists( t, universeTap, assetID, groupKey, scriptKeyBytes, @@ -1689,8 +1721,8 @@ func defaultCoOpCloseBalanceCheck(t *testing.T, local, remote *HarnessNode, // function that can be used when the initiator has a zero asset balance. func initiatorZeroAssetBalanceCoOpBalanceCheck(t *testing.T, _, remote *HarnessNode, closeTx *wire.MsgTx, - closeUpdate *lnrpc.ChannelCloseUpdate, assetID, groupKey []byte, - universeTap *tapClient) { + closeUpdate *lnrpc.ChannelCloseUpdate, assetIDs [][]byte, + groupKey []byte, universeTap *tapClient) { // With the channel closed, we'll now assert that the co-op close // transaction was inserted into the local universe. @@ -1736,11 +1768,15 @@ func initiatorZeroAssetBalanceCoOpBalanceCheck(t *testing.T, _, ) require.NoError(t, err) + assetIDStrings := fn.Map(hex.EncodeToString, assetIDs) for assetIDStr, scriptKeyStr := range remoteAssetCloseOut.ScriptKeys { scriptKeyBytes, err := hex.DecodeString(scriptKeyStr) require.NoError(t, err) - require.Equal(t, hex.EncodeToString(assetID), assetIDStr) + require.Contains(t, assetIDStrings, assetIDStr) + + assetID, err := hex.DecodeString(assetIDStr) + require.NoError(t, err) a := assertUniverseProofExists( t, universeTap, assetID, groupKey, scriptKeyBytes, @@ -1883,12 +1919,15 @@ func assertAssetBalance(t *testing.T, client *tapClient, assetID []byte, }, } + var lastBalances *taprpc.ListBalancesResponse err := wait.NoError(func() error { assetIDBalances, err := client.ListBalances(ctxt, req) if err != nil { return err } + lastBalances = assetIDBalances + assetIDFound := false for _, balance := range assetIDBalances.AssetBalances { if !bytes.Equal(balance.AssetGenesis.AssetId, assetID) { @@ -1909,11 +1948,16 @@ func assertAssetBalance(t *testing.T, client *tapClient, assetID []byte, return nil }, shortTimeout) if err != nil { - r, err2 := client.ListAssets(ctxb, &taprpc.ListAssetRequest{}) + listAssetsResp, err2 := client.ListAssets( + ctxb, &taprpc.ListAssetRequest{}, + ) require.NoError(t, err2) - t.Logf("Failed to assert expected balance of %d, current "+ - "assets: %v", expectedBalance, toProtoJSON(t, r)) + t.Logf("Failed to assert expected balance of %d for asset ID "+ + "%x: %v", expectedBalance, assetID, err) + + t.Logf("Last balances: %v", toProtoJSON(t, lastBalances)) + t.Logf("Current assets: %v", toProtoJSON(t, listAssetsResp)) utxos, err3 := client.ListUtxos( ctxb, &taprpc.ListUtxosRequest{}, @@ -1926,40 +1970,57 @@ func assertAssetBalance(t *testing.T, client *tapClient, assetID []byte, } } -// assertSpendableBalance differs from assertAssetBalance in that it asserts -// that the entire balance is spendable. We consider something spendable if we -// have a local script key for it. -func assertSpendableBalance(t *testing.T, client *tapClient, assetID []byte, - expectedBalance uint64) { - - t.Helper() +func spendableBalance(client *tapClient, assetID, + groupKey []byte) (uint64, error) { ctxb := context.Background() ctxt, cancel := context.WithTimeout(ctxb, shortTimeout) defer cancel() - err := wait.NoError(func() error { - utxos, err := client.ListUtxos(ctxt, &taprpc.ListUtxosRequest{}) - if err != nil { - return err + utxos, err := client.ListUtxos(ctxt, &taprpc.ListUtxosRequest{}) + if err != nil { + return 0, err + } + + assets := tapfn.FlatMap( + maps.Values(utxos.ManagedUtxos), + func(utxo *taprpc.ManagedUtxo) []*taprpc.Asset { + return utxo.Assets + }, + ) + + relevantAssets := fn.Filter(func(utxo *taprpc.Asset) bool { + if len(groupKey) > 0 { + return bytes.Equal( + utxo.AssetGroup.TweakedGroupKey, groupKey, + ) } - assets := tapfn.FlatMap( - maps.Values(utxos.ManagedUtxos), - func(utxo *taprpc.ManagedUtxo) []*taprpc.Asset { - return utxo.Assets - }, - ) + return bytes.Equal(utxo.AssetGenesis.AssetId, assetID) + }, assets) - relevantAssets := fn.Filter(func(utxo *taprpc.Asset) bool { - return bytes.Equal(utxo.AssetGenesis.AssetId, assetID) - }, assets) + var assetSum uint64 + for _, a := range relevantAssets { + if a.ScriptKeyIsLocal { + assetSum += a.Amount + } + } - var assetSum uint64 - for _, asset := range relevantAssets { - if asset.ScriptKeyIsLocal { - assetSum += asset.Amount - } + return assetSum, nil +} + +// assertSpendableBalance differs from assertAssetBalance in that it asserts +// that the entire balance is spendable. We consider something spendable if we +// have a local script key for it. +func assertSpendableBalance(t *testing.T, client *tapClient, assetID, + groupKey []byte, expectedBalance uint64) { + + t.Helper() + + err := wait.NoError(func() error { + assetSum, err := spendableBalance(client, assetID, groupKey) + if err != nil { + return err } if assetSum != expectedBalance { @@ -1970,16 +2031,19 @@ func assertSpendableBalance(t *testing.T, client *tapClient, assetID []byte, return nil }, shortTimeout) if err != nil { - r, err2 := client.ListAssets(ctxb, &taprpc.ListAssetRequest{}) + ctxb := context.Background() + listResp, err2 := client.ListAssets( + ctxb, &taprpc.ListAssetRequest{}, + ) require.NoError(t, err2) t.Logf("Failed to assert expected balance of %d, current "+ - "assets: %v", expectedBalance, toProtoJSON(t, r)) + "assets: %v", expectedBalance, toProtoJSON(t, listResp)) - utxos, err3 := client.ListUtxos( + utxos, err2 := client.ListUtxos( ctxb, &taprpc.ListUtxosRequest{}, ) - require.NoError(t, err3) + require.NoError(t, err2) t.Logf("Current UTXOs: %v", toProtoJSON(t, utxos)) @@ -2093,8 +2157,9 @@ func logBalance(t *testing.T, nodes []*HarnessNode, assetID []byte, time.Sleep(time.Millisecond * 250) for _, node := range nodes { - local, remote, localSat, remoteSat := - getAssetChannelBalance(t, node, assetID, false) + local, remote, localSat, remoteSat := getAssetChannelBalance( + t, node, [][]byte{assetID}, false, + ) t.Logf("%-7s balance: local=%-9d remote=%-9d, localSat=%-9d, "+ "remoteSat=%-9d (%v)", node.Cfg.Name, local, remote, @@ -2327,9 +2392,9 @@ func assertPendingChannelAssetData(t *testing.T, node *HarnessNode, "data: %v", err) } - if len(closeData.FundingAssets) != 1 { - return fmt.Errorf("expected 1 funding asset, got %d", - len(closeData.FundingAssets)) + if len(closeData.FundingAssets) == 0 { + return fmt.Errorf("expected at least 1 funding asset, "+ + "got %d", len(closeData.FundingAssets)) } return nil @@ -2425,3 +2490,47 @@ func assertClosedChannelAssetData(t *testing.T, node *HarnessNode, require.GreaterOrEqual(t, len(closeData.FundingAssets), 1) } + +func findForceCloseTransfer(t *testing.T, node1, node2 *tapClient, + closeTxid *chainhash.Hash) *taprpc.ListTransfersResponse { + + var ( + ctxb = context.Background() + result *taprpc.ListTransfersResponse + err error + ) + fErr := wait.NoError(func() error { + result, err = node1.ListTransfers( + ctxb, &taprpc.ListTransfersRequest{ + AnchorTxid: closeTxid.String(), + }, + ) + if err != nil { + return fmt.Errorf("unable to list node1 transfers: %w", + err) + } + if len(result.Transfers) != 1 { + return fmt.Errorf("node1 is missing force close " + + "transfer") + } + + forceCloseTransfer2, err := node2.ListTransfers( + ctxb, &taprpc.ListTransfersRequest{ + AnchorTxid: closeTxid.String(), + }, + ) + if err != nil { + return fmt.Errorf("unable to list node2 transfers: %w", + err) + } + if len(forceCloseTransfer2.Transfers) != 1 { + return fmt.Errorf("node2 is missing force close " + + "transfer") + } + + return nil + }, defaultTimeout) + require.NoError(t, fErr) + + return result +} diff --git a/itest/litd_custom_channels_test.go b/itest/litd_custom_channels_test.go index 256275f56..b752320b3 100644 --- a/itest/litd_custom_channels_test.go +++ b/itest/litd_custom_channels_test.go @@ -34,7 +34,6 @@ import ( "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/port" - "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/stretchr/testify/require" @@ -279,7 +278,7 @@ func testCustomChannelsLarge(_ context.Context, net *NetworkHarness, // balance is on the non-initiator (recipient) side. t.Logf("Closing Charlie -> Dave channel") closeAssetChannelAndAssert( - t, net, charlie, dave, chanPointCD, assetID, nil, + t, net, charlie, dave, chanPointCD, [][]byte{assetID}, nil, universeTap, initiatorZeroAssetBalanceCoOpBalanceCheck, ) } @@ -615,19 +614,19 @@ func testCustomChannels(ctx context.Context, net *NetworkHarness, // ------------ t.Logf("Closing Charlie -> Dave channel") closeAssetChannelAndAssert( - t, net, charlie, dave, chanPointCD, assetID, nil, + t, net, charlie, dave, chanPointCD, [][]byte{assetID}, nil, universeTap, assertDefaultCoOpCloseBalance(true, true), ) t.Logf("Closing Dave -> Yara channel, close initiated by Yara") closeAssetChannelAndAssert( - t, net, yara, dave, chanPointDY, assetID, nil, + t, net, yara, dave, chanPointDY, [][]byte{assetID}, nil, universeTap, assertDefaultCoOpCloseBalance(false, true), ) t.Logf("Closing Erin -> Fabia channel") closeAssetChannelAndAssert( - t, net, erin, fabia, chanPointEF, assetID, nil, + t, net, erin, fabia, chanPointEF, [][]byte{assetID}, nil, universeTap, assertDefaultCoOpCloseBalance(true, true), ) @@ -668,7 +667,9 @@ func testCustomChannels(ctx context.Context, net *NetworkHarness, t.t, universeTap, assetID, nil, fundingScriptTreeBytes, fmt.Sprintf("%v:%v", fundRespCD.Txid, fundRespCD.OutputIndex), ) - assertAssetChan(t.t, charlie, dave, fundingAmount, cents) + assertAssetChan( + t.t, charlie, dave, fundingAmount, []*taprpc.Asset{cents}, + ) // And let's just close the channel again. chanPointCD = &lnrpc.ChannelPoint{ @@ -680,7 +681,7 @@ func testCustomChannels(ctx context.Context, net *NetworkHarness, t.Logf("Closing Charlie -> Dave channel") closeAssetChannelAndAssert( - t, net, charlie, dave, chanPointCD, assetID, nil, + t, net, charlie, dave, chanPointCD, [][]byte{assetID}, nil, universeTap, assertDefaultCoOpCloseBalance(false, false), ) @@ -1048,19 +1049,19 @@ func testCustomChannelsGroupedAsset(ctx context.Context, net *NetworkHarness, // ------------ t.Logf("Closing Charlie -> Dave channel") closeAssetChannelAndAssert( - t, net, charlie, dave, chanPointCD, assetID, groupID, + t, net, charlie, dave, chanPointCD, [][]byte{assetID}, groupID, universeTap, assertDefaultCoOpCloseBalance(true, true), ) t.Logf("Closing Dave -> Yara channel, close initiated by Yara") closeAssetChannelAndAssert( - t, net, yara, dave, chanPointDY, assetID, groupID, + t, net, yara, dave, chanPointDY, [][]byte{assetID}, groupID, universeTap, assertDefaultCoOpCloseBalance(false, true), ) t.Logf("Closing Erin -> Fabia channel") closeAssetChannelAndAssert( - t, net, erin, fabia, chanPointEF, assetID, groupID, + t, net, erin, fabia, chanPointEF, [][]byte{assetID}, groupID, universeTap, assertDefaultCoOpCloseBalance(true, true), ) @@ -1102,7 +1103,9 @@ func testCustomChannelsGroupedAsset(ctx context.Context, net *NetworkHarness, t.t, universeTap, nil, groupID, fundingScriptTreeBytes, fmt.Sprintf("%v:%v", fundRespCD.Txid, fundRespCD.OutputIndex), ) - assertAssetChan(t.t, charlie, dave, fundingAmount, cents) + assertAssetChan( + t.t, charlie, dave, fundingAmount, []*taprpc.Asset{cents}, + ) // And let's just close the channel again. chanPointCD = &lnrpc.ChannelPoint{ @@ -1114,7 +1117,7 @@ func testCustomChannelsGroupedAsset(ctx context.Context, net *NetworkHarness, t.Logf("Closing Charlie -> Dave channel") closeAssetChannelAndAssert( - t, net, charlie, dave, chanPointCD, assetID, groupID, + t, net, charlie, dave, chanPointCD, [][]byte{assetID}, groupID, universeTap, assertDefaultCoOpCloseBalance(false, false), ) @@ -1276,7 +1279,9 @@ func testCustomChannelsForceClose(ctx context.Context, net *NetworkHarness, ) // Make sure the channel shows the correct asset information. - assertAssetChan(t.t, charlie, dave, fundingAmount, cents) + assertAssetChan( + t.t, charlie, dave, fundingAmount, []*taprpc.Asset{cents}, + ) // Before we start sending out payments, let's make sure each node can // see the other one in the graph and has all required features. @@ -1331,40 +1336,9 @@ func testCustomChannelsForceClose(ctx context.Context, net *NetworkHarness, // At this point, we should have the force close transaction in the set // of transfers for both nodes. - var forceCloseTransfer *taprpc.ListTransfersResponse - fErr := wait.NoError(func() error { - forceCloseTransfer, err = charlieTap.ListTransfers( - ctx, &taprpc.ListTransfersRequest{ - AnchorTxid: closeTxid.String(), - }, - ) - if err != nil { - return fmt.Errorf("unable to list charlie transfers: "+ - "%w", err) - } - if len(forceCloseTransfer.Transfers) != 1 { - return fmt.Errorf("charlie is missing force close " + - "transfer") - } - - forceCloseTransfer2, err := daveTap.ListTransfers( - ctx, &taprpc.ListTransfersRequest{ - AnchorTxid: closeTxid.String(), - }, - ) - if err != nil { - return fmt.Errorf("unable to list dave transfers: %w", - err) - } - if len(forceCloseTransfer2.Transfers) != 1 { - return fmt.Errorf("dave is missing force close " + - "transfer") - } - - return nil - }, defaultTimeout) - require.NoError(t.t, fErr) - + forceCloseTransfer := findForceCloseTransfer( + t.t, charlieTap, daveTap, closeTxid, + ) t.Logf("Force close transfer: %v", toProtoJSON(t.t, forceCloseTransfer)) // Now that we have the transfer on disk, we'll also assert that the @@ -1604,7 +1578,9 @@ func testCustomChannelsBreach(ctx context.Context, net *NetworkHarness, ) // Make sure the channel shows the correct asset information. - assertAssetChan(t.t, charlie, dave, fundingAmount, cents) + assertAssetChan( + t.t, charlie, dave, fundingAmount, []*taprpc.Asset{cents}, + ) // Before we start sending out payments, let's make sure each node can // see the other one in the graph and has all required features. @@ -2566,7 +2542,8 @@ func testCustomChannelsBalanceConsistency(ctx context.Context, // Make sure the channel shows the correct asset information. assertAssetChan( - t.t, charlieTap.node, daveTap.node, charlieBalance, cents, + t.t, charlieTap.node, daveTap.node, charlieBalance, + []*taprpc.Asset{cents}, ) logBalance(t.t, nodes, assetID, "initial") @@ -2748,7 +2725,8 @@ func testCustomChannelsSingleAssetMultiInput(ctx context.Context, // Make sure the channel shows the correct asset information. assertAssetChan( - t.t, charlieTap.node, daveTap.node, 2*halfCentsAmount, cents, + t.t, charlieTap.node, daveTap.node, 2*halfCentsAmount, + []*taprpc.Asset{cents}, ) } @@ -3043,20 +3021,20 @@ func testCustomChannelsOraclePricing(ctx context.Context, net *NetworkHarness, t.Logf("Closing Charlie -> Dave channel") closeAssetChannelAndAssert( - t, net, charlie, dave, chanPointCD, assetID, nil, universeTap, - noOpCoOpCloseBalanceCheck, + t, net, charlie, dave, chanPointCD, [][]byte{assetID}, nil, + universeTap, noOpCoOpCloseBalanceCheck, ) t.Logf("Closing Dave -> Yara channel, close initiated by Yara") closeAssetChannelAndAssert( - t, net, yara, dave, chanPointDY, assetID, nil, universeTap, - noOpCoOpCloseBalanceCheck, + t, net, yara, dave, chanPointDY, [][]byte{assetID}, nil, + universeTap, noOpCoOpCloseBalanceCheck, ) t.Logf("Closing Erin -> Fabia channel") closeAssetChannelAndAssert( - t, net, erin, fabia, chanPointEF, assetID, nil, universeTap, - noOpCoOpCloseBalanceCheck, + t, net, erin, fabia, chanPointEF, [][]byte{assetID}, nil, + universeTap, noOpCoOpCloseBalanceCheck, ) } @@ -3427,7 +3405,7 @@ func runCustomChannelsHtlcForceClose(ctx context.Context, t *harnessTest, bobExpectedBalance := closeExpiryInfo.remoteAssetBalance + uint64(assetInvoiceAmt-1) t.Logf("Expecting Bob's balance to be %d", bobExpectedBalance) - assertSpendableBalance(t.t, bobTap, assetID, bobExpectedBalance) + assertSpendableBalance(t.t, bobTap, assetID, nil, bobExpectedBalance) // With Bob's HTLC settled, we'll now have Alice do the same. For her, // it'll be a 2nd level sweep, which requires an extra transaction. @@ -3458,7 +3436,7 @@ func runCustomChannelsHtlcForceClose(ctx context.Context, t *harnessTest, aliceExpectedBalance := itestAsset.Amount - fundingAmount aliceExpectedBalance += closeExpiryInfo.localAssetBalance assertSpendableBalance( - t.t, aliceTap, assetID, aliceExpectedBalance, + t.t, aliceTap, assetID, nil, aliceExpectedBalance, ) t.Logf("Settling Alice's hodl invoice") @@ -3559,7 +3537,7 @@ func runCustomChannelsHtlcForceClose(ctx context.Context, t *harnessTest, // incremented by the amt of the HTLC. aliceExpectedBalance += uint64(assetInvoiceAmt - 1) assertSpendableBalance( - t.t, aliceTap, assetID, aliceExpectedBalance, + t.t, aliceTap, assetID, nil, aliceExpectedBalance, ) t.Logf("Mining enough blocks to time out the remaining HTLCs") @@ -3655,7 +3633,7 @@ func runCustomChannelsHtlcForceClose(ctx context.Context, t *harnessTest, // HTLC value. bobExpectedBalance += uint64(assetInvoiceAmt - 1) assertSpendableBalance( - t.t, bobTap, assetID, bobExpectedBalance, + t.t, bobTap, assetID, nil, bobExpectedBalance, ) t.Logf("Mining extra blocks for Alice's CSV to expire on 2nd level txn") @@ -3708,7 +3686,7 @@ func runCustomChannelsHtlcForceClose(ctx context.Context, t *harnessTest, aliceExpectedBalance += uint64(assetInvoiceAmt - 1) t.Logf("Expecting Alice's balance to be %d", aliceExpectedBalance) assertSpendableBalance( - t.t, aliceTap, assetID, aliceExpectedBalance, + t.t, aliceTap, assetID, nil, aliceExpectedBalance, ) t.Logf("Sending all settled funds to Zane") @@ -3756,7 +3734,7 @@ func runCustomChannelsHtlcForceClose(ctx context.Context, t *harnessTest, // Zane's balance should now be the sum of Alice's and Bob's balances. zaneExpectedBalance := aliceExpectedBalance + bobExpectedBalance assertSpendableBalance( - t.t, zaneTap, assetID, zaneExpectedBalance, + t.t, zaneTap, assetID, nil, zaneExpectedBalance, ) } @@ -3970,7 +3948,7 @@ func testCustomChannelsForwardBandwidth(ctx context.Context, // Finally, we close the channel between Erin and Fabia to make sure // everything is settled correctly. closeAssetChannelAndAssert( - t, net, erin, fabia, chanPointEF, assetID, nil, + t, net, erin, fabia, chanPointEF, [][]byte{assetID}, nil, universeTap, noOpCoOpCloseBalanceCheck, ) } From a70609b5c8053d95becdbd64c0afdf88895d8555 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 29 Apr 2025 14:46:47 +0200 Subject: [PATCH 5/8] itest: refactor out assertForceCloseSweeps for re-use We'll be adding a new HTLC force close test where we'll be able to re-use this functionality. --- itest/assets_test.go | 403 ++++++++++++++++++++++++++++ itest/litd_custom_channels_test.go | 407 +---------------------------- 2 files changed, 417 insertions(+), 393 deletions(-) diff --git a/itest/assets_test.go b/itest/assets_test.go index 690fbb116..ba422ecb9 100644 --- a/itest/assets_test.go +++ b/itest/assets_test.go @@ -36,6 +36,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" + "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntest/rpc" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntypes" @@ -2534,3 +2535,405 @@ func findForceCloseTransfer(t *testing.T, node1, node2 *tapClient, return result } + +// assertForceCloseSweeps asserts that the force close sweeps are initiated +// correctly. +func assertForceCloseSweeps(ctx context.Context, net *NetworkHarness, + t *harnessTest, alice, bob *HarnessNode, chanPoint *lnrpc.ChannelPoint, + aliceStartAmount uint64, assetInvoiceAmt, assetsPerMPPShard int, + assetID, groupKey []byte, aliceHodlInvoices, + bobHodlInvoices []assetHodlInvoice, mpp bool) (uint64, uint64) { + + aliceTap := newTapClient(t.t, alice) + bobTap := newTapClient(t.t, bob) + + // At this point, both sides should have 4 (or +4 with MPP) HTLCs + // active. + numHtlcs := 4 + numAdditionalShards := assetInvoiceAmt / assetsPerMPPShard + if mpp { + numHtlcs += numAdditionalShards * 2 + } + t.Logf("Asserting both Alice and Bob have %d HTLCs...", numHtlcs) + assertNumHtlcs(t.t, alice, numHtlcs) + assertNumHtlcs(t.t, bob, numHtlcs) + + // Before we force close, we'll grab the current height, the CSV delay + // needed, and also the absolute timeout of the set of active HTLCs. + closeExpiryInfo := newCloseExpiryInfo(t.t, alice) + + // With all of the HTLCs established, we'll now force close the channel + // with Alice. + t.Logf("Force close by Alice w/ HTLCs...") + _, closeTxid, err := net.CloseChannel(alice, chanPoint, true) + require.NoError(t.t, err) + + t.Logf("Channel closed! Mining blocks, close_txid=%v", closeTxid) + + // The channel should first be in "waiting close" until it confirms. + assertWaitingCloseChannelAssetData(t.t, alice, chanPoint) + + // Next, we'll mine a block which should start the clock ticking on the + // relative timeout for the Alice, and Bob. + // + // After this next block, both of them can start to sweep. + // + // For Alice, she'll go to the second level, revealing her preimage in + // the process. She'll then need to wait for the relative timeout to + // expire before she can sweep her output. + // + // For Bob, since the remote party (Alice) closed, he can try to sweep + // right away after initial confirmation. + mineBlocks(t, net, 1, 1) + + // After force closing, Bob should now have a transfer that tracks the + // force closed commitment transaction. + locateAssetTransfers(t.t, bobTap, *closeTxid) + + t.Logf("Settling Bob's hodl invoice") + + // It should then go to "pending force closed". + assertPendingForceCloseChannelAssetData(t.t, alice, chanPoint) + + // At this point, the commitment transaction has been mined, and we have + // 4 total HTLCs on Alice's commitment transaction: + // + // * 2x outgoing HTLCs from Alice to Bob + // * 2x incoming HTLCs from Bob to Alice (+2 with MPP) + // + // We'll leave half the HTLCs timeout, while pulling the other half. + // To start, we'll signal Bob to settle one of his incoming HTLCs on + // Alice's commitment transaction. For him, this is a remote success + // spend, so there's no CSV delay other than the 1 CSV (carve out), and + // he can spend directly from the commitment transaction. + _, err = bob.InvoicesClient.SettleInvoice( + ctx, &invoicesrpc.SettleInvoiceMsg{ + Preimage: bobHodlInvoices[0].preimage[:], + }, + ) + require.NoError(t.t, err) + + // We'll pause here for Bob to extend the sweep request to the sweeper. + assertSweepExists( + t.t, bob, + walletrpc.WitnessType_TAPROOT_HTLC_ACCEPTED_REMOTE_SUCCESS, + ) + + bobSweepTx1, err := waitForNTxsInMempool( + net.Miner.Client, 1, shortTimeout, + ) + require.NoError(t.t, err) + + // Next, we'll mine an additional block, this should allow Bob to sweep + // both his commitment output, and the incoming HTLC that we just + // settled above. + mineBlocks(t, net, 1, 1) + + // At this point, we should have the next sweep transaction in the + // mempool: Bob's incoming HTLC sweep directly off the commitment + // transaction. + bobSweepTx2, err := waitForNTxsInMempool( + net.Miner.Client, 1, shortTimeout, + ) + require.NoError(t.t, err) + + // We'll now mine the next block, which should confirm Bob's HTLC sweep + // transaction. + mineBlocks(t, net, 1, 1) + + bobSweepTransfer1 := locateAssetTransfers(t.t, bobTap, *bobSweepTx1[0]) + bobSweepTransfer2 := locateAssetTransfers(t.t, bobTap, *bobSweepTx2[0]) + t.Logf("Bob's sweep transfer 1: %v", + toProtoJSON(t.t, bobSweepTransfer1)) + t.Logf("Bob's sweep transfer 2: %v", + toProtoJSON(t.t, bobSweepTransfer2)) + + t.Logf("Confirming Bob's remote HTLC success sweep") + + // Bob's balance should now reflect that he's gained the value of the + // HTLC, in addition to his settled balance. We need to subtract 1 from + // the final balance due to the rounding down of the asset amount during + // RFQ conversion. + bobExpectedBalance := closeExpiryInfo.remoteAssetBalance + + uint64(assetInvoiceAmt-1) + t.Logf("Expecting Bob's balance to be %d", bobExpectedBalance) + assertSpendableBalance( + t.t, bobTap, assetID, groupKey, bobExpectedBalance, + ) + + // With Bob's HTLC settled, we'll now have Alice do the same. For her, + // it'll be a 2nd level sweep, which requires an extra transaction. + // + // Before, we do that though, enough blocks have passed so Alice can now + // sweep her to-local output. So we'll mine an extra block, then assert + // that she's swept everything properly. With the way the sweeper works, + // we need to mine one extra block before the sweeper picks things up. + mineBlocks(t, net, 1, 0) + + aliceSweepTx1, err := waitForNTxsInMempool( + net.Miner.Client, 1, shortTimeout, + ) + require.NoError(t.t, err) + + mineBlocks(t, net, 1, 1) + + aliceSweepTransfer1 := locateAssetTransfers( + t.t, aliceTap, *aliceSweepTx1[0], + ) + t.Logf("Alice's sweep transfer 1: %v", + toProtoJSON(t.t, aliceSweepTransfer1)) + + t.Logf("Confirming Alice's to-local sweep") + + // With this extra block mined, Alice's settled balance should be the + // starting balance, minus the 2 HTLCs, plus her settled balance. + aliceExpectedBalance := aliceStartAmount + aliceExpectedBalance += closeExpiryInfo.localAssetBalance + assertSpendableBalance( + t.t, aliceTap, assetID, groupKey, aliceExpectedBalance, + ) + + t.Logf("Settling Alice's hodl invoice") + + // With her commitment output swept above, we'll now settle one of + // Alice's incoming HTLCs. + _, err = alice.InvoicesClient.SettleInvoice( + ctx, &invoicesrpc.SettleInvoiceMsg{ + Preimage: aliceHodlInvoices[0].preimage[:], + }, + ) + require.NoError(t.t, err) + + // We'll pause here for Alice to extend the sweep request to the + // sweeper. + assertSweepExists( + t.t, alice, + walletrpc.WitnessType_TAPROOT_HTLC_ACCEPTED_LOCAL_SUCCESS, + ) + + // We'll now mine a block, which should trigger Alice's broadcast of the + // second level sweep transaction. + sweepBlocks := mineBlocks(t, net, 1, 0) + + // If the block mined above didn't also mine our sweep, then we'll mine + // one final block which will confirm Alice's sweep transaction. + if len(sweepBlocks[0].Transactions) == 1 { + sweepTx, err := waitForNTxsInMempool( + net.Miner.Client, 1, shortTimeout, + ) + require.NoError(t.t, err) + + // With the sweep transaction in the mempool, we'll mine a block + // to confirm the sweep. + mineBlocks(t, net, 1, 1) + + aliceSweepTransfer := locateAssetTransfers( + t.t, aliceTap, *sweepTx[0], + ) + t.Logf("Alice's first-level sweep transfer: %v", + toProtoJSON(t.t, aliceSweepTransfer)) + } else { + sweepTx := sweepBlocks[0].Transactions[1] + aliceSweepTransfer := locateAssetTransfers( + t.t, aliceTap, sweepTx.TxHash(), + ) + t.Logf("Alice's first-level sweep transfer: %v", + toProtoJSON(t.t, aliceSweepTransfer)) + } + + t.Logf("Confirming Alice's second level remote HTLC success sweep") + + // Next, we'll mine enough blocks to trigger the CSV expiry so Alice can + // sweep the HTLC into her wallet. + mineBlocks(t, net, closeExpiryInfo.csvDelay, 0) + + // We'll pause here and wait until the sweeper recognizes that we've + // offered the second level sweep transaction. + assertSweepExists( + t.t, alice, + //nolint: lll + walletrpc.WitnessType_TAPROOT_HTLC_ACCEPTED_SUCCESS_SECOND_LEVEL, + ) + + t.Logf("Confirming Alice's local HTLC success sweep") + + // Now that we know the sweep was offered, we'll mine an extra block to + // actually trigger a sweeper broadcast. Due to an internal block race + // condition, the sweep transaction may have already been + // published+mined. If so, we don't need to mine the extra block. + sweepBlocks = mineBlocks(t, net, 1, 0) + + // If the block mined above didn't also mine our sweep, then we'll mine + // one final block which will confirm Alice's sweep transaction. + if len(sweepBlocks[0].Transactions) == 1 { + sweepTx, err := waitForNTxsInMempool( + net.Miner.Client, 1, shortTimeout, + ) + require.NoError(t.t, err) + + mineBlocks(t, net, 1, 1) + + aliceSweepTransfer := locateAssetTransfers( + t.t, aliceTap, *sweepTx[0], + ) + t.Logf("Alice's second-level sweep transfer: %v", + toProtoJSON(t.t, aliceSweepTransfer)) + } else { + sweepTx := sweepBlocks[0].Transactions[1] + aliceSweepTransfer := locateAssetTransfers( + t.t, aliceTap, sweepTx.TxHash(), + ) + t.Logf("Alice's second-level sweep transfer: %v", + toProtoJSON(t.t, aliceSweepTransfer)) + } + + // With the sweep transaction confirmed, Alice's balance should have + // incremented by the amt of the HTLC. + aliceExpectedBalance += uint64(assetInvoiceAmt - 1) + assertSpendableBalance( + t.t, aliceTap, assetID, groupKey, aliceExpectedBalance, + ) + + t.Logf("Mining enough blocks to time out the remaining HTLCs") + + // At this point, we've swept two HTLCs: one from the remote commit, and + // one via the second layer. We'll now mine the remaining amount of + // blocks to time out the HTLCs. + blockToMine := closeExpiryInfo.blockTillExpiry( + aliceHodlInvoices[1].preimage.Hash(), + ) + mineBlocks(t, net, blockToMine, 0) + + // We'll wait for both Alice and Bob to present their respective sweeps + // to the sweeper. + numTimeoutHTLCs := 1 + if mpp { + numTimeoutHTLCs += numAdditionalShards + } + assertSweepExists( + t.t, alice, + walletrpc.WitnessType_TAPROOT_HTLC_LOCAL_OFFERED_TIMEOUT, + ) + assertSweepExists( + t.t, bob, + walletrpc.WitnessType_TAPROOT_HTLC_OFFERED_REMOTE_TIMEOUT, + ) + + t.Logf("Confirming initial HTLC timeout txns") + + timeoutSweeps, err := waitForNTxsInMempool( + net.Miner.Client, 2, shortTimeout, + ) + require.NoError(t.t, err) + + t.Logf("Asserting balance on sweeps: %v", timeoutSweeps) + + // Finally, we'll mine a single block to confirm them. + mineBlocks(t, net, 1, 2) + + // Make sure Bob swept all his HTLCs. + bobSweeps, err := bob.WalletKitClient.ListSweeps( + ctx, &walletrpc.ListSweepsRequest{ + Verbose: true, + }, + ) + require.NoError(t.t, err) + + var bobSweepTx *wire.MsgTx + for _, sweep := range bobSweeps.GetTransactionDetails().Transactions { + for _, tx := range timeoutSweeps { + if sweep.TxHash == tx.String() { + txBytes, err := hex.DecodeString(sweep.RawTxHex) + require.NoError(t.t, err) + + bobSweepTx = &wire.MsgTx{} + err = bobSweepTx.Deserialize( + bytes.NewReader(txBytes), + ) + require.NoError(t.t, err) + } + } + } + require.NotNil(t.t, bobSweepTx, "Bob's sweep transaction not found") + + // There's always an extra input that pays for the fees. So we can only + // count the remainder as HTLC inputs. + numSweptHTLCs := len(bobSweepTx.TxIn) - 1 + + // If we didn't yet sweep all HTLCs, then we need to wait for another + // sweep. + if numSweptHTLCs < numTimeoutHTLCs { + assertSweepExists( + t.t, bob, + // nolint: lll + walletrpc.WitnessType_TAPROOT_HTLC_OFFERED_REMOTE_TIMEOUT, + ) + + t.Logf("Confirming additional HTLC timeout sweep txns") + + additionalTimeoutSweeps, err := waitForNTxsInMempool( + net.Miner.Client, 1, shortTimeout, + ) + require.NoError(t.t, err) + + t.Logf("Asserting balance on additional timeout sweeps: %v", + additionalTimeoutSweeps) + + // Finally, we'll mine a single block to confirm them. + mineBlocks(t, net, 1, 1) + } + + // At this point, Bob's balance should be incremented by an additional + // HTLC value. + bobExpectedBalance += uint64(assetInvoiceAmt - 1) + assertSpendableBalance( + t.t, bobTap, assetID, groupKey, bobExpectedBalance, + ) + + t.Logf("Mining extra blocks for Alice's CSV to expire on 2nd level txn") + + // Next, we'll mine 4 additional blocks to Alice's CSV delay expires for + // the second level timeout output. + mineBlocks(t, net, closeExpiryInfo.csvDelay, 0) + + // Wait for Alice to extend the second level output to the sweeper + // before we mine the next block to the sweeper. + assertSweepExists( + t.t, alice, + walletrpc.WitnessType_TAPROOT_HTLC_OFFERED_TIMEOUT_SECOND_LEVEL, + ) + + t.Logf("Confirming Alice's final timeout sweep") + + // With the way the sweeper works, we'll now need to mine an extra block + // to trigger the sweep. + sweepBlocks = mineBlocks(t, net, 1, 0) + + // If the block mined above didn't also mine our sweep, then we'll mine + // one final block which will confirm Alice's sweep transaction. + if len(sweepBlocks[0].Transactions) == 1 { + sweepTx, err := waitForNTxsInMempool( + net.Miner.Client, 1, shortTimeout, + ) + require.NoError(t.t, err) + + // We'll mine one final block which will confirm Alice's sweep + // transaction. + mineBlocks(t, net, 1, 1) + + aliceSweepTransfer := locateAssetTransfers( + t.t, aliceTap, *sweepTx[0], + ) + t.Logf("Alice's final timeout sweep transfer: %v", + toProtoJSON(t.t, aliceSweepTransfer)) + } else { + sweepTx := sweepBlocks[0].Transactions[1] + aliceSweepTransfer := locateAssetTransfers( + t.t, aliceTap, sweepTx.TxHash(), + ) + t.Logf("Alice's final timeout sweep transfer: %v", + toProtoJSON(t.t, aliceSweepTransfer)) + } + + return aliceExpectedBalance, bobExpectedBalance +} diff --git a/itest/litd_custom_channels_test.go b/itest/litd_custom_channels_test.go index b752320b3..e610eb687 100644 --- a/itest/litd_custom_channels_test.go +++ b/itest/litd_custom_channels_test.go @@ -3,7 +3,6 @@ package itest import ( "bytes" "context" - "encoding/hex" "fmt" "math" "math/big" @@ -14,7 +13,6 @@ import ( "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/itest" "github.com/lightninglabs/taproot-assets/proof" @@ -30,7 +28,6 @@ import ( "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" - "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/port" @@ -3207,6 +3204,14 @@ func runCustomChannelsHtlcForceClose(ctx context.Context, t *harnessTest, }, ) require.NoError(t.t, err) + + aliceChanPoint := &lnrpc.ChannelPoint{ + OutputIndex: uint32(assetFundResp.OutputIndex), + FundingTxid: &lnrpc.ChannelPoint_FundingTxidStr{ + FundingTxidStr: assetFundResp.Txid, + }, + } + t.Logf("Funded channel between Alice and Bob: %v", assetFundResp) // With the channel open, mine a block to confirm it. @@ -3289,397 +3294,13 @@ func runCustomChannelsHtlcForceClose(ctx context.Context, t *harnessTest, ) } - // At this point, both sides should have 4 (or +4 with MPP) HTLCs - // active. - numHtlcs := 4 - numAdditionalShards := assetInvoiceAmt / assetsPerMPPShard - if mpp { - numHtlcs += numAdditionalShards * 2 - } - t.Logf("Asserting both Alice and Bob have %d HTLCs...", numHtlcs) - assertNumHtlcs(t.t, alice, numHtlcs) - assertNumHtlcs(t.t, bob, numHtlcs) - - // Before we force close, we'll grab the current height, the CSV delay - // needed, and also the absolute timeout of the set of active HTLCs. - closeExpiryInfo := newCloseExpiryInfo(t.t, alice) - - // With all of the HTLCs established, we'll now force close the channel - // with Alice. - t.Logf("Force close by Alice w/ HTLCs...") - aliceChanPoint := &lnrpc.ChannelPoint{ - OutputIndex: uint32(assetFundResp.OutputIndex), - FundingTxid: &lnrpc.ChannelPoint_FundingTxidStr{ - FundingTxidStr: assetFundResp.Txid, - }, - } - _, closeTxid, err := net.CloseChannel(alice, aliceChanPoint, true) - require.NoError(t.t, err) - - t.Logf("Channel closed! Mining blocks, close_txid=%v", closeTxid) - - // The channel should first be in "waiting close" until it confirms. - assertWaitingCloseChannelAssetData(t.t, alice, aliceChanPoint) - - // Next, we'll mine a block which should start the clock ticking on the - // relative timeout for the Alice, and Bob. - // - // After this next block, both of them can start to sweep. - // - // For Alice, she'll go to the second level, revealing her preimage in - // the process. She'll then need to wait for the relative timeout to - // expire before she can sweep her output. - // - // For Bob, since the remote party (Alice) closed, he can try to sweep - // right away after initial confirmation. - mineBlocks(t, net, 1, 1) - - // After force closing, Bob should now have a transfer that tracks the - // force closed commitment transaction. - locateAssetTransfers(t.t, bobTap, *closeTxid) - - t.Logf("Settling Bob's hodl invoice") - - // It should then go to "pending force closed". - assertPendingForceCloseChannelAssetData(t.t, alice, aliceChanPoint) - - // At this point, the commitment transaction has been mined, and we have - // 4 total HTLCs on Alice's commitment transaction: - // - // * 2x outgoing HTLCs from Alice to Bob - // * 2x incoming HTLCs from Bob to Alice (+2 with MPP) - // - // We'll leave half the HTLCs timeout, while pulling the other half. - // To start, we'll signal Bob to settle one of his incoming HTLCs on - // Alice's commitment transaction. For him, this is a remote success - // spend, so there's no CSV delay other than the 1 CSV (carve out), and - // he can spend directly from the commitment transaction. - _, err = bob.InvoicesClient.SettleInvoice( - ctx, &invoicesrpc.SettleInvoiceMsg{ - Preimage: bobHodlInvoices[0].preimage[:], - }, - ) - require.NoError(t.t, err) - - // We'll pause here for Bob to extend the sweep request to the sweeper. - assertSweepExists( - t.t, bob, - walletrpc.WitnessType_TAPROOT_HTLC_ACCEPTED_REMOTE_SUCCESS, - ) - - bobSweepTx1, err := waitForNTxsInMempool( - net.Miner.Client, 1, shortTimeout, - ) - require.NoError(t.t, err) - - // Next, we'll mine an additional block, this should allow Bob to sweep - // both his commitment output, and the incoming HTLC that we just - // settled above. - mineBlocks(t, net, 1, 1) - - // At this point, we should have the next sweep transaction in the - // mempool: Bob's incoming HTLC sweep directly off the commitment - // transaction. - bobSweepTx2, err := waitForNTxsInMempool( - net.Miner.Client, 1, shortTimeout, - ) - require.NoError(t.t, err) - - // We'll now mine the next block, which should confirm Bob's HTLC sweep - // transaction. - mineBlocks(t, net, 1, 1) - - bobSweepTransfer1 := locateAssetTransfers(t.t, bobTap, *bobSweepTx1[0]) - bobSweepTransfer2 := locateAssetTransfers(t.t, bobTap, *bobSweepTx2[0]) - t.Logf("Bob's sweep transfer 1: %v", - toProtoJSON(t.t, bobSweepTransfer1)) - t.Logf("Bob's sweep transfer 2: %v", - toProtoJSON(t.t, bobSweepTransfer2)) - - t.Logf("Confirming Bob's remote HTLC success sweep") - - // Bob's balance should now reflect that he's gained the value of the - // HTLC, in addition to his settled balance. We need to subtract 1 from - // the final balance due to the rounding down of the asset amount during - // RFQ conversion. - bobExpectedBalance := closeExpiryInfo.remoteAssetBalance + - uint64(assetInvoiceAmt-1) - t.Logf("Expecting Bob's balance to be %d", bobExpectedBalance) - assertSpendableBalance(t.t, bobTap, assetID, nil, bobExpectedBalance) - - // With Bob's HTLC settled, we'll now have Alice do the same. For her, - // it'll be a 2nd level sweep, which requires an extra transaction. - // - // Before, we do that though, enough blocks have passed so Alice can now - // sweep her to-local output. So we'll mine an extra block, then assert - // that she's swept everything properly. With the way the sweeper works, - // we need to mine one extra block before the sweeper picks things up. - mineBlocks(t, net, 1, 0) - - aliceSweepTx1, err := waitForNTxsInMempool( - net.Miner.Client, 1, shortTimeout, - ) - require.NoError(t.t, err) - - mineBlocks(t, net, 1, 1) - - aliceSweepTransfer1 := locateAssetTransfers( - t.t, aliceTap, *aliceSweepTx1[0], - ) - t.Logf("Alice's sweep transfer 1: %v", - toProtoJSON(t.t, aliceSweepTransfer1)) - - t.Logf("Confirming Alice's to-local sweep") - - // With this extra block mined, Alice's settled balance should be the - // starting balance, minus the 2 HTLCs, plus her settled balance. - aliceExpectedBalance := itestAsset.Amount - fundingAmount - aliceExpectedBalance += closeExpiryInfo.localAssetBalance - assertSpendableBalance( - t.t, aliceTap, assetID, nil, aliceExpectedBalance, - ) - - t.Logf("Settling Alice's hodl invoice") - - // With her commitment output swept above, we'll now settle one of - // Alice's incoming HTLCs. - _, err = alice.InvoicesClient.SettleInvoice( - ctx, &invoicesrpc.SettleInvoiceMsg{ - Preimage: aliceHodlInvoices[0].preimage[:], - }, - ) - require.NoError(t.t, err) - - // We'll pause here for Alice to extend the sweep request to the - // sweeper. - assertSweepExists( - t.t, alice, - walletrpc.WitnessType_TAPROOT_HTLC_ACCEPTED_LOCAL_SUCCESS, - ) - - // We'll now mine a block, which should trigger Alice's broadcast of the - // second level sweep transaction. - sweepBlocks := mineBlocks(t, net, 1, 0) - - // If the block mined above didn't also mine our sweep, then we'll mine - // one final block which will confirm Alice's sweep transaction. - if len(sweepBlocks[0].Transactions) == 1 { - sweepTx, err := waitForNTxsInMempool( - net.Miner.Client, 1, shortTimeout, - ) - require.NoError(t.t, err) - - // With the sweep transaction in the mempool, we'll mine a block - // to confirm the sweep. - mineBlocks(t, net, 1, 1) - - aliceSweepTransfer := locateAssetTransfers( - t.t, aliceTap, *sweepTx[0], - ) - t.Logf("Alice's first-level sweep transfer: %v", - toProtoJSON(t.t, aliceSweepTransfer)) - } else { - sweepTx := sweepBlocks[0].Transactions[1] - aliceSweepTransfer := locateAssetTransfers( - t.t, aliceTap, sweepTx.TxHash(), - ) - t.Logf("Alice's first-level sweep transfer: %v", - toProtoJSON(t.t, aliceSweepTransfer)) - } - - t.Logf("Confirming Alice's second level remote HTLC success sweep") - - // Next, we'll mine enough blocks to trigger the CSV expiry so Alice can - // sweep the HTLC into her wallet. - mineBlocks(t, net, closeExpiryInfo.csvDelay, 0) - - // We'll pause here and wait until the sweeper recognizes that we've - // offered the second level sweep transaction. - assertSweepExists( - t.t, alice, - //nolint: lll - walletrpc.WitnessType_TAPROOT_HTLC_ACCEPTED_SUCCESS_SECOND_LEVEL, - ) - - t.Logf("Confirming Alice's local HTLC success sweep") - - // Now that we know the sweep was offered, we'll mine an extra block to - // actually trigger a sweeper broadcast. Due to an internal block race - // condition, the sweep transaction may have already been - // published+mined. If so, we don't need to mine the extra block. - sweepBlocks = mineBlocks(t, net, 1, 0) - - // If the block mined above didn't also mine our sweep, then we'll mine - // one final block which will confirm Alice's sweep transaction. - if len(sweepBlocks[0].Transactions) == 1 { - sweepTx, err := waitForNTxsInMempool( - net.Miner.Client, 1, shortTimeout, - ) - require.NoError(t.t, err) - - mineBlocks(t, net, 1, 1) - - aliceSweepTransfer := locateAssetTransfers( - t.t, aliceTap, *sweepTx[0], - ) - t.Logf("Alice's second-level sweep transfer: %v", - toProtoJSON(t.t, aliceSweepTransfer)) - } else { - sweepTx := sweepBlocks[0].Transactions[1] - aliceSweepTransfer := locateAssetTransfers( - t.t, aliceTap, sweepTx.TxHash(), - ) - t.Logf("Alice's second-level sweep transfer: %v", - toProtoJSON(t.t, aliceSweepTransfer)) - } - - // With the sweep transaction confirmed, Alice's balance should have - // incremented by the amt of the HTLC. - aliceExpectedBalance += uint64(assetInvoiceAmt - 1) - assertSpendableBalance( - t.t, aliceTap, assetID, nil, aliceExpectedBalance, - ) - - t.Logf("Mining enough blocks to time out the remaining HTLCs") - - // At this point, we've swept two HTLCs: one from the remote commit, and - // one via the second layer. We'll now mine the remaining amount of - // blocks to time out the HTLCs. - blockToMine := closeExpiryInfo.blockTillExpiry( - aliceHodlInvoices[1].preimage.Hash(), - ) - mineBlocks(t, net, blockToMine, 0) - - // We'll wait for both Alice and Bob to present their respective sweeps - // to the sweeper. - numTimeoutHTLCs := 1 - if mpp { - numTimeoutHTLCs += numAdditionalShards - } - assertSweepExists( - t.t, alice, - walletrpc.WitnessType_TAPROOT_HTLC_LOCAL_OFFERED_TIMEOUT, - ) - assertSweepExists( - t.t, bob, - walletrpc.WitnessType_TAPROOT_HTLC_OFFERED_REMOTE_TIMEOUT, - ) - - t.Logf("Confirming initial HTLC timeout txns") - - timeoutSweeps, err := waitForNTxsInMempool( - net.Miner.Client, 2, shortTimeout, + // Make sure we can sweep all the HTLCs. + aliceExpectedBalance, bobExpectedBalance := assertForceCloseSweeps( + ctx, net, t, alice, bob, aliceChanPoint, + itestAsset.Amount-fundingAmount, assetInvoiceAmt, + assetsPerMPPShard, assetID, nil, aliceHodlInvoices, + bobHodlInvoices, mpp, ) - require.NoError(t.t, err) - - t.Logf("Asserting balance on sweeps: %v", timeoutSweeps) - - // Finally, we'll mine a single block to confirm them. - mineBlocks(t, net, 1, 2) - - // Make sure Bob swept all his HTLCs. - bobSweeps, err := bob.WalletKitClient.ListSweeps( - ctx, &walletrpc.ListSweepsRequest{ - Verbose: true, - }, - ) - require.NoError(t.t, err) - - var bobSweepTx *wire.MsgTx - for _, sweep := range bobSweeps.GetTransactionDetails().Transactions { - for _, tx := range timeoutSweeps { - if sweep.TxHash == tx.String() { - txBytes, err := hex.DecodeString(sweep.RawTxHex) - require.NoError(t.t, err) - - bobSweepTx = &wire.MsgTx{} - err = bobSweepTx.Deserialize( - bytes.NewReader(txBytes), - ) - require.NoError(t.t, err) - } - } - } - require.NotNil(t.t, bobSweepTx, "Bob's sweep transaction not found") - - // There's always an extra input that pays for the fees. So we can only - // count the remainder as HTLC inputs. - numSweptHTLCs := len(bobSweepTx.TxIn) - 1 - - // If we didn't yet sweep all HTLCs, then we need to wait for another - // sweep. - if numSweptHTLCs < numTimeoutHTLCs { - assertSweepExists( - t.t, bob, - // nolint: lll - walletrpc.WitnessType_TAPROOT_HTLC_OFFERED_REMOTE_TIMEOUT, - ) - - t.Logf("Confirming additional HTLC timeout sweep txns") - - additionalTimeoutSweeps, err := waitForNTxsInMempool( - net.Miner.Client, 1, shortTimeout, - ) - require.NoError(t.t, err) - - t.Logf("Asserting balance on additional timeout sweeps: %v", - additionalTimeoutSweeps) - - // Finally, we'll mine a single block to confirm them. - mineBlocks(t, net, 1, 1) - } - - // At this point, Bob's balance should be incremented by an additional - // HTLC value. - bobExpectedBalance += uint64(assetInvoiceAmt - 1) - assertSpendableBalance( - t.t, bobTap, assetID, nil, bobExpectedBalance, - ) - - t.Logf("Mining extra blocks for Alice's CSV to expire on 2nd level txn") - - // Next, we'll mine 4 additional blocks to Alice's CSV delay expires for - // the second level timeout output. - mineBlocks(t, net, closeExpiryInfo.csvDelay, 0) - - // Wait for Alice to extend the second level output to the sweeper - // before we mine the next block to the sweeper. - assertSweepExists( - t.t, alice, - walletrpc.WitnessType_TAPROOT_HTLC_OFFERED_TIMEOUT_SECOND_LEVEL, - ) - - t.Logf("Confirming Alice's final timeout sweep") - - // With the way the sweeper works, we'll now need to mine an extra block - // to trigger the sweep. - sweepBlocks = mineBlocks(t, net, 1, 0) - - // If the block mined above didn't also mine our sweep, then we'll mine - // one final block which will confirm Alice's sweep transaction. - if len(sweepBlocks[0].Transactions) == 1 { - sweepTx, err := waitForNTxsInMempool( - net.Miner.Client, 1, shortTimeout, - ) - require.NoError(t.t, err) - - // We'll mine one final block which will confirm Alice's sweep - // transaction. - mineBlocks(t, net, 1, 1) - - aliceSweepTransfer := locateAssetTransfers( - t.t, aliceTap, *sweepTx[0], - ) - t.Logf("Alice's final timeout sweep transfer: %v", - toProtoJSON(t.t, aliceSweepTransfer)) - } else { - sweepTx := sweepBlocks[0].Transactions[1] - aliceSweepTransfer := locateAssetTransfers( - t.t, aliceTap, sweepTx.TxHash(), - ) - t.Logf("Alice's final timeout sweep transfer: %v", - toProtoJSON(t.t, aliceSweepTransfer)) - } // Finally, we'll assert that Alice's balance has been incremented by // the timeout value. From 1dec5b62365eb90450c6ef074b59c9c3bd3f1df2 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 26 Feb 2025 16:34:53 +0100 Subject: [PATCH 6/8] itest: add grouped asset multi tranche test --- itest/assets_test.go | 295 +++++++++++++++++++++++++++++ itest/litd_custom_channels_test.go | 231 ++++++++++++++++++++++ itest/litd_test_list_on_test.go | 5 + 3 files changed, 531 insertions(+) diff --git a/itest/assets_test.go b/itest/assets_test.go index ba422ecb9..8ab67e598 100644 --- a/itest/assets_test.go +++ b/itest/assets_test.go @@ -17,6 +17,8 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" + taprootassets "github.com/lightninglabs/taproot-assets" + "github.com/lightninglabs/taproot-assets/asset" tapfn "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/itest" "github.com/lightninglabs/taproot-assets/proof" @@ -292,6 +294,281 @@ func createTestAssetNetwork(t *harnessTest, net *NetworkHarness, charlieTap, return chanPointCD, chanPointDY, chanPointEF } +// createTestAssetNetworkGroupKey sets up a test network with Charlie, Dave, +// Erin and Fabia and creates asset channels between Charlie->Dave and +// Erin-Fabia in a way that there are two equally sized asset pieces for each +// minted asset (currently limited to exactly two assets). The channels are then +// confirmed and balances asserted. +func createTestAssetNetworkGroupKey(ctx context.Context, t *harnessTest, + net *NetworkHarness, charlieTap, daveTap, erinTap, fabiaTap, + universeTap *tapClient, mintedAssets []*taprpc.Asset, + charlieFundingAmount, erinFundingAmount uint64, + pushSat int64) (*lnrpc.ChannelPoint, *lnrpc.ChannelPoint) { + + var groupKey []byte + for _, mintedAsset := range mintedAssets { + require.NotNil(t.t, mintedAsset.AssetGroup) + + if groupKey == nil { + groupKey = mintedAsset.AssetGroup.TweakedGroupKey + + continue + } + + require.Equal( + t.t, groupKey, mintedAsset.AssetGroup.TweakedGroupKey, + ) + } + + // We first do a transfer to Charlie by itself, so we get the correct + // asset pieces that we want for the channel funding. + sendAssetsAndAssert( + ctx, t, charlieTap, charlieTap, universeTap, mintedAssets[0], + charlieFundingAmount/2, 0, 1, 0, + ) + sendAssetsAndAssert( + ctx, t, charlieTap, charlieTap, universeTap, mintedAssets[1], + charlieFundingAmount/2, 1, 2, 0, + ) + + // We need to send some assets to Erin, so he can fund an asset channel + // with Fabia. + sendAssetsAndAssert( + ctx, t, erinTap, charlieTap, universeTap, mintedAssets[0], + erinFundingAmount/2, 2, 1, charlieFundingAmount/2, + ) + sendAssetsAndAssert( + ctx, t, erinTap, charlieTap, universeTap, mintedAssets[1], + erinFundingAmount/2, 3, 2, charlieFundingAmount/2, + ) + + // Then we burn everything but a single asset piece. We do this to make + // sure that the channel funding code will select the correct asset + // UTXOs during the channel funding. Otherwise, it's super hard to + // predict what exactly goes into the channel funding transaction. And + // this way it's also easier to assert overall balances. + assetID1 := mintedAssets[0].AssetGenesis.AssetId + assetID2 := mintedAssets[1].AssetGenesis.AssetId + burnAmount1 := mintedAssets[0].Amount - charlieFundingAmount/2 - + erinFundingAmount/2 - 1 + _, err := charlieTap.BurnAsset(ctx, &taprpc.BurnAssetRequest{ + Asset: &taprpc.BurnAssetRequest_AssetId{ + AssetId: assetID1, + }, + AmountToBurn: burnAmount1, + ConfirmationText: taprootassets.AssetBurnConfirmationText, + }) + require.NoError(t.t, err) + + mineBlocks(t, net, 1, 1) + + burnAmount2 := mintedAssets[1].Amount - charlieFundingAmount/2 - + erinFundingAmount/2 - 1 + _, err = charlieTap.BurnAsset(ctx, &taprpc.BurnAssetRequest{ + Asset: &taprpc.BurnAssetRequest_AssetId{ + AssetId: assetID2, + }, + AmountToBurn: burnAmount2, + ConfirmationText: taprootassets.AssetBurnConfirmationText, + }) + require.NoError(t.t, err) + + mineBlocks(t, net, 1, 1) + + t.Logf("Opening asset channels...") + + // The first channel we create has a push amount, so Charlie can receive + // payments immediately and not run into the channel reserve issue. + fundRespCD, err := charlieTap.FundChannel( + ctx, &tchrpc.FundChannelRequest{ + AssetAmount: charlieFundingAmount, + GroupKey: groupKey, + PeerPubkey: daveTap.node.PubKey[:], + FeeRateSatPerVbyte: 5, + PushSat: pushSat, + }, + ) + require.NoError(t.t, err) + t.Logf("Funded channel between Charlie and Dave: %v", fundRespCD) + + fundRespEF, err := erinTap.FundChannel( + ctx, &tchrpc.FundChannelRequest{ + AssetAmount: erinFundingAmount, + GroupKey: groupKey, + PeerPubkey: fabiaTap.node.PubKey[:], + FeeRateSatPerVbyte: 5, + PushSat: pushSat, + }, + ) + require.NoError(t.t, err) + t.Logf("Funded channel between Erin and Fabia: %v", fundRespEF) + + // Make sure the pending channel shows up in the list and has the + // custom records set as JSON. + assertPendingChannels( + t.t, charlieTap.node, mintedAssets[0], 1, + charlieFundingAmount/2, 0, + ) + assertPendingChannels( + t.t, charlieTap.node, mintedAssets[1], 1, + charlieFundingAmount/2, 0, + ) + assertPendingChannels( + t.t, erinTap.node, mintedAssets[0], 1, erinFundingAmount/2, 0, + ) + assertPendingChannels( + t.t, erinTap.node, mintedAssets[1], 1, erinFundingAmount/2, 0, + ) + + // Now that we've looked at the pending channels, let's actually confirm + // all three of them. + mineBlocks(t, net, 6, 2) + + var id1, id2 asset.ID + copy(id1[:], assetID1) + copy(id2[:], assetID2) + + fundingTree1, err := tapscript.NewChannelFundingScriptTreeUniqueID( + id1, + ) + require.NoError(t.t, err) + fundingScriptKey1 := fundingTree1.TaprootKey + fundingScriptTreeBytes1 := fundingScriptKey1.SerializeCompressed() + + fundingTree2, err := tapscript.NewChannelFundingScriptTreeUniqueID( + id2, + ) + require.NoError(t.t, err) + fundingScriptKey2 := fundingTree2.TaprootKey + fundingScriptTreeBytes2 := fundingScriptKey2.SerializeCompressed() + + // TODO(guggero): Those asset balances should be 1, 1, 0, 0 + // respectively, but because we now have unique script keys, we need + // https://github.com/lightninglabs/taproot-assets/pull/1198 first. + assertAssetBalance(t.t, charlieTap, assetID1, 25001) + assertAssetBalance(t.t, charlieTap, assetID2, 25001) + assertAssetBalance(t.t, erinTap, assetID1, 25000) + assertAssetBalance(t.t, erinTap, assetID2, 25000) + + // There should be two asset pieces for Charlie for both asset IDs, one + // in the channel and one with a single unit from the burn. + assertNumAssetOutputs(t.t, charlieTap, assetID1, 2) + assertNumAssetOutputs(t.t, charlieTap, assetID2, 2) + assertAssetExists( + t.t, charlieTap, assetID1, charlieFundingAmount/2, + fundingScriptKey1, false, true, true, + ) + assertAssetExists( + t.t, charlieTap, assetID1, 1, nil, true, false, false, + ) + assertAssetExists( + t.t, charlieTap, assetID2, charlieFundingAmount/2, + fundingScriptKey2, false, true, true, + ) + assertAssetExists( + t.t, charlieTap, assetID2, 1, nil, true, false, false, + ) + + // Erin should just have one output for each asset ID, the one in the + // channel. + assertNumAssetOutputs(t.t, erinTap, assetID1, 1) + assertNumAssetOutputs(t.t, erinTap, assetID2, 1) + assertAssetExists( + t.t, erinTap, assetID1, erinFundingAmount/2, fundingScriptKey1, + false, true, true, + ) + assertAssetExists( + t.t, erinTap, assetID2, erinFundingAmount/2, fundingScriptKey2, + false, true, true, + ) + + // Assert that the proofs for both channels has been uploaded to the + // designated Universe server. + assertUniverseProofExists( + t.t, universeTap, assetID1, groupKey, fundingScriptTreeBytes1, + fmt.Sprintf("%v:%v", fundRespCD.Txid, fundRespCD.OutputIndex), + ) + assertUniverseProofExists( + t.t, universeTap, assetID2, groupKey, fundingScriptTreeBytes2, + fmt.Sprintf("%v:%v", fundRespCD.Txid, fundRespCD.OutputIndex), + ) + assertUniverseProofExists( + t.t, universeTap, assetID1, groupKey, fundingScriptTreeBytes1, + fmt.Sprintf("%v:%v", fundRespEF.Txid, fundRespEF.OutputIndex), + ) + assertUniverseProofExists( + t.t, universeTap, assetID2, groupKey, fundingScriptTreeBytes2, + fmt.Sprintf("%v:%v", fundRespEF.Txid, fundRespEF.OutputIndex), + ) + + // Make sure the channel shows the correct asset information. + assertAssetChan( + t.t, charlieTap.node, daveTap.node, charlieFundingAmount, + mintedAssets, + ) + assertAssetChan( + t.t, erinTap.node, fabiaTap.node, erinFundingAmount, + mintedAssets, + ) + + chanPointCD := &lnrpc.ChannelPoint{ + OutputIndex: uint32(fundRespCD.OutputIndex), + FundingTxid: &lnrpc.ChannelPoint_FundingTxidStr{ + FundingTxidStr: fundRespCD.Txid, + }, + } + chanPointEF := &lnrpc.ChannelPoint{ + OutputIndex: uint32(fundRespEF.OutputIndex), + FundingTxid: &lnrpc.ChannelPoint_FundingTxidStr{ + FundingTxidStr: fundRespEF.Txid, + }, + } + + return chanPointCD, chanPointEF +} + +// sendAssetsAndAssert sends the given amount of assets to the recipient and +// asserts that the transfer was successful. It also checks that the asset +// balance of the sender and recipient is as expected. +func sendAssetsAndAssert(ctx context.Context, t *harnessTest, + recipient, sender, universe *tapClient, mintedAsset *taprpc.Asset, + assetSendAmount uint64, idx, numTransfers int, + previousSentAmount uint64) { + + assetID := mintedAsset.AssetGenesis.AssetId + recipientAddr, err := recipient.NewAddr(ctx, &taprpc.NewAddrRequest{ + Amt: assetSendAmount, + AssetId: assetID, + ProofCourierAddr: fmt.Sprintf( + "%s://%s", proof.UniverseRpcCourierType, + universe.node.Cfg.LitAddr(), + ), + }) + require.NoError(t.t, err) + + t.Logf("Sending %v asset units to %s...", assetSendAmount, + recipient.node.Cfg.Name) + + // We assume that we sent the same size in a previous send. + totalSent := assetSendAmount + previousSentAmount + + // Send the assets to recipient. + itest.AssertAddrCreated( + t.t, recipient, mintedAsset, recipientAddr, + ) + sendResp, err := sender.SendAsset(ctx, &taprpc.SendAssetRequest{ + TapAddrs: []string{recipientAddr.Encoded}, + }) + require.NoError(t.t, err) + itest.ConfirmAndAssertOutboundTransfer( + t.t, t.lndHarness.Miner.Client, sender, sendResp, + assetID, + []uint64{mintedAsset.Amount - totalSent, assetSendAmount}, + idx, idx+1, + ) + itest.AssertNonInteractiveRecvComplete(t.t, recipient, numTransfers) +} + func assertNumAssetUTXOs(t *testing.T, tapdClient *tapClient, numUTXOs int) *taprpc.ListUtxosResponse { @@ -2168,6 +2445,24 @@ func logBalance(t *testing.T, nodes []*HarnessNode, assetID []byte, } } +func logBalanceGroup(t *testing.T, nodes []*HarnessNode, assetIDs [][]byte, + occasion string) { + + t.Helper() + + time.Sleep(time.Millisecond * 250) + + for _, node := range nodes { + local, remote, localSat, remoteSat := getAssetChannelBalance( + t, node, assetIDs, false, + ) + + t.Logf("%-7s balance: local=%-9d remote=%-9d, localSat=%-9d, "+ + "remoteSat=%-9d (%v)", node.Cfg.Name, local, remote, + localSat, remoteSat, occasion) + } +} + // readMacaroon tries to read the macaroon file at the specified path and create // gRPC dial options from it. func readMacaroon(macPath string) (grpc.DialOption, error) { diff --git a/itest/litd_custom_channels_test.go b/itest/litd_custom_channels_test.go index e610eb687..cb6d61db0 100644 --- a/itest/litd_custom_channels_test.go +++ b/itest/litd_custom_channels_test.go @@ -1139,6 +1139,237 @@ func testCustomChannelsGroupedAsset(ctx context.Context, net *NetworkHarness, assertAssetBalance(t.t, fabiaTap, assetID, fabiaAssetBalance) } +// testCustomChannelsGroupTranchesForceClose tests that we can successfully open +// a custom channel with multiple pieces of a grouped asset. We then test that +// we can successfully co-op and force close such channels and sweep the +// remaining channel balances. +func testCustomChannelsGroupTranchesForceClose(ctx context.Context, + net *NetworkHarness, t *harnessTest) { + + lndArgs := slices.Clone(lndArgsTemplate) + litdArgs := slices.Clone(litdArgsTemplate) + + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() + litdArgs = append(litdArgs, fmt.Sprintf( + "--taproot-assets.proofcourieraddr=%s://%s", + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), + )) + + // The topology we are going for looks like the following: + // + // Charlie --[assets]--> Dave --[sats]--> Erin --[assets]--> Fabia + // + // With [assets] being a custom channel and [sats] being a normal, BTC + // only channel. + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", lndArgs, false, true, charliePort, litdArgs..., + ) + require.NoError(t.t, err) + + dave, err := net.NewNode(t.t, "Dave", lndArgs, false, true, litdArgs...) + require.NoError(t.t, err) + erin, err := net.NewNode(t.t, "Erin", lndArgs, false, true, litdArgs...) + require.NoError(t.t, err) + fabia, err := net.NewNode( + t.t, "Fabia", lndArgs, false, true, litdArgs..., + ) + require.NoError(t.t, err) + + nodes := []*HarnessNode{charlie, dave, erin, fabia} + connectAllNodes(t.t, net, nodes) + fundAllNodes(t.t, net, nodes) + + // Create the normal channel between Dave and Erin. + t.Logf("Opening normal channel between Dave and Erin...") + channelOp := openChannelAndAssert( + t, net, dave, erin, lntest.OpenChannelParams{ + Amt: 5_000_000, + SatPerVByte: 5, + }, + ) + defer closeChannelAndAssert(t, net, dave, channelOp, false) + + // This is the only public channel, we need everyone to be aware of it. + assertChannelKnown(t.t, charlie, channelOp) + assertChannelKnown(t.t, fabia, channelOp) + + universeTap := newTapClient(t.t, charlie) + charlieTap := newTapClient(t.t, charlie) + daveTap := newTapClient(t.t, dave) + erinTap := newTapClient(t.t, erin) + fabiaTap := newTapClient(t.t, fabia) + + groupAssetReq := itest.CopyRequest(&mintrpc.MintAssetRequest{ + Asset: itestAsset, + }) + groupAssetReq.Asset.NewGroupedAsset = true + + // Mint the asset tranches 1 and 2 on Charlie and sync all nodes to + // Charlie as the universe. + mintedAssetsT1 := itest.MintAssetsConfirmBatch( + t.t, t.lndHarness.Miner.Client, charlieTap, + []*mintrpc.MintAssetRequest{groupAssetReq}, + ) + centsT1 := mintedAssetsT1[0] + assetID1 := centsT1.AssetGenesis.AssetId + groupKey := centsT1.GetAssetGroup().GetTweakedGroupKey() + + groupAssetReq = itest.CopyRequest(&mintrpc.MintAssetRequest{ + Asset: itestAsset, + }) + groupAssetReq.Asset.GroupedAsset = true + groupAssetReq.Asset.GroupKey = groupKey + groupAssetReq.Asset.Name = "itest-asset-cents-tranche-2" + + mintedAssetsT2 := itest.MintAssetsConfirmBatch( + t.t, t.lndHarness.Miner.Client, charlieTap, + []*mintrpc.MintAssetRequest{groupAssetReq}, + ) + centsT2 := mintedAssetsT2[0] + assetID2 := centsT2.AssetGenesis.AssetId + + t.Logf("Minted lightning cents tranche 1 (%x) and 2 (%x) for group "+ + "key %x, syncing universes...", assetID1, assetID2, groupKey) + syncUniverses(t.t, charlieTap, dave, erin, fabia) + t.Logf("Universes synced between all nodes, distributing assets...") + + chanPointCD, chanPointEF := createTestAssetNetworkGroupKey( + ctx, t, net, charlieTap, daveTap, erinTap, fabiaTap, + universeTap, []*taprpc.Asset{centsT1, centsT2}, + fundingAmount, fundingAmount, DefaultPushSat, + ) + + t.Logf("Created channels %v and %v", chanPointCD, chanPointEF) + + // We now send some assets over the channels to test the functionality. + // Print initial channel balances. + groupIDs := [][]byte{assetID1, assetID2} + logBalanceGroup(t.t, nodes, groupIDs, "initial") + + // ------------ + // Test case 1: Send a few direct keysend payments from Charlie to Dave. + // We want to send at least 30k assets, so we use up one channel + // internal tranche of assets and should at least once have an HTLC + // that transports assets from two tranches. + // ------------ + const ( + keySendAmount = 5000 + numSends = 6 + totalFirstSend = keySendAmount * numSends + ) + for i := 0; i < numSends; i++ { + sendAssetKeySendPayment( + t.t, charlie, dave, keySendAmount, nil, + fn.None[int64](), withGroupKey(groupKey), + ) + } + logBalanceGroup(t.t, nodes, groupIDs, "after keysend Charlie->Dave") + + // ------------ + // Test case 2: Send a few direct keysend payments from Erin to Fabia. + // ------------ + for i := 0; i < numSends; i++ { + sendAssetKeySendPayment( + t.t, erin, fabia, keySendAmount, nil, + fn.None[int64](), withGroupKey(groupKey), + ) + } + logBalanceGroup(t.t, nodes, groupIDs, "after keysend Erin->Fabia") + + // ------------ + // Test case 3: Co-op close the channel between Charlie and Dave. + // ------------ + t.Logf("Closing Charlie -> Dave channel") + closeAssetChannelAndAssert( + t, net, charlie, dave, chanPointCD, + [][]byte{assetID1, assetID2}, groupKey, universeTap, + // TODO(guggero): replace this with + // assertDefaultCoOpCloseBalance(true, true) once we have the + // ability to check the custom data in the closed channel list. + noOpCoOpCloseBalanceCheck, + ) + + assertSpendableBalance( + t.t, charlieTap, nil, groupKey, fundingAmount-totalFirstSend+2, + ) + assertSpendableBalance(t.t, daveTap, nil, groupKey, totalFirstSend) + + // ------------ + // Test case 4: Force close the channel between Erin and Fabia. + // ------------ + _, closeTxid, err := net.CloseChannel(erin, chanPointEF, true) + require.NoError(t.t, err) + + t.Logf("Channel force closed! Mining blocks, close_txid=%v", closeTxid) + + // Next, we'll mine a block to confirm the force close. + mineBlocks(t, net, 1, 1) + + // At this point, we should have the force close transaction in the set + // of transfers for both nodes. + forceCloseTransfer := findForceCloseTransfer( + t.t, erinTap, fabiaTap, closeTxid, + ) + t.Logf("Force close transfer: %v", toProtoJSON(t.t, forceCloseTransfer)) + + // Now that we have the transfer on disk, we'll also assert that the + // universe also has proof for both the relevant transfer outputs. + for _, transfer := range forceCloseTransfer.Transfers { + for _, transferOut := range transfer.Outputs { + assertUniverseProofExists( + t.t, universeTap, transferOut.AssetId, groupKey, + transferOut.ScriptKey, + transferOut.Anchor.Outpoint, + ) + } + } + + t.Logf("Universe proofs located!") + + // We should also have a new sweep transaction in the mempool. + fabiaSweepTxid, err := waitForNTxsInMempool( + net.Miner.Client, 1, shortTimeout, + ) + require.NoError(t.t, err) + + t.Logf("Fabia sweep txid: %v", fabiaSweepTxid) + + mineBlocks(t, net, 1, 1) + + // Fabia should have her sweep output confirmed now, and the assets + // should be back in her on-chain wallet and spendable. + assertSpendableBalance(t.t, fabiaTap, nil, groupKey, totalFirstSend) + + // Next, we'll mine three additional blocks to trigger the CSV delay + // for Erin. + mineBlocks(t, net, 4, 0) + + // We expect that Erin's sweep transaction has been broadcast. + erinSweepTxid, err := waitForNTxsInMempool( + net.Miner.Client, 1, shortTimeout, + ) + require.NoError(t.t, err) + + t.Logf("Erin sweep txid: %v", erinSweepTxid) + + // Now we'll mine a block to confirm Erin's sweep transaction. + mineBlocks(t, net, 1, 1) + + // Charlie should now have an asset transfer for his sweep transaction. + erinSweepTransfer := locateAssetTransfers( + t.t, erinTap, *erinSweepTxid[0], + ) + + t.Logf("Erin sweep transfer: %v", toProtoJSON(t.t, erinSweepTransfer)) + + assertSpendableBalance( + t.t, erinTap, nil, groupKey, fundingAmount-totalFirstSend, + ) +} + // testCustomChannelsForceClose tests a force close scenario after both parties // have an active asset balance. func testCustomChannelsForceClose(ctx context.Context, net *NetworkHarness, diff --git a/itest/litd_test_list_on_test.go b/itest/litd_test_list_on_test.go index a306794b1..afd2b2a5d 100644 --- a/itest/litd_test_list_on_test.go +++ b/itest/litd_test_list_on_test.go @@ -39,6 +39,11 @@ var allTestCases = []*testCase{ test: testCustomChannelsGroupedAsset, noAliceBob: true, }, + { + name: "custom channels group tranches force close", + test: testCustomChannelsGroupTranchesForceClose, + noAliceBob: true, + }, { name: "custom channels force close", test: testCustomChannelsForceClose, From fc35c0a662448ae6f6fe84b947b4c0be626f89a1 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 23 Apr 2025 15:46:01 +0200 Subject: [PATCH 7/8] itest: add grouped asset multi tranche HTLC test --- itest/litd_custom_channels_test.go | 309 +++++++++++++++++++++++++++++ itest/litd_test_list_on_test.go | 5 + 2 files changed, 314 insertions(+) diff --git a/itest/litd_custom_channels_test.go b/itest/litd_custom_channels_test.go index cb6d61db0..c50d91467 100644 --- a/itest/litd_custom_channels_test.go +++ b/itest/litd_custom_channels_test.go @@ -1370,6 +1370,315 @@ func testCustomChannelsGroupTranchesForceClose(ctx context.Context, ) } +// testCustomChannelsGroupTranchesHtlcForceClose tests that we can successfully +// open a custom channel with multiple pieces of a grouped asset, then force +// close it while having pending HTLCs. We then test that we can successfully +// sweep all balances from those HTLCs. +func testCustomChannelsGroupTranchesHtlcForceClose(ctx context.Context, + net *NetworkHarness, t *harnessTest) { + + lndArgs := slices.Clone(lndArgsTemplate) + litdArgs := slices.Clone(litdArgsTemplate) + + // We use Charlie as the proof courier. But in order for Charlie to also + // use itself, we need to define its port upfront. + charliePort := port.NextAvailablePort() + litdArgs = append(litdArgs, fmt.Sprintf( + "--taproot-assets.proofcourieraddr=%s://%s", + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), + )) + + // The topology we are going for looks like the following: + // + // Charlie --[assets]--> Dave --[sats]--> Erin --[assets]--> Fabia + // + // With [assets] being a custom channel and [sats] being a normal, BTC + // only channel. + charlie, err := net.NewNodeWithPort( + t.t, "Charlie", lndArgs, false, true, charliePort, litdArgs..., + ) + require.NoError(t.t, err) + + dave, err := net.NewNode(t.t, "Dave", lndArgs, false, true, litdArgs...) + require.NoError(t.t, err) + erin, err := net.NewNode(t.t, "Erin", lndArgs, false, true, litdArgs...) + require.NoError(t.t, err) + fabia, err := net.NewNode( + t.t, "Fabia", lndArgs, false, true, litdArgs..., + ) + require.NoError(t.t, err) + + nodes := []*HarnessNode{charlie, dave, erin, fabia} + connectAllNodes(t.t, net, nodes) + fundAllNodes(t.t, net, nodes) + + // Create the normal channel between Dave and Erin. + t.Logf("Opening normal channel between Dave and Erin...") + channelOp := openChannelAndAssert( + t, net, dave, erin, lntest.OpenChannelParams{ + Amt: 5_000_000, + SatPerVByte: 5, + }, + ) + defer closeChannelAndAssert(t, net, dave, channelOp, false) + + // This is the only public channel, we need everyone to be aware of it. + assertChannelKnown(t.t, charlie, channelOp) + assertChannelKnown(t.t, fabia, channelOp) + + universeTap := newTapClient(t.t, charlie) + charlieTap := newTapClient(t.t, charlie) + daveTap := newTapClient(t.t, dave) + erinTap := newTapClient(t.t, erin) + fabiaTap := newTapClient(t.t, fabia) + + groupAssetReq := itest.CopyRequest(&mintrpc.MintAssetRequest{ + Asset: itestAsset, + }) + groupAssetReq.Asset.NewGroupedAsset = true + + // Mint the asset tranches 1 and 2 on Charlie and sync all nodes to + // Charlie as the universe. + mintedAssetsT1 := itest.MintAssetsConfirmBatch( + t.t, t.lndHarness.Miner.Client, charlieTap, + []*mintrpc.MintAssetRequest{groupAssetReq}, + ) + centsT1 := mintedAssetsT1[0] + assetID1 := centsT1.AssetGenesis.AssetId + groupKey := centsT1.GetAssetGroup().GetTweakedGroupKey() + + groupAssetReq = itest.CopyRequest(&mintrpc.MintAssetRequest{ + Asset: itestAsset, + }) + groupAssetReq.Asset.GroupedAsset = true + groupAssetReq.Asset.GroupKey = groupKey + groupAssetReq.Asset.Name = "itest-asset-cents-tranche-2" + + mintedAssetsT2 := itest.MintAssetsConfirmBatch( + t.t, t.lndHarness.Miner.Client, charlieTap, + []*mintrpc.MintAssetRequest{groupAssetReq}, + ) + centsT2 := mintedAssetsT2[0] + assetID2 := centsT2.AssetGenesis.AssetId + + t.Logf("Minted lightning cents tranche 1 (%x) and 2 (%x) for group "+ + "key %x, syncing universes...", assetID1, assetID2, groupKey) + syncUniverses(t.t, charlieTap, dave, erin, fabia) + t.Logf("Universes synced between all nodes, distributing assets...") + + chanPointCD, chanPointEF := createTestAssetNetworkGroupKey( + ctx, t, net, charlieTap, daveTap, erinTap, fabiaTap, + universeTap, []*taprpc.Asset{centsT1, centsT2}, + fundingAmount, fundingAmount, DefaultPushSat, + ) + + t.Logf("Created channels %v and %v", chanPointCD, chanPointEF) + + // We now send some assets over the channels to test the functionality. + // Print initial channel balances. + groupIDs := [][]byte{assetID1, assetID2} + logBalanceGroup(t.t, nodes, groupIDs, "initial") + + // First, we'll send over some funds from Charlie to Dave, as we want + // Dave to be able to extend HTLCs in the other direction. + const ( + numPayments = 10 + keySendAmount = 2_500 + ) + for i := 0; i < numPayments; i++ { + sendAssetKeySendPayment( + t.t, charlie, dave, keySendAmount, nil, + fn.None[int64](), withGroupKey(groupKey), + ) + } + + // Now that both parties have some funds, we'll move onto the main test. + // + // We'll make 2 hodl invoice for each peer, so 4 total. From Charlie's + // PoV, he'll have 6 outgoing HTLCs, and two incoming HTLCs. + var ( + daveHodlInvoices []assetHodlInvoice + charlieHodlInvoices []assetHodlInvoice + + // The default oracle rate is 17_180 mSat/asset unit, so 10_000 + // will be equal to 171_800_000 mSat. When we use the mpp bool + // for the smallShards param of payInvoiceWithAssets, that + // means we'll split the payment into shards of 80_000_000 mSat + // max. So we'll get three shards per payment. + assetInvoiceAmt = 10_000 + assetsPerMPPShard = 4656 + ) + for i := 0; i < 2; i++ { + daveHodlInvoices = append( + daveHodlInvoices, createAssetHodlInvoice( + t.t, charlie, dave, uint64(assetInvoiceAmt), + nil, withInvGroupKey(groupKey), + ), + ) + charlieHodlInvoices = append( + charlieHodlInvoices, createAssetHodlInvoice( + t.t, dave, charlie, uint64(assetInvoiceAmt), + nil, withInvGroupKey(groupKey), + ), + ) + } + + // Now we'll have both Dave and Charlie pay each other's invoices. We + // only care that they're in flight at this point, as they won't be + // settled yet. + baseOpts := []payOpt{ + withGroupKey(groupKey), + withFailure( + lnrpc.Payment_IN_FLIGHT, + lnrpc.PaymentFailureReason_FAILURE_REASON_NONE, + ), + } + for _, charlieInvoice := range charlieHodlInvoices { + // For this direction, we also want to enforce MPP. + opts := append(slices.Clone(baseOpts), withSmallShards()) + payInvoiceWithAssets( + t.t, dave, charlie, charlieInvoice.payReq, nil, opts..., + ) + } + for _, daveInvoice := range daveHodlInvoices { + payInvoiceWithAssets( + t.t, charlie, dave, daveInvoice.payReq, nil, + baseOpts..., + ) + } + + // Make sure we can sweep all the HTLCs. + const charlieStartAmount = 2 + charlieExpectedBalance, _ := assertForceCloseSweeps( + ctx, net, t, charlie, dave, chanPointCD, charlieStartAmount, + assetInvoiceAmt, assetsPerMPPShard, nil, groupKey, + charlieHodlInvoices, daveHodlInvoices, true, + ) + + // Finally, we'll assert that Charlie's balance has been incremented by + // the timeout value. + charlieExpectedBalance += uint64(assetInvoiceAmt - 1) + t.Logf("Expecting Charlie's balance to be %d", charlieExpectedBalance) + assertSpendableBalance( + t.t, charlieTap, nil, groupKey, charlieExpectedBalance, + ) + + t.Logf("Sending all settled funds to Fabia") + + // As a final sanity check, both Charlie and Dave should be able to send + // their entire balances to Fabia, our 3rd party. + // + // We'll make two addrs for Fabia, one for Charlie, and one for Dave. + charlieSpendableBalanceAsset1, err := spendableBalance( + charlieTap, assetID1, nil, + ) + require.NoError(t.t, err) + charlieSpendableBalanceAsset2, err := spendableBalance( + charlieTap, assetID2, nil, + ) + require.NoError(t.t, err) + + t.Logf("Charlie's spendable balance asset 1: %d, asset 2: %d", + charlieSpendableBalanceAsset1, charlieSpendableBalanceAsset2) + + fabiaCourierAddr := fmt.Sprintf( + "%s://%s", proof.UniverseRpcCourierType, + fabiaTap.node.Cfg.LitAddr(), + ) + charlieAddr1, err := fabiaTap.NewAddr(ctx, &taprpc.NewAddrRequest{ + Amt: charlieSpendableBalanceAsset1, + AssetId: assetID1, + ProofCourierAddr: fabiaCourierAddr, + }) + require.NoError(t.t, err) + charlieAddr2, err := fabiaTap.NewAddr(ctx, &taprpc.NewAddrRequest{ + Amt: charlieSpendableBalanceAsset2, + AssetId: assetID2, + ProofCourierAddr: fabiaCourierAddr, + }) + require.NoError(t.t, err) + + daveSpendableBalanceAsset1, err := spendableBalance( + daveTap, assetID1, nil, + ) + require.NoError(t.t, err) + daveSpendableBalanceAsset2, err := spendableBalance( + daveTap, assetID2, nil, + ) + require.NoError(t.t, err) + + t.Logf("Daves's spendable balance asset 1: %d, asset 2: %d", + daveSpendableBalanceAsset1, daveSpendableBalanceAsset2) + + daveAddr1, err := fabiaTap.NewAddr(ctx, &taprpc.NewAddrRequest{ + Amt: daveSpendableBalanceAsset1, + AssetId: assetID1, + ProofCourierAddr: fabiaCourierAddr, + }) + require.NoError(t.t, err) + daveAddr2, err := fabiaTap.NewAddr(ctx, &taprpc.NewAddrRequest{ + Amt: daveSpendableBalanceAsset2, + AssetId: assetID2, + ProofCourierAddr: fabiaCourierAddr, + }) + require.NoError(t.t, err) + + _, err = charlieTap.SendAsset(ctx, &taprpc.SendAssetRequest{ + TapAddrs: []string{charlieAddr1.Encoded}, + }) + require.NoError(t.t, err) + mineBlocks(t, net, 1, 1) + + itest.AssertNonInteractiveRecvComplete(t.t, fabiaTap, 1) + + ctxb := context.Background() + charlieAssets, err := charlieTap.ListAssets( + ctxb, &taprpc.ListAssetRequest{ + IncludeSpent: true, + }, + ) + require.NoError(t.t, err) + charlieTransfers, err := charlieTap.ListTransfers( + ctxb, &taprpc.ListTransfersRequest{}, + ) + require.NoError(t.t, err) + + t.Logf("Charlie's assets: %v", toProtoJSON(t.t, charlieAssets)) + t.Logf("Charlie's transfers: %v", toProtoJSON(t.t, charlieTransfers)) + + _, err = charlieTap.SendAsset(ctx, &taprpc.SendAssetRequest{ + TapAddrs: []string{charlieAddr2.Encoded}, + }) + require.NoError(t.t, err) + mineBlocks(t, net, 1, 1) + + itest.AssertNonInteractiveRecvComplete(t.t, fabiaTap, 2) + + _, err = daveTap.SendAsset(ctx, &taprpc.SendAssetRequest{ + TapAddrs: []string{daveAddr1.Encoded}, + }) + require.NoError(t.t, err) + mineBlocks(t, net, 1, 1) + + itest.AssertNonInteractiveRecvComplete(t.t, fabiaTap, 3) + + _, err = daveTap.SendAsset(ctx, &taprpc.SendAssetRequest{ + TapAddrs: []string{daveAddr2.Encoded}, + }) + require.NoError(t.t, err) + mineBlocks(t, net, 1, 1) + + itest.AssertNonInteractiveRecvComplete(t.t, fabiaTap, 4) + + // Fabia's balance should now be the sum of Charlie's and Dave's + // balances. + fabiaExpectedBalance := uint64(50_002) + assertSpendableBalance( + t.t, fabiaTap, nil, groupKey, fabiaExpectedBalance, + ) +} + // testCustomChannelsForceClose tests a force close scenario after both parties // have an active asset balance. func testCustomChannelsForceClose(ctx context.Context, net *NetworkHarness, diff --git a/itest/litd_test_list_on_test.go b/itest/litd_test_list_on_test.go index afd2b2a5d..41ccc8937 100644 --- a/itest/litd_test_list_on_test.go +++ b/itest/litd_test_list_on_test.go @@ -44,6 +44,11 @@ var allTestCases = []*testCase{ test: testCustomChannelsGroupTranchesForceClose, noAliceBob: true, }, + { + name: "custom channels group tranches htlc force close", + test: testCustomChannelsGroupTranchesHtlcForceClose, + noAliceBob: true, + }, { name: "custom channels force close", test: testCustomChannelsForceClose, From 4225d65b40f409e69f2387420a951ea64eae8262 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 23 Apr 2025 16:04:50 +0200 Subject: [PATCH 8/8] docs: add release notes --- docs/release-notes/release-notes-0.14.2.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/release-notes/release-notes-0.14.2.md b/docs/release-notes/release-notes-0.14.2.md index 6e91c8932..e0cc00577 100644 --- a/docs/release-notes/release-notes-0.14.2.md +++ b/docs/release-notes/release-notes-0.14.2.md @@ -55,14 +55,23 @@ ### LND +* Updated [`lnd` to + `v0.19.0-beta.rc2`](https://github.com/lightninglabs/lightning-terminal/pull/987). + ### Loop +* Updated [Loop to + `v0.30.0-beta`](https://github.com/lightninglabs/lightning-terminal/pull/987). + ### Pool ### Faraday ### Taproot Assets +* Updated [`tapd` to + `v0.5.2`](https://github.com/lightninglabs/lightning-terminal/pull/987). + # Contributors (Alphabetical Order) * Elle Mouton