Skip to content

Multi rfq send itest #1097

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ GO_VERSION = 1.23.9
# installed before running the integration tests which include backward
# compatibility tests. The list of versions must be in sync with any version
# used in the backwardCompat map in itest/litd_test_list_on_test.go.
LITD_COMPAT_VERSIONS = v0.14.1-alpha
LITD_COMPAT_VERSIONS = v0.14.1-alpha v0.15.0-alpha

LOOP_COMMIT := $(shell cat go.mod | \
grep $(LOOP_PKG) | \
Expand Down
10 changes: 4 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,18 @@ require (
github.com/lightninglabs/lightning-terminal/litrpc v1.0.2
github.com/lightninglabs/lightning-terminal/perms v1.0.1
github.com/lightninglabs/lndclient v0.19.0-12
github.com/lightninglabs/loop v0.31.2-beta.0.20250724051925-36c5d6cee7d3
github.com/lightninglabs/loop v0.31.2-beta.0.20250730111713-3b0f6e84dc14
github.com/lightninglabs/loop/looprpc v1.0.8
github.com/lightninglabs/loop/swapserverrpc v1.0.15
github.com/lightninglabs/pool v0.6.6-beta
github.com/lightninglabs/pool/auctioneerrpc v1.1.3
github.com/lightninglabs/pool/poolrpc v1.0.1
github.com/lightninglabs/taproot-assets v0.6.1
github.com/lightninglabs/taproot-assets/taprpc v1.0.10-0.20250725201706-0e736bb0d7b1
github.com/lightninglabs/taproot-assets v0.6.1-0.20250729190616-3f323918a96e
github.com/lightninglabs/taproot-assets/taprpc v1.0.10-0.20250729190616-3f323918a96e
github.com/lightningnetwork/lnd v0.19.2-beta
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 v1.2.5
github.com/lightningnetwork/lnd/fn/v2 v2.0.8
github.com/lightningnetwork/lnd/kvdb v1.4.16
github.com/lightningnetwork/lnd/sqldb v1.0.11-0.20250623231731-45c15646c68b
Expand Down Expand Up @@ -247,5 +247,3 @@ replace google.golang.org/protobuf => github.com/lightninglabs/protobuf-go-hex-d
// 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

replace github.com/lightninglabs/taproot-assets => github.com/lightninglabs/taproot-assets v0.6.1-0.20250725201706-0e736bb0d7b1
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1152,8 +1152,8 @@ github.com/lightninglabs/lightning-node-connect/mailbox v1.0.1 h1:RWmohykp3n/DTM
github.com/lightninglabs/lightning-node-connect/mailbox v1.0.1/go.mod h1:NYtNexZE9gO1eOeegTxmIW9fqanl7eZ9cOrE9yewSAk=
github.com/lightninglabs/lndclient v0.19.0-12 h1:aSIKfnvnHKiyFWppUGHJG5fn8VoF5WG5Lx958ksLmqs=
github.com/lightninglabs/lndclient v0.19.0-12/go.mod h1:cicoJY1AwZuRVXGD8Knp50TRT7TGBmw1k37uPQsGQiw=
github.com/lightninglabs/loop v0.31.2-beta.0.20250724051925-36c5d6cee7d3 h1:N+p1pdmVG9STFZ0PIc+qzJ2einOyNzFy6KEjPmT7HAY=
github.com/lightninglabs/loop v0.31.2-beta.0.20250724051925-36c5d6cee7d3/go.mod h1:7DLIgIyg0Z9uQAjEUr0qNQ3v3IFPCDsJxIttHOxhcvA=
github.com/lightninglabs/loop v0.31.2-beta.0.20250730111713-3b0f6e84dc14 h1:PA/bTHYZ/leIoky3mFgbD4h9FV1lamzS+bw45GLd4l8=
github.com/lightninglabs/loop v0.31.2-beta.0.20250730111713-3b0f6e84dc14/go.mod h1:ukAfrXOf5OqpJORSYLjsyFzOGgIASyC2Qbstsl0ZvWw=
github.com/lightninglabs/loop/looprpc v1.0.8 h1:OFmJNLjem6fLuH1YUO+3G6QA1wmjAd0zyhvdHONOBDs=
github.com/lightninglabs/loop/looprpc v1.0.8/go.mod h1:c7WykKQZ3PspCMVvv2kr9o4l3bgJBEBVv0SOoBOjPOw=
github.com/lightninglabs/loop/swapserverrpc v1.0.15 h1:vEZBF65Lv0T7MPydCRxHSIlEJzHBkZ4I8FtSD6OJK88=
Expand All @@ -1172,10 +1172,10 @@ github.com/lightninglabs/pool/poolrpc v1.0.1 h1:XbNx28TYwEj/PVsnnF9TnveVCMCYfS1v
github.com/lightninglabs/pool/poolrpc v1.0.1/go.mod h1:836icifg/SBnZbiae0v3jeRRzCrT6LWo32SqCS/JiGk=
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.6.1-0.20250725201706-0e736bb0d7b1 h1:lm/PJTGwTPk7kRInRdGiUbaKqpNQlV+9OTXpBUiFb9E=
github.com/lightninglabs/taproot-assets v0.6.1-0.20250725201706-0e736bb0d7b1/go.mod h1:mIgx0p/GkMZeEjEm91vYQsH41YQBAgJl7TP6JcT+wgs=
github.com/lightninglabs/taproot-assets/taprpc v1.0.10-0.20250725201706-0e736bb0d7b1 h1:V9x07euSL3wR9JxPUOhrsAVmC6pg4o3y7N5GsHLVoP0=
github.com/lightninglabs/taproot-assets/taprpc v1.0.10-0.20250725201706-0e736bb0d7b1/go.mod h1:c8gTEcKEUoUPVChgZNwqTL1hss7UWa5FDeObr8WBzQk=
github.com/lightninglabs/taproot-assets v0.6.1-0.20250729190616-3f323918a96e h1:wlaM8dTlpCQ0uNj0TBskBDeNTTDessxiXiakYDB4RFo=
github.com/lightninglabs/taproot-assets v0.6.1-0.20250729190616-3f323918a96e/go.mod h1:mIgx0p/GkMZeEjEm91vYQsH41YQBAgJl7TP6JcT+wgs=
github.com/lightninglabs/taproot-assets/taprpc v1.0.10-0.20250729190616-3f323918a96e h1:MnXspinwkd6VhV8G9I+TdSak05UitfYyNBCQhTIIr0g=
github.com/lightninglabs/taproot-assets/taprpc v1.0.10-0.20250729190616-3f323918a96e/go.mod h1:c8gTEcKEUoUPVChgZNwqTL1hss7UWa5FDeObr8WBzQk=
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.2-beta h1:3SKVrKYFY4IJLlrMf7cDzZcBeT+MxjI9Xy6YpY+EEX4=
Expand All @@ -1184,8 +1184,8 @@ github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf
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=
github.com/lightningnetwork/lnd/clock v1.1.1/go.mod h1:mGnAhPyjYZQJmebS7aevElXKTFDuO+uNFFfMXK1W8xQ=
github.com/lightningnetwork/lnd/fn v1.2.3 h1:Q1OrgNSgQynVheBNa16CsKVov1JI5N2AR6G07x9Mles=
github.com/lightningnetwork/lnd/fn v1.2.3/go.mod h1:SyFohpVrARPKH3XVAJZlXdVe+IwMYc4OMAvrDY32kw0=
github.com/lightningnetwork/lnd/fn v1.2.5 h1:pGMz0BDUxrhvOtShD4FIysdVy+ulfFAnFvTKjZO5Pp8=
github.com/lightningnetwork/lnd/fn v1.2.5/go.mod h1:SyFohpVrARPKH3XVAJZlXdVe+IwMYc4OMAvrDY32kw0=
github.com/lightningnetwork/lnd/fn/v2 v2.0.8 h1:r2SLz7gZYQPVc3IZhU82M66guz3Zk2oY+Rlj9QN5S3g=
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=
Expand Down
144 changes: 99 additions & 45 deletions itest/assets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,25 +77,32 @@ type itestNode struct {
// multiRfqNodes contains all the itest nodes that are required to set up the
// multi RFQ network topology.
type multiRfqNodes struct {
charlie, dave, erin, fabia, yara itestNode
universeTap *tapClient
charlie, dave, erin, fabia, yara, george itestNode
universeTap *tapClient
}

// createTestMultiRFQAssetNetwork creates a lightning network topology which
// consists of both bitcoin and asset channels. It focuses on the property of
// having multiple channels available on both the sender and receiver side.
// The George node is using a way different oracle that provides asset rates
// that fall outside of the configured tolerance bounds, leading to RFQ
// negotiation failures.
//
// The topology we are going for looks like the following:
//
// /---[sats]--> Erin --[assets]--\
// / \
// / \
// /---[sats]--> Erin --[assets]--\
// / \
// / \
// / \
//
// Charlie -----[sats]--> Dave --[assets]---->Fabia
// Charlie -----[sats]--> Dave --[assets]--> Fabia
//
// \ /
// \ /
// \---[sats]--> Yara --[assets]--/
// \ / /
// \ / /
// \---[sats]--> Yara --[assets]-----/ /
// \ /
// \ /
// \--[sats]-> George --[assets]-/
func createTestMultiRFQAssetNetwork(t *harnessTest, net *NetworkHarness,
nodes multiRfqNodes, mintedAsset *taprpc.Asset, assetSendAmount,
fundingAmount uint64, pushSat int64) (*lnrpc.ChannelPoint,
Expand All @@ -106,6 +113,7 @@ func createTestMultiRFQAssetNetwork(t *harnessTest, net *NetworkHarness,
erin, erinTap := nodes.erin.Lnd, nodes.erin.Tapd
_, fabiaTap := nodes.fabia.Lnd, nodes.fabia.Tapd
yara, yaraTap := nodes.yara.Lnd, nodes.yara.Tapd
george, georgeTap := nodes.george.Lnd, nodes.george.Tapd
universeTap := nodes.universeTap

// Let's open the normal sats channels between Charlie and the routing
Expand All @@ -131,6 +139,13 @@ func createTestMultiRFQAssetNetwork(t *harnessTest, net *NetworkHarness,
},
)

_ = openChannelAndAssert(
t, net, charlie, george, lntest.OpenChannelParams{
Amt: 10_000_000,
SatPerVByte: 5,
},
)

ctxb := context.Background()
assetID := mintedAsset.AssetGenesis.AssetId
var groupKey []byte
Expand Down Expand Up @@ -225,6 +240,34 @@ func createTestMultiRFQAssetNetwork(t *harnessTest, net *NetworkHarness,
)
itest.AssertNonInteractiveRecvComplete(t.t, yaraTap, 1)

// We need to send some assets to George, so he can fund an asset
// channel with Fabia.
georgeAddr, err := georgeTap.NewAddr(ctxb, &taprpc.NewAddrRequest{
Amt: assetSendAmount,
AssetId: assetID,
ProofCourierAddr: fmt.Sprintf(
"%s://%s", proof.UniverseRpcCourierType,
charlieTap.node.Cfg.LitAddr(),
),
})
require.NoError(t.t, err)

t.Logf("Sending %v asset units to George...", assetSendAmount)

// Send the assets to George.
itest.AssertAddrCreated(t.t, georgeTap, mintedAsset, georgeAddr)
sendResp, err = charlieTap.SendAsset(ctxb, &taprpc.SendAssetRequest{
TapAddrs: []string{georgeAddr.Encoded},
})
require.NoError(t.t, err)
itest.ConfirmAndAssertOutboundTransfer(
t.t, t.lndHarness.Miner.Client, charlieTap, sendResp, assetID,
[]uint64{
mintedAsset.Amount - 4*assetSendAmount, assetSendAmount,
}, 3, 4,
)
itest.AssertNonInteractiveRecvComplete(t.t, georgeTap, 1)

// We fund the Dave->Fabia channel.
fundRespDF, err := daveTap.FundChannel(
ctxb, &tchrpc.FundChannelRequest{
Expand Down Expand Up @@ -264,6 +307,19 @@ func createTestMultiRFQAssetNetwork(t *harnessTest, net *NetworkHarness,
require.NoError(t.t, err)
t.Logf("Funded channel between Yara and Fabia: %v", fundRespYF)

// We fund the George->Fabia channel.
fundRespGF, err := georgeTap.FundChannel(
ctxb, &tchrpc.FundChannelRequest{
AssetAmount: fundingAmount,
AssetId: assetID,
PeerPubkey: fabiaTap.node.PubKey[:],
FeeRateSatPerVbyte: 5,
PushSat: pushSat,
},
)
require.NoError(t.t, err)
t.Logf("Funded channel between George and Fabia: %v", fundRespGF)

// Make sure the pending channel shows up in the list and has the
// custom records set as JSON.
assertPendingChannels(
Expand All @@ -275,17 +331,21 @@ func createTestMultiRFQAssetNetwork(t *harnessTest, net *NetworkHarness,
assertPendingChannels(
t.t, yaraTap.node, mintedAsset, 1, fundingAmount, 0,
)
assertPendingChannels(
t.t, georgeTap.node, mintedAsset, 1, fundingAmount, 0,
)

// Now that we've looked at the pending channels, let's actually confirm
// all three of them.
mineBlocks(t, net, 6, 3)
mineBlocks(t, net, 6, 4)

// We'll be tracking the expected asset balances throughout the test, so
// we can assert it after each action.
charlieAssetBalance := mintedAsset.Amount - 3*assetSendAmount
charlieAssetBalance := mintedAsset.Amount - 4*assetSendAmount
daveAssetBalance := assetSendAmount - fundingAmount
erinAssetBalance := assetSendAmount - fundingAmount
yaraAssetBalance := assetSendAmount - fundingAmount
georgeAssetBalance := assetSendAmount - fundingAmount

itest.AssertBalances(
t.t, charlieTap, charlieAssetBalance,
Expand All @@ -304,6 +364,10 @@ func createTestMultiRFQAssetNetwork(t *harnessTest, net *NetworkHarness,
t.t, yaraTap, yaraAssetBalance, itest.WithAssetID(assetID),
)

itest.AssertBalances(
t.t, georgeTap, georgeAssetBalance, itest.WithAssetID(assetID),
)

// Assert that the proofs for both channels has been uploaded to the
// designated Universe server.
assertUniverseProofExists(
Expand All @@ -318,6 +382,10 @@ func createTestMultiRFQAssetNetwork(t *harnessTest, net *NetworkHarness,
t.t, universeTap, assetID, groupKey, fundingScriptTreeBytes,
fmt.Sprintf("%v:%v", fundRespYF.Txid, fundRespYF.OutputIndex),
)
assertUniverseProofExists(
t.t, universeTap, assetID, groupKey, fundingScriptTreeBytes,
fmt.Sprintf("%v:%v", fundRespGF.Txid, fundRespGF.OutputIndex),
)

return nil, nil, nil
}
Expand Down Expand Up @@ -1355,7 +1423,7 @@ func sendAssetKeySendPayment(t *testing.T, src, dst *HarnessNode, amt uint64,
return
}

result, err := getAssetPaymentResult(stream, false)
result, _, err := getAssetPaymentResult(t, stream, false)
require.NoError(t, err)
if result.Status == lnrpc.Payment_FAILED {
t.Logf("Failure reason: %v", result.FailureReason)
Expand Down Expand Up @@ -1687,14 +1755,18 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode,
sendReq.MaxShardSizeMsat = 80_000_000
}

var rfqBytes []byte
var rfqBytes, peerPubKey []byte
cfg.rfq.WhenSome(func(i rfqmsg.ID) {
rfqBytes = make([]byte, len(i[:]))
copy(rfqBytes, i[:])
})

if rfqPeer != nil {
peerPubKey = rfqPeer.PubKey[:]
}

request := &tchrpc.SendPaymentRequest{
PeerPubkey: rfqPeer.PubKey[:],
PeerPubkey: peerPubKey,
PaymentRequest: sendReq,
RfqId: rfqBytes,
AllowOverpay: cfg.allowOverpay,
Expand All @@ -1715,7 +1787,13 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode,
// was established, no network or auth error), we expect the error to be
// returned on the first read on the stream.
if cfg.errSubStr != "" {
_, err := stream.Recv()
msg, err := stream.Recv()

// On errors we still get an empty set of RFQs as a response.
if msg.GetAcceptedSellOrders() != nil {
_, err = stream.Recv()
}

require.ErrorContains(t, err, cfg.errSubStr)

return 0, rfqmath.BigIntFixedPoint{}
Expand All @@ -1725,42 +1803,18 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode,
numUnits uint64
rateVal rfqmath.FixedPoint[rfqmath.BigInt]
)
if cfg.rfq.IsNone() {
// We want to receive the accepted quote message first, so we
// know how many assets we're going to pay.
quoteMsg, err := stream.Recv()
require.NoError(t, err)
acceptedQuote := quoteMsg.GetAcceptedSellOrder()
require.NotNil(t, acceptedQuote)

peerPubKey := acceptedQuote.Peer
require.Equal(t, peerPubKey, rfqPeer.PubKeyStr)

rpcRate := acceptedQuote.BidAssetRate
rate, err := rpcutils.UnmarshalRfqFixedPoint(rpcRate)
require.NoError(t, err)

rateVal = *rate

t.Logf("Got quote for %v asset units per BTC", rate)

amountMsat := lnwire.MilliSatoshi(decodedInvoice.NumMsat)
milliSatsFP := rfqmath.MilliSatoshiToUnits(amountMsat, *rate)
numUnits = milliSatsFP.ScaleTo(0).ToUint64()
msatPerUnit := float64(decodedInvoice.NumMsat) /
float64(numUnits)
t.Logf("Got quote for %v asset units at %3f msat/unit from "+
"peer %s with SCID %d", numUnits, msatPerUnit,
peerPubKey, acceptedQuote.Scid)
}

result, err := getAssetPaymentResult(
stream, cfg.payStatus == lnrpc.Payment_IN_FLIGHT,
result, rateVal, err := getAssetPaymentResult(
t, stream, cfg.payStatus == lnrpc.Payment_IN_FLIGHT,
)
require.NoError(t, err)
require.Equal(t, cfg.payStatus, result.Status)
require.Equal(t, cfg.failureReason, result.FailureReason)

amountMsat := lnwire.MilliSatoshi(decodedInvoice.NumMsat)
milliSatsFP := rfqmath.MilliSatoshiToUnits(amountMsat, rateVal)
numUnits = milliSatsFP.ScaleTo(0).ToUint64()

return numUnits, rateVal
}

Expand Down
Loading