@@ -38,6 +38,7 @@ import (
3838 "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
3939 "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
4040 "github.com/lightningnetwork/lnd/lnrpc/walletrpc"
41+ "github.com/lightningnetwork/lnd/lntest"
4142 "github.com/lightningnetwork/lnd/lntest/rpc"
4243 "github.com/lightningnetwork/lnd/lntest/wait"
4344 "github.com/lightningnetwork/lnd/lntypes"
@@ -66,6 +67,242 @@ var (
6667 failureNone = lnrpc .PaymentFailureReason_FAILURE_REASON_NONE
6768)
6869
70+ // createTestMultiRFQAssetNetwork creates a lightning network topology which
71+ // consists of both bitcoin and asset channels. It focuses on the property of
72+ // having multiple channels available on both the sender and receiver side.
73+ //
74+ // The topology we are going for looks like the following:
75+ //
76+ // /---[sats]--> Erin --[assets]--\
77+ // / \
78+ // / \
79+ //
80+ // Charlie -----[sats]--> Dave --[assets]---->Fabia
81+ //
82+ // \ /
83+ // \ /
84+ // \---[sats]--> Yara --[assets]--/
85+ func createTestMultiRFQAssetNetwork (t * harnessTest , net * NetworkHarness ,
86+ charlie , dave , erin , fabia , yara * HarnessNode , charlieTap , daveTap ,
87+ erinTap , fabiaTap , yaraTap , universeTap * tapClient ,
88+ mintedAsset * taprpc.Asset , assetSendAmount , fundingAmount uint64 ,
89+ pushSat int64 ) (* lnrpc.ChannelPoint , * lnrpc.ChannelPoint ,
90+ * lnrpc.ChannelPoint ) {
91+
92+ // Let's open the normal sats channels between Charlie and the routing
93+ // peers.
94+ _ = openChannelAndAssert (
95+ t , net , charlie , erin , lntest.OpenChannelParams {
96+ Amt : 10_000_000 ,
97+ SatPerVByte : 5 ,
98+ },
99+ )
100+
101+ _ = openChannelAndAssert (
102+ t , net , charlie , dave , lntest.OpenChannelParams {
103+ Amt : 10_000_000 ,
104+ SatPerVByte : 5 ,
105+ },
106+ )
107+
108+ _ = openChannelAndAssert (
109+ t , net , charlie , yara , lntest.OpenChannelParams {
110+ Amt : 10_000_000 ,
111+ SatPerVByte : 5 ,
112+ },
113+ )
114+
115+ ctxb := context .Background ()
116+ assetID := mintedAsset .AssetGenesis .AssetId
117+ var groupKey []byte
118+ if mintedAsset .AssetGroup != nil {
119+ groupKey = mintedAsset .AssetGroup .TweakedGroupKey
120+ }
121+
122+ fundingScriptTree := tapscript .NewChannelFundingScriptTree ()
123+ fundingScriptKey := fundingScriptTree .TaprootKey
124+ fundingScriptTreeBytes := fundingScriptKey .SerializeCompressed ()
125+
126+ // We need to send some assets to Dave, so he can fund an asset channel
127+ // with Fabia.
128+ daveAddr , err := daveTap .NewAddr (ctxb , & taprpc.NewAddrRequest {
129+ Amt : assetSendAmount ,
130+ AssetId : assetID ,
131+ ProofCourierAddr : fmt .Sprintf (
132+ "%s://%s" , proof .UniverseRpcCourierType ,
133+ charlieTap .node .Cfg .LitAddr (),
134+ ),
135+ })
136+ require .NoError (t .t , err )
137+
138+ t .Logf ("Sending %v asset units to Dave..." , assetSendAmount )
139+
140+ // Send the assets to Dave.
141+ itest .AssertAddrCreated (t .t , daveTap , mintedAsset , daveAddr )
142+ sendResp , err := charlieTap .SendAsset (ctxb , & taprpc.SendAssetRequest {
143+ TapAddrs : []string {daveAddr .Encoded },
144+ })
145+ require .NoError (t .t , err )
146+ itest .ConfirmAndAssertOutboundTransfer (
147+ t .t , t .lndHarness .Miner .Client , charlieTap , sendResp , assetID ,
148+ []uint64 {mintedAsset .Amount - assetSendAmount , assetSendAmount },
149+ 0 , 1 ,
150+ )
151+ itest .AssertNonInteractiveRecvComplete (t .t , daveTap , 1 )
152+
153+ // We need to send some assets to Erin, so he can fund an asset channel
154+ // with Fabia.
155+ erinAddr , err := erinTap .NewAddr (ctxb , & taprpc.NewAddrRequest {
156+ Amt : assetSendAmount ,
157+ AssetId : assetID ,
158+ ProofCourierAddr : fmt .Sprintf (
159+ "%s://%s" , proof .UniverseRpcCourierType ,
160+ charlieTap .node .Cfg .LitAddr (),
161+ ),
162+ })
163+ require .NoError (t .t , err )
164+
165+ t .Logf ("Sending %v asset units to Erin..." , assetSendAmount )
166+
167+ // Send the assets to Erin.
168+ itest .AssertAddrCreated (t .t , erinTap , mintedAsset , erinAddr )
169+ sendResp , err = charlieTap .SendAsset (ctxb , & taprpc.SendAssetRequest {
170+ TapAddrs : []string {erinAddr .Encoded },
171+ })
172+ require .NoError (t .t , err )
173+ itest .ConfirmAndAssertOutboundTransfer (
174+ t .t , t .lndHarness .Miner .Client , charlieTap , sendResp , assetID ,
175+ []uint64 {
176+ mintedAsset .Amount - 2 * assetSendAmount , assetSendAmount ,
177+ }, 1 , 2 ,
178+ )
179+ itest .AssertNonInteractiveRecvComplete (t .t , erinTap , 1 )
180+
181+ // We need to send some assets to Yara, so he can fund an asset channel
182+ // with Fabia.
183+ yaraAddr , err := yaraTap .NewAddr (ctxb , & taprpc.NewAddrRequest {
184+ Amt : assetSendAmount ,
185+ AssetId : assetID ,
186+ ProofCourierAddr : fmt .Sprintf (
187+ "%s://%s" , proof .UniverseRpcCourierType ,
188+ charlieTap .node .Cfg .LitAddr (),
189+ ),
190+ })
191+ require .NoError (t .t , err )
192+
193+ t .Logf ("Sending %v asset units to Yara..." , assetSendAmount )
194+
195+ // Send the assets to Yara.
196+ itest .AssertAddrCreated (t .t , yaraTap , mintedAsset , yaraAddr )
197+ sendResp , err = charlieTap .SendAsset (ctxb , & taprpc.SendAssetRequest {
198+ TapAddrs : []string {yaraAddr .Encoded },
199+ })
200+ require .NoError (t .t , err )
201+ itest .ConfirmAndAssertOutboundTransfer (
202+ t .t , t .lndHarness .Miner .Client , charlieTap , sendResp , assetID ,
203+ []uint64 {
204+ mintedAsset .Amount - 3 * assetSendAmount , assetSendAmount ,
205+ }, 2 , 3 ,
206+ )
207+ itest .AssertNonInteractiveRecvComplete (t .t , yaraTap , 1 )
208+
209+ // We fund the Dave->Fabia channel.
210+ fundRespDF , err := daveTap .FundChannel (
211+ ctxb , & tchrpc.FundChannelRequest {
212+ AssetAmount : fundingAmount ,
213+ AssetId : assetID ,
214+ PeerPubkey : fabiaTap .node .PubKey [:],
215+ FeeRateSatPerVbyte : 5 ,
216+ PushSat : pushSat ,
217+ },
218+ )
219+ require .NoError (t .t , err )
220+ t .Logf ("Funded channel between Dave and Fabia: %v" , fundRespDF )
221+
222+ // We fund the Erin->Fabia channel.
223+ fundRespEF , err := erinTap .FundChannel (
224+ ctxb , & tchrpc.FundChannelRequest {
225+ AssetAmount : fundingAmount ,
226+ AssetId : assetID ,
227+ PeerPubkey : fabiaTap .node .PubKey [:],
228+ FeeRateSatPerVbyte : 5 ,
229+ PushSat : pushSat ,
230+ },
231+ )
232+ require .NoError (t .t , err )
233+ t .Logf ("Funded channel between Erin and Fabia: %v" , fundRespEF )
234+
235+ // We fund the Yara->Fabia channel.
236+ fundRespYF , err := yaraTap .FundChannel (
237+ ctxb , & tchrpc.FundChannelRequest {
238+ AssetAmount : fundingAmount ,
239+ AssetId : assetID ,
240+ PeerPubkey : fabiaTap .node .PubKey [:],
241+ FeeRateSatPerVbyte : 5 ,
242+ PushSat : pushSat ,
243+ },
244+ )
245+ require .NoError (t .t , err )
246+ t .Logf ("Funded channel between Yara and Fabia: %v" , fundRespYF )
247+
248+ // Make sure the pending channel shows up in the list and has the
249+ // custom records set as JSON.
250+ assertPendingChannels (
251+ t .t , daveTap .node , mintedAsset , 1 , fundingAmount , 0 ,
252+ )
253+ assertPendingChannels (
254+ t .t , erinTap .node , mintedAsset , 1 , fundingAmount , 0 ,
255+ )
256+ assertPendingChannels (
257+ t .t , yaraTap .node , mintedAsset , 1 , fundingAmount , 0 ,
258+ )
259+
260+ // Now that we've looked at the pending channels, let's actually confirm
261+ // all three of them.
262+ mineBlocks (t , net , 6 , 3 )
263+
264+ // We'll be tracking the expected asset balances throughout the test, so
265+ // we can assert it after each action.
266+ charlieAssetBalance := mintedAsset .Amount - 3 * assetSendAmount
267+ daveAssetBalance := assetSendAmount - fundingAmount
268+ erinAssetBalance := assetSendAmount - fundingAmount
269+ yaraAssetBalance := assetSendAmount - fundingAmount
270+
271+ itest .AssertBalances (
272+ t .t , charlieTap , charlieAssetBalance ,
273+ itest .WithAssetID (assetID ), itest .WithNumUtxos (1 ),
274+ )
275+
276+ itest .AssertBalances (
277+ t .t , daveTap , daveAssetBalance , itest .WithAssetID (assetID ),
278+ )
279+
280+ itest .AssertBalances (
281+ t .t , erinTap , erinAssetBalance , itest .WithAssetID (assetID ),
282+ )
283+
284+ itest .AssertBalances (
285+ t .t , yaraTap , yaraAssetBalance , itest .WithAssetID (assetID ),
286+ )
287+
288+ // Assert that the proofs for both channels has been uploaded to the
289+ // designated Universe server.
290+ assertUniverseProofExists (
291+ t .t , universeTap , assetID , groupKey , fundingScriptTreeBytes ,
292+ fmt .Sprintf ("%v:%v" , fundRespDF .Txid , fundRespDF .OutputIndex ),
293+ )
294+ assertUniverseProofExists (
295+ t .t , universeTap , assetID , groupKey , fundingScriptTreeBytes ,
296+ fmt .Sprintf ("%v:%v" , fundRespEF .Txid , fundRespEF .OutputIndex ),
297+ )
298+ assertUniverseProofExists (
299+ t .t , universeTap , assetID , groupKey , fundingScriptTreeBytes ,
300+ fmt .Sprintf ("%v:%v" , fundRespYF .Txid , fundRespYF .OutputIndex ),
301+ )
302+
303+ return nil , nil , nil
304+ }
305+
69306// createTestAssetNetwork sends asset funds from Charlie to Dave and Erin, so
70307// they can fund asset channels with Yara and Fabia, respectively. So the asset
71308// channels created are Charlie->Dave, Dave->Yara, Erin->Fabia. The channels
0 commit comments