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