@@ -424,6 +424,112 @@ func testRfqAssetSellHtlcIntercept(t *harnessTest) {
424424 require .NoError (t .t , err )
425425}
426426
427+ // testRfqNegotiationGroupKey checks that two nodes can negotiate and register
428+ // quotes based on a specifier that only uses a group key.
429+ func testRfqNegotiationGroupKey (t * harnessTest ) {
430+ // Initialize a new test scenario.
431+ ts := newRfqTestScenario (t )
432+
433+ // Mint an asset with Alice's tapd node.
434+ rpcAssets := MintAssetsConfirmBatch (
435+ t .t , t .lndHarness .Miner ().Client , ts .AliceTapd ,
436+ []* mintrpc.MintAssetRequest {issuableAssets [0 ]},
437+ )
438+
439+ mintedAssetGroupKey := rpcAssets [0 ].AssetGroup .TweakedGroupKey
440+
441+ ctxb := context .Background ()
442+ ctxt , cancel := context .WithTimeout (ctxb , defaultWaitTimeout )
443+ defer cancel ()
444+
445+ // Subscribe to Alice's RFQ events stream.
446+ aliceEventNtfns , err := ts .AliceTapd .SubscribeRfqEventNtfns (
447+ ctxb , & rfqrpc.SubscribeRfqEventNtfnsRequest {},
448+ )
449+ require .NoError (t .t , err )
450+
451+ // Alice sends a sell order to Bob for some amount of the newly minted
452+ // asset.
453+ askAmt := uint64 (42000 )
454+ sellOrderExpiry := uint64 (time .Now ().Add (24 * time .Hour ).Unix ())
455+
456+ // We first try to add a sell order without specifying the asset skip
457+ // flag. That should result in an error, since we only have a normal
458+ // channel and not an asset channel.
459+ sellReq := & rfqrpc.AddAssetSellOrderRequest {
460+ AssetSpecifier : & rfqrpc.AssetSpecifier {
461+ Id : & rfqrpc.AssetSpecifier_GroupKey {
462+ GroupKey : mintedAssetGroupKey ,
463+ },
464+ },
465+ PaymentMaxAmt : askAmt ,
466+ Expiry : sellOrderExpiry ,
467+
468+ // Here we explicitly specify Bob as the destination
469+ // peer for the sell order. This will prompt Alice's
470+ // tapd node to send a request for quote message to
471+ // Bob's node.
472+ PeerPubKey : ts .BobLnd .PubKey [:],
473+
474+ TimeoutSeconds : uint32 (rfqTimeout .Seconds ()),
475+ }
476+ _ , err = ts .AliceTapd .AddAssetSellOrder (ctxt , sellReq )
477+ require .ErrorContains (
478+ t .t , err , "no asset channel balance found" ,
479+ )
480+
481+ // Now we set the skip flag and we shouldn't get an error anymore.
482+ sellReq .SkipAssetChannelCheck = true
483+ _ , err = ts .AliceTapd .AddAssetSellOrder (ctxt , sellReq )
484+ require .NoError (t .t , err , "unable to upsert asset sell order" )
485+
486+ // Wait until Alice receives an incoming sell quote accept message (sent
487+ // from Bob) RFQ event notification.
488+ BeforeTimeout (t .t , func () {
489+ event , err := aliceEventNtfns .Recv ()
490+ require .NoError (t .t , err )
491+
492+ _ , ok := event .Event .(* rfqrpc.RfqEvent_PeerAcceptedSellQuote )
493+ require .True (t .t , ok , "unexpected event: %v" , event )
494+ }, rfqTimeout )
495+
496+ // We now repeat the same flow, where Alice is making a BuyOrderRequest.
497+ assetMaxAmt := uint64 (1000 )
498+ buyOrderExpiry := sellOrderExpiry
499+
500+ buyReq := & rfqrpc.AddAssetBuyOrderRequest {
501+ AssetSpecifier : & rfqrpc.AssetSpecifier {
502+ Id : & rfqrpc.AssetSpecifier_GroupKey {
503+ GroupKey : mintedAssetGroupKey ,
504+ },
505+ },
506+ AssetMaxAmt : assetMaxAmt ,
507+ Expiry : buyOrderExpiry ,
508+ PeerPubKey : ts .BobLnd .PubKey [:],
509+ TimeoutSeconds : uint32 (rfqTimeout .Seconds ()),
510+ }
511+
512+ _ , err = ts .AliceTapd .AddAssetBuyOrder (ctxt , buyReq )
513+ require .ErrorContains (
514+ t .t , err , "no asset channel balance found" ,
515+ )
516+
517+ // Now we set the skip flag and we shouldn't get an error anymore.
518+ buyReq .SkipAssetChannelCheck = true
519+ _ , err = ts .AliceTapd .AddAssetBuyOrder (ctxt , buyReq )
520+ require .NoError (t .t , err )
521+
522+ // Wait until Alice receives an incoming buy quote accept message (sent
523+ // from Bob) RFQ event notification.
524+ BeforeTimeout (t .t , func () {
525+ event , err := aliceEventNtfns .Recv ()
526+ require .NoError (t .t , err )
527+
528+ _ , ok := event .Event .(* rfqrpc.RfqEvent_PeerAcceptedBuyQuote )
529+ require .True (t .t , ok , "unexpected event: %v" , event )
530+ }, rfqTimeout )
531+ }
532+
427533// rfqTestScenario is a struct which holds test scenario helper infra.
428534type rfqTestScenario struct {
429535 testHarness * harnessTest
0 commit comments