@@ -38,6 +38,7 @@ import (
38
38
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
39
39
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
40
40
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
41
+ "github.com/lightningnetwork/lnd/lntest"
41
42
"github.com/lightningnetwork/lnd/lntest/rpc"
42
43
"github.com/lightningnetwork/lnd/lntest/wait"
43
44
"github.com/lightningnetwork/lnd/lntypes"
@@ -66,6 +67,260 @@ var (
66
67
failureNone = lnrpc .PaymentFailureReason_FAILURE_REASON_NONE
67
68
)
68
69
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
+
69
324
// createTestAssetNetwork sends asset funds from Charlie to Dave and Erin, so
70
325
// they can fund asset channels with Yara and Fabia, respectively. So the asset
71
326
// channels created are Charlie->Dave, Dave->Yara, Erin->Fabia. The channels
0 commit comments