diff --git a/Makefile b/Makefile index 31ee0b937..1df75054c 100644 --- a/Makefile +++ b/Makefile @@ -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) | \ diff --git a/app/src/types/generated/lnd_pb.d.ts b/app/src/types/generated/lnd_pb.d.ts index d57424a6f..3eb8e8451 100644 --- a/app/src/types/generated/lnd_pb.d.ts +++ b/app/src/types/generated/lnd_pb.d.ts @@ -3752,11 +3752,6 @@ export class ChannelEventUpdate extends jspb.Message { getFullyResolvedChannel(): ChannelPoint | undefined; setFullyResolvedChannel(value?: ChannelPoint): void; - hasChannelFundingTimeout(): boolean; - clearChannelFundingTimeout(): void; - getChannelFundingTimeout(): ChannelPoint | undefined; - setChannelFundingTimeout(value?: ChannelPoint): void; - getType(): ChannelEventUpdate.UpdateTypeMap[keyof ChannelEventUpdate.UpdateTypeMap]; setType(value: ChannelEventUpdate.UpdateTypeMap[keyof ChannelEventUpdate.UpdateTypeMap]): void; @@ -3779,7 +3774,6 @@ export namespace ChannelEventUpdate { inactiveChannel?: ChannelPoint.AsObject, pendingOpenChannel?: PendingUpdate.AsObject, fullyResolvedChannel?: ChannelPoint.AsObject, - channelFundingTimeout?: ChannelPoint.AsObject, type: ChannelEventUpdate.UpdateTypeMap[keyof ChannelEventUpdate.UpdateTypeMap], } @@ -3790,7 +3784,6 @@ export namespace ChannelEventUpdate { INACTIVE_CHANNEL: 3; PENDING_OPEN_CHANNEL: 4; FULLY_RESOLVED_CHANNEL: 5; - CHANNEL_FUNDING_TIMEOUT: 6; } export const UpdateType: UpdateTypeMap; @@ -3803,7 +3796,6 @@ export namespace ChannelEventUpdate { INACTIVE_CHANNEL = 4, PENDING_OPEN_CHANNEL = 6, FULLY_RESOLVED_CHANNEL = 7, - CHANNEL_FUNDING_TIMEOUT = 8, } } diff --git a/app/src/types/generated/lnd_pb.js b/app/src/types/generated/lnd_pb.js index 2be008d1f..8e68a0521 100644 --- a/app/src/types/generated/lnd_pb.js +++ b/app/src/types/generated/lnd_pb.js @@ -31214,7 +31214,7 @@ proto.lnrpc.ChannelEventSubscription.serializeBinaryToWriter = function(message, * @private {!Array>} * @const */ -proto.lnrpc.ChannelEventUpdate.oneofGroups_ = [[1,2,3,4,6,7,8]]; +proto.lnrpc.ChannelEventUpdate.oneofGroups_ = [[1,2,3,4,6,7]]; /** * @enum {number} @@ -31226,8 +31226,7 @@ proto.lnrpc.ChannelEventUpdate.ChannelCase = { ACTIVE_CHANNEL: 3, INACTIVE_CHANNEL: 4, PENDING_OPEN_CHANNEL: 6, - FULLY_RESOLVED_CHANNEL: 7, - CHANNEL_FUNDING_TIMEOUT: 8 + FULLY_RESOLVED_CHANNEL: 7 }; /** @@ -31274,7 +31273,6 @@ proto.lnrpc.ChannelEventUpdate.toObject = function(includeInstance, msg) { inactiveChannel: (f = msg.getInactiveChannel()) && proto.lnrpc.ChannelPoint.toObject(includeInstance, f), pendingOpenChannel: (f = msg.getPendingOpenChannel()) && proto.lnrpc.PendingUpdate.toObject(includeInstance, f), fullyResolvedChannel: (f = msg.getFullyResolvedChannel()) && proto.lnrpc.ChannelPoint.toObject(includeInstance, f), - channelFundingTimeout: (f = msg.getChannelFundingTimeout()) && proto.lnrpc.ChannelPoint.toObject(includeInstance, f), type: jspb.Message.getFieldWithDefault(msg, 5, 0) }; @@ -31342,11 +31340,6 @@ proto.lnrpc.ChannelEventUpdate.deserializeBinaryFromReader = function(msg, reade reader.readMessage(value,proto.lnrpc.ChannelPoint.deserializeBinaryFromReader); msg.setFullyResolvedChannel(value); break; - case 8: - var value = new proto.lnrpc.ChannelPoint; - reader.readMessage(value,proto.lnrpc.ChannelPoint.deserializeBinaryFromReader); - msg.setChannelFundingTimeout(value); - break; case 5: var value = /** @type {!proto.lnrpc.ChannelEventUpdate.UpdateType} */ (reader.readEnum()); msg.setType(value); @@ -31428,14 +31421,6 @@ proto.lnrpc.ChannelEventUpdate.serializeBinaryToWriter = function(message, write proto.lnrpc.ChannelPoint.serializeBinaryToWriter ); } - f = message.getChannelFundingTimeout(); - if (f != null) { - writer.writeMessage( - 8, - f, - proto.lnrpc.ChannelPoint.serializeBinaryToWriter - ); - } f = message.getType(); if (f !== 0.0) { writer.writeEnum( @@ -31455,8 +31440,7 @@ proto.lnrpc.ChannelEventUpdate.UpdateType = { ACTIVE_CHANNEL: 2, INACTIVE_CHANNEL: 3, PENDING_OPEN_CHANNEL: 4, - FULLY_RESOLVED_CHANNEL: 5, - CHANNEL_FUNDING_TIMEOUT: 6 + FULLY_RESOLVED_CHANNEL: 5 }; /** @@ -31681,43 +31665,6 @@ proto.lnrpc.ChannelEventUpdate.prototype.hasFullyResolvedChannel = function() { }; -/** - * optional ChannelPoint channel_funding_timeout = 8; - * @return {?proto.lnrpc.ChannelPoint} - */ -proto.lnrpc.ChannelEventUpdate.prototype.getChannelFundingTimeout = function() { - return /** @type{?proto.lnrpc.ChannelPoint} */ ( - jspb.Message.getWrapperField(this, proto.lnrpc.ChannelPoint, 8)); -}; - - -/** - * @param {?proto.lnrpc.ChannelPoint|undefined} value - * @return {!proto.lnrpc.ChannelEventUpdate} returns this -*/ -proto.lnrpc.ChannelEventUpdate.prototype.setChannelFundingTimeout = function(value) { - return jspb.Message.setOneofWrapperField(this, 8, proto.lnrpc.ChannelEventUpdate.oneofGroups_[0], value); -}; - - -/** - * Clears the message field making it undefined. - * @return {!proto.lnrpc.ChannelEventUpdate} returns this - */ -proto.lnrpc.ChannelEventUpdate.prototype.clearChannelFundingTimeout = function() { - return this.setChannelFundingTimeout(undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.lnrpc.ChannelEventUpdate.prototype.hasChannelFundingTimeout = function() { - return jspb.Message.getField(this, 8) != null; -}; - - /** * optional UpdateType type = 5; * @return {!proto.lnrpc.ChannelEventUpdate.UpdateType} diff --git a/dev.Dockerfile b/dev.Dockerfile index a67686c86..2ab4253fa 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -30,13 +30,35 @@ COPY --from=nodejsbuilder /go/src/github.com/lightninglabs/lightning-terminal /g # queries required to connect to linked containers succeed. ENV GODEBUG netdns=cgo -# Allow forcing a specific lnd, taproot-assets, and taprpc version through a -# build argument. +# Allow forcing a specific lnd, taproot-assets, taprpc, and/or loop repo so that +# commits referenced by LND_VERSION, TAPROOT_ASSETS_VERSION, TAPRPC_VERSION, and +# LOOP_VERSION don't have to exist in the default repository. If any of these +# build arguments are not defined, the build continues using the default +# repository for that module. NOTE: If these arguments ARE defined then the +# corresponding `_VERSION` argument MUST also be defined, otherwise the build +# continues using the default repository defined for that module. +ARG LND_REPO +ARG TAPROOT_ASSETS_REPO +ARG TAPRPC_REPO +ARG LOOP_REPO + +# Allow forcing a specific lnd, taproot-assets, taprpc, and/or loop version +# through a build argument. # Please see https://go.dev/ref/mod#version-queries for the types of # queries that can be used to define a version. +# If any of these build arguments are not defined then build uses the version +# already defined in go.mod and go.sum for that module. +# Note: If the corresponding `_REPO` argument is not defined, `go get` will +# be used along with `go mod tidy`, which sometimes may change the version you +# are trying to use because some other module requires the same requirement +# but of a different version. A trick to overcome this is to also use the +# `_REPO` argument and just put in the default repository for that module and +# that will cause a `go mod edit -replace=` to be used instead which won't have +# this issue. ARG LND_VERSION ARG TAPROOT_ASSETS_VERSION ARG TAPRPC_VERSION +ARG LOOP_VERSION # Need to restate this since running in a new container from above. ARG NO_UI @@ -46,17 +68,42 @@ RUN apk add --no-cache --update alpine-sdk make \ && cd /go/src/github.com/lightninglabs/lightning-terminal \ # If a custom lnd version is supplied, force it now. && if [ -n "$LND_VERSION" ]; then \ - go get -v github.com/lightningnetwork/lnd@$LND_VERSION \ + # If a custom lnd repo is supplied, force it now. + if [ -n "$LND_REPO" ]; then \ + go mod edit -replace=github.com/lightningnetwork/lnd=$LND_REPO@$LND_VERSION; \ + else \ + go get -v github.com/lightningnetwork/lnd@$LND_VERSION; \ + fi \ && go mod tidy; \ fi \ # If a custom taproot-assets version is supplied, force it now. && if [ -n "$TAPROOT_ASSETS_VERSION" ]; then \ - go get -v github.com/lightninglabs/taproot-assets@$TAPROOT_ASSETS_VERSION \ + # If a custom taproot-assets repo is supplied, force it now. + if [ -n "$TAPROOT_ASSETS_REPO" ]; then \ + go mod edit -replace=github.com/lightninglabs/taproot-assets=$TAPROOT_ASSETS_REPO@$TAPROOT_ASSETS_VERSION; \ + else \ + go get -v github.com/lightninglabs/taproot-assets@$TAPROOT_ASSETS_VERSION; \ + fi \ && go mod tidy; \ fi \ # If a custom taprpc version is supplied, force it now. && if [ -n "$TAPRPC_VERSION" ]; then \ - go get -v github.com/lightninglabs/taproot-assets/taprpc@$TAPRPC_VERSION \ + # If a custom taprpc repo is supplied, force it now. + if [ -n "$TAPRPC_REPO" ]; then \ + go mod edit -replace=github.com/lightninglabs/taproot-assets/taprpc=$TAPRPC_REPO@$TAPRPC_VERSION; \ + else \ + go get -v github.com/lightninglabs/taproot-assets/taprpc@$TAPRPC_VERSION; \ + fi \ + && go mod tidy; \ + fi \ + # If a custom loop version is supplied, force it now. + && if [ -n "$LOOP_VERSION" ]; then \ + # If a custom loop repo is supplied, force it now. + if [ -n "$LOOP_REPO" ]; then \ + go mod edit -replace=github.com/lightninglabs/loop=$LOOP_REPO@$LOOP_VERSION; \ + else \ + go get -v github.com/lightninglabs/loop@$LOOP_VERSION; \ + fi \ && go mod tidy; \ fi \ && if [ "$NO_UI" -eq "1" ]; then \ diff --git a/go.mod b/go.mod index 65f98cb62..5ff0354d5 100644 --- a/go.mod +++ b/go.mod @@ -26,15 +26,15 @@ 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 + github.com/lightninglabs/loop v0.31.2-beta.0.20250722125130-549098452c84 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.8-0.20250716163904-2ef55ba74036 - github.com/lightningnetwork/lnd v0.19.2-beta + github.com/lightninglabs/taproot-assets v0.6.1-0.20250728103203-0466670b5bee + github.com/lightninglabs/taproot-assets/taprpc v1.0.10-0.20250728103203-0466670b5bee + github.com/lightningnetwork/lnd v0.19.2-beta.rc2 github.com/lightningnetwork/lnd/cert v1.2.2 github.com/lightningnetwork/lnd/clock v1.1.1 github.com/lightningnetwork/lnd/fn v1.2.3 @@ -247,7 +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 - -// tapd wants v0.19.0-12, but loop can't handle that yet. So we'll just use the -// previous version for now. -replace github.com/lightninglabs/lndclient => github.com/lightninglabs/lndclient v0.19.0-11 diff --git a/go.sum b/go.sum index 4e5da46b6..00f176cd1 100644 --- a/go.sum +++ b/go.sum @@ -1150,10 +1150,10 @@ github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.3 h1:NuDp6Z+QNM github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.3/go.mod h1:bDnEKRN1u13NFBuy/C+bFLhxA5bfd3clT25y76QY0AM= github.com/lightninglabs/lightning-node-connect/mailbox v1.0.1 h1:RWmohykp3n/DTMWY8b18RNTEcLDf+KT/AZHKYdOObkM= github.com/lightninglabs/lightning-node-connect/mailbox v1.0.1/go.mod h1:NYtNexZE9gO1eOeegTxmIW9fqanl7eZ9cOrE9yewSAk= -github.com/lightninglabs/lndclient v0.19.0-11 h1:/WwowlNff19lb7DXzq3c6L4nRMvwBZjbjLOy1/u4a5Y= -github.com/lightninglabs/lndclient v0.19.0-11/go.mod h1:cicoJY1AwZuRVXGD8Knp50TRT7TGBmw1k37uPQsGQiw= -github.com/lightninglabs/loop v0.31.2-beta h1:lm5t5FqDpSfQCxoz/vTvXpylxSgU+gvJJIbfJiKeyUk= -github.com/lightninglabs/loop v0.31.2-beta/go.mod h1:xnPKuZmLusNERwzz15RZ7mpQ8xuSqqh3g8Qw/PRyiRE= +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.20250722125130-549098452c84 h1:0Mbi0MvoGTdQZptqweH0032wp6O//pYnthFlHUeEHBc= +github.com/lightninglabs/loop v0.31.2-beta.0.20250722125130-549098452c84/go.mod h1:mpfTqwnzKnzeO1hDlpKNElMorCG9jECizcIPbMNBm9g= 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= @@ -1172,14 +1172,14 @@ 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 h1:98XCk7nvAridyE67uct0NDVpyY1evpIdvPQpeNElskM= -github.com/lightninglabs/taproot-assets v0.6.1/go.mod h1:rF+GwuUVuDVUejAHsUCml4Nru9xnl7A4YZQfR4qLMzY= -github.com/lightninglabs/taproot-assets/taprpc v1.0.8-0.20250716163904-2ef55ba74036 h1:ZUQrEmdZa72RieBKI/NiUQJGAnh6R6Pq6FoXh+VCOJQ= -github.com/lightninglabs/taproot-assets/taprpc v1.0.8-0.20250716163904-2ef55ba74036/go.mod h1:vOM2Ap2wYhEZjiJU7bNNg+e5tDxkvRAuyXwf/KQ4tgo= +github.com/lightninglabs/taproot-assets v0.6.1-0.20250728103203-0466670b5bee h1:rkJSpdyQPxk2VRnZivOi+8IAKHGCEIxEozSejXR2zLc= +github.com/lightninglabs/taproot-assets v0.6.1-0.20250728103203-0466670b5bee/go.mod h1:mIgx0p/GkMZeEjEm91vYQsH41YQBAgJl7TP6JcT+wgs= +github.com/lightninglabs/taproot-assets/taprpc v1.0.10-0.20250728103203-0466670b5bee h1:sxUKvVWVA/scfXn81ga2PKAI1QaJNWHElgthkVGnsjg= +github.com/lightninglabs/taproot-assets/taprpc v1.0.10-0.20250728103203-0466670b5bee/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= -github.com/lightningnetwork/lnd v0.19.2-beta/go.mod h1:+yKUfIGKKYRHGewgzQ6xi0S26DIfBiMv1zCdB3m6YxA= +github.com/lightningnetwork/lnd v0.19.2-beta.rc2 h1:vPIMjQr8SWZJHn/j3QSFct4AbCVa0WA7k0j0lHqFDAA= +github.com/lightningnetwork/lnd v0.19.2-beta.rc2/go.mod h1:+yKUfIGKKYRHGewgzQ6xi0S26DIfBiMv1zCdB3m6YxA= github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf0d0Uy4qBjI= 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= diff --git a/itest/assets_test.go b/itest/assets_test.go index 1fb456dac..600c247c3 100644 --- a/itest/assets_test.go +++ b/itest/assets_test.go @@ -27,6 +27,7 @@ import ( "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/taprpc" "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc" + "github.com/lightninglabs/taproot-assets/taprpc/authmailboxrpc" "github.com/lightninglabs/taproot-assets/taprpc/mintrpc" "github.com/lightninglabs/taproot-assets/taprpc/rfqrpc" tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc" @@ -76,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, @@ -105,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 @@ -130,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 @@ -224,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{ @@ -263,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( @@ -274,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, @@ -303,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( @@ -317,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 } @@ -1686,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, @@ -1726,31 +1799,46 @@ func payInvoiceWithAssets(t *testing.T, payer, rfqPeer *HarnessNode, ) if cfg.rfq.IsNone() { // We want to receive the accepted quote message first, so we - // know how many assets we're going to pay. + // know how many assets we're going to pay. Even after the + // multi-rfq update this is still returned to maintain backwards + // compatibility, so let's read it. 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) + // We now want to read the array of quotes that was established + // for this payment. + quotesMsg, err := stream.Recv() require.NoError(t, err) + acceptedQuotes := quotesMsg.GetAcceptedSellOrders() + require.NotNil(t, acceptedQuotes) - rateVal = *rate + acceptedRFQs := acceptedQuotes.AcceptedSellOrders - t.Logf("Got quote for %v asset units per BTC", rate) + // Let's log some info on each accepted quote. + for _, quote := range acceptedRFQs { + rpcRate := quote.BidAssetRate + rate, err := rpcutils.UnmarshalRfqFixedPoint(rpcRate) + require.NoError(t, err) - 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) + rateVal = *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, quote.Scid) + } } result, err := getAssetPaymentResult( @@ -2548,6 +2636,7 @@ type tapClient struct { rfqrpc.RfqClient tchrpc.TaprootAssetChannelsClient universerpc.UniverseClient + authmailboxrpc.MailboxClient } func newTapClient(t *testing.T, node *HarnessNode) *tapClient { @@ -2578,6 +2667,7 @@ func newTapClient(t *testing.T, node *HarnessNode) *tapClient { rfqClient := rfqrpc.NewRfqClient(rawConn) tchClient := tchrpc.NewTaprootAssetChannelsClient(rawConn) universeClient := universerpc.NewUniverseClient(rawConn) + mboxClient := authmailboxrpc.NewMailboxClient(rawConn) return &tapClient{ node: node, @@ -2588,6 +2678,7 @@ func newTapClient(t *testing.T, node *HarnessNode) *tapClient { RfqClient: rfqClient, TaprootAssetChannelsClient: tchClient, UniverseClient: universeClient, + MailboxClient: mboxClient, } } diff --git a/itest/litd_custom_channels_test.go b/itest/litd_custom_channels_test.go index 7a23314fb..d431cd848 100644 --- a/itest/litd_custom_channels_test.go +++ b/itest/litd_custom_channels_test.go @@ -94,6 +94,16 @@ var ( "not_use_on_mainnet", "--taproot-assets.experimental.rfq.mockoracleassetsperbtc=" + "5820600", + "--taproot-assets.experimental.rfq.acceptpricedeviationppm=50000", + }...) + + litdArgsTemplateDiffOracle = append(litdArgsTemplateNoOracle, []string{ + "--taproot-assets.experimental.rfq.priceoracleaddress=" + + "use_mock_price_oracle_service_promise_to_" + + "not_use_on_mainnet", + "--taproot-assets.experimental.rfq.mockoracleassetsperbtc=" + + "8820600", + "--taproot-assets.experimental.rfq.acceptpricedeviationppm=50000", }...) ) @@ -2971,14 +2981,16 @@ func testCustomChannelsLiquidityEdgeCasesGroup(ctx context.Context, testCustomChannelsLiquidityEdgeCasesCore(ctx, net, t, true) } -// testCustomChannelsMultiRFQReceive tests that a node creating an invoice with -// multiple RFQ quotes can actually guide the payer into using multiple private -// taproot asset channels to pay the invoice. -func testCustomChannelsMultiRFQReceive(ctx context.Context, net *NetworkHarness, +// testCustomChannelsMultiRFQ tests that sending and receiving payments works +// when using the multi-rfq features of tapd. This means that liquidity across +// multiple channels and peers can be used to send out a payment, or receive to +// an invoice. +func testCustomChannelsMultiRFQ(ctx context.Context, net *NetworkHarness, t *harnessTest) { lndArgs := slices.Clone(lndArgsTemplate) litdArgs := slices.Clone(litdArgsTemplate) + litdArgsDiffOracle := slices.Clone(litdArgsTemplateDiffOracle) charlie, err := net.NewNode( t.t, "Charlie", lndArgs, false, true, litdArgs..., @@ -2990,6 +3002,11 @@ func testCustomChannelsMultiRFQReceive(ctx context.Context, net *NetworkHarness, proof.UniverseRpcCourierType, charlie.Cfg.LitAddr(), )) + litdArgsDiffOracle = append(litdArgsDiffOracle, fmt.Sprintf( + "--taproot-assets.proofcourieraddr=%s://%s", + proof.UniverseRpcCourierType, charlie.Cfg.LitAddr(), + )) + dave, err := net.NewNode(t.t, "Dave", lndArgs, false, true, litdArgs...) require.NoError(t.t, err) erin, err := net.NewNode(t.t, "Erin", lndArgs, false, true, litdArgs...) @@ -3002,8 +3019,12 @@ func testCustomChannelsMultiRFQReceive(ctx context.Context, net *NetworkHarness, t.t, "Yara", lndArgs, false, true, litdArgs..., ) require.NoError(t.t, err) + george, err := net.NewNode( + t.t, "George", lndArgs, false, true, litdArgsDiffOracle..., + ) + require.NoError(t.t, err) - nodes := []*HarnessNode{charlie, dave, erin, fabia, yara} + nodes := []*HarnessNode{charlie, dave, erin, fabia, yara, george} connectAllNodes(t.t, net, nodes) fundAllNodes(t.t, net, nodes) @@ -3013,6 +3034,7 @@ func testCustomChannelsMultiRFQReceive(ctx context.Context, net *NetworkHarness, erinTap := newTapClient(t.t, erin) fabiaTap := newTapClient(t.t, fabia) yaraTap := newTapClient(t.t, yara) + georgeTap := newTapClient(t.t, george) assetReq := itest.CopyRequest(&mintrpc.MintAssetRequest{ Asset: itestAsset, @@ -3030,7 +3052,7 @@ func testCustomChannelsMultiRFQReceive(ctx context.Context, net *NetworkHarness, assetID := cents.AssetGenesis.AssetId groupID := cents.GetAssetGroup().GetTweakedGroupKey() - syncUniverses(t.t, charlieTap, dave, erin, fabia, yara) + syncUniverses(t.t, charlieTap, dave, erin, fabia, yara, george) multiRfqNodes := multiRfqNodes{ charlie: itestNode{ @@ -3053,6 +3075,10 @@ func testCustomChannelsMultiRFQReceive(ctx context.Context, net *NetworkHarness, Lnd: yara, Tapd: yaraTap, }, + george: itestNode{ + Lnd: george, + Tapd: georgeTap, + }, universeTap: charlieTap, } @@ -3068,7 +3094,6 @@ func testCustomChannelsMultiRFQReceive(ctx context.Context, net *NetworkHarness, t.t, charlie, &lnrpc.AddInvoiceResponse{ PaymentRequest: hodlInv.payReq, }, - withGroupKey(groupID), withFailure(lnrpc.Payment_IN_FLIGHT, failureNone), ) @@ -3100,13 +3125,88 @@ func testCustomChannelsMultiRFQReceive(ctx context.Context, net *NetworkHarness, // Now let's create a normal invoice that will be settled once all the // HTLCs have been received. This is only possible because the payer // uses multiple bolt11 hop hints to reach the destination. - invoiceResp := createAssetInvoice(t.t, nil, fabia, 15_000, assetID) + invoiceResp := createAssetInvoice( + t.t, nil, fabia, 15_000, nil, withInvGroupKey(groupID), + ) payInvoiceWithSatoshi( - t.t, charlie, invoiceResp, withGroupKey(groupID), + t.t, charlie, invoiceResp, ) logBalance(t.t, nodes, assetID, "after multi-rfq receive") + + // Now we'll test that sending with multiple rfq quotes works. + + // Let's start by providing some liquidity to Charlie's peers, in order + // for them to be able to push some amount if Fabia picks them as part + // of the route. + sendKeySendPayment(t.t, charlie, erin, 800_000) + sendKeySendPayment(t.t, charlie, dave, 800_000) + sendKeySendPayment(t.t, charlie, yara, 800_000) + + // Let's ask for the rough equivalent of ~15k assets. Fabia, who's going + // to pay the invoice, only has parts of assets that are less than 10k + // in channels with one of the 3 intermediate peers. The only way to + // pay this invoice is by splitting the payment across multiple peers by + // using multiple RFQ quotes. + invAmt := int64(15_000 * 17) + + iResp, err := charlie.AddHoldInvoice( + ctx, &invoicesrpc.AddHoldInvoiceRequest{ + Memo: "", + Value: invAmt, + Hash: payHash[:], + }, + ) + require.NoError(t.t, err) + + payReq := iResp.PaymentRequest + + payInvoiceWithAssets( + t.t, fabia, nil, payReq, assetID, + withFailure(lnrpc.Payment_IN_FLIGHT, failureNone), + ) + + assertMinNumHtlcs(t.t, charlie, 2) + assertMinNumHtlcs(t.t, fabia, 2) + + logBalance(t.t, nodes, assetID, "multi-rfq send in-flight") + + _, err = charlie.SettleInvoice(ctx, &invoicesrpc.SettleInvoiceMsg{ + Preimage: hodlInv.preimage[:], + }) + require.NoError(t.t, err) + + assertNumHtlcs(t.t, charlie, 0) + assertNumHtlcs(t.t, fabia, 0) + + logBalance(t.t, nodes, assetID, "after multi-rfq send") + + // Let's make another round-trip involving multi-rfq functionality. + // Let's have Fabia receive another large payment and send it back + // again, this time with a greater amount. + invoiceResp = createAssetInvoice(t.t, nil, fabia, 25_000, assetID) + + payInvoiceWithSatoshi( + t.t, charlie, invoiceResp, + ) + + logBalance(t.t, nodes, assetID, "after multi-rfq receive (2nd)") + + // Let's bump up the invoice amount a bit, to roughly ~22k assets. + invAmt = 22_000 * 17 + inv, err := charlie.AddInvoice(ctx, &lnrpc.Invoice{ + Value: invAmt, + }) + require.NoError(t.t, err) + + payReq = inv.PaymentRequest + + payInvoiceWithAssets( + t.t, fabia, nil, payReq, nil, withGroupKey(groupID), + ) + + logBalance(t.t, nodes, assetID, "after multi-rfq send (2nd)") } // testCustomChannelsStrictForwarding is a test that tests the strict forwarding diff --git a/itest/litd_test_list_on_test.go b/itest/litd_test_list_on_test.go index 36685b0ab..9d5e325ec 100644 --- a/itest/litd_test_list_on_test.go +++ b/itest/litd_test_list_on_test.go @@ -130,8 +130,11 @@ var allTestCases = []*testCase{ }, { name: "custom channels multi rfq", - test: testCustomChannelsMultiRFQReceive, + test: testCustomChannelsMultiRFQ, noAliceBob: true, + backwardCompat: map[string]string{ + "Yara": "v0.15.0-alpha", + }, }, { name: "custom channels multi channel pathfinding", diff --git a/proto/lnd.proto b/proto/lnd.proto index 03c4b9ab5..255b20288 100644 --- a/proto/lnd.proto +++ b/proto/lnd.proto @@ -2980,7 +2980,6 @@ message ChannelEventUpdate { ChannelPoint inactive_channel = 4; PendingUpdate pending_open_channel = 6; ChannelPoint fully_resolved_channel = 7; - ChannelPoint channel_funding_timeout = 8; } enum UpdateType { @@ -2990,7 +2989,6 @@ message ChannelEventUpdate { INACTIVE_CHANNEL = 3; PENDING_OPEN_CHANNEL = 4; FULLY_RESOLVED_CHANNEL = 5; - CHANNEL_FUNDING_TIMEOUT = 6; } UpdateType type = 5; diff --git a/subservers/taproot-assets.go b/subservers/taproot-assets.go index 26f0a8c3a..45334619f 100644 --- a/subservers/taproot-assets.go +++ b/subservers/taproot-assets.go @@ -12,11 +12,6 @@ import ( "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/tapcfg" "github.com/lightninglabs/taproot-assets/taprpc" - "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc" - "github.com/lightninglabs/taproot-assets/taprpc/mintrpc" - "github.com/lightninglabs/taproot-assets/taprpc/rfqrpc" - tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc" - "github.com/lightninglabs/taproot-assets/taprpc/universerpc" "github.com/lightningnetwork/lnd/lnrpc" "google.golang.org/grpc" "gopkg.in/macaroon-bakery.v2/bakery" @@ -57,7 +52,7 @@ func NewTaprootAssetsSubServer(network string, cfg *tapcfg.Config, chainCfg := address.ParamsForChain(network) return &taprootAssetsSubServer{ - Server: tap.NewServer(&chainCfg, nil), + Server: tap.NewServer(&chainCfg), cfg: cfg, remoteCfg: remoteCfg, remote: remote, @@ -126,12 +121,7 @@ func (t *taprootAssetsSubServer) Start(_ lnrpc.LightningClient, func (t *taprootAssetsSubServer) RegisterGrpcService( registrar grpc.ServiceRegistrar) { - taprpc.RegisterTaprootAssetsServer(registrar, t) - mintrpc.RegisterMintServer(registrar, t) - assetwalletrpc.RegisterAssetWalletServer(registrar, t) - rfqrpc.RegisterRfqServer(registrar, t) - tchrpc.RegisterTaprootAssetChannelsServer(registrar, t) - universerpc.RegisterUniverseServer(registrar, t) + _ = t.Server.RegisterWithGrpcServer(registrar) } // RegisterRestService registers the sub-server's REST handlers with the given @@ -142,58 +132,7 @@ func (t *taprootAssetsSubServer) RegisterRestService(ctx context.Context, mux *restProxy.ServeMux, endpoint string, dialOpts []grpc.DialOption) error { - err := taprpc.RegisterTaprootAssetsHandlerFromEndpoint( - ctx, mux, endpoint, dialOpts, - ) - if err != nil { - return err - } - - err = mintrpc.RegisterMintHandlerFromEndpoint( - ctx, mux, endpoint, dialOpts, - ) - if err != nil { - return err - } - - err = assetwalletrpc.RegisterAssetWalletHandlerFromEndpoint( - ctx, mux, endpoint, dialOpts, - ) - if err != nil { - return err - } - - err = rfqrpc.RegisterRfqHandlerFromEndpoint( - ctx, mux, endpoint, dialOpts, - ) - if err != nil { - return err - } - - err = tchrpc.RegisterTaprootAssetChannelsHandlerFromEndpoint( - ctx, mux, endpoint, dialOpts, - ) - if err != nil { - return err - } - - err = rfqrpc.RegisterRfqHandlerFromEndpoint( - ctx, mux, endpoint, dialOpts, - ) - if err != nil { - return err - } - - err = tchrpc.RegisterTaprootAssetChannelsHandlerFromEndpoint( - ctx, mux, endpoint, dialOpts, - ) - if err != nil { - return err - } - - err = universerpc.RegisterUniverseHandlerFromEndpoint( - ctx, mux, endpoint, dialOpts, - ) + err := t.Server.RegisterWithRestProxy(ctx, mux, dialOpts, endpoint) if err != nil { return err }