From 94f0f6cabfb6a49054d13ba179439924306c3103 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Wed, 16 Sep 2026 15:36:27 +0200 Subject: [PATCH 1/8] build: Update Taproot Assets to v0.8.3 Align taprpc and LND with the Taproot Assets v0.8.3 dependency graph. Raise the minimum Go build version to 1.25.13 and refresh module sums for both the main and client RPC modules. Include the dependency metadata needed by the shared asset packages added next. --- docs/release-notes/release-notes-next.md | 3 + go.mod | 14 +- go.sum | 1101 +++++++++++++++++++++- looprpc/go.mod | 8 +- looprpc/go.sum | 12 +- 5 files changed, 1112 insertions(+), 26 deletions(-) diff --git a/docs/release-notes/release-notes-next.md b/docs/release-notes/release-notes-next.md index 34fd3d750..206d69bc7 100644 --- a/docs/release-notes/release-notes-next.md +++ b/docs/release-notes/release-notes-next.md @@ -40,6 +40,9 @@ #### Maintenance +* Update Taproot Assets to v0.8.3, taprpc to v1.3.3, and the LND dependency to + v0.21.3-beta. The minimum Go build version is now 1.25.13. + * The Docker image build now verifies that every platform of the image index holds binaries for the architecture it advertises, and gives a release its tag only once that check has passed. diff --git a/go.mod b/go.mod index 322497f84..7ccdbd790 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/btcsuite/btcd/btcutil/psbt v1.1.10 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/btcsuite/btclog/v2 v2.0.1-0.20250728225537-6090e87c6c5b - github.com/btcsuite/btcwallet v0.16.18 + github.com/btcsuite/btcwallet v0.16.19 github.com/btcsuite/btcwallet/wtxmgr v1.5.6 github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 @@ -22,15 +22,15 @@ require ( github.com/lightninglabs/lndclient v0.21.0-2 github.com/lightninglabs/loop/looprpc v1.0.7 github.com/lightninglabs/loop/swapserverrpc v1.0.14 - github.com/lightninglabs/taproot-assets v0.8.1 - github.com/lightninglabs/taproot-assets/taprpc v1.2.0 - github.com/lightningnetwork/lnd v0.21.0-beta + github.com/lightninglabs/taproot-assets v0.8.3 + github.com/lightninglabs/taproot-assets/taprpc v1.3.3 + github.com/lightningnetwork/lnd v0.21.3-beta github.com/lightningnetwork/lnd/cert v1.2.2 github.com/lightningnetwork/lnd/clock v1.1.1 github.com/lightningnetwork/lnd/queue v1.2.0 github.com/lightningnetwork/lnd/ticker v1.1.1 github.com/lightningnetwork/lnd/tlv v1.3.2 - github.com/lightningnetwork/lnd/tor v1.1.6 + github.com/lightningnetwork/lnd/tor v1.1.7 github.com/ory/dockertest/v3 v3.10.0 github.com/stretchr/testify v1.11.1 github.com/urfave/cli-docs/v3 v3.1.1-0.20251020101624-bec07369b4f6 @@ -44,6 +44,8 @@ require ( modernc.org/sqlite v1.34.5 ) +require github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.4-0.20250610182311-2f1d46ef18b7 // indirect + require ( dario.cat/mergo v1.0.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect @@ -230,7 +232,7 @@ replace go.augendre.info/arangolint => github.com/Crocmagnon/arangolint v0.4.0 replace go.augendre.info/fatcontext => github.com/Crocmagnon/fatcontext v0.9.0 -go 1.25.12 +go 1.25.13 // This sqldb revision contains lnd's pgx v5 migration, which removes the // vulnerable legacy pgx v4/pgproto3 dependency chain. Remove this replacement diff --git a/go.sum b/go.sum index bd17f2c30..418087406 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -13,30 +14,594 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= +cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= +cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= +cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= +cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= +cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= +cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= +cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= +cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= +cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= +cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= +cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= +cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= +cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= +cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= +cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= +cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= +cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= +cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= +cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= +cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= +cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= +cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= +cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= +cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= +cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= +cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= +cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= +cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= +cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= +cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= +cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= +cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= +cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= +cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= +cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= +cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= +cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= +cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= +cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= +cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= +cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= +cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= +cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= +cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= +cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= +cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= +cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= +cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= +cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= +cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= +cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= +cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= +cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= +cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= +cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= +cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= +cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= +cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= +cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= +cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= +cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= +cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= +cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= +cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= +cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= +cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= +cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= +cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= +cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= +cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e h1:n+DcnTNkQnHlwpsrHoQtkrJIO7CBx029fw6oR4vIob4= @@ -45,24 +610,34 @@ github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82 h1:MG93+PZYs9 github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82/go.mod h1:GbuBk21JqF+driLX3XtJYNZjGa45YDoa9IqCTzNSfEc= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok= github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= +github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= @@ -92,8 +667,8 @@ github.com/btcsuite/btclog v1.0.0/go.mod h1:w7xnGOhwT3lmrS4H3b/D1XAXxvh+tbhUm8xe github.com/btcsuite/btclog/v2 v2.0.1-0.20250728225537-6090e87c6c5b h1:MQ+Q6sDy37V1wP1Yu79A5KqJutolqUGwA99UZWQDWZM= github.com/btcsuite/btclog/v2 v2.0.1-0.20250728225537-6090e87c6c5b/go.mod h1:XItGUfVOxotJL8kkuk2Hj3EVow5KCugXl3wWfQ6K0AE= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcwallet v0.16.18 h1:6h0kMxij4igPu35jOPAWZbn22ceOC4me4L3jj8Za6Zk= -github.com/btcsuite/btcwallet v0.16.18/go.mod h1:4TTru0cgIPbCZpY4aRfAVwX87zrQw4GXM8MH6+A5xZw= +github.com/btcsuite/btcwallet v0.16.19 h1:F52y5luPecfjSDajteVm8qYR6H8vsZ40Ot+Mim8e3JA= +github.com/btcsuite/btcwallet v0.16.19/go.mod h1:u4908XXsJNEl8t8uYz01dEMZqGg6cMN4WCumcuHmbUE= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 h1:Rr0njWI3r341nhSPesKQ2JF+ugDSzdPoeckS75SeDZk= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5/go.mod h1:+tXJ3Ym0nlQc/iHSwW1qzjmPs3ev+UVWMbGgfV1OZqU= github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 h1:YEO+Lx1ZJJAtdRrjuhXjWrYsmAk26wLTlNzxt2q0lhk= @@ -118,8 +693,12 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -127,6 +706,18 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= @@ -173,16 +764,30 @@ github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= +github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fergusstrange/embedded-postgres v1.25.0 h1:sa+k2Ycrtz40eCRPOzI7Ry7TtkWXXJ+YRsxpKMDhxK0= github.com/fergusstrange/embedded-postgres v1.25.0/go.mod h1:t/MLs0h9ukYM6FSt99R7InCHs1nW0ordoVCcnzmpTYw= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k= @@ -196,6 +801,10 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -203,6 +812,7 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -214,18 +824,24 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-macaroon-bakery/macaroonpb v1.0.0 h1:It9exBaRMZ9iix1iJ6gwzfwsDE6ExNuwtAJ9e09v6XE= github.com/go-macaroon-bakery/macaroonpb v1.0.0/go.mod h1:UzrGOcbiwTXISFP2XDLDPjfhMINZa+fX/7A2lMd31zc= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -236,6 +852,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -247,31 +865,46 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gonum/gonum v0.11.0 h1:Lffpf1Aq8WeVUJTbfiKHlOVlFm+c021iePrlwvE5woI= github.com/gonum/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +github.com/gonum/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -279,25 +912,56 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0-rc.0 h1:mdLirNAJBxnGgyB6pjZLcs6ue/6eZGBui6gXspfq4ks= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0-rc.0/go.mod h1:kdXbOySqcQeTxiqglW7aahTmWZy3Pgi6SYL36yvKeyA= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3 h1:o95KDiV/b1xdkumY5YbLR0/n2+wBxUpgf3HgfKgTyLI= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3/go.mod h1:hTxjzRcX49ogbTGVJ1sM5mz5s+SSgiGIyL3jjPxl32E= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -308,7 +972,9 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= @@ -355,18 +1021,25 @@ github.com/juju/schema v1.0.0/go.mod h1:Y+ThzXpUJ0E7NYYocAbuvJ7vTivXfrof/IfRPq/0 github.com/juju/webbrowser v0.0.0-20160309143629-54b8c57083b4/go.mod h1:G6PCelgkM6cuvyD10iYJsjLBsSadVXtJ+nBxFAxE2BU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8= github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -393,14 +1066,14 @@ github.com/lightninglabs/neutrino/cache v1.1.3 h1:rgnabC41W+XaPuBTQrdeFjFCCAVKh1 github.com/lightninglabs/neutrino/cache v1.1.3/go.mod h1:qxkJb+pUxR5p84jl5uIGFCR4dGdFkhNUwMSxw3EUWls= 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.8.1 h1:E0xbHcQ/B+KmMZz6o3nqiYSMcDs2Vu0aWd+FuomDFYg= -github.com/lightninglabs/taproot-assets v0.8.1/go.mod h1:iq4ZybixussUx6NUaMYyo20uXMptibhbVLM3y/0ihIc= -github.com/lightninglabs/taproot-assets/taprpc v1.2.0 h1:CDiGyo19DTUyJcZTdhp5d6AQBz94N+mpGQNO0kEcnPk= -github.com/lightninglabs/taproot-assets/taprpc v1.2.0/go.mod h1:RkBWxHKN8JpPtoa/yBK5qZW5svz5pR9GAOT2rUP7oOI= +github.com/lightninglabs/taproot-assets v0.8.3 h1:X2Hroccy7fFKxxAADVB4QpwfA9UVFtLgD/JayWs4iro= +github.com/lightninglabs/taproot-assets v0.8.3/go.mod h1:qb76QDcVd6uwCR+tqjqiV4XeSiV1HhkvjeMeZFExZic= +github.com/lightninglabs/taproot-assets/taprpc v1.3.3 h1:KwscIMQ7Nyy8SleDiUUTWPCgixpgrN9nunM+QwM/KJQ= +github.com/lightninglabs/taproot-assets/taprpc v1.3.3/go.mod h1:0dBS6jLkddm8EvpJSpJjPUMWr294/YRoM/IAzdfo3pw= github.com/lightningnetwork/lightning-onion v1.3.0 h1:FqILgHjD6euc/Muo1VOzZ4+XDPuFnw6EYROBq0rR/5c= github.com/lightningnetwork/lightning-onion v1.3.0/go.mod h1:nP85zMHG7c0si/eHBbSQpuDCtnIXfSvFrK3tW6YWzmU= -github.com/lightningnetwork/lnd v0.21.0-beta h1:bDP5UH15E7DVGTztsmBPQLqgyilq5EXDrglvQFmRc3U= -github.com/lightningnetwork/lnd v0.21.0-beta/go.mod h1:HcKq9DyxbVEZXuR28TIyGbIIgAjANCxI+N6dqOnRBAA= +github.com/lightningnetwork/lnd v0.21.3-beta h1:Tp1YdwnP0daq/wenwObkaCqe0hckIRnxibN2QKoUv9Y= +github.com/lightningnetwork/lnd v0.21.3-beta/go.mod h1:cUmbodV1/27JGrM/aKib8mXfz8bFxT9iGm0WJi647ds= github.com/lightningnetwork/lnd/actor v0.0.6 h1:Ge8N2wivARG+27qJBwTlB0vwsypStZYZy8vk4Zl38sU= github.com/lightningnetwork/lnd/actor v0.0.6/go.mod h1:YAsoniSbY/cAM9HTVNfZLvt7RI6swDxy6wzPspTcMZg= github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf0d0Uy4qBjI= @@ -421,18 +1094,27 @@ github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6 github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA= github.com/lightningnetwork/lnd/tlv v1.3.2 h1:MO4FCk7F4k5xPMqVZF6Nb/kOpxlwPrUQpYjmyKny5s0= github.com/lightningnetwork/lnd/tlv v1.3.2/go.mod h1:pJuiBj1ecr1WWLOtcZ+2+hu9Ey25aJWFIsjmAoPPnmc= -github.com/lightningnetwork/lnd/tor v1.1.6 h1:WHUumk7WgU6BUFsqHuqszI9P6nfhMeIG+rjJBlVE6OE= -github.com/lightningnetwork/lnd/tor v1.1.6/go.mod h1:qSRB8llhAK+a6kaTPWOLLXSZc6Hg8ZC0mq1sUQ/8JfI= +github.com/lightningnetwork/lnd/tor v1.1.7 h1:BfY1KQGz3LVaeaAGI4XYGYCwDQR1ADFD1P/uXsS2dQc= +github.com/lightningnetwork/lnd/tor v1.1.7/go.mod h1:DANQ6QCQBiR+CqrM+mKudgkMy6CR/RS4ALDbjb2W4FU= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY= github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA= +github.com/lukechampine/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= @@ -480,10 +1162,17 @@ github.com/opencontainers/runc v1.3.6/go.mod h1:o1wyv76EDlTkcf0KTFgN8bMWLPvgF/Hf github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -512,16 +1201,21 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -529,6 +1223,10 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -540,9 +1238,13 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= @@ -571,6 +1273,10 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= @@ -594,6 +1300,9 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= @@ -614,6 +1323,9 @@ go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRk go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -636,24 +1348,39 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 h1:3yiSh9fhy5/RhCSntf4Sy0Tnx50DmMpQ4MQdKKk4yg4= golang.org/x/exp v0.0.0-20250811191247-51f88131bc50/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -664,6 +1391,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -672,7 +1401,15 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/net v0.0.0-20150829230318-ea47fc708ee3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -709,12 +1446,39 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -722,8 +1486,30 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -735,6 +1521,11 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -760,6 +1551,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -776,25 +1568,72 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -802,13 +1641,25 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -826,6 +1677,7 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -854,14 +1706,36 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -878,12 +1752,54 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= +google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -910,10 +1826,113 @@ google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= @@ -932,9 +1951,38 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.83.1 h1:HIO0+BEtBP6soyqvqC8sNUjZ7bTs+0hFQuFF+RAy++Y= google.golang.org/grpc v1.83.1/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -979,33 +2027,66 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= +modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g= modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= diff --git a/looprpc/go.mod b/looprpc/go.mod index db04b88d3..85e3d72ed 100644 --- a/looprpc/go.mod +++ b/looprpc/go.mod @@ -1,11 +1,11 @@ module github.com/lightninglabs/loop/looprpc -go 1.25.12 +go 1.25.13 require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 github.com/lightninglabs/loop/swapserverrpc v1.0.14 - github.com/lightningnetwork/lnd v0.21.0-beta + github.com/lightningnetwork/lnd v0.21.3-beta google.golang.org/grpc v1.83.1 google.golang.org/protobuf v1.36.11 gopkg.in/macaroon-bakery.v2 v2.3.0 @@ -28,7 +28,7 @@ require ( github.com/btcsuite/btcd/v2transport v1.0.1 // indirect github.com/btcsuite/btclog v1.0.0 // indirect github.com/btcsuite/btclog/v2 v2.0.1-0.20250728225537-6090e87c6c5b // indirect - github.com/btcsuite/btcwallet v0.16.18 // indirect + github.com/btcsuite/btcwallet v0.16.19 // indirect github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 // indirect github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 // indirect github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5 // indirect @@ -98,7 +98,7 @@ require ( github.com/lightningnetwork/lnd/sqldb v1.0.13 // indirect github.com/lightningnetwork/lnd/ticker v1.1.1 // indirect github.com/lightningnetwork/lnd/tlv v1.3.2 // indirect - github.com/lightningnetwork/lnd/tor v1.1.6 // indirect + github.com/lightningnetwork/lnd/tor v1.1.7 // indirect github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect diff --git a/looprpc/go.sum b/looprpc/go.sum index 399ace352..b03f03f7d 100644 --- a/looprpc/go.sum +++ b/looprpc/go.sum @@ -52,8 +52,8 @@ github.com/btcsuite/btclog v1.0.0/go.mod h1:w7xnGOhwT3lmrS4H3b/D1XAXxvh+tbhUm8xe github.com/btcsuite/btclog/v2 v2.0.1-0.20250728225537-6090e87c6c5b h1:MQ+Q6sDy37V1wP1Yu79A5KqJutolqUGwA99UZWQDWZM= github.com/btcsuite/btclog/v2 v2.0.1-0.20250728225537-6090e87c6c5b/go.mod h1:XItGUfVOxotJL8kkuk2Hj3EVow5KCugXl3wWfQ6K0AE= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcwallet v0.16.18 h1:6h0kMxij4igPu35jOPAWZbn22ceOC4me4L3jj8Za6Zk= -github.com/btcsuite/btcwallet v0.16.18/go.mod h1:4TTru0cgIPbCZpY4aRfAVwX87zrQw4GXM8MH6+A5xZw= +github.com/btcsuite/btcwallet v0.16.19 h1:F52y5luPecfjSDajteVm8qYR6H8vsZ40Ot+Mim8e3JA= +github.com/btcsuite/btcwallet v0.16.19/go.mod h1:u4908XXsJNEl8t8uYz01dEMZqGg6cMN4WCumcuHmbUE= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 h1:Rr0njWI3r341nhSPesKQ2JF+ugDSzdPoeckS75SeDZk= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5/go.mod h1:+tXJ3Ym0nlQc/iHSwW1qzjmPs3ev+UVWMbGgfV1OZqU= github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 h1:YEO+Lx1ZJJAtdRrjuhXjWrYsmAk26wLTlNzxt2q0lhk= @@ -289,8 +289,8 @@ github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display h1:w7FM5LH9 github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= github.com/lightningnetwork/lightning-onion v1.3.0 h1:FqILgHjD6euc/Muo1VOzZ4+XDPuFnw6EYROBq0rR/5c= github.com/lightningnetwork/lightning-onion v1.3.0/go.mod h1:nP85zMHG7c0si/eHBbSQpuDCtnIXfSvFrK3tW6YWzmU= -github.com/lightningnetwork/lnd v0.21.0-beta h1:bDP5UH15E7DVGTztsmBPQLqgyilq5EXDrglvQFmRc3U= -github.com/lightningnetwork/lnd v0.21.0-beta/go.mod h1:HcKq9DyxbVEZXuR28TIyGbIIgAjANCxI+N6dqOnRBAA= +github.com/lightningnetwork/lnd v0.21.3-beta h1:Tp1YdwnP0daq/wenwObkaCqe0hckIRnxibN2QKoUv9Y= +github.com/lightningnetwork/lnd v0.21.3-beta/go.mod h1:cUmbodV1/27JGrM/aKib8mXfz8bFxT9iGm0WJi647ds= github.com/lightningnetwork/lnd/actor v0.0.6 h1:Ge8N2wivARG+27qJBwTlB0vwsypStZYZy8vk4Zl38sU= github.com/lightningnetwork/lnd/actor v0.0.6/go.mod h1:YAsoniSbY/cAM9HTVNfZLvt7RI6swDxy6wzPspTcMZg= github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0= @@ -309,8 +309,8 @@ github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6 github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA= github.com/lightningnetwork/lnd/tlv v1.3.2 h1:MO4FCk7F4k5xPMqVZF6Nb/kOpxlwPrUQpYjmyKny5s0= github.com/lightningnetwork/lnd/tlv v1.3.2/go.mod h1:pJuiBj1ecr1WWLOtcZ+2+hu9Ey25aJWFIsjmAoPPnmc= -github.com/lightningnetwork/lnd/tor v1.1.6 h1:WHUumk7WgU6BUFsqHuqszI9P6nfhMeIG+rjJBlVE6OE= -github.com/lightningnetwork/lnd/tor v1.1.6/go.mod h1:qSRB8llhAK+a6kaTPWOLLXSZc6Hg8ZC0mq1sUQ/8JfI= +github.com/lightningnetwork/lnd/tor v1.1.7 h1:BfY1KQGz3LVaeaAGI4XYGYCwDQR1ADFD1P/uXsS2dQc= +github.com/lightningnetwork/lnd/tor v1.1.7/go.mod h1:DANQ6QCQBiR+CqrM+mKudgkMy6CR/RS4ALDbjb2W4FU= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY= github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA= From 45e5496e0d9ff426508a0bab12cc148ddee8eabd Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Wed, 2 Sep 2026 16:06:45 +0200 Subject: [PATCH 2/8] assets: Add shared asset HTLC kit Move the existing server asset HTLC contract into Loop so both sides can derive and spend the same commitment. Freeze the legacy vectors and bind witness construction to verified proofs, prevouts, and input indices. Reserve future policies so Loop Asset Out can choose its contract explicitly instead of inheriting the deposit key path. --- assets/htlc/doc.go | 8 + assets/htlc/script.go | 125 ++++ assets/htlc/swapkit.go | 917 ++++++++++++++++++++++++++ assets/htlc/swapkit_test.go | 1226 +++++++++++++++++++++++++++++++++++ 4 files changed, 2276 insertions(+) create mode 100644 assets/htlc/doc.go create mode 100644 assets/htlc/script.go create mode 100644 assets/htlc/swapkit.go create mode 100644 assets/htlc/swapkit_test.go diff --git a/assets/htlc/doc.go b/assets/htlc/doc.go new file mode 100644 index 000000000..ba30f82de --- /dev/null +++ b/assets/htlc/doc.go @@ -0,0 +1,8 @@ +// Package htlc defines the shared, versioned Taproot Asset HTLC construction +// used by Loop and the Loop server. +// +// LegacyDepositV0 preserves the server's existing asset deposit contract. A +// new protocol must add and explicitly select its own policy before it can +// construct a contract, so this compatibility layer does not silently choose +// the on-chain terms of a future Loop Asset Out protocol. +package htlc diff --git a/assets/htlc/script.go b/assets/htlc/script.go new file mode 100644 index 000000000..af65296e6 --- /dev/null +++ b/assets/htlc/script.go @@ -0,0 +1,125 @@ +package htlc + +import ( + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" +) + +// GenSuccessPathScript constructs the success payment path. The final +// relative lock of one block is part of the version-zero contract and must not +// be changed without introducing a new contract version. +func GenSuccessPathScript(receiverHtlcKey *btcec.PublicKey, + swapHash lntypes.Hash) ([]byte, error) { + + if receiverHtlcKey == nil { + return nil, fmt.Errorf("receiver HTLC key is required") + } + + builder := txscript.NewScriptBuilder() + builder.AddData(schnorr.SerializePubKey(receiverHtlcKey)) + builder.AddOp(txscript.OP_CHECKSIGVERIFY) + builder.AddOp(txscript.OP_SIZE) + builder.AddInt64(32) + builder.AddOp(txscript.OP_EQUALVERIFY) + builder.AddOp(txscript.OP_HASH160) + builder.AddData(input.Ripemd160H(swapHash[:])) + builder.AddOp(txscript.OP_EQUALVERIFY) + builder.AddInt64(int64(SuccessSequence)) + builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) + + return builder.Script() +} + +// GenTimeoutPathScript constructs the timeout payment path. +func GenTimeoutPathScript(senderHtlcKey *btcec.PublicKey, csvExpiry int64) ( + []byte, error) { + + if senderHtlcKey == nil { + return nil, fmt.Errorf("sender HTLC key is required") + } + if csvExpiry <= 0 { + return nil, fmt.Errorf("CSV expiry must be positive") + } + if csvExpiry > int64(wire.SequenceLockTimeMask) { + return nil, fmt.Errorf("CSV expiry exceeds block-based BIP68 range") + } + + builder := txscript.NewScriptBuilder() + builder.AddData(schnorr.SerializePubKey(senderHtlcKey)) + builder.AddOp(txscript.OP_CHECKSIGVERIFY) + builder.AddInt64(csvExpiry) + builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) + + return builder.Script() +} + +// GetOpTrueScript returns a script that always evaluates to true. +func GetOpTrueScript() ([]byte, error) { + return txscript.NewScriptBuilder().AddOp(txscript.OP_TRUE).Script() +} + +// CreateOpTrueLeaf creates the legacy Taproot Asset script key whose only +// script path is OP_TRUE beneath the public Taproot Assets NUMS key. +func CreateOpTrueLeaf() (asset.ScriptKey, txscript.TapLeaf, + *txscript.IndexedTapScriptTree, *txscript.ControlBlock, error) { + + tapScript, err := GetOpTrueScript() + if err != nil { + return asset.ScriptKey{}, txscript.TapLeaf{}, nil, nil, err + } + + tapLeaf := txscript.NewBaseTapLeaf(tapScript) + tree := txscript.AssembleTaprootScriptTree(tapLeaf) + rootHash := tree.RootNode.TapHash() + tapKey := txscript.ComputeTaprootOutputKey( + asset.NUMSPubKey, rootHash[:], + ) + + controlBlock := &txscript.ControlBlock{ + LeafVersion: txscript.BaseLeafVersion, + InternalKey: asset.NUMSPubKey, + } + tapScriptKey := asset.ScriptKey{ + PubKey: tapKey, + TweakedScriptKey: &asset.TweakedScriptKey{ + RawKey: keychain.KeyDescriptor{ + PubKey: asset.NUMSPubKey, + }, + Tweak: rootHash[:], + }, + } + if tapKey.SerializeCompressed()[0] == + secp256k1.PubKeyFormatCompressedOdd { + + controlBlock.OutputKeyYIsOdd = true + } + + return tapScriptKey, tapLeaf, tree, controlBlock, nil +} + +// GetOpTrueScriptKey returns the compressed legacy OP_TRUE Taproot Asset +// script key. +func GetOpTrueScriptKey() ([]byte, error) { + opTrueScriptKey, _, _, _, err := CreateOpTrueLeaf() + if err != nil { + return nil, err + } + + scriptKey, err := schnorr.ParsePubKey( + opTrueScriptKey.PubKey.SerializeCompressed()[1:], + ) + if err != nil { + return nil, err + } + + return scriptKey.SerializeCompressed(), nil +} diff --git a/assets/htlc/swapkit.go b/assets/htlc/swapkit.go new file mode 100644 index 000000000..71e1c3600 --- /dev/null +++ b/assets/htlc/swapkit.go @@ -0,0 +1,917 @@ +package htlc + +import ( + "bytes" + "context" + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil/psbt" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/taproot-assets/address" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/commitment" + "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/tappsbt" + "github.com/lightninglabs/taproot-assets/tapscript" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" +) + +const ( + // SuccessSequence is the relative lock used by the success path. It is + // part of the legacy version-zero policy. + SuccessSequence uint32 = 1 + + // rawSimNetHDCoinType is the BIP-0044 coin type used by btcd's native + // simnet parameters. LND deliberately uses the testnet coin type for + // simnet instead, so both forms need explicit, stable definitions. + rawSimNetHDCoinType uint32 = 115 +) + +// Policy identifies an immutable version of an asset HTLC contract. New +// protocols must use distinct policies so they cannot silently change an +// existing on-chain contract. +type Policy uint8 + +const ( + // PolicyUnknown reserves the zero value so every caller must choose the + // protocol whose compatibility rules it expects. + PolicyUnknown Policy = iota + + // LegacyDepositV0 preserves the asset deposit HTLC contract that predates + // this shared package. + LegacyDepositV0 +) + +// Params contains the validated inputs used to construct a SwapKit. +type Params struct { + SenderPubKey *btcec.PublicKey + ReceiverPubKey *btcec.PublicKey + AssetID asset.ID + Amount uint64 + SwapHash lntypes.Hash + CsvExpiry uint32 + AddressParams *address.ChainParams +} + +// SwapKit holds the immutable information required to construct and spend an +// on-chain Taproot Asset HTLC. NewSwapKit is the only constructor so a funded +// contract cannot later be changed by mutating a key, hash, amount, or expiry. +type SwapKit struct { + policy Policy + senderPubKey *btcec.PublicKey + receiverPubKey *btcec.PublicKey + assetID asset.ID + amount uint64 + swapHash lntypes.Hash + csvExpiry uint32 + addressParams address.ChainParams +} + +// NewSwapKit returns a defensively validated asset HTLC kit. +func NewSwapKit(policy Policy, params Params) (*SwapKit, error) { + if params.SenderPubKey == nil { + return nil, fmt.Errorf("sender public key is required") + } + if params.ReceiverPubKey == nil { + return nil, fmt.Errorf("receiver public key is required") + } + if params.AddressParams == nil { + return nil, fmt.Errorf("address parameters are required") + } + if params.AddressParams.Params == nil || params.AddressParams.TapHRP == "" { + return nil, fmt.Errorf("address parameters are incomplete") + } + canonicalParams, err := canonicalAddressParams(params.AddressParams) + if err != nil { + return nil, err + } + + senderKey, err := btcec.ParsePubKey( + params.SenderPubKey.SerializeCompressed(), + ) + if err != nil { + return nil, fmt.Errorf("invalid sender public key: %w", err) + } + receiverKey, err := btcec.ParsePubKey( + params.ReceiverPubKey.SerializeCompressed(), + ) + if err != nil { + return nil, fmt.Errorf("invalid receiver public key: %w", err) + } + if senderKey.IsEqual(receiverKey) { + return nil, fmt.Errorf("sender and receiver keys must differ") + } + + addressParams := cloneAddressParams(*canonicalParams) + kit := &SwapKit{ + policy: policy, + senderPubKey: senderKey, + receiverPubKey: receiverKey, + assetID: params.AssetID, + amount: params.Amount, + swapHash: params.SwapHash, + csvExpiry: params.CsvExpiry, + addressParams: *addressParams, + } + if err := kit.Validate(); err != nil { + return nil, err + } + + return kit, nil +} + +// canonicalAddressParams returns the supported network parameters matching the +// caller's Taproot Asset address tuple. +func canonicalAddressParams(params *address.ChainParams) ( + *address.ChainParams, error) { + + rawSimNet := cloneAddressParams(address.SimNetTap) + rawSimNet.HDCoinType = rawSimNetHDCoinType + lndSimNet := cloneAddressParams(address.SimNetTap) + lndSimNet.HDCoinType = address.TestNet3Tap.HDCoinType + + knownNetworks := []*address.ChainParams{ + &address.MainNetTap, + &address.TestNet3Tap, + &address.TestNet4Tap, + &address.RegressionNetTap, + &address.SigNetTap, + rawSimNet, + lndSimNet, + } + for _, known := range knownNetworks { + if params.TapHRP != known.TapHRP || params.Net != known.Net || + params.HDCoinType != known.HDCoinType { + + continue + } + + return cloneAddressParams(*known), nil + } + + return nil, fmt.Errorf("unsupported or mismatched address parameters") +} + +// Policy returns the immutable contract policy. +func (s *SwapKit) Policy() Policy { + return s.policy +} + +// SenderPubKey returns the sender's refund public key. +func (s *SwapKit) SenderPubKey() *btcec.PublicKey { + key, _ := btcec.ParsePubKey(s.senderPubKey.SerializeCompressed()) + + return key +} + +// ReceiverPubKey returns the receiver's claim public key. +func (s *SwapKit) ReceiverPubKey() *btcec.PublicKey { + key, _ := btcec.ParsePubKey(s.receiverPubKey.SerializeCompressed()) + + return key +} + +// AssetID returns the exact asset identifier. +func (s *SwapKit) AssetID() asset.ID { + return s.assetID +} + +// Amount returns the amount in the asset's native unit. +func (s *SwapKit) Amount() uint64 { + return s.amount +} + +// SwapHash returns the payment hash committed to by the success path. +func (s *SwapKit) SwapHash() lntypes.Hash { + return s.swapHash +} + +// CsvExpiry returns the sender's block-based relative refund delay. +func (s *SwapKit) CsvExpiry() uint32 { + return s.csvExpiry +} + +// AddressParams returns a copy of the contract's address parameters. +func (s *SwapKit) AddressParams() address.ChainParams { + return *cloneAddressParams(s.addressParams) +} + +// cloneAddressParams returns an independent copy of address parameters, +// including the nested Bitcoin network parameters when present. +func cloneAddressParams(params address.ChainParams) *address.ChainParams { + paramsCopy := params + if params.Params != nil { + bitcoinParams := *params.Params + paramsCopy.Params = &bitcoinParams + } + + return ¶msCopy +} + +// Validate verifies every field required to create an HTLC virtual packet. +func (s *SwapKit) Validate() error { + if err := s.validateScripts(); err != nil { + return err + } + if s.amount == 0 { + return fmt.Errorf("asset amount must be positive") + } + if s.assetID == (asset.ID{}) { + return fmt.Errorf("asset ID is required") + } + if s.swapHash == (lntypes.Hash{}) { + return fmt.Errorf("swap hash is required") + } + + return nil +} + +// validateScripts checks the immutable policy inputs used to derive the HTLC +// script tree. +func (s *SwapKit) validateScripts() error { + if s == nil { + return fmt.Errorf("swap kit is required") + } + if s.policy != LegacyDepositV0 { + return fmt.Errorf("unknown asset HTLC policy: %d", s.policy) + } + if s.senderPubKey == nil { + return fmt.Errorf("sender public key is required") + } + if s.receiverPubKey == nil { + return fmt.Errorf("receiver public key is required") + } + if s.csvExpiry <= SuccessSequence { + return fmt.Errorf("CSV expiry must exceed success sequence") + } + if s.csvExpiry > wire.SequenceLockTimeMask { + return fmt.Errorf("CSV expiry exceeds block-based BIP68 range") + } + + return nil +} + +// GetSuccessScript returns the success path script of the swap HTLC. +func (s *SwapKit) GetSuccessScript() ([]byte, error) { + if s == nil || s.receiverPubKey == nil { + return nil, fmt.Errorf("receiver public key is required") + } + if s.policy != LegacyDepositV0 { + return nil, fmt.Errorf("unknown asset HTLC policy: %d", s.policy) + } + + return GenSuccessPathScript(s.receiverPubKey, s.swapHash) +} + +// GetTimeoutScript returns the timeout path script of the swap HTLC. +func (s *SwapKit) GetTimeoutScript() ([]byte, error) { + if s == nil { + return nil, fmt.Errorf("swap kit is required") + } + if s.policy != LegacyDepositV0 { + return nil, fmt.Errorf("unknown asset HTLC policy: %d", s.policy) + } + + return GenTimeoutPathScript(s.senderPubKey, int64(s.csvExpiry)) +} + +// GetAggregateKey returns the sorted MuSig2 aggregate key used in the swap +// HTLC. The key is deliberately returned before any Taproot tweak is applied. +func (s *SwapKit) GetAggregateKey() (*btcec.PublicKey, error) { + if err := s.validateScripts(); err != nil { + return nil, err + } + + aggregateKey, err := input.MuSig2CombineKeys( + input.MuSig2Version100RC2, + []*btcec.PublicKey{s.senderPubKey, s.receiverPubKey}, true, + &input.MuSig2Tweaks{}, + ) + if err != nil { + return nil, err + } + + return aggregateKey.PreTweakedKey, nil +} + +// GetTimeoutLeaf returns the timeout leaf of the swap. +func (s *SwapKit) GetTimeoutLeaf() (txscript.TapLeaf, error) { + timeoutScript, err := s.GetTimeoutScript() + if err != nil { + return txscript.TapLeaf{}, err + } + + return txscript.NewBaseTapLeaf(timeoutScript), nil +} + +// GetTimeOutLeaf returns the timeout leaf of the swap. The historical method +// capitalization is retained while callers migrate to GetTimeoutLeaf. +func (s *SwapKit) GetTimeOutLeaf() (txscript.TapLeaf, error) { + return s.GetTimeoutLeaf() +} + +// GetSuccessLeaf returns the success leaf of the swap. +func (s *SwapKit) GetSuccessLeaf() (txscript.TapLeaf, error) { + successScript, err := s.GetSuccessScript() + if err != nil { + return txscript.TapLeaf{}, err + } + + return txscript.NewBaseTapLeaf(successScript), nil +} + +// GetSiblingPreimage returns the branch preimage placed beside the Taproot +// Asset commitment in the Bitcoin output tree. +func (s *SwapKit) GetSiblingPreimage() ( + commitment.TapscriptPreimage, error) { + + timeoutLeaf, err := s.GetTimeoutLeaf() + if err != nil { + return commitment.TapscriptPreimage{}, err + } + + successLeaf, err := s.GetSuccessLeaf() + if err != nil { + return commitment.TapscriptPreimage{}, err + } + + branch := txscript.NewTapBranch(timeoutLeaf, successLeaf) + + return commitment.NewPreimageFromBranch(branch), nil +} + +// CreateHtlcVpkt creates the version-one virtual packet for the HTLC. The +// split-root and HTLC output indices and their interactive flags are consensus +// with the existing server implementation. +func (s *SwapKit) CreateHtlcVpkt() (*tappsbt.VPacket, error) { + if err := s.Validate(); err != nil { + return nil, err + } + + btcInternalKey, err := s.GetAggregateKey() + if err != nil { + return nil, err + } + + siblingPreimage, err := s.GetSiblingPreimage() + if err != nil { + return nil, err + } + + tapScriptKey, _, _, _, err := CreateOpTrueLeaf() + if err != nil { + return nil, err + } + + pkt := &tappsbt.VPacket{ + Inputs: []*tappsbt.VInput{{ + PrevID: asset.PrevID{ID: s.assetID}, + }}, + Outputs: make([]*tappsbt.VOutput, 0, 2), + ChainParams: cloneAddressParams(s.addressParams), + Version: tappsbt.V1, + } + pkt.Outputs = append(pkt.Outputs, &tappsbt.VOutput{ + AssetVersion: asset.V1, + Amount: 0, + Type: tappsbt.TypeSplitRoot, + AnchorOutputIndex: 0, + ScriptKey: asset.NUMSScriptKey, + Interactive: true, + }) + pkt.Outputs = append(pkt.Outputs, &tappsbt.VOutput{ + AssetVersion: asset.V1, + Amount: s.amount, + Interactive: true, + AnchorOutputIndex: 1, + ScriptKey: asset.NewScriptKey(tapScriptKey.PubKey), + AnchorOutputInternalKey: btcInternalKey, + AnchorOutputTapscriptSibling: &siblingPreimage, + }) + + return pkt, nil +} + +// GenTimeoutBtcControlBlock generates the Bitcoin control block for the +// timeout path. The supplied root must be the exact Taproot Asset commitment +// root committed to by the anchor output. +func (s *SwapKit) GenTimeoutBtcControlBlock(taprootAssetRoot []byte) ( + *txscript.ControlBlock, error) { + + return s.genBtcControlBlock(false, taprootAssetRoot) +} + +// GenSuccessBtcControlBlock generates the Bitcoin control block for the +// success path. +func (s *SwapKit) GenSuccessBtcControlBlock(taprootAssetRoot []byte) ( + *txscript.ControlBlock, error) { + + return s.genBtcControlBlock(true, taprootAssetRoot) +} + +// GetPkScriptFromRoot returns the Bitcoin anchor script for an exact Taproot +// Asset commitment root. +func (s *SwapKit) GetPkScriptFromRoot(taprootAssetRoot []byte) ( + []byte, error) { + + controlBlock, err := s.GenSuccessBtcControlBlock(taprootAssetRoot) + if err != nil { + return nil, err + } + successScript, err := s.GetSuccessScript() + if err != nil { + return nil, err + } + + rootHash := controlBlock.RootHash(successScript) + internalKey, err := s.GetAggregateKey() + if err != nil { + return nil, err + } + outputKey := txscript.ComputeTaprootOutputKey(internalKey, rootHash) + + return txscript.PayToTaprootScript(outputKey) +} + +// genBtcControlBlock derives a control block for either the success or timeout +// leaf under the proof-derived Taproot Asset root. +func (s *SwapKit) genBtcControlBlock(success bool, + taprootAssetRoot []byte) (*txscript.ControlBlock, error) { + + if len(taprootAssetRoot) != 32 { + return nil, fmt.Errorf("Taproot Asset root must be 32 bytes") + } + + internalKey, err := s.GetAggregateKey() + if err != nil { + return nil, err + } + + timeoutLeaf, err := s.GetTimeoutLeaf() + if err != nil { + return nil, err + } + successLeaf, err := s.GetSuccessLeaf() + if err != nil { + return nil, err + } + + spendLeaf := timeoutLeaf + siblingLeaf := successLeaf + if success { + spendLeaf = successLeaf + siblingLeaf = timeoutLeaf + } + + siblingHash := siblingLeaf.TapHash() + inclusionProof := make([]byte, 0, 64) + inclusionProof = append(inclusionProof, siblingHash[:]...) + inclusionProof = append(inclusionProof, taprootAssetRoot...) + + controlBlock := &txscript.ControlBlock{ + InternalKey: internalKey, + LeafVersion: txscript.BaseLeafVersion, + InclusionProof: inclusionProof, + } + + rootHash := controlBlock.RootHash(spendLeaf.Script) + tapKey := txscript.ComputeTaprootOutputKey(internalKey, rootHash) + if tapKey.SerializeCompressed()[0] == + secp256k1.PubKeyFormatCompressedOdd { + + controlBlock.OutputKeyYIsOdd = true + } + + return controlBlock, nil +} + +// GenTaprootAssetRootFromProof verifies the inclusion, exclusion, and optional +// split-root commitment proofs before returning the committed Taproot Asset +// root. It does not verify proof provenance, chain inclusion, confirmation +// depth, or whether the anchor outpoint remains unspent. +func GenTaprootAssetRootFromProof(assetProof *proof.Proof) ([]byte, error) { + if assetProof == nil { + return nil, fmt.Errorf("asset proof is required") + } + + assetCommitment, err := assetProof.VerifyProofs() + if err != nil { + return nil, fmt.Errorf("invalid asset proof: %w", err) + } + + taprootAssetRoot := assetCommitment.TapscriptRoot(nil) + + return taprootAssetRoot[:], nil +} + +// GetPkScriptFromProof returns the top-level Bitcoin script that commits to +// the verified proof's exact Taproot Asset commitment under this kit's HTLC +// tree. The proof is required because an asset alone does not identify its +// anchor commitment version or any co-anchored leaves. +func (s *SwapKit) GetPkScriptFromProof(assetProof *proof.Proof) ( + []byte, error) { + + if assetProof == nil { + return nil, fmt.Errorf("asset proof is required") + } + if err := s.validateAsset(&assetProof.Asset); err != nil { + return nil, err + } + + taprootAssetRoot, err := GenTaprootAssetRootFromProof(assetProof) + if err != nil { + return nil, err + } + + return s.GetPkScriptFromRoot(taprootAssetRoot) +} + +// validateAsset binds an asset leaf to the exact HTLC identity, amount, and +// script policy. +func (s *SwapKit) validateAsset(htlcAsset *asset.Asset) error { + if err := s.validateScripts(); err != nil { + return err + } + if s.amount == 0 { + return fmt.Errorf("asset amount must be positive") + } + if htlcAsset == nil { + return fmt.Errorf("asset is required") + } + + proofID := htlcAsset.Genesis.ID() + if !bytes.Equal(s.assetID[:], proofID[:]) { + return fmt.Errorf("asset proof ID does not match swap") + } + if htlcAsset.Amount != s.amount { + return fmt.Errorf("asset proof amount does not match swap") + } + if htlcAsset.Version != asset.V0 && + htlcAsset.Version != asset.V1 { + + return fmt.Errorf("unsupported legacy deposit asset version") + } + if htlcAsset.ScriptVersion != asset.ScriptV0 { + return fmt.Errorf("asset proof script version does not match swap") + } + if htlcAsset.LockTime != 0 || htlcAsset.RelativeLockTime != 0 { + return fmt.Errorf("asset proof contains an unexpected locktime") + } + + expectedScriptKey, _, _, _, err := CreateOpTrueLeaf() + if err != nil { + return err + } + expectedScriptKey = asset.NewScriptKey(expectedScriptKey.PubKey) + if htlcAsset.ScriptKey.PubKey == nil || + !htlcAsset.ScriptKey.PubKey.IsEqual(expectedScriptKey.PubKey) { + + return fmt.Errorf("asset proof script key does not match swap") + } + + return nil +} + +type validatedSweep struct { + assetInputIndex int + prevOutputs []*wire.TxOut + assetRoot []byte +} + +// SpendWitness binds a witness stack to the exact transaction input selected +// by the proof outpoint. +type SpendWitness struct { + InputIndex uint32 + Witness wire.TxWitness +} + +// findUniqueInput returns the sole transaction input that spends outpoint. +func findUniqueInput(tx *wire.MsgTx, outpoint wire.OutPoint) (int, error) { + inputIndex := -1 + for idx, txIn := range tx.TxIn { + if txIn == nil { + return 0, fmt.Errorf("sweep input %d is nil", idx) + } + if txIn.PreviousOutPoint != outpoint { + continue + } + if inputIndex >= 0 { + return 0, fmt.Errorf("proof outpoint appears more than once") + } + + inputIndex = idx + } + if inputIndex < 0 { + return 0, fmt.Errorf("asset input does not spend proof outpoint") + } + + return inputIndex, nil +} + +// validateSweep binds a PSBT input and witness UTXO to the HTLC proof and +// reconstructs the exact Taproot output being spent. +func (s *SwapKit) validateSweep(assetProof *proof.Proof, + sweepPacket *psbt.Packet) (*validatedSweep, error) { + + if assetProof == nil { + return nil, fmt.Errorf("asset proof is required") + } + if err := s.validateAsset(&assetProof.Asset); err != nil { + return nil, err + } + assetCommitment, err := assetProof.VerifyProofs() + if err != nil { + return nil, fmt.Errorf("invalid asset proof: %w", err) + } + assetRootHash := assetCommitment.TapscriptRoot(nil) + assetRoot := append([]byte(nil), assetRootHash[:]...) + if sweepPacket == nil || sweepPacket.UnsignedTx == nil { + return nil, fmt.Errorf("sweep PSBT is required") + } + if len(sweepPacket.UnsignedTx.TxIn) == 0 || + len(sweepPacket.UnsignedTx.TxIn) != len(sweepPacket.Inputs) { + + return nil, fmt.Errorf("sweep PSBT input metadata is incomplete") + } + if sweepPacket.UnsignedTx.Version < 2 { + return nil, fmt.Errorf("sweep transaction must use version 2") + } + + proofOutpoint := assetProof.OutPoint() + assetInputIndex, err := findUniqueInput( + sweepPacket.UnsignedTx, proofOutpoint, + ) + if err != nil { + return nil, err + } + prevOutputs := make([]*wire.TxOut, len(sweepPacket.Inputs)) + for idx := range sweepPacket.Inputs { + witnessUtxo := sweepPacket.Inputs[idx].WitnessUtxo + if witnessUtxo == nil { + return nil, fmt.Errorf( + "sweep input %d requires a witness UTXO", idx, + ) + } + prevOutputs[idx] = &wire.TxOut{ + Value: witnessUtxo.Value, + PkScript: append([]byte(nil), witnessUtxo.PkScript...), + } + } + siblingPreimage, err := s.GetSiblingPreimage() + if err != nil { + return nil, err + } + siblingHash, err := siblingPreimage.TapHash() + if err != nil { + return nil, err + } + internalKey, err := s.GetAggregateKey() + if err != nil { + return nil, err + } + expectedPkScript, err := tapscript.PayToAddrScript( + *internalKey, siblingHash, *assetCommitment, + ) + if err != nil { + return nil, err + } + proofOutputIndex := int(proofOutpoint.Index) + if proofOutputIndex >= len(assetProof.AnchorTx.TxOut) { + return nil, fmt.Errorf("proof output index is out of bounds") + } + proofTxOut := assetProof.AnchorTx.TxOut[proofOutputIndex] + if proofTxOut == nil { + return nil, fmt.Errorf("proof anchor output is nil") + } + assetPrevOut := prevOutputs[assetInputIndex] + if proofTxOut.Value != assetPrevOut.Value || + !bytes.Equal(proofTxOut.PkScript, assetPrevOut.PkScript) { + + return nil, fmt.Errorf("asset prevout does not match proof anchor") + } + if !bytes.Equal(expectedPkScript, assetPrevOut.PkScript) { + return nil, fmt.Errorf("asset input script does not match proof") + } + + return &validatedSweep{ + assetInputIndex: assetInputIndex, + prevOutputs: prevOutputs, + assetRoot: assetRoot, + }, nil +} + +// AssetInputIndex returns the unique input that spends the proof outpoint. It +// validates all PSBT prevouts and the proof-bound anchor output before exposing +// the index. Callers use this index to set the required relative sequence +// before asking the kit to sign. +func (s *SwapKit) AssetInputIndex(assetProof *proof.Proof, + sweepPacket *psbt.Packet) (uint32, error) { + + sweep, err := s.validateSweep(assetProof, sweepPacket) + if err != nil { + return 0, err + } + + return uint32(sweep.assetInputIndex), nil +} + +// validateSequence requires the proof-selected input to use the contract's +// relative lock value before a witness is signed. +func validateSequence(sweep *validatedSweep, sweepPacket *psbt.Packet, + required uint32) error { + + sequence := sweepPacket.UnsignedTx.TxIn[sweep.assetInputIndex].Sequence + if sequence != required { + return fmt.Errorf( + "asset input sequence must be %d, got %d", + required, sequence, + ) + } + + return nil +} + +// verifyTapscriptSignature checks a signer response against the exact input, +// prevouts, script, and public key used by an HTLC spend. +func verifyTapscriptSignature(tx *wire.MsgTx, sweep *validatedSweep, + witnessScript []byte, pubKey *btcec.PublicKey, signature []byte) error { + + parsedSignature, err := schnorr.ParseSignature(signature) + if err != nil { + return fmt.Errorf("signer returned an invalid Schnorr signature: %w", + err) + } + + prevOutFetcher := txscript.NewMultiPrevOutFetcher(nil) + for idx, txIn := range tx.TxIn { + prevOutFetcher.AddPrevOut( + txIn.PreviousOutPoint, sweep.prevOutputs[idx], + ) + } + sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher) + tapLeaf := txscript.NewBaseTapLeaf(witnessScript) + sigHash, err := txscript.CalcTapscriptSignaturehash( + sigHashes, txscript.SigHashDefault, tx, + sweep.assetInputIndex, prevOutFetcher, tapLeaf, + ) + if err != nil { + return fmt.Errorf("unable to calculate asset input sighash: %w", err) + } + if !parsedSignature.Verify(sigHash, pubKey) { + return fmt.Errorf("signer returned a signature for the wrong key") + } + + return nil +} + +// CreatePreimageWitness signs the exact proof-bound asset input and returns a +// success-path witness in the legacy stack order. The caller must set the +// proof-selected input's sequence to SuccessSequence before calling this +// method; the PSBT is never mutated here. +func (s *SwapKit) CreatePreimageWitness(ctx context.Context, + signer lndclient.SignerClient, htlcProof *proof.Proof, + sweepBtcPacket *psbt.Packet, keyLocator keychain.KeyLocator, + preimage lntypes.Preimage) (*SpendWitness, error) { + + if signer == nil { + return nil, fmt.Errorf("signer is required") + } + if preimage.Hash() != s.swapHash { + return nil, fmt.Errorf("preimage does not match swap hash") + } + + sweep, err := s.validateSweep(htlcProof, sweepBtcPacket) + if err != nil { + return nil, err + } + if err := validateSequence( + sweep, sweepBtcPacket, SuccessSequence, + ); err != nil { + return nil, err + } + + successScript, err := s.GetSuccessScript() + if err != nil { + return nil, err + } + + signDesc := &lndclient.SignDescriptor{ + KeyDesc: keychain.KeyDescriptor{KeyLocator: keyLocator}, + SignMethod: input.TaprootScriptSpendSignMethod, + WitnessScript: successScript, + Output: sweep.prevOutputs[sweep.assetInputIndex], + InputIndex: sweep.assetInputIndex, + } + sigs, err := signer.SignOutputRaw( + ctx, sweepBtcPacket.UnsignedTx, + []*lndclient.SignDescriptor{signDesc}, sweep.prevOutputs, + ) + if err != nil { + return nil, err + } + if len(sigs) != 1 || len(sigs[0]) == 0 { + return nil, fmt.Errorf("signer returned an invalid signature set") + } + if err := verifyTapscriptSignature( + sweepBtcPacket.UnsignedTx, sweep, successScript, + s.receiverPubKey, sigs[0], + ); err != nil { + return nil, err + } + + successControlBlock, err := s.GenSuccessBtcControlBlock( + sweep.assetRoot, + ) + if err != nil { + return nil, err + } + controlBlockBytes, err := successControlBlock.ToBytes() + if err != nil { + return nil, err + } + + return &SpendWitness{ + InputIndex: uint32(sweep.assetInputIndex), + Witness: wire.TxWitness{ + preimage[:], sigs[0], successScript, controlBlockBytes, + }, + }, nil +} + +// CreateTimeoutWitness signs the exact proof-bound asset input and returns a +// timeout-path witness in the legacy stack order. The caller must set the +// proof-selected input's sequence to CsvExpiry before calling this method; the +// PSBT is never mutated here. +func (s *SwapKit) CreateTimeoutWitness(ctx context.Context, + signer lndclient.SignerClient, htlcProof *proof.Proof, + sweepBtcPacket *psbt.Packet, keyLocator keychain.KeyLocator) ( + *SpendWitness, error) { + + if signer == nil { + return nil, fmt.Errorf("signer is required") + } + + sweep, err := s.validateSweep(htlcProof, sweepBtcPacket) + if err != nil { + return nil, err + } + if err := validateSequence( + sweep, sweepBtcPacket, s.csvExpiry, + ); err != nil { + return nil, err + } + + timeoutScript, err := s.GetTimeoutScript() + if err != nil { + return nil, err + } + + signDesc := &lndclient.SignDescriptor{ + KeyDesc: keychain.KeyDescriptor{KeyLocator: keyLocator}, + SignMethod: input.TaprootScriptSpendSignMethod, + WitnessScript: timeoutScript, + Output: sweep.prevOutputs[sweep.assetInputIndex], + InputIndex: sweep.assetInputIndex, + } + sigs, err := signer.SignOutputRaw( + ctx, sweepBtcPacket.UnsignedTx, + []*lndclient.SignDescriptor{signDesc}, sweep.prevOutputs, + ) + if err != nil { + return nil, err + } + if len(sigs) != 1 || len(sigs[0]) == 0 { + return nil, fmt.Errorf("signer returned an invalid signature set") + } + if err := verifyTapscriptSignature( + sweepBtcPacket.UnsignedTx, sweep, timeoutScript, + s.senderPubKey, sigs[0], + ); err != nil { + return nil, err + } + + timeoutControlBlock, err := s.GenTimeoutBtcControlBlock( + sweep.assetRoot, + ) + if err != nil { + return nil, err + } + controlBlockBytes, err := timeoutControlBlock.ToBytes() + if err != nil { + return nil, err + } + + return &SpendWitness{ + InputIndex: uint32(sweep.assetInputIndex), + Witness: wire.TxWitness{ + sigs[0], timeoutScript, controlBlockBytes, + }, + }, nil +} diff --git a/assets/htlc/swapkit_test.go b/assets/htlc/swapkit_test.go new file mode 100644 index 000000000..5e389e86d --- /dev/null +++ b/assets/htlc/swapkit_test.go @@ -0,0 +1,1226 @@ +package htlc + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil/psbt" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/taproot-assets/address" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/commitment" + "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/tappsbt" + "github.com/lightninglabs/taproot-assets/tapscript" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/stretchr/testify/require" +) + +const ( + legacySuccessScript = "20c6047f9441ed7d6d3045406e95c07cd85c778e" + + "4b8cef3ca7abac09b95c709ee5ad82012088a914e6babb9619d7a812" + + "72711fc546a16b211dd939578851b2" + legacyTimeoutScript = "2079be667ef9dcbbac55a06295ce870b07029bfcdb2d" + + "ce28d959f2815b16f81798ad029000b2" + legacyAggregateKey = "023b46d262d2f610e9038b44beabdfe97ab5a0feb" + + "89870acc2264edfb7f63ec2ec" + legacySibling = "01cbf40123d0ca1191c0dba575c7a92ea255f9e9fc81484e" + + "3929afeaa1a6f459d600114fbfd01b5de82dfa6f54f0f27af41d5a66" + + "ed39ac1a87d614fa12808f9adf" + legacyOpTrueKey = "037bbbb85ff774141ee36c2cc9f38b40be4a02c2a156c8a" + + "1def8781cf39177deca" + legacyOpTrueTweak = "a85b2107f791b26a84e7586c28cec7cb61202ed3d019" + + "44d832500f363782d675" + legacyOpTrueControl = "c17c79b9b26e463895eef5679d8558942c86c4ad2233" + + "adef01bc3e6d540b3653fe" + legacyOpTrueScriptKey = "027bbbb85ff774141ee36c2cc9f38b40be4a02c2a" + + "156c8a1def8781cf39177deca" + legacySuccessControl = "c13b46d262d2f610e9038b44beabdfe97ab5a0feb89" + + "870acc2264edfb7f63ec2eccbf40123d0ca1191c0dba575c7a92ea255" + + "f9e9fc81484e3929afeaa1a6f459d6a0a1a2a3a4a5a6a7a8a9aaab" + + "acadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + legacyTimeoutControl = "c13b46d262d2f610e9038b44beabdfe97ab5a0feb89" + + "870acc2264edfb7f63ec2ec00114fbfd01b5de82dfa6f54f0f27af41" + + "d5a66ed39ac1a87d614fa12808f9adfa0a1a2a3a4a5a6a7a8a9aaab" + + "acadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + legacyVPacket = "70736274ff010089020000000100000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000002000000" + + "00000000002251207c79b9b26e463895eef5679d8558942c86c4ad2233" + + "adef01bc3e6d540b3653fee8030000000000002251207bbbb85ff77414" + + "1ee36c2cc9f38b40be4a02c2a156c8a1def8781cf39177deca00000000" + + "0170010101710574617072740172010100017065000000000000000000" + + "0000000000000000000000000000000000000000000000000000000001" + + "02030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e" + + "1f00000000000000000000000000000000000000000000000000000000" + + "0000000000017108000000000000000001720001730800000000000000" + + "0001750001780000017001010171010101720800000000000000000179" + + "0101017c080000000000000000017d0800000000000000000001700100" + + "017101010172080000000000000001017321023b46d262d2f610e9038b" + + "44beabdfe97ab5a0feb89870acc2264edfb7f63ec2ec01784101cbf401" + + "23d0ca1191c0dba575c7a92ea255f9e9fc81484e3929afeaa1a6f459d6" + + "00114fbfd01b5de82dfa6f54f0f27af41d5a66ed39ac1a87d614fa1280" + + "8f9adf01790101017c080000000000000000017d080000000000000000" + + "00" + legacyAnchorPkScript = "512021b15bddf2d991b8791ec7c2105fa69be61008d78" + + "b67d261451290d7a44dce5f" +) + +// scalarKey derives a deterministic test key from a one-byte scalar. +func scalarKey(t *testing.T, scalar byte) (*btcec.PrivateKey, + *btcec.PublicKey) { + + t.Helper() + + keyBytes := make([]byte, 32) + keyBytes[31] = scalar + privateKey, publicKey := btcec.PrivKeyFromBytes(keyBytes) + + return privateKey, publicKey +} + +// vectorParams returns the immutable inputs used by the legacy contract +// vectors. +func vectorParams(t *testing.T) Params { + t.Helper() + + _, sender := scalarKey(t, 1) + _, receiver := scalarKey(t, 2) + var assetID asset.ID + var swapHash lntypes.Hash + for idx := range assetID { + assetID[idx] = byte(idx) + swapHash[idx] = byte(idx) + } + + return Params{ + SenderPubKey: sender, + ReceiverPubKey: receiver, + AssetID: assetID, + Amount: 1000, + SwapHash: swapHash, + CsvExpiry: 144, + AddressParams: cloneAddressParams(address.RegressionNetTap), + } +} + +// newVectorKit creates a swap kit from the legacy contract vector inputs. +func newVectorKit(t *testing.T, policy Policy) *SwapKit { + t.Helper() + + kit, err := NewSwapKit(policy, vectorParams(t)) + require.NoError(t, err) + + return kit +} + +// TestLegacyDepositVectors freezes the scripts and aggregate key used by the +// existing valid server deposit HTLC contract. +func TestLegacyDepositVectors(t *testing.T) { + legacyKit := newVectorKit(t, LegacyDepositV0) + + successScript, err := legacyKit.GetSuccessScript() + require.NoError(t, err) + require.Equal(t, legacySuccessScript, + hex.EncodeToString(successScript)) + + timeoutScript, err := legacyKit.GetTimeoutScript() + require.NoError(t, err) + require.Equal(t, legacyTimeoutScript, + hex.EncodeToString(timeoutScript)) + + aggregateKey, err := legacyKit.GetAggregateKey() + require.NoError(t, err) + require.Equal(t, legacyAggregateKey, + hex.EncodeToString(aggregateKey.SerializeCompressed())) + + sibling, err := legacyKit.GetSiblingPreimage() + require.NoError(t, err) + siblingBytes, _, err := commitment.MaybeEncodeTapscriptPreimage( + &sibling, + ) + require.NoError(t, err) + require.Equal(t, legacySibling, hex.EncodeToString(siblingBytes)) + + opTrueKey, _, _, opTrueControl, err := CreateOpTrueLeaf() + require.NoError(t, err) + require.Equal(t, legacyOpTrueKey, hex.EncodeToString( + opTrueKey.PubKey.SerializeCompressed(), + )) + require.Equal(t, legacyOpTrueTweak, + hex.EncodeToString(opTrueKey.TweakedScriptKey.Tweak)) + opTrueControlBytes, err := opTrueControl.ToBytes() + require.NoError(t, err) + require.Equal(t, legacyOpTrueControl, + hex.EncodeToString(opTrueControlBytes)) + opTrueScriptKey, err := GetOpTrueScriptKey() + require.NoError(t, err) + require.Equal(t, legacyOpTrueScriptKey, + hex.EncodeToString(opTrueScriptKey)) + + assetRoot := make([]byte, 32) + for idx := range assetRoot { + assetRoot[idx] = byte(0xa0 + idx) + } + successControl, err := legacyKit.GenSuccessBtcControlBlock(assetRoot) + require.NoError(t, err) + successControlBytes, err := successControl.ToBytes() + require.NoError(t, err) + require.Equal(t, legacySuccessControl, + hex.EncodeToString(successControlBytes)) + timeoutControl, err := legacyKit.GenTimeoutBtcControlBlock(assetRoot) + require.NoError(t, err) + timeoutControlBytes, err := timeoutControl.ToBytes() + require.NoError(t, err) + require.Equal(t, legacyTimeoutControl, + hex.EncodeToString(timeoutControlBytes)) + anchorPkScript, err := legacyKit.GetPkScriptFromRoot(assetRoot) + require.NoError(t, err) + require.Equal(t, legacyAnchorPkScript, + hex.EncodeToString(anchorPkScript)) + + packet, err := legacyKit.CreateHtlcVpkt() + require.NoError(t, err) + var packetBytes bytes.Buffer + require.NoError(t, packet.Serialize(&packetBytes)) + require.Equal(t, legacyVPacket, + hex.EncodeToString(packetBytes.Bytes())) +} + +// TestTimeoutScriptNumberEncoding freezes BIP68 boundary encodings so a +// library update cannot silently change an already negotiated script. +func TestTimeoutScriptNumberEncoding(t *testing.T) { + _, sender := scalarKey(t, 1) + const prefix = "2079be667ef9dcbbac55a06295ce870b07029bfcdb2dce" + + "28d959f2815b16f81798ad" + tests := []struct { + expiry int64 + hex string + }{ + {expiry: 1, hex: prefix + "51b2"}, + {expiry: 16, hex: prefix + "60b2"}, + {expiry: 128, hex: prefix + "028000b2"}, + {expiry: 256, hex: prefix + "020001b2"}, + } + + for _, testCase := range tests { + script, err := GenTimeoutPathScript(sender, testCase.expiry) + require.NoError(t, err) + require.Equal(t, testCase.hex, hex.EncodeToString(script)) + } +} + +// TestNewSwapKitValidation verifies malformed contracts fail before any +// scripts or addresses can be derived. +func TestNewSwapKitValidation(t *testing.T) { + validParams := vectorParams(t) + _, err := NewSwapKit(PolicyUnknown, validParams) + require.Error(t, err) + _, err = NewSwapKit(Policy(255), validParams) + require.Error(t, err) + + tests := []struct { + name string + mutate func(*Params) + }{ + { + name: "missing sender", + mutate: func(params *Params) { + params.SenderPubKey = nil + }, + }, + { + name: "missing receiver", + mutate: func(params *Params) { + params.ReceiverPubKey = nil + }, + }, + { + name: "identical keys", + mutate: func(params *Params) { + params.ReceiverPubKey = params.SenderPubKey + }, + }, + { + name: "zero amount", + mutate: func(params *Params) { + params.Amount = 0 + }, + }, + { + name: "zero asset ID", + mutate: func(params *Params) { + params.AssetID = asset.ID{} + }, + }, + { + name: "zero swap hash", + mutate: func(params *Params) { + params.SwapHash = lntypes.Hash{} + }, + }, + { + name: "zero expiry", + mutate: func(params *Params) { + params.CsvExpiry = 0 + }, + }, + { + name: "expiry equals success sequence", + mutate: func(params *Params) { + params.CsvExpiry = SuccessSequence + }, + }, + { + name: "time based expiry", + mutate: func(params *Params) { + params.CsvExpiry = wire.SequenceLockTimeIsSeconds + }, + }, + { + name: "missing address params", + mutate: func(params *Params) { + params.AddressParams = nil + }, + }, + { + name: "incomplete address params", + mutate: func(params *Params) { + params.AddressParams = &address.ChainParams{} + }, + }, + { + name: "mismatched address params", + mutate: func(params *Params) { + mismatch := address.RegressionNetTap + mismatch.TapHRP = address.MainNetTap.TapHRP + params.AddressParams = &mismatch + }, + }, + { + name: "unsupported address params", + mutate: func(params *Params) { + unsupported := address.RegressionNetTap + unsupported.TapHRP = "tapunknown" + params.AddressParams = &unsupported + }, + }, + } + + for _, testCase := range tests { + t.Run(testCase.name, func(t *testing.T) { + params := validParams + if testCase.mutate != nil { + testCase.mutate(¶ms) + } + _, err := NewSwapKit(LegacyDepositV0, params) + require.Error(t, err) + }) + } + + _, err = GenSuccessPathScript(nil, lntypes.Hash{}) + require.Error(t, err) + _, err = GenTimeoutPathScript(nil, 1) + require.Error(t, err) + _, err = GenTimeoutPathScript(validParams.SenderPubKey, 0) + require.Error(t, err) +} + +// TestSupportedNetworks ensures shared testnet HRPs do not collapse signet or +// testnet4 into testnet3, and both native and lnd-compatible simnet coin types +// remain supported. +func TestSupportedNetworks(t *testing.T) { + rawSimNet := cloneAddressParams(address.SimNetTap) + rawSimNet.HDCoinType = rawSimNetHDCoinType + lndSimNet := cloneAddressParams( + address.ParamsForChain(address.SimNetTap.Name), + ) + + networks := []*address.ChainParams{ + &address.MainNetTap, + &address.TestNet3Tap, + &address.TestNet4Tap, + &address.RegressionNetTap, + &address.SigNetTap, + rawSimNet, + lndSimNet, + } + for _, network := range networks { + params := vectorParams(t) + params.AddressParams = network + kit, err := NewSwapKit(LegacyDepositV0, params) + require.NoError(t, err) + require.Equal(t, network.Net, kit.AddressParams().Net) + require.Equal(t, network.TapHRP, kit.AddressParams().TapHRP) + require.Equal(t, network.HDCoinType, + kit.AddressParams().HDCoinType) + } +} + +// TestSwapKitIsImmutable verifies constructor inputs and returned address +// parameters cannot alter a previously constructed contract. +func TestSwapKitIsImmutable(t *testing.T) { + params := vectorParams(t) + kit, err := NewSwapKit(LegacyDepositV0, params) + require.NoError(t, err) + + params.AssetID[0] = 99 + params.AddressParams.TapHRP = "mutated" + require.Zero(t, kit.AssetID()[0]) + require.Equal(t, address.RegressionNetTap.TapHRP, + kit.AddressParams().TapHRP) + + returnedParams := kit.AddressParams() + returnedParams.TapHRP = "also-mutated" + returnedParams.Params.Name = "mutated-chain" + require.Equal(t, address.RegressionNetTap.TapHRP, + kit.AddressParams().TapHRP) + require.Equal(t, address.RegressionNetTap.Params.Name, + kit.AddressParams().Params.Name) + + packet, err := kit.CreateHtlcVpkt() + require.NoError(t, err) + packet.ChainParams.TapHRP = "packet-mutated" + nextPacket, err := kit.CreateHtlcVpkt() + require.NoError(t, err) + require.Equal(t, address.RegressionNetTap.TapHRP, + nextPacket.ChainParams.TapHRP) +} + +// TestCreateHtlcVpkt freezes the output roles and flags that existing Taproot +// Assets deposit transfers depend upon. +func TestCreateHtlcVpkt(t *testing.T) { + kit := newVectorKit(t, LegacyDepositV0) + packet, err := kit.CreateHtlcVpkt() + require.NoError(t, err) + + require.Equal(t, tappsbt.V1, packet.Version) + require.Len(t, packet.Inputs, 1) + require.Equal(t, kit.AssetID(), packet.Inputs[0].PrevID.ID) + require.Len(t, packet.Outputs, 2) + + splitRoot := packet.Outputs[0] + require.Equal(t, asset.V1, splitRoot.AssetVersion) + require.Zero(t, splitRoot.Amount) + require.Equal(t, uint32(0), splitRoot.AnchorOutputIndex) + require.True(t, splitRoot.Interactive) + require.Equal(t, asset.NUMSScriptKey.PubKey.SerializeCompressed(), + splitRoot.ScriptKey.PubKey.SerializeCompressed()) + + htlcOutput := packet.Outputs[1] + require.Equal(t, asset.V1, htlcOutput.AssetVersion) + require.Equal(t, kit.Amount(), htlcOutput.Amount) + require.Equal(t, uint32(1), htlcOutput.AnchorOutputIndex) + require.True(t, htlcOutput.Interactive) + require.NotNil(t, htlcOutput.AnchorOutputInternalKey) + require.NotNil(t, htlcOutput.AnchorOutputTapscriptSibling) +} + +// TestAssetHtlcScriptEngine proves both paths execute and reject the wrong +// preimage, signer, and relative sequence. +func TestAssetHtlcScriptEngine(t *testing.T) { + senderPriv, senderPub := scalarKey(t, 1) + receiverPriv, receiverPub := scalarKey(t, 2) + var assetID asset.ID + var preimage lntypes.Preimage + for idx := range preimage { + assetID[idx] = byte(idx) + preimage[idx] = byte(31 - idx) + } + + kit, err := NewSwapKit(LegacyDepositV0, Params{ + SenderPubKey: senderPub, ReceiverPubKey: receiverPub, + AssetID: assetID, Amount: 1000, SwapHash: preimage.Hash(), + CsvExpiry: 144, AddressParams: &address.RegressionNetTap, + }) + require.NoError(t, err) + + tests := []struct { + name string + success bool + sequence uint32 + signingKey *btcec.PrivateKey + preimage lntypes.Preimage + valid bool + }{ + { + name: "success", success: true, + sequence: SuccessSequence, signingKey: receiverPriv, + preimage: preimage, valid: true, + }, + { + name: "wrong preimage", success: true, + sequence: SuccessSequence, signingKey: receiverPriv, + preimage: lntypes.Preimage{1}, + }, + { + name: "wrong success key", success: true, + sequence: SuccessSequence, signingKey: senderPriv, + preimage: preimage, + }, + { + name: "timeout", sequence: kit.CsvExpiry(), + signingKey: senderPriv, valid: true, + }, + { + name: "timeout one block early", + sequence: kit.CsvExpiry() - 1, signingKey: senderPriv, + }, + { + name: "wrong timeout key", sequence: kit.CsvExpiry(), + signingKey: receiverPriv, + }, + } + + for _, testCase := range tests { + t.Run(testCase.name, func(t *testing.T) { + assertScriptSpend(t, kit, testCase.success, + testCase.sequence, testCase.signingKey, + testCase.preimage, testCase.valid) + }) + } +} + +// assertScriptSpend executes one HTLC script path and checks its result. +func assertScriptSpend(t *testing.T, kit *SwapKit, success bool, + sequence uint32, signingKey *btcec.PrivateKey, + preimage lntypes.Preimage, valid bool) { + + t.Helper() + + assetRoot := bytes.Repeat([]byte{0x42}, 32) + var ( + controlBlock *txscript.ControlBlock + leaf txscript.TapLeaf + script []byte + err error + ) + if success { + controlBlock, err = kit.GenSuccessBtcControlBlock(assetRoot) + require.NoError(t, err) + leaf, err = kit.GetSuccessLeaf() + require.NoError(t, err) + script = leaf.Script + } else { + controlBlock, err = kit.GenTimeoutBtcControlBlock(assetRoot) + require.NoError(t, err) + leaf, err = kit.GetTimeoutLeaf() + require.NoError(t, err) + script = leaf.Script + } + + rootHash := controlBlock.RootHash(script) + internalKey, err := kit.GetAggregateKey() + require.NoError(t, err) + outputKey := txscript.ComputeTaprootOutputKey(internalKey, rootHash) + pkScript, err := txscript.PayToTaprootScript(outputKey) + require.NoError(t, err) + + const value = int64(50_000) + tx := wire.NewMsgTx(2) + tx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: wire.OutPoint{ + Hash: chainhash.Hash{1}, Index: 2, + }, + Sequence: sequence, + }) + tx.AddTxOut(&wire.TxOut{Value: value - 1000, + PkScript: []byte{txscript.OP_TRUE}}) + prevOutFetcher := txscript.NewCannedPrevOutputFetcher(pkScript, value) + sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher) + signature, err := txscript.RawTxInTapscriptSignature( + tx, sigHashes, 0, value, pkScript, leaf, + txscript.SigHashDefault, signingKey, + ) + require.NoError(t, err) + controlBytes, err := controlBlock.ToBytes() + require.NoError(t, err) + + if success { + tx.TxIn[0].Witness = wire.TxWitness{ + preimage[:], signature, script, controlBytes, + } + } else { + tx.TxIn[0].Witness = wire.TxWitness{ + signature, script, controlBytes, + } + } + + engine, err := txscript.NewEngine( + pkScript, tx, 0, txscript.StandardVerifyFlags, nil, + sigHashes, value, prevOutFetcher, + ) + require.NoError(t, err) + if valid { + require.NoError(t, engine.Execute()) + } else { + require.Error(t, engine.Execute()) + } +} + +type localSigner struct { + lndclient.SignerClient + + privateKey *btcec.PrivateKey + expectedInput int + calls int +} + +func (s *localSigner) SignOutputRaw(_ context.Context, tx *wire.MsgTx, + descriptors []*lndclient.SignDescriptor, + prevOutputs []*wire.TxOut) ([][]byte, error) { + + s.calls++ + if len(descriptors) != 1 { + return nil, fmt.Errorf("expected one sign descriptor") + } + if len(prevOutputs) != len(tx.TxIn) { + return nil, fmt.Errorf("expected all previous outputs") + } + + descriptor := descriptors[0] + if descriptor.InputIndex != s.expectedInput { + return nil, fmt.Errorf("unexpected input index: %d", + descriptor.InputIndex) + } + if descriptor.SignMethod != input.TaprootScriptSpendSignMethod { + return nil, fmt.Errorf("unexpected signing method") + } + + prevOutFetcher := txscript.NewMultiPrevOutFetcher(nil) + for idx, txIn := range tx.TxIn { + prevOutFetcher.AddPrevOut( + txIn.PreviousOutPoint, prevOutputs[idx], + ) + } + sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher) + tapLeaf := txscript.NewBaseTapLeaf(descriptor.WitnessScript) + signature, err := txscript.RawTxInTapscriptSignature( + tx, sigHashes, descriptor.InputIndex, + descriptor.Output.Value, descriptor.Output.PkScript, tapLeaf, + txscript.SigHashDefault, s.privateKey, + ) + if err != nil { + return nil, err + } + + return [][]byte{signature}, nil +} + +type witnessFixture struct { + kit *SwapKit + proof *proof.Proof + packet *psbt.Packet + prevOutputs []*wire.TxOut + senderKey *btcec.PrivateKey + receiverKey *btcec.PrivateKey + preimage lntypes.Preimage + assetInIndex int +} + +// newWitnessFixture creates a proof-bound HTLC and sweep packet using the +// default asset and proof versions. +func newWitnessFixture(t *testing.T, sequence uint32) *witnessFixture { + return newWitnessFixtureWithVersions( + t, sequence, asset.V1, commitment.TapCommitmentV2, + ) +} + +// newWitnessFixtureWithVersions creates a proof-bound HTLC and sweep packet +// using explicit asset and proof versions. +func newWitnessFixtureWithVersions(t *testing.T, sequence uint32, + assetVersion asset.Version, + commitmentVersion commitment.TapCommitmentVersion) *witnessFixture { + + t.Helper() + + senderKey, senderPubKey := scalarKey(t, 1) + receiverKey, receiverPubKey := scalarKey(t, 2) + var preimage lntypes.Preimage + for idx := range preimage { + preimage[idx] = byte(31 - idx) + } + + genesis := asset.Genesis{ + FirstPrevOut: wire.OutPoint{ + Hash: chainhash.Hash{0x31}, + Index: 3, + }, + Tag: "asset htlc witness test", + OutputIndex: 0, + Type: asset.Normal, + } + opTrueScriptKey, _, _, _, err := CreateOpTrueLeaf() + require.NoError(t, err) + opTrueScriptKey = asset.NewScriptKey(opTrueScriptKey.PubKey) + htlcAsset, err := asset.New( + genesis, 1000, 0, 0, opTrueScriptKey, nil, + asset.WithAssetVersion(assetVersion), + ) + require.NoError(t, err) + + kit, err := NewSwapKit(LegacyDepositV0, Params{ + SenderPubKey: senderPubKey, + ReceiverPubKey: receiverPubKey, + AssetID: genesis.ID(), + Amount: htlcAsset.Amount, + SwapHash: preimage.Hash(), + CsvExpiry: 144, + AddressParams: &address.RegressionNetTap, + }) + require.NoError(t, err) + + tapCommitment, err := commitment.FromAssets( + &commitmentVersion, htlcAsset, + ) + require.NoError(t, err) + _, commitmentProof, err := tapCommitment.Proof( + htlcAsset.TapCommitmentKey(), + htlcAsset.AssetCommitmentKey(), + ) + require.NoError(t, err) + sibling, err := kit.GetSiblingPreimage() + require.NoError(t, err) + siblingHash, err := sibling.TapHash() + require.NoError(t, err) + internalKey, err := kit.GetAggregateKey() + require.NoError(t, err) + anchorPkScript, err := tapscript.PayToAddrScript( + *internalKey, siblingHash, *tapCommitment, + ) + require.NoError(t, err) + + const anchorValue = int64(50_000) + anchorTx := wire.NewMsgTx(2) + anchorTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: genesis.FirstPrevOut, + }) + anchorTx.AddTxOut(&wire.TxOut{ + Value: 1000, + PkScript: []byte{txscript.OP_TRUE}, + }) + anchorTx.AddTxOut(&wire.TxOut{ + Value: anchorValue, + PkScript: anchorPkScript, + }) + + assetProof := &proof.Proof{ + AnchorTx: *anchorTx, + Asset: *htlcAsset, + InclusionProof: proof.TaprootProof{ + OutputIndex: 1, + InternalKey: internalKey, + CommitmentProof: &proof.CommitmentProof{ + Proof: *commitmentProof, + TapSiblingPreimage: &sibling, + }, + }, + } + _, err = assetProof.VerifyProofs() + require.NoError(t, err) + + feeOutPoint := wire.OutPoint{ + Hash: chainhash.Hash{0x42}, + Index: 5, + } + assetInIndex := 1 + sweepTx := wire.NewMsgTx(2) + sweepTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: feeOutPoint, + Sequence: wire.MaxTxInSequenceNum, + }) + sweepTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: assetProof.OutPoint(), + Sequence: sequence, + }) + sweepTx.AddTxOut(&wire.TxOut{ + Value: anchorValue - 2000, + PkScript: []byte{txscript.OP_TRUE}, + }) + sweepPacket, err := psbt.NewFromUnsignedTx(sweepTx) + require.NoError(t, err) + prevOutputs := []*wire.TxOut{ + { + Value: 3000, + PkScript: []byte{txscript.OP_TRUE}, + }, + { + Value: anchorValue, + PkScript: append([]byte(nil), anchorPkScript...), + }, + } + for idx := range prevOutputs { + sweepPacket.Inputs[idx].WitnessUtxo = prevOutputs[idx] + } + + return &witnessFixture{ + kit: kit, proof: assetProof, packet: sweepPacket, + prevOutputs: prevOutputs, senderKey: senderKey, + receiverKey: receiverKey, preimage: preimage, + assetInIndex: assetInIndex, + } +} + +// TestGetPkScriptFromProof verifies that anchor reconstruction follows the +// commitment version proven on chain. The asset version alone cannot +// distinguish a legacy commitment from a version-two commitment. +func TestGetPkScriptFromProof(t *testing.T) { + tests := []struct { + name string + assetVersion asset.Version + commitmentVersion commitment.TapCommitmentVersion + }{ + { + name: "legacy v0", + assetVersion: asset.V0, + commitmentVersion: commitment.TapCommitmentV0, + }, + { + name: "legacy v1", + assetVersion: asset.V1, + commitmentVersion: commitment.TapCommitmentV1, + }, + { + name: "version two", + assetVersion: asset.V1, + commitmentVersion: commitment.TapCommitmentV2, + }, + } + + for _, testCase := range tests { + t.Run(testCase.name, func(t *testing.T) { + fixture := newWitnessFixtureWithVersions( + t, SuccessSequence, testCase.assetVersion, + testCase.commitmentVersion, + ) + pkScript, err := fixture.kit.GetPkScriptFromProof( + fixture.proof, + ) + require.NoError(t, err) + require.Equal( + t, fixture.prevOutputs[fixture.assetInIndex].PkScript, + pkScript, + ) + }) + } +} + +// executeWitness runs the virtual asset VM and Bitcoin script engine against a +// generated witness. +func (f *witnessFixture) executeWitness(t *testing.T, + witness *SpendWitness) { + + t.Helper() + + require.Equal(t, uint32(f.assetInIndex), witness.InputIndex) + tx := f.packet.UnsignedTx + tx.TxIn[witness.InputIndex].Witness = witness.Witness + + prevOutFetcher := txscript.NewMultiPrevOutFetcher(nil) + for idx, txIn := range tx.TxIn { + prevOutFetcher.AddPrevOut( + txIn.PreviousOutPoint, f.prevOutputs[idx], + ) + } + sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher) + prevOutput := f.prevOutputs[witness.InputIndex] + engine, err := txscript.NewEngine( + prevOutput.PkScript, tx, int(witness.InputIndex), + txscript.StandardVerifyFlags, nil, sigHashes, + prevOutput.Value, prevOutFetcher, + ) + require.NoError(t, err) + require.NoError(t, engine.Execute()) +} + +// TestCreateProofBoundWitnesses proves both witness builders sign the input +// selected by the asset proof even when an unrelated input precedes it. +func TestCreateProofBoundWitnesses(t *testing.T) { + t.Run("success", func(t *testing.T) { + fixture := newWitnessFixture(t, SuccessSequence) + inputIndex, err := fixture.kit.AssetInputIndex( + fixture.proof, fixture.packet, + ) + require.NoError(t, err) + require.Equal(t, uint32(fixture.assetInIndex), inputIndex) + + signer := &localSigner{ + privateKey: fixture.receiverKey, + expectedInput: fixture.assetInIndex, + } + witness, err := fixture.kit.CreatePreimageWitness( + t.Context(), signer, fixture.proof, fixture.packet, + keychain.KeyLocator{}, fixture.preimage, + ) + require.NoError(t, err) + require.Equal(t, 1, signer.calls) + require.Len(t, witness.Witness, 4) + fixture.executeWitness(t, witness) + }) + + t.Run("timeout", func(t *testing.T) { + fixture := newWitnessFixture(t, 144) + signer := &localSigner{ + privateKey: fixture.senderKey, + expectedInput: fixture.assetInIndex, + } + witness, err := fixture.kit.CreateTimeoutWitness( + t.Context(), signer, fixture.proof, fixture.packet, + keychain.KeyLocator{}, + ) + require.NoError(t, err) + require.Equal(t, 1, signer.calls) + require.Len(t, witness.Witness, 3) + fixture.executeWitness(t, witness) + }) +} + +// TestProofBoundWitnessValidation freezes the checks that keep proof, PSBT, +// contract, and signing key bound to the same funded asset output. +func TestProofBoundWitnessValidation(t *testing.T) { + tests := []struct { + name string + mutate func(*witnessFixture) + sign func(*testing.T, *witnessFixture, *localSigner) error + }{ + { + name: "wrong success sequence", + mutate: func(fixture *witnessFixture) { + fixture.packet.UnsignedTx.TxIn[1].Sequence = 2 + }, + }, + { + name: "wrong preimage", + sign: func(t *testing.T, fixture *witnessFixture, + signer *localSigner) error { + + _, err := fixture.kit.CreatePreimageWitness( + t.Context(), signer, fixture.proof, + fixture.packet, keychain.KeyLocator{}, + lntypes.Preimage{1}, + ) + return err + }, + }, + { + name: "wrong timeout sequence", + mutate: func(fixture *witnessFixture) { + fixture.packet.UnsignedTx.TxIn[1].Sequence = 143 + }, + sign: func(t *testing.T, fixture *witnessFixture, + signer *localSigner) error { + + _, err := fixture.kit.CreateTimeoutWitness( + t.Context(), signer, fixture.proof, + fixture.packet, keychain.KeyLocator{}, + ) + return err + }, + }, + { + name: "wrong success signing key", + mutate: func(fixture *witnessFixture) { + fixture.receiverKey = fixture.senderKey + }, + }, + { + name: "wrong timeout signing key", + sign: func(t *testing.T, fixture *witnessFixture, + signer *localSigner) error { + + fixture.packet.UnsignedTx.TxIn[1].Sequence = + fixture.kit.CsvExpiry() + _, err := fixture.kit.CreateTimeoutWitness( + t.Context(), signer, fixture.proof, + fixture.packet, keychain.KeyLocator{}, + ) + return err + }, + }, + { + name: "witness value mismatch", + mutate: func(fixture *witnessFixture) { + fixture.packet.Inputs[1].WitnessUtxo.Value++ + }, + }, + { + name: "witness script mismatch", + mutate: func(fixture *witnessFixture) { + fixture.packet.Inputs[1].WitnessUtxo.PkScript = + []byte{txscript.OP_TRUE} + }, + }, + { + name: "missing proof input", + mutate: func(fixture *witnessFixture) { + fixture.packet.UnsignedTx.TxIn = + fixture.packet.UnsignedTx.TxIn[:1] + fixture.packet.Inputs = fixture.packet.Inputs[:1] + }, + }, + { + name: "duplicate proof input", + mutate: func(fixture *witnessFixture) { + assetInput := *fixture.packet.UnsignedTx.TxIn[1] + fixture.packet.UnsignedTx.TxIn = append( + fixture.packet.UnsignedTx.TxIn, &assetInput, + ) + fixture.packet.Inputs = append( + fixture.packet.Inputs, + psbt.PInput{WitnessUtxo: fixture.prevOutputs[1]}, + ) + }, + }, + { + name: "missing fee input metadata", + mutate: func(fixture *witnessFixture) { + fixture.packet.Inputs[0].WitnessUtxo = nil + }, + }, + { + name: "version one sweep", + mutate: func(fixture *witnessFixture) { + fixture.packet.UnsignedTx.Version = 1 + }, + }, + { + name: "asset version", + mutate: func(fixture *witnessFixture) { + fixture.proof.Asset.Version = asset.V0 + }, + }, + { + name: "asset script key", + mutate: func(fixture *witnessFixture) { + _, publicKey := scalarKey(t, 3) + fixture.proof.Asset.ScriptKey = + asset.NewScriptKey(publicKey) + }, + }, + { + name: "asset script version", + mutate: func(fixture *witnessFixture) { + fixture.proof.Asset.ScriptVersion++ + }, + }, + { + name: "asset lock time", + mutate: func(fixture *witnessFixture) { + fixture.proof.Asset.LockTime = 1 + }, + }, + { + name: "asset relative lock time", + mutate: func(fixture *witnessFixture) { + fixture.proof.Asset.RelativeLockTime = 1 + }, + }, + } + + for _, testCase := range tests { + t.Run(testCase.name, func(t *testing.T) { + fixture := newWitnessFixture(t, SuccessSequence) + if testCase.mutate != nil { + testCase.mutate(fixture) + } + signer := &localSigner{ + privateKey: fixture.receiverKey, + expectedInput: fixture.assetInIndex, + } + if testCase.sign != nil { + require.Error(t, testCase.sign(t, fixture, signer)) + return + } + + _, err := fixture.kit.CreatePreimageWitness( + t.Context(), signer, fixture.proof, + fixture.packet, keychain.KeyLocator{}, + fixture.preimage, + ) + require.Error(t, err) + }) + } +} + +// TestValidateAssetPolicy isolates the asset-layer checks from proof +// verification so weakening one boundary cannot be masked by the other. +func TestValidateAssetPolicy(t *testing.T) { + fixture := newWitnessFixture(t, SuccessSequence) + require.NoError(t, fixture.kit.validateAsset(&fixture.proof.Asset)) + + tests := []struct { + name string + mutate func(*asset.Asset) + }{ + { + name: "asset ID", + mutate: func(candidate *asset.Asset) { + candidate.Genesis.Tag = "different asset" + }, + }, + { + name: "amount", + mutate: func(candidate *asset.Asset) { + candidate.Amount++ + }, + }, + { + name: "asset version", + mutate: func(candidate *asset.Asset) { + candidate.Version = asset.Version(255) + }, + }, + { + name: "script version", + mutate: func(candidate *asset.Asset) { + candidate.ScriptVersion++ + }, + }, + { + name: "script key", + mutate: func(candidate *asset.Asset) { + _, publicKey := scalarKey(t, 3) + candidate.ScriptKey = asset.NewScriptKey(publicKey) + }, + }, + { + name: "absolute locktime", + mutate: func(candidate *asset.Asset) { + candidate.LockTime = 1 + }, + }, + { + name: "relative locktime", + mutate: func(candidate *asset.Asset) { + candidate.RelativeLockTime = 1 + }, + }, + } + + for _, testCase := range tests { + t.Run(testCase.name, func(t *testing.T) { + candidate := fixture.proof.Asset.CopySpendTemplate() + testCase.mutate(candidate) + require.Error(t, fixture.kit.validateAsset(candidate)) + }) + } + + legacyV0Asset := fixture.proof.Asset.CopySpendTemplate() + legacyV0Asset.Version = asset.V0 + require.NoError(t, fixture.kit.validateAsset(legacyV0Asset)) +} + +// TestControlBlockRootLength rejects roots that cannot represent a Taproot +// branch hash. +func TestControlBlockRootLength(t *testing.T) { + kit := newVectorKit(t, LegacyDepositV0) + _, err := kit.GenSuccessBtcControlBlock(make([]byte, 31)) + require.Error(t, err) + _, err = kit.GenTimeoutBtcControlBlock(make([]byte, 33)) + require.Error(t, err) +} + +// TestFindUniqueInput proves sweep input selection is based on the exact +// proof outpoint rather than a positional convention. +func TestFindUniqueInput(t *testing.T) { + target := wire.OutPoint{Hash: chainhash.Hash{9}, Index: 7} + other := wire.OutPoint{Hash: chainhash.Hash{8}, Index: 3} + + tests := []struct { + name string + inputs []*wire.TxIn + expected int + expectErr bool + }{ + { + name: "first", + inputs: []*wire.TxIn{ + {PreviousOutPoint: target}, + {PreviousOutPoint: other}, + }, + expected: 0, + }, + { + name: "middle of three", + inputs: []*wire.TxIn{ + {PreviousOutPoint: other}, + {PreviousOutPoint: target}, + { + PreviousOutPoint: wire.OutPoint{ + Hash: chainhash.Hash{7}, + }, + }, + }, + expected: 1, + }, + { + name: "missing", + inputs: []*wire.TxIn{ + {PreviousOutPoint: other}, + }, + expectErr: true, + }, + { + name: "duplicate", + inputs: []*wire.TxIn{ + {PreviousOutPoint: target}, + {PreviousOutPoint: target}, + }, + expectErr: true, + }, + { + name: "nil input", + inputs: []*wire.TxIn{ + {PreviousOutPoint: target}, nil, + }, + expectErr: true, + }, + } + + for _, testCase := range tests { + t.Run(testCase.name, func(t *testing.T) { + tx := wire.NewMsgTx(2) + tx.TxIn = testCase.inputs + index, err := findUniqueInput(tx, target) + if testCase.expectErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, testCase.expected, index) + }) + } +} + +// TestSiblingPreimageRoundTrip ensures the branch preimage retains its +// Taproot hash when encoded for an address RPC. +func TestSiblingPreimageRoundTrip(t *testing.T) { + kit := newVectorKit(t, LegacyDepositV0) + sibling, err := kit.GetSiblingPreimage() + require.NoError(t, err) + encoded, _, err := commitment.MaybeEncodeTapscriptPreimage(&sibling) + require.NoError(t, err) + require.NotEmpty(t, encoded) + + timeoutLeaf, err := kit.GetTimeOutLeaf() + require.NoError(t, err) + successLeaf, err := kit.GetSuccessLeaf() + require.NoError(t, err) + expectedPreimage := commitment.NewPreimageFromBranch( + txscript.NewTapBranch(timeoutLeaf, successLeaf), + ) + decoded, err := expectedPreimage.TapHash() + require.NoError(t, err) + actualHash, err := sibling.TapHash() + require.NoError(t, err) + require.Equal(t, decoded, actualHash) +} From 397d5d4a7573e94d5975c7b0213f9deefa4b476a Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Wed, 16 Sep 2026 15:40:12 +0200 Subject: [PATCH 3/8] assets: Move deposit kit into Loop Consolidate the remaining deposit and OP_TRUE virtual-packet helpers behind Loop-owned packages. Replace positional sweep assumptions with proof-bound input selection, complete prevout validation, and explicit signature verification. Preserve the complete anchor Merkle root, including the timeout sibling, so cooperative MuSig2 spends reproduce the output Taproot tweak. Expose separate boundaries for verified commitments and untrusted proof files, binding the latter to the expected deposit outpoint and amount. --- assets/deposit/doc.go | 5 + assets/deposit/kit.go | 760 +++++++++++++++++++++++++++++++++++++ assets/deposit/kit_test.go | 552 +++++++++++++++++++++++++++ assets/tapkit.go | 146 +++++++ assets/tapkit_test.go | 107 ++++++ 5 files changed, 1570 insertions(+) create mode 100644 assets/deposit/doc.go create mode 100644 assets/deposit/kit.go create mode 100644 assets/deposit/kit_test.go create mode 100644 assets/tapkit.go create mode 100644 assets/tapkit_test.go diff --git a/assets/deposit/doc.go b/assets/deposit/doc.go new file mode 100644 index 000000000..53198dc66 --- /dev/null +++ b/assets/deposit/doc.go @@ -0,0 +1,5 @@ +// Package deposit contains the legacy Taproot Asset deposit contract used by +// the Loop integration environment. It owns deposit address construction, +// proof validation, and the unilateral timeout path. Asset HTLC construction +// remains in the sibling htlc package. +package deposit diff --git a/assets/deposit/kit.go b/assets/deposit/kit.go new file mode 100644 index 000000000..56b58ff5d --- /dev/null +++ b/assets/deposit/kit.go @@ -0,0 +1,760 @@ +package deposit + +import ( + "bytes" + "context" + "fmt" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/btcsuite/btcd/btcutil/psbt" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop/assets/htlc" + "github.com/lightninglabs/taproot-assets/address" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/commitment" + "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/rpcutils" + "github.com/lightninglabs/taproot-assets/taprpc" + "github.com/lightninglabs/taproot-assets/tapscript" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" + "google.golang.org/grpc" +) + +// AddressProofClient is the narrow tapd boundary needed to create deposit +// addresses and export their proofs. +type AddressProofClient interface { + NewAddr(context.Context, *taprpc.NewAddrRequest, + ...grpc.CallOption) (*taprpc.Addr, error) + ExportProof(context.Context, *taprpc.ExportProofRequest, + ...grpc.CallOption) (*taprpc.ProofFile, error) +} + +// ProofVerifier is the narrow tapd boundary needed to verify a complete proof +// file against tapd's chain backend and asset transition verifier. +type ProofVerifier interface { + VerifyProof(context.Context, *taprpc.ProofFile, + ...grpc.CallOption) (*taprpc.VerifyProofResponse, error) +} + +var _ ProofVerifier = (taprpc.TaprootAssetsClient)(nil) + +// Kit contains the immutable information needed to create and operate a +// two-party MuSig2 asset deposit. +type Kit struct { + funderKey *btcec.PublicKey + coSignerKey *btcec.PublicKey + keyLocator keychain.KeyLocator + assetID asset.ID + csvExpiry uint32 + muSig2Key *musig2.AggregateKey + chainParams *address.ChainParams +} + +// NewKit creates a validated legacy asset deposit kit. +func NewKit(funderKey, coSignerKey *btcec.PublicKey, + keyLocator keychain.KeyLocator, assetID asset.ID, csvExpiry uint32, + chainParams *address.ChainParams) (*Kit, error) { + + if funderKey == nil { + return nil, fmt.Errorf("funder public key is required") + } + if coSignerKey == nil { + return nil, fmt.Errorf("co-signer public key is required") + } + if funderKey.IsEqual(coSignerKey) { + return nil, fmt.Errorf("funder and co-signer keys must differ") + } + if assetID == (asset.ID{}) { + return nil, fmt.Errorf("asset ID is required") + } + if csvExpiry == 0 { + return nil, fmt.Errorf("CSV expiry must be positive") + } + if chainParams == nil || chainParams.Params == nil || + chainParams.TapHRP == "" { + + return nil, fmt.Errorf("address parameters are incomplete") + } + + funderKeyCopy, err := btcec.ParsePubKey( + funderKey.SerializeCompressed(), + ) + if err != nil { + return nil, fmt.Errorf("invalid funder public key: %w", err) + } + coSignerKeyCopy, err := btcec.ParsePubKey( + coSignerKey.SerializeCompressed(), + ) + if err != nil { + return nil, fmt.Errorf("invalid co-signer public key: %w", err) + } + + sortKeys := true + muSig2Key, err := input.MuSig2CombineKeys( + input.MuSig2Version100RC2, + []*btcec.PublicKey{funderKeyCopy, coSignerKeyCopy}, sortKeys, + // The actual BIP341 tweak includes the asset and timeout roots, + // which are only known once the deposit proof is available. + &input.MuSig2Tweaks{}, + ) + if err != nil { + return nil, err + } + + return &Kit{ + funderKey: funderKeyCopy, + coSignerKey: coSignerKeyCopy, + keyLocator: keyLocator, + assetID: assetID, + csvExpiry: csvExpiry, + muSig2Key: muSig2Key, + chainParams: cloneAddressParams(*chainParams), + }, nil +} + +// cloneAddressParams returns an independent copy of address parameters, +// including the nested Bitcoin network parameters when present. +func cloneAddressParams(params address.ChainParams) *address.ChainParams { + paramsCopy := params + if params.Params != nil { + bitcoinParams := *params.Params + paramsCopy.Params = &bitcoinParams + } + + return ¶msCopy +} + +// GenTimeoutPathScript constructs the deposit funder's CSV timeout script. +// +// OP_CHECKSIGVERIFY OP_CHECKSEQUENCEVERIFY +func (d *Kit) GenTimeoutPathScript() ([]byte, error) { + if d == nil || d.funderKey == nil { + return nil, fmt.Errorf("deposit kit is incomplete") + } + + builder := txscript.NewScriptBuilder() + builder.AddData(schnorr.SerializePubKey(d.funderKey)) + builder.AddOp(txscript.OP_CHECKSIGVERIFY) + builder.AddInt64(int64(d.csvExpiry)) + builder.AddOp(txscript.OP_CHECKSEQUENCEVERIFY) + + return builder.Script() +} + +// timeoutPathSibling returns the timeout leaf as a Taproot Asset sibling +// preimage. +func (d *Kit) timeoutPathSibling() (*commitment.TapscriptPreimage, error) { + timeoutScript, err := d.GenTimeoutPathScript() + if err != nil { + return nil, err + } + + btcTapLeaf := txscript.NewBaseTapLeaf(timeoutScript) + sibling, err := commitment.NewPreimageFromLeaf(btcTapLeaf) + if err != nil { + return nil, err + } + + return sibling, nil +} + +// encodedTimeoutPathSibling returns the timeout sibling in its canonical wire +// encoding. +func (d *Kit) encodedTimeoutPathSibling() ([]byte, error) { + sibling, err := d.timeoutPathSibling() + if err != nil { + return nil, err + } + + siblingBytes, _, err := commitment.MaybeEncodeTapscriptPreimage(sibling) + if err != nil { + return nil, err + } + + return siblingBytes, nil +} + +// NewAddr creates a two-party MuSig2 deposit address with a unilateral funder +// timeout path. +func (d *Kit) NewAddr(ctx context.Context, client AddressProofClient, + amount uint64) (*taprpc.Addr, error) { + + if d == nil { + return nil, fmt.Errorf("deposit kit is required") + } + if client == nil { + return nil, fmt.Errorf("tapd client is required") + } + if amount == 0 { + return nil, fmt.Errorf("deposit amount must be positive") + } + + siblingBytes, err := d.encodedTimeoutPathSibling() + if err != nil { + return nil, err + } + tapScriptKey, _, _, _, err := htlc.CreateOpTrueLeaf() + if err != nil { + return nil, err + } + + return client.NewAddr(ctx, &taprpc.NewAddrRequest{ + AssetId: d.assetID[:], + Amt: amount, + ScriptKey: rpcutils.MarshalScriptKey(tapScriptKey), + InternalKey: &taprpc.KeyDescriptor{ + RawKeyBytes: d.muSig2Key.PreTweakedKey.SerializeCompressed(), + }, + TapscriptSibling: siblingBytes, + }) +} + +// NewHtlcAddr creates an HTLC address using the deposit parties as sender and +// receiver under the immutable legacy deposit policy. +func (d *Kit) NewHtlcAddr(ctx context.Context, client AddressProofClient, + amount uint64, swapHash lntypes.Hash, csvExpiry uint32) ( + *taprpc.Addr, *htlc.SwapKit, error) { + + if d == nil { + return nil, nil, fmt.Errorf("deposit kit is required") + } + if client == nil { + return nil, nil, fmt.Errorf("tapd client is required") + } + + swapKit, err := d.newHtlcSwapKit(amount, swapHash, csvExpiry) + if err != nil { + return nil, nil, err + } + btcInternalKey, err := swapKit.GetAggregateKey() + if err != nil { + return nil, nil, err + } + sibling, err := swapKit.GetSiblingPreimage() + if err != nil { + return nil, nil, err + } + siblingBytes, _, err := commitment.MaybeEncodeTapscriptPreimage(&sibling) + if err != nil { + return nil, nil, err + } + tapScriptKey, _, _, _, err := htlc.CreateOpTrueLeaf() + if err != nil { + return nil, nil, err + } + + htlcAddr, err := client.NewAddr(ctx, &taprpc.NewAddrRequest{ + AssetId: d.assetID[:], + Amt: amount, + ScriptKey: rpcutils.MarshalScriptKey(tapScriptKey), + InternalKey: &taprpc.KeyDescriptor{ + RawKeyBytes: btcInternalKey.SerializeCompressed(), + }, + TapscriptSibling: siblingBytes, + }) + if err != nil { + return nil, nil, err + } + + return htlcAddr, swapKit, nil +} + +// newHtlcSwapKit derives the immutable legacy HTLC contract for a deposit. +func (d *Kit) newHtlcSwapKit(amount uint64, swapHash lntypes.Hash, + csvExpiry uint32) (*htlc.SwapKit, error) { + + return htlc.NewSwapKit(htlc.LegacyDepositV0, htlc.Params{ + SenderPubKey: d.funderKey, + ReceiverPubKey: d.coSignerKey, + AssetID: d.assetID, + Amount: amount, + SwapHash: swapHash, + CsvExpiry: csvExpiry, + AddressParams: d.chainParams, + }) +} + +// TapScriptKey returns the OP_TRUE asset script key used by deposits. +func (d *Kit) TapScriptKey() (asset.ScriptKey, error) { + tapScriptKey, _, _, _, err := htlc.CreateOpTrueLeaf() + if err != nil { + return asset.ScriptKey{}, err + } + + return asset.NewScriptKey(tapScriptKey.PubKey), nil +} + +// ExportProof exports the proof for an exact deposit outpoint. +func (d *Kit) ExportProof(ctx context.Context, client AddressProofClient, + outpoint *wire.OutPoint) (*taprpc.ProofFile, error) { + + if d == nil { + return nil, fmt.Errorf("deposit kit is required") + } + if client == nil { + return nil, fmt.Errorf("tapd client is required") + } + if outpoint == nil { + return nil, fmt.Errorf("deposit outpoint is required") + } + scriptKey, err := d.TapScriptKey() + if err != nil { + return nil, err + } + + return client.ExportProof(ctx, &taprpc.ExportProofRequest{ + AssetId: d.assetID[:], + ScriptKey: scriptKey.PubKey.SerializeCompressed(), + Outpoint: &taprpc.OutPoint{ + Txid: outpoint.Hash[:], + OutputIndex: outpoint.Index, + }, + }) +} + +// validateAsset binds an asset leaf to the identity and script policy of this +// deposit kit. Exact amounts are checked by the protocol operation that knows +// the expected deposit amount. +func (d *Kit) validateAsset(depositAsset *asset.Asset) error { + if depositAsset == nil { + return fmt.Errorf("deposit asset is required") + } + proofID := depositAsset.Genesis.ID() + if !bytes.Equal(d.assetID[:], proofID[:]) { + return fmt.Errorf("asset proof ID does not match deposit") + } + if depositAsset.Amount == 0 { + return fmt.Errorf("deposit asset amount must be positive") + } + if depositAsset.Version != asset.V0 && depositAsset.Version != asset.V1 { + return fmt.Errorf("unsupported legacy deposit asset version") + } + if depositAsset.ScriptVersion != asset.ScriptV0 { + return fmt.Errorf("asset proof script version does not match deposit") + } + if depositAsset.LockTime != 0 || depositAsset.RelativeLockTime != 0 { + return fmt.Errorf("asset proof contains an unexpected locktime") + } + + expectedScriptKey, err := d.TapScriptKey() + if err != nil { + return err + } + if depositAsset.ScriptKey.PubKey == nil || + !depositAsset.ScriptKey.PubKey.IsEqual(expectedScriptKey.PubKey) { + + return fmt.Errorf("asset proof script key does not match deposit") + } + + return nil +} + +// validateProof verifies the proof's commitment structure and binds its +// internal key and timeout sibling to this deposit kit. +func (d *Kit) validateProof(depositProof *proof.Proof) ( + *commitment.TapCommitment, error) { + + if d == nil { + return nil, fmt.Errorf("deposit kit is required") + } + if depositProof == nil { + return nil, fmt.Errorf("deposit proof is required") + } + if err := d.validateAsset(&depositProof.Asset); err != nil { + return nil, err + } + + tapCommitment, err := depositProof.VerifyProofs() + if err != nil { + return nil, fmt.Errorf("invalid deposit proof: %w", err) + } + internalKey := depositProof.InclusionProof.InternalKey + if internalKey == nil || + !internalKey.IsEqual(d.muSig2Key.PreTweakedKey) { + + return nil, fmt.Errorf("deposit internal key mismatch") + } + commitmentProof := depositProof.InclusionProof.CommitmentProof + if commitmentProof == nil || + commitmentProof.TapSiblingPreimage == nil { + + return nil, fmt.Errorf("deposit sibling preimage is required") + } + expectedSibling, err := d.encodedTimeoutPathSibling() + if err != nil { + return nil, err + } + actualSibling, _, err := commitment.MaybeEncodeTapscriptPreimage( + commitmentProof.TapSiblingPreimage, + ) + if err != nil { + return nil, fmt.Errorf("invalid deposit sibling preimage: %w", err) + } + if !bytes.Equal(expectedSibling, actualSibling) { + return nil, fmt.Errorf("deposit sibling preimage mismatch") + } + + return tapCommitment, nil +} + +// anchorRootFromProofCommitment verifies the proof's commitment structure and +// returns the complete anchor Taproot Merkle root. +func (d *Kit) anchorRootFromProofCommitment(depositProof *proof.Proof) ( + []byte, error) { + + tapCommitment, err := d.validateProof(depositProof) + if err != nil { + return nil, err + } + sibling, err := d.timeoutPathSibling() + if err != nil { + return nil, err + } + siblingHash, err := sibling.TapHash() + if err != nil { + return nil, err + } + root := tapCommitment.TapscriptRoot(siblingHash) + + return append([]byte(nil), root[:]...), nil +} + +// AnchorRootFromProofCommitment verifies the proof's commitment structure, +// binds its asset ID, script key, internal key, timeout sibling, and exact +// amount to this deposit, then returns the complete anchor Taproot Merkle root. +// It does not verify the proof's chain inclusion, provenance, state transition, +// confirmation depth, or whether the anchor outpoint remains unspent. Use +// VerifyProofFile when a proof file has not already been fully verified. +func (d *Kit) AnchorRootFromProofCommitment(depositProof *proof.Proof, + expectedAmount uint64) ([]byte, error) { + + if d == nil { + return nil, fmt.Errorf("deposit kit is required") + } + if expectedAmount == 0 { + return nil, fmt.Errorf("expected deposit amount must be positive") + } + if depositProof == nil { + return nil, fmt.Errorf("deposit proof is required") + } + if depositProof.Asset.Amount != expectedAmount { + return nil, fmt.Errorf( + "deposit asset amount mismatch: expected %d, got %d", + expectedAmount, depositProof.Asset.Amount, + ) + } + + return d.anchorRootFromProofCommitment(depositProof) +} + +// VerifyProofFile asks tapd to fully verify a proof file, binds its terminal +// proof to the expected deposit outpoint and amount, and returns the complete +// anchor Taproot Merkle root. The caller must still enforce confirmation depth +// and verify that the anchor outpoint remains unspent. +func (d *Kit) VerifyProofFile(ctx context.Context, verifier ProofVerifier, + proofFile *taprpc.ProofFile, expectedOutpoint *wire.OutPoint, + expectedAmount uint64) ([]byte, error) { + + if d == nil { + return nil, fmt.Errorf("deposit kit is required") + } + if verifier == nil { + return nil, fmt.Errorf("proof verifier is required") + } + if proofFile == nil || len(proofFile.RawProofFile) == 0 { + return nil, fmt.Errorf("proof file is required") + } + if expectedOutpoint == nil { + return nil, fmt.Errorf("expected deposit outpoint is required") + } + if expectedAmount == 0 { + return nil, fmt.Errorf("expected deposit amount must be positive") + } + + proofFileCopy := &taprpc.ProofFile{ + RawProofFile: append([]byte(nil), proofFile.RawProofFile...), + GenesisPoint: proofFile.GenesisPoint, + } + verifyResponse, err := verifier.VerifyProof(ctx, proofFileCopy) + if err != nil { + return nil, fmt.Errorf("unable to verify deposit proof file: %w", err) + } + if verifyResponse == nil || !verifyResponse.Valid { + return nil, fmt.Errorf("invalid deposit proof file") + } + + decodedFile, err := proof.DecodeFile(proofFile.RawProofFile) + if err != nil { + return nil, fmt.Errorf("unable to decode deposit proof file: %w", err) + } + depositProof, err := decodedFile.LastProof() + if err != nil { + return nil, fmt.Errorf("unable to read terminal deposit proof: %w", err) + } + if depositProof.OutPoint() != *expectedOutpoint { + return nil, fmt.Errorf( + "deposit proof outpoint mismatch: expected %v, got %v", + *expectedOutpoint, depositProof.OutPoint(), + ) + } + + return d.AnchorRootFromProofCommitment(depositProof, expectedAmount) +} + +// VerifyProof verifies only the proof commitment and binds it to this +// deposit's asset, internal key, and timeout sibling. It does not validate the +// expected amount, provenance, chain inclusion, or unspent status. +// +// Deprecated: use VerifyProofFile for untrusted proof files, or +// AnchorRootFromProofCommitment for proofs that were already fully verified. +func (d *Kit) VerifyProof(depositProof *proof.Proof) ([]byte, error) { + return d.anchorRootFromProofCommitment(depositProof) +} + +// GenTimeoutBtcControlBlock creates the deposit timeout-path control block. +func (d *Kit) GenTimeoutBtcControlBlock(taprootAssetRoot []byte) ( + *txscript.ControlBlock, error) { + + if d == nil || d.muSig2Key == nil { + return nil, fmt.Errorf("deposit kit is incomplete") + } + if len(taprootAssetRoot) != chainhash.HashSize { + return nil, fmt.Errorf("asset root must be %d bytes", + chainhash.HashSize) + } + controlBlock := &txscript.ControlBlock{ + InternalKey: d.muSig2Key.PreTweakedKey, + LeafVersion: txscript.BaseLeafVersion, + InclusionProof: append([]byte(nil), taprootAssetRoot...), + } + timeoutScript, err := d.GenTimeoutPathScript() + if err != nil { + return nil, err + } + rootHash := controlBlock.RootHash(timeoutScript) + tapKey := txscript.ComputeTaprootOutputKey( + d.muSig2Key.PreTweakedKey, rootHash, + ) + controlBlock.OutputKeyYIsOdd = tapKey.SerializeCompressed()[0] == + secp256k1.PubKeyFormatCompressedOdd + + return controlBlock, nil +} + +type validatedSweep struct { + assetInputIndex int + prevOutputs []*wire.TxOut + assetRoot []byte +} + +// findUniqueInput returns the sole transaction input that spends outpoint. +func findUniqueInput(tx *wire.MsgTx, outpoint wire.OutPoint) (int, error) { + inputIndex := -1 + for idx, txIn := range tx.TxIn { + if txIn == nil { + return 0, fmt.Errorf("sweep input %d is nil", idx) + } + if txIn.PreviousOutPoint != outpoint { + continue + } + if inputIndex >= 0 { + return 0, fmt.Errorf("proof outpoint appears more than once") + } + inputIndex = idx + } + if inputIndex < 0 { + return 0, fmt.Errorf("asset input does not spend proof outpoint") + } + + return inputIndex, nil +} + +// validateSweep binds a PSBT input and witness UTXO to the deposit proof and +// reconstructs the exact Taproot output being spent. +func (d *Kit) validateSweep(depositProof *proof.Proof, + sweepPacket *psbt.Packet) (*validatedSweep, error) { + + tapCommitment, err := d.validateProof(depositProof) + if err != nil { + return nil, err + } + if sweepPacket == nil || sweepPacket.UnsignedTx == nil { + return nil, fmt.Errorf("sweep PSBT is required") + } + if len(sweepPacket.UnsignedTx.TxIn) == 0 || + len(sweepPacket.UnsignedTx.TxIn) != len(sweepPacket.Inputs) { + + return nil, fmt.Errorf("sweep PSBT input metadata is incomplete") + } + if sweepPacket.UnsignedTx.Version < 2 { + return nil, fmt.Errorf("sweep transaction must use version 2") + } + + assetInputIndex, err := findUniqueInput( + sweepPacket.UnsignedTx, depositProof.OutPoint(), + ) + if err != nil { + return nil, err + } + prevOutputs := make([]*wire.TxOut, len(sweepPacket.Inputs)) + for idx := range sweepPacket.Inputs { + witnessUtxo := sweepPacket.Inputs[idx].WitnessUtxo + if witnessUtxo == nil { + return nil, fmt.Errorf( + "sweep input %d requires a witness UTXO", idx, + ) + } + prevOutputs[idx] = &wire.TxOut{ + Value: witnessUtxo.Value, + PkScript: append( + []byte(nil), witnessUtxo.PkScript..., + ), + } + } + + proofOutputIndex := int(depositProof.InclusionProof.OutputIndex) + if proofOutputIndex >= len(depositProof.AnchorTx.TxOut) { + return nil, fmt.Errorf("proof output index is out of bounds") + } + proofTxOut := depositProof.AnchorTx.TxOut[proofOutputIndex] + if proofTxOut == nil { + return nil, fmt.Errorf("proof anchor output is nil") + } + assetPrevOut := prevOutputs[assetInputIndex] + if proofTxOut.Value != assetPrevOut.Value || + !bytes.Equal(proofTxOut.PkScript, assetPrevOut.PkScript) { + + return nil, fmt.Errorf("asset prevout does not match proof anchor") + } + + sibling, err := d.timeoutPathSibling() + if err != nil { + return nil, err + } + siblingHash, err := sibling.TapHash() + if err != nil { + return nil, err + } + expectedPkScript, err := tapscript.PayToAddrScript( + *d.muSig2Key.PreTweakedKey, siblingHash, *tapCommitment, + ) + if err != nil { + return nil, err + } + if !bytes.Equal(expectedPkScript, assetPrevOut.PkScript) { + return nil, fmt.Errorf("asset input script does not match proof") + } + + assetRootHash := tapCommitment.TapscriptRoot(nil) + return &validatedSweep{ + assetInputIndex: assetInputIndex, + prevOutputs: prevOutputs, + assetRoot: append([]byte(nil), assetRootHash[:]...), + }, nil +} + +// verifyTapscriptSignature checks a signer response against the exact input, +// prevouts, script, and public key used by the timeout spend. +func verifyTapscriptSignature(tx *wire.MsgTx, sweep *validatedSweep, + witnessScript []byte, pubKey *btcec.PublicKey, signature []byte) error { + + parsedSignature, err := schnorr.ParseSignature(signature) + if err != nil { + return fmt.Errorf("signer returned an invalid Schnorr signature: %w", + err) + } + prevOutFetcher := txscript.NewMultiPrevOutFetcher(nil) + for idx, txIn := range tx.TxIn { + prevOutFetcher.AddPrevOut( + txIn.PreviousOutPoint, sweep.prevOutputs[idx], + ) + } + sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher) + tapLeaf := txscript.NewBaseTapLeaf(witnessScript) + sigHash, err := txscript.CalcTapscriptSignaturehash( + sigHashes, txscript.SigHashDefault, tx, + sweep.assetInputIndex, prevOutFetcher, tapLeaf, + ) + if err != nil { + return fmt.Errorf("unable to calculate asset input sighash: %w", err) + } + if !parsedSignature.Verify(sigHash, pubKey) { + return fmt.Errorf("signer returned a signature for the wrong key") + } + + return nil +} + +// CreateTimeoutWitness signs the exact proof-bound deposit input and returns +// its input index and timeout-path witness. The method sets the required CSV +// sequence on a transaction copy before signing, then writes that sequence to +// the caller's PSBT only after the signature has been verified. +func (d *Kit) CreateTimeoutWitness(ctx context.Context, + signer lndclient.SignerClient, depositProof *proof.Proof, + sweepPacket *psbt.Packet) (*htlc.SpendWitness, error) { + + if signer == nil { + return nil, fmt.Errorf("signer is required") + } + sweep, err := d.validateSweep(depositProof, sweepPacket) + if err != nil { + return nil, err + } + sweepTx := sweepPacket.UnsignedTx.Copy() + sweepTx.TxIn[sweep.assetInputIndex].Sequence = d.csvExpiry + timeoutScript, err := d.GenTimeoutPathScript() + if err != nil { + return nil, err + } + + signDesc := &lndclient.SignDescriptor{ + KeyDesc: keychain.KeyDescriptor{ + KeyLocator: d.keyLocator, + }, + SignMethod: input.TaprootScriptSpendSignMethod, + WitnessScript: timeoutScript, + Output: sweep.prevOutputs[sweep.assetInputIndex], + InputIndex: sweep.assetInputIndex, + } + sigs, err := signer.SignOutputRaw( + ctx, sweepTx, + []*lndclient.SignDescriptor{signDesc}, sweep.prevOutputs, + ) + if err != nil { + return nil, err + } + if len(sigs) != 1 || len(sigs[0]) == 0 { + return nil, fmt.Errorf("signer returned an invalid signature set") + } + if err := verifyTapscriptSignature( + sweepTx, sweep, timeoutScript, d.funderKey, + sigs[0], + ); err != nil { + return nil, err + } + + controlBlock, err := d.GenTimeoutBtcControlBlock(sweep.assetRoot) + if err != nil { + return nil, err + } + controlBlockBytes, err := controlBlock.ToBytes() + if err != nil { + return nil, err + } + sweepPacket.UnsignedTx.TxIn[sweep.assetInputIndex].Sequence = d.csvExpiry + + return &htlc.SpendWitness{ + InputIndex: uint32(sweep.assetInputIndex), + Witness: wire.TxWitness{ + sigs[0], timeoutScript, controlBlockBytes, + }, + }, nil +} diff --git a/assets/deposit/kit_test.go b/assets/deposit/kit_test.go new file mode 100644 index 000000000..f1abb9649 --- /dev/null +++ b/assets/deposit/kit_test.go @@ -0,0 +1,552 @@ +package deposit + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil/psbt" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop/assets/htlc" + "github.com/lightninglabs/taproot-assets/address" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/commitment" + "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/taprpc" + "github.com/lightninglabs/taproot-assets/tapscript" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +const ( + legacySuccessScript = "20c6047f9441ed7d6d3045406e95c07cd85c778e" + + "4b8cef3ca7abac09b95c709ee5ad82012088a914e6babb9619d7a812" + + "72711fc546a16b211dd939578851b2" + legacyTimeoutScript = "2079be667ef9dcbbac55a06295ce870b07029bfcdb2d" + + "ce28d959f2815b16f81798ad029000b2" + legacyAggregateKey = "023b46d262d2f610e9038b44beabdfe97ab5a0feb" + + "89870acc2264edfb7f63ec2ec" + legacySibling = "01cbf40123d0ca1191c0dba575c7a92ea255f9e9fc81484e" + + "3929afeaa1a6f459d600114fbfd01b5de82dfa6f54f0f27af41d5a66" + + "ed39ac1a87d614fa12808f9adf" + legacyAnchorPkScript = "512021b15bddf2d991b8791ec7c2105fa69be61008d78" + + "b67d261451290d7a44dce5f" +) + +// scalarKey derives a deterministic test key from a one-byte scalar. +func scalarKey(t *testing.T, scalar byte) (*btcec.PrivateKey, + *btcec.PublicKey) { + + t.Helper() + keyBytes := make([]byte, 32) + keyBytes[31] = scalar + privateKey, publicKey := btcec.PrivKeyFromBytes(keyBytes) + + return privateKey, publicKey +} + +// TestNewHtlcSwapKitLegacyContract proves the moved deposit adapter retains +// the exact contract used by the existing valid asset deposit flow. +func TestNewHtlcSwapKitLegacyContract(t *testing.T) { + _, funderKey := scalarKey(t, 1) + _, coSignerKey := scalarKey(t, 2) + var assetID asset.ID + var swapHash lntypes.Hash + for idx := range assetID { + assetID[idx] = byte(idx) + swapHash[idx] = byte(idx) + } + + depositKit, err := NewKit( + funderKey, coSignerKey, keychain.KeyLocator{}, assetID, 4032, + &address.RegressionNetTap, + ) + require.NoError(t, err) + + swapKit, err := depositKit.newHtlcSwapKit(1000, swapHash, 144) + require.NoError(t, err) + require.Equal(t, htlc.LegacyDepositV0, swapKit.Policy()) + + successScript, err := swapKit.GetSuccessScript() + require.NoError(t, err) + require.Equal(t, legacySuccessScript, hex.EncodeToString(successScript)) + timeoutScript, err := swapKit.GetTimeoutScript() + require.NoError(t, err) + require.Equal(t, legacyTimeoutScript, hex.EncodeToString(timeoutScript)) + aggregateKey, err := swapKit.GetAggregateKey() + require.NoError(t, err) + require.Equal(t, legacyAggregateKey, + hex.EncodeToString(aggregateKey.SerializeCompressed())) + sibling, err := swapKit.GetSiblingPreimage() + require.NoError(t, err) + siblingBytes, _, err := commitment.MaybeEncodeTapscriptPreimage(&sibling) + require.NoError(t, err) + require.Equal(t, legacySibling, hex.EncodeToString(siblingBytes)) + + assetRoot := make([]byte, chainhash.HashSize) + for idx := range assetRoot { + assetRoot[idx] = byte(0xa0 + idx) + } + pkScript, err := swapKit.GetPkScriptFromRoot(assetRoot) + require.NoError(t, err) + require.Equal(t, legacyAnchorPkScript, hex.EncodeToString(pkScript)) + + packet, err := swapKit.CreateHtlcVpkt() + require.NoError(t, err) + require.Equal(t, &address.RegressionNetTap, packet.ChainParams) + require.Len(t, packet.Outputs, 2) + require.Equal(t, uint32(0), packet.Outputs[0].AnchorOutputIndex) + require.Equal(t, uint32(1), packet.Outputs[1].AnchorOutputIndex) + require.Equal(t, uint64(1000), packet.Outputs[1].Amount) + require.True(t, packet.Outputs[0].Interactive) + require.True(t, packet.Outputs[1].Interactive) +} + +// TestNewKitValidation verifies invalid or mutable constructor inputs cannot +// create or alter a deposit contract. +func TestNewKitValidation(t *testing.T) { + _, funderKey := scalarKey(t, 1) + _, coSignerKey := scalarKey(t, 2) + assetID := asset.ID{1} + params := cloneAddressParams(address.RegressionNetTap) + emptyParams := cloneAddressParams(address.ChainParams{}) + require.Nil(t, emptyParams.Params) + + tests := []struct { + name string + funder *btcec.PublicKey + coSigner *btcec.PublicKey + assetID asset.ID + expiry uint32 + chainParams *address.ChainParams + }{ + { + name: "nil funder", coSigner: coSignerKey, + assetID: assetID, expiry: 1, chainParams: params, + }, + { + name: "nil co-signer", funder: funderKey, + assetID: assetID, expiry: 1, chainParams: params, + }, + { + name: "same keys", funder: funderKey, coSigner: funderKey, + assetID: assetID, expiry: 1, chainParams: params, + }, + { + name: "zero asset ID", funder: funderKey, + coSigner: coSignerKey, expiry: 1, chainParams: params, + }, + { + name: "zero expiry", funder: funderKey, + coSigner: coSignerKey, assetID: assetID, + chainParams: params, + }, + { + name: "nil chain parameters", funder: funderKey, + coSigner: coSignerKey, assetID: assetID, expiry: 1, + }, + } + + for _, testCase := range tests { + t.Run(testCase.name, func(t *testing.T) { + _, err := NewKit( + testCase.funder, testCase.coSigner, + keychain.KeyLocator{}, testCase.assetID, + testCase.expiry, testCase.chainParams, + ) + require.Error(t, err) + }) + } + + kit, err := NewKit( + funderKey, coSignerKey, keychain.KeyLocator{}, assetID, 144, + params, + ) + require.NoError(t, err) + params.TapHRP = "mutated" + params.Params.Name = "mutated" + swapKit, err := kit.newHtlcSwapKit(1, lntypes.Hash{1}, 2) + require.NoError(t, err) + require.Equal(t, address.RegressionNetTap.TapHRP, + swapKit.AddressParams().TapHRP) + require.Equal(t, address.RegressionNetTap.Name, + swapKit.AddressParams().Name) + + var nilKit *Kit + _, err = nilKit.NewAddr(t.Context(), nil, 1) + require.Error(t, err) +} + +type localSigner struct { + lndclient.SignerClient + + privateKey *btcec.PrivateKey + expectedInput int + calls int + invalidSet bool +} + +type proofVerifierMock struct { + response *taprpc.VerifyProofResponse + err error + calls int +} + +// VerifyProof records a proof verification request and returns the configured +// tapd response. +func (m *proofVerifierMock) VerifyProof(_ context.Context, + _ *taprpc.ProofFile, _ ...grpc.CallOption) (*taprpc.VerifyProofResponse, + error) { + + m.calls++ + + return m.response, m.err +} + +func (s *localSigner) SignOutputRaw(_ context.Context, tx *wire.MsgTx, + descriptors []*lndclient.SignDescriptor, + prevOutputs []*wire.TxOut) ([][]byte, error) { + + s.calls++ + if s.invalidSet { + return nil, nil + } + if len(descriptors) != 1 { + return nil, fmt.Errorf("expected one sign descriptor") + } + if len(prevOutputs) != len(tx.TxIn) { + return nil, fmt.Errorf("expected all previous outputs") + } + descriptor := descriptors[0] + if descriptor.InputIndex != s.expectedInput { + return nil, fmt.Errorf("unexpected input index: %d", + descriptor.InputIndex) + } + if descriptor.SignMethod != input.TaprootScriptSpendSignMethod { + return nil, fmt.Errorf("unexpected signing method") + } + + prevOutFetcher := txscript.NewMultiPrevOutFetcher(nil) + for idx, txIn := range tx.TxIn { + prevOutFetcher.AddPrevOut( + txIn.PreviousOutPoint, prevOutputs[idx], + ) + } + sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher) + tapLeaf := txscript.NewBaseTapLeaf(descriptor.WitnessScript) + signature, err := txscript.RawTxInTapscriptSignature( + tx, sigHashes, descriptor.InputIndex, + descriptor.Output.Value, descriptor.Output.PkScript, tapLeaf, + txscript.SigHashDefault, s.privateKey, + ) + if err != nil { + return nil, err + } + + return [][]byte{signature}, nil +} + +type witnessFixture struct { + kit *Kit + proof *proof.Proof + packet *psbt.Packet + prevOutputs []*wire.TxOut + funderKey *btcec.PrivateKey + assetInIndex int +} + +// newWitnessFixture creates a proof-bound deposit and multi-input sweep packet. +func newWitnessFixture(t *testing.T) *witnessFixture { + t.Helper() + + funderKey, funderPubKey := scalarKey(t, 1) + _, coSignerPubKey := scalarKey(t, 2) + genesis := asset.Genesis{ + FirstPrevOut: wire.OutPoint{ + Hash: chainhash.Hash{0x31}, Index: 3, + }, + Tag: "asset deposit witness test", OutputIndex: 0, + Type: asset.Normal, + } + kit, err := NewKit( + funderPubKey, coSignerPubKey, keychain.KeyLocator{Family: 7, + Index: 9}, genesis.ID(), 144, &address.RegressionNetTap, + ) + require.NoError(t, err) + opTrueScriptKey, _, _, _, err := htlc.CreateOpTrueLeaf() + require.NoError(t, err) + depositAsset, err := asset.New( + genesis, 1000, 0, 0, + asset.NewScriptKey(opTrueScriptKey.PubKey), nil, + asset.WithAssetVersion(asset.V1), + ) + require.NoError(t, err) + commitmentVersion := commitment.TapCommitmentV2 + tapCommitment, err := commitment.FromAssets( + &commitmentVersion, depositAsset, + ) + require.NoError(t, err) + _, commitmentProof, err := tapCommitment.Proof( + depositAsset.TapCommitmentKey(), + depositAsset.AssetCommitmentKey(), + ) + require.NoError(t, err) + sibling, err := kit.timeoutPathSibling() + require.NoError(t, err) + siblingHash, err := sibling.TapHash() + require.NoError(t, err) + anchorPkScript, err := tapscript.PayToAddrScript( + *kit.muSig2Key.PreTweakedKey, siblingHash, *tapCommitment, + ) + require.NoError(t, err) + + const anchorValue = int64(50_000) + anchorTx := wire.NewMsgTx(2) + anchorTx.AddTxIn(&wire.TxIn{PreviousOutPoint: genesis.FirstPrevOut}) + anchorTx.AddTxOut(&wire.TxOut{ + Value: 1000, PkScript: []byte{txscript.OP_TRUE}, + }) + anchorTx.AddTxOut(&wire.TxOut{ + Value: anchorValue, PkScript: anchorPkScript, + }) + assetProof := &proof.Proof{ + AnchorTx: *anchorTx, + Asset: *depositAsset, + InclusionProof: proof.TaprootProof{ + OutputIndex: 1, + InternalKey: kit.muSig2Key.PreTweakedKey, + CommitmentProof: &proof.CommitmentProof{ + Proof: *commitmentProof, + TapSiblingPreimage: sibling, + }, + }, + } + _, err = assetProof.VerifyProofs() + require.NoError(t, err) + + feeOutpoint := wire.OutPoint{Hash: chainhash.Hash{0x42}, Index: 5} + assetInIndex := 1 + sweepTx := wire.NewMsgTx(2) + sweepTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: feeOutpoint, + Sequence: wire.MaxTxInSequenceNum, + }) + sweepTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: assetProof.OutPoint(), + Sequence: wire.MaxTxInSequenceNum, + }) + sweepTx.AddTxOut(&wire.TxOut{ + Value: anchorValue - 2000, PkScript: []byte{txscript.OP_TRUE}, + }) + packet, err := psbt.NewFromUnsignedTx(sweepTx) + require.NoError(t, err) + prevOutputs := []*wire.TxOut{ + {Value: 3000, PkScript: []byte{txscript.OP_TRUE}}, + {Value: anchorValue, PkScript: anchorPkScript}, + } + for idx := range prevOutputs { + packet.Inputs[idx].WitnessUtxo = prevOutputs[idx] + } + + return &witnessFixture{ + kit: kit, proof: assetProof, packet: packet, + prevOutputs: prevOutputs, funderKey: funderKey, + assetInIndex: assetInIndex, + } +} + +// TestVerifyProofReturnsAnchorRoot verifies the proof result can be used as +// the Taproot tweak for the deposit's cooperative MuSig2 key-path spend. +func TestVerifyProofReturnsAnchorRoot(t *testing.T) { + fixture := newWitnessFixture(t) + + root, err := fixture.kit.VerifyProof(fixture.proof) + require.NoError(t, err) + require.Len(t, root, chainhash.HashSize) + boundRoot, err := fixture.kit.AnchorRootFromProofCommitment( + fixture.proof, fixture.proof.Asset.Amount, + ) + require.NoError(t, err) + require.Equal(t, root, boundRoot) + + _, err = fixture.kit.AnchorRootFromProofCommitment( + fixture.proof, fixture.proof.Asset.Amount-1, + ) + require.ErrorContains(t, err, "deposit asset amount mismatch") + + outputKey := txscript.ComputeTaprootOutputKey( + fixture.kit.muSig2Key.PreTweakedKey, root, + ) + pkScript, err := txscript.PayToTaprootScript(outputKey) + require.NoError(t, err) + require.Equal( + t, fixture.prevOutputs[fixture.assetInIndex].PkScript, pkScript, + ) +} + +// TestVerifyProofFile verifies that untrusted proof files are first delegated +// to tapd and then bound to the expected deposit outpoint and amount. +func TestVerifyProofFile(t *testing.T) { + fixture := newWitnessFixture(t) + proofFile, err := proof.NewFile(proof.V0, *fixture.proof) + require.NoError(t, err) + var encodedProof bytes.Buffer + require.NoError(t, proofFile.Encode(&encodedProof)) + rpcProofFile := &taprpc.ProofFile{RawProofFile: encodedProof.Bytes()} + expectedOutpoint := fixture.proof.OutPoint() + + verifier := &proofVerifierMock{ + response: &taprpc.VerifyProofResponse{Valid: true}, + } + root, err := fixture.kit.VerifyProofFile( + t.Context(), verifier, rpcProofFile, &expectedOutpoint, + fixture.proof.Asset.Amount, + ) + require.NoError(t, err) + require.Len(t, root, chainhash.HashSize) + require.Equal(t, 1, verifier.calls) + + wrongOutpoint := expectedOutpoint + wrongOutpoint.Index++ + _, err = fixture.kit.VerifyProofFile( + t.Context(), verifier, rpcProofFile, &wrongOutpoint, + fixture.proof.Asset.Amount, + ) + require.ErrorContains(t, err, "deposit proof outpoint mismatch") + + verifier.response.Valid = false + _, err = fixture.kit.VerifyProofFile( + t.Context(), verifier, rpcProofFile, &expectedOutpoint, + fixture.proof.Asset.Amount, + ) + require.ErrorContains(t, err, "invalid deposit proof file") +} + +// TestCreateTimeoutWitness verifies the refund signature and witness bind to +// the proof-selected input rather than an assumed position. +func TestCreateTimeoutWitness(t *testing.T) { + fixture := newWitnessFixture(t) + signer := &localSigner{ + privateKey: fixture.funderKey, expectedInput: fixture.assetInIndex, + } + spend, err := fixture.kit.CreateTimeoutWitness( + t.Context(), signer, fixture.proof, fixture.packet, + ) + require.NoError(t, err) + require.Equal(t, uint32(fixture.assetInIndex), spend.InputIndex) + require.Equal(t, fixture.kit.csvExpiry, + fixture.packet.UnsignedTx.TxIn[fixture.assetInIndex].Sequence) + require.Len(t, spend.Witness, 3) + require.Equal(t, 1, signer.calls) + + tx := fixture.packet.UnsignedTx + tx.TxIn[fixture.assetInIndex].Witness = spend.Witness + prevOutFetcher := txscript.NewMultiPrevOutFetcher(nil) + for idx, txIn := range tx.TxIn { + prevOutFetcher.AddPrevOut( + txIn.PreviousOutPoint, fixture.prevOutputs[idx], + ) + } + sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher) + engine, err := txscript.NewEngine( + fixture.prevOutputs[fixture.assetInIndex].PkScript, tx, + fixture.assetInIndex, txscript.StandardVerifyFlags, nil, + sigHashes, fixture.prevOutputs[fixture.assetInIndex].Value, + prevOutFetcher, + ) + require.NoError(t, err) + require.NoError(t, engine.Execute()) +} + +// TestCreateTimeoutWitnessRejectsInvalidInputs covers the payment boundary +// checks that the legacy server helper previously omitted. +func TestCreateTimeoutWitnessRejectsInvalidInputs(t *testing.T) { + tests := []struct { + name string + mutate func(*witnessFixture, *localSigner) + }{ + { + name: "missing proof input", + mutate: func(f *witnessFixture, _ *localSigner) { + f.packet.UnsignedTx.TxIn[1].PreviousOutPoint.Index++ + }, + }, + { + name: "duplicate proof input", + mutate: func(f *witnessFixture, _ *localSigner) { + inputCopy := *f.packet.UnsignedTx.TxIn[1] + f.packet.UnsignedTx.TxIn = append( + f.packet.UnsignedTx.TxIn, &inputCopy, + ) + f.packet.Inputs = append( + f.packet.Inputs, + psbt.PInput{WitnessUtxo: f.prevOutputs[1]}, + ) + }, + }, + { + name: "missing fee prevout", + mutate: func(f *witnessFixture, _ *localSigner) { + f.packet.Inputs[0].WitnessUtxo = nil + }, + }, + { + name: "mismatched anchor value", + mutate: func(f *witnessFixture, _ *localSigner) { + f.packet.Inputs[1].WitnessUtxo.Value++ + }, + }, + { + name: "invalid signature set", + mutate: func(_ *witnessFixture, signer *localSigner) { + signer.invalidSet = true + }, + }, + { + name: "wrong signing key", + mutate: func(_ *witnessFixture, signer *localSigner) { + signer.privateKey, _ = scalarKey(t, 3) + }, + }, + } + + for _, testCase := range tests { + t.Run(testCase.name, func(t *testing.T) { + fixture := newWitnessFixture(t) + signer := &localSigner{ + privateKey: fixture.funderKey, + expectedInput: fixture.assetInIndex, + } + testCase.mutate(fixture, signer) + _, err := fixture.kit.CreateTimeoutWitness( + t.Context(), signer, fixture.proof, + fixture.packet, + ) + require.Error(t, err) + require.Equal( + t, wire.MaxTxInSequenceNum, + fixture.packet.UnsignedTx. + TxIn[fixture.assetInIndex].Sequence, + ) + }) + } + + fixture := newWitnessFixture(t) + _, err := fixture.kit.CreateTimeoutWitness( + t.Context(), nil, fixture.proof, fixture.packet, + ) + require.Error(t, err) + _, err = fixture.kit.CreateTimeoutWitness( + t.Context(), &localSigner{}, nil, fixture.packet, + ) + require.Error(t, err) +} diff --git a/assets/tapkit.go b/assets/tapkit.go new file mode 100644 index 000000000..a503db7a8 --- /dev/null +++ b/assets/tapkit.go @@ -0,0 +1,146 @@ +package assets + +import ( + "context" + "fmt" + "math" + + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil/psbt" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/loop/assets/htlc" + "github.com/lightninglabs/taproot-assets/address" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/tappsbt" + "github.com/lightninglabs/taproot-assets/tapsend" +) + +// CreateOpTrueSweepVpkt creates a virtual packet that spends proof-bound +// OP_TRUE assets to the given address. +func CreateOpTrueSweepVpkt(ctx context.Context, proofs []*proof.Proof, + addr *address.Tap) (*tappsbt.VPacket, error) { + + if len(proofs) == 0 { + return nil, fmt.Errorf("at least one asset proof is required") + } + if addr == nil { + return nil, fmt.Errorf("sweep address is required") + } + if addr.ChainParams == nil || addr.ChainParams.Params == nil || + addr.ChainParams.TapHRP == "" { + + return nil, fmt.Errorf("sweep address parameters are incomplete") + } + if addr.Amount == 0 { + return nil, fmt.Errorf("sweep address amount must be positive") + } + if addr.AssetID == asset.ZeroID { + return nil, fmt.Errorf("group sweep addresses are unsupported") + } + opTrueScriptKey, _, _, controlBlock, err := htlc.CreateOpTrueLeaf() + if err != nil { + return nil, err + } + + total := uint64(0) + for idx, assetProof := range proofs { + if assetProof == nil { + return nil, fmt.Errorf("asset proof %d is nil", idx) + } + if _, err := assetProof.VerifyProofs(); err != nil { + return nil, fmt.Errorf("invalid asset proof %d: %w", idx, err) + } + proofID := assetProof.Asset.Genesis.ID() + if proofID != addr.AssetID { + return nil, fmt.Errorf( + "asset proof %d does not match sweep address", idx, + ) + } + proofScriptKey := assetProof.Asset.ScriptKey.PubKey + if proofScriptKey == nil || + !proofScriptKey.IsEqual(opTrueScriptKey.PubKey) { + + return nil, fmt.Errorf( + "asset proof %d is not an OP_TRUE asset", idx, + ) + } + if math.MaxUint64-total < assetProof.Asset.Amount { + return nil, fmt.Errorf("asset proof amount overflow") + } + total += assetProof.Asset.Amount + } + if total != addr.Amount { + return nil, fmt.Errorf("total proof amount does not match address") + } + + sweepVpkt, err := tappsbt.FromProofs( + proofs, addr.ChainParams, tappsbt.V1, + ) + if err != nil { + return nil, err + } + if len(sweepVpkt.Inputs) != len(proofs) { + return nil, fmt.Errorf("proof inputs were not preserved") + } + for idx, input := range sweepVpkt.Inputs { + if input == nil || input.Anchor.InternalKey == nil { + return nil, fmt.Errorf("asset input %d is incomplete", idx) + } + inputKey := input.Anchor.InternalKey + input.Anchor.Bip32Derivation = []*psbt.Bip32Derivation{ + {PubKey: inputKey.SerializeCompressed()}, + } + input.Anchor.TrBip32Derivation = + []*psbt.TaprootBip32Derivation{ + { + XOnlyPubKey: schnorr.SerializePubKey( + inputKey, + ), + }, + } + } + + sweepVpkt.Outputs = append(sweepVpkt.Outputs, &tappsbt.VOutput{ + AssetVersion: addr.AssetVersion, + Amount: addr.Amount, + Interactive: true, + AnchorOutputIndex: 0, + ScriptKey: asset.NewScriptKey(&addr.ScriptKey), + AnchorOutputInternalKey: &addr.InternalKey, + AnchorOutputTapscriptSibling: addr.TapscriptSibling, + ProofDeliveryAddress: &addr.ProofCourierAddr, + }) + if err := tapsend.PrepareOutputAssets(ctx, sweepVpkt); err != nil { + return nil, err + } + + controlBlockBytes, err := controlBlock.ToBytes() + if err != nil { + return nil, err + } + opTrueScript, err := htlc.GetOpTrueScript() + if err != nil { + return nil, err + } + + if len(sweepVpkt.Outputs) == 0 || sweepVpkt.Outputs[0] == nil || + sweepVpkt.Outputs[0].Asset == nil || + len(sweepVpkt.Outputs[0].Asset.PrevWitnesses) == 0 { + + return nil, fmt.Errorf("prepared asset output is incomplete") + } + firstPrevWitness := &sweepVpkt.Outputs[0].Asset.PrevWitnesses[0] + if sweepVpkt.Outputs[0].Asset.HasSplitCommitmentWitness() { + rootAsset := firstPrevWitness.SplitCommitment.RootAsset + if len(rootAsset.PrevWitnesses) == 0 { + return nil, fmt.Errorf("split root asset witness is incomplete") + } + firstPrevWitness = &rootAsset.PrevWitnesses[0] + } + firstPrevWitness.TxWitness = wire.TxWitness{ + opTrueScript, controlBlockBytes, + } + + return sweepVpkt, nil +} diff --git a/assets/tapkit_test.go b/assets/tapkit_test.go new file mode 100644 index 000000000..3fa3c3ae1 --- /dev/null +++ b/assets/tapkit_test.go @@ -0,0 +1,107 @@ +package assets + +import ( + "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/taproot-assets/address" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/commitment" + "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/tapscript" + "github.com/stretchr/testify/require" +) + +func validNonOpTrueProof(t *testing.T) (*proof.Proof, *address.Tap) { + t.Helper() + + _, scriptKey := btcec.PrivKeyFromBytes([]byte{1}) + _, internalKey := btcec.PrivKeyFromBytes([]byte{2}) + genesis := asset.Genesis{ + FirstPrevOut: wire.OutPoint{ + Hash: chainhash.Hash{1}, Index: 1, + }, + Tag: "non OP_TRUE asset", OutputIndex: 0, Type: asset.Normal, + } + proofAsset, err := asset.New( + genesis, 1, 0, 0, asset.NewScriptKey(scriptKey), nil, + asset.WithAssetVersion(asset.V1), + ) + require.NoError(t, err) + version := commitment.TapCommitmentV2 + tapCommitment, err := commitment.FromAssets(&version, proofAsset) + require.NoError(t, err) + _, commitmentProof, err := tapCommitment.Proof( + proofAsset.TapCommitmentKey(), proofAsset.AssetCommitmentKey(), + ) + require.NoError(t, err) + anchorScript, err := tapscript.PayToAddrScript( + *internalKey, nil, *tapCommitment, + ) + require.NoError(t, err) + anchorTx := wire.NewMsgTx(2) + anchorTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: genesis.FirstPrevOut, + }) + anchorTx.AddTxOut(&wire.TxOut{ + Value: 1_000, PkScript: anchorScript, + }) + assetProof := &proof.Proof{ + AnchorTx: *anchorTx, Asset: *proofAsset, + InclusionProof: proof.TaprootProof{ + OutputIndex: 0, InternalKey: internalKey, + CommitmentProof: &proof.CommitmentProof{ + Proof: *commitmentProof, + }, + }, + } + _, err = assetProof.VerifyProofs() + require.NoError(t, err) + + return assetProof, &address.Tap{ + AssetID: genesis.ID(), Amount: 1, + ChainParams: &address.RegressionNetTap, + } +} + +// TestCreateOpTrueSweepVpktValidation verifies malformed proof and destination +// inputs fail before virtual-packet construction can index into them. +func TestCreateOpTrueSweepVpktValidation(t *testing.T) { + _, err := CreateOpTrueSweepVpkt(t.Context(), nil, nil) + require.Error(t, err) + + proofs := []*proof.Proof{{}} + _, err = CreateOpTrueSweepVpkt(t.Context(), proofs, nil) + require.Error(t, err) + + addr := &address.Tap{Amount: 1, AssetID: asset.ID{1}} + _, err = CreateOpTrueSweepVpkt(t.Context(), proofs, addr) + require.Error(t, err) + + addr.ChainParams = &address.RegressionNetTap + addr.Amount = 0 + _, err = CreateOpTrueSweepVpkt(t.Context(), proofs, addr) + require.Error(t, err) + + addr.Amount = 1 + addr.AssetID = asset.ZeroID + _, err = CreateOpTrueSweepVpkt(t.Context(), proofs, addr) + require.Error(t, err) + + addr.AssetID = asset.ID{1} + _, err = CreateOpTrueSweepVpkt( + t.Context(), []*proof.Proof{nil}, addr, + ) + require.Error(t, err) + + _, err = CreateOpTrueSweepVpkt(t.Context(), proofs, addr) + require.Error(t, err) + + nonOpTrueProof, matchingAddr := validNonOpTrueProof(t) + _, err = CreateOpTrueSweepVpkt( + t.Context(), []*proof.Proof{nonOpTrueProof}, matchingAddr, + ) + require.ErrorContains(t, err, "is not an OP_TRUE asset") +} From 5805570980b6c9679ccf3ccccb6398f2efaf293a Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Wed, 16 Sep 2026 15:40:32 +0200 Subject: [PATCH 4/8] assets: Align sweep packets with Taproot Assets v0.8.3 Use the address constructor to select the virtual packet version and non-interactive split-root layout. Canonicalize OP_TRUE and destination script keys and populate every input witness in the prepared outputs. Cover v0 and v1 destination addresses and multi-input witness creation. --- assets/tapkit.go | 72 ++++++++----- assets/tapkit_test.go | 124 +++++++++++++++++++++++ docs/release-notes/release-notes-next.md | 3 + 3 files changed, 174 insertions(+), 25 deletions(-) diff --git a/assets/tapkit.go b/assets/tapkit.go index a503db7a8..3e69383c8 100644 --- a/assets/tapkit.go +++ b/assets/tapkit.go @@ -42,6 +42,7 @@ func CreateOpTrueSweepVpkt(ctx context.Context, proofs []*proof.Proof, if err != nil { return nil, err } + opTrueScriptKey = asset.NewScriptKey(opTrueScriptKey.PubKey) total := uint64(0) for idx, assetProof := range proofs { @@ -74,8 +75,19 @@ func CreateOpTrueSweepVpkt(ctx context.Context, proofs []*proof.Proof, return nil, fmt.Errorf("total proof amount does not match address") } + // An address always describes a non-interactive transfer. Let the + // Taproot Assets address constructor choose both the virtual packet + // version and the split-root/recipient output layout so these semantics + // stay aligned with the address version. + addressVpkt, err := tappsbt.FromAddresses( + []*address.Tap{addr}, 1, + ) + if err != nil { + return nil, err + } + sweepVpkt, err := tappsbt.FromProofs( - proofs, addr.ChainParams, tappsbt.V1, + proofs, addr.ChainParams, addressVpkt.Version, ) if err != nil { return nil, err @@ -101,16 +113,16 @@ func CreateOpTrueSweepVpkt(ctx context.Context, proofs []*proof.Proof, } } - sweepVpkt.Outputs = append(sweepVpkt.Outputs, &tappsbt.VOutput{ - AssetVersion: addr.AssetVersion, - Amount: addr.Amount, - Interactive: true, - AnchorOutputIndex: 0, - ScriptKey: asset.NewScriptKey(&addr.ScriptKey), - AnchorOutputInternalKey: &addr.InternalKey, - AnchorOutputTapscriptSibling: addr.TapscriptSibling, - ProofDeliveryAddress: &addr.ProofCourierAddr, - }) + destinationScriptKey, err := addr.ScriptKeyForAssetID(addr.AssetID) + if err != nil { + return nil, fmt.Errorf("invalid sweep script key: %w", err) + } + recipientOutput, err := addressVpkt.FirstNonSplitRootOutput() + if err != nil { + return nil, err + } + recipientOutput.ScriptKey = asset.NewScriptKey(destinationScriptKey) + sweepVpkt.Outputs = addressVpkt.Outputs if err := tapsend.PrepareOutputAssets(ctx, sweepVpkt); err != nil { return nil, err } @@ -124,22 +136,32 @@ func CreateOpTrueSweepVpkt(ctx context.Context, proofs []*proof.Proof, return nil, err } - if len(sweepVpkt.Outputs) == 0 || sweepVpkt.Outputs[0] == nil || - sweepVpkt.Outputs[0].Asset == nil || - len(sweepVpkt.Outputs[0].Asset.PrevWitnesses) == 0 { + for outputIdx, output := range sweepVpkt.Outputs { + if output == nil || output.Asset == nil { + return nil, fmt.Errorf( + "prepared asset output %d is incomplete", outputIdx, + ) + } - return nil, fmt.Errorf("prepared asset output is incomplete") - } - firstPrevWitness := &sweepVpkt.Outputs[0].Asset.PrevWitnesses[0] - if sweepVpkt.Outputs[0].Asset.HasSplitCommitmentWitness() { - rootAsset := firstPrevWitness.SplitCommitment.RootAsset - if len(rootAsset.PrevWitnesses) == 0 { - return nil, fmt.Errorf("split root asset witness is incomplete") + outputAssets := []*asset.Asset{output.Asset} + if output.SplitAsset != nil { + outputAssets = append(outputAssets, output.SplitAsset) + } + for _, outputAsset := range outputAssets { + prevWitnesses := outputAsset.Witnesses() + if len(prevWitnesses) != len(sweepVpkt.Inputs) { + return nil, fmt.Errorf( + "prepared asset output %d witnesses are "+ + "incomplete", outputIdx, + ) + } + for idx := range prevWitnesses { + prevWitnesses[idx].TxWitness = wire.TxWitness{ + append([]byte(nil), opTrueScript...), + append([]byte(nil), controlBlockBytes...), + } + } } - firstPrevWitness = &rootAsset.PrevWitnesses[0] - } - firstPrevWitness.TxWitness = wire.TxWitness{ - opTrueScript, controlBlockBytes, } return sweepVpkt, nil diff --git a/assets/tapkit_test.go b/assets/tapkit_test.go index 3fa3c3ae1..e11601f93 100644 --- a/assets/tapkit_test.go +++ b/assets/tapkit_test.go @@ -6,14 +6,18 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/loop/assets/htlc" "github.com/lightninglabs/taproot-assets/address" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/tappsbt" "github.com/lightninglabs/taproot-assets/tapscript" "github.com/stretchr/testify/require" ) +// validNonOpTrueProof creates a structurally valid proof whose asset cannot be +// spent through the OP_TRUE sweep path. func validNonOpTrueProof(t *testing.T) (*proof.Proof, *address.Tap) { t.Helper() @@ -66,6 +70,56 @@ func validNonOpTrueProof(t *testing.T) (*proof.Proof, *address.Tap) { } } +// validOpTrueProof creates a structurally valid OP_TRUE proof with the given +// asset identity, amount, and deterministic anchor key. +func validOpTrueProof(t *testing.T, genesis asset.Genesis, + amount uint64, keyScalar byte) *proof.Proof { + + t.Helper() + + _, internalKey := btcec.PrivKeyFromBytes([]byte{keyScalar}) + opTrueScriptKey, _, _, _, err := htlc.CreateOpTrueLeaf() + require.NoError(t, err) + proofAsset, err := asset.New( + genesis, amount, 0, 0, + asset.NewScriptKey(opTrueScriptKey.PubKey), nil, + asset.WithAssetVersion(asset.V1), + ) + require.NoError(t, err) + version := commitment.TapCommitmentV2 + tapCommitment, err := commitment.FromAssets(&version, proofAsset) + require.NoError(t, err) + _, commitmentProof, err := tapCommitment.Proof( + proofAsset.TapCommitmentKey(), + proofAsset.AssetCommitmentKey(), + ) + require.NoError(t, err) + anchorScript, err := tapscript.PayToAddrScript( + *internalKey, nil, *tapCommitment, + ) + require.NoError(t, err) + anchorTx := wire.NewMsgTx(2) + anchorTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: genesis.FirstPrevOut, + }) + anchorTx.AddTxOut(&wire.TxOut{ + Value: 1_000, PkScript: anchorScript, + }) + assetProof := &proof.Proof{ + AnchorTx: *anchorTx, Asset: *proofAsset, + InclusionProof: proof.TaprootProof{ + OutputIndex: 0, InternalKey: internalKey, + CommitmentProof: &proof.CommitmentProof{ + Proof: *commitmentProof, + }, + }, + } + _, err = assetProof.VerifyProofs() + require.NoError(t, err) + + return assetProof +} + // TestCreateOpTrueSweepVpktValidation verifies malformed proof and destination // inputs fail before virtual-packet construction can index into them. func TestCreateOpTrueSweepVpktValidation(t *testing.T) { @@ -104,4 +158,74 @@ func TestCreateOpTrueSweepVpktValidation(t *testing.T) { t.Context(), []*proof.Proof{nonOpTrueProof}, matchingAddr, ) require.ErrorContains(t, err, "is not an OP_TRUE asset") + +} + +// TestCreateOpTrueSweepVpktMultipleInputs verifies every asset input receives +// its OP_TRUE virtual witness with the address-selected output layout. +func TestCreateOpTrueSweepVpktMultipleInputs(t *testing.T) { + genesis := asset.Genesis{ + FirstPrevOut: wire.OutPoint{ + Hash: chainhash.Hash{9}, Index: 1, + }, + Tag: "multi-input OP_TRUE asset", OutputIndex: 0, + Type: asset.Normal, + } + proofs := []*proof.Proof{ + validOpTrueProof(t, genesis, 2, 11), + validOpTrueProof(t, genesis, 3, 12), + } + _, destinationScriptKey := btcec.PrivKeyFromBytes([]byte{21}) + _, destinationInternalKey := btcec.PrivKeyFromBytes([]byte{22}) + addr := address.Tap{ + AssetVersion: asset.V1, + AssetID: genesis.ID(), + ScriptKey: *destinationScriptKey, + InternalKey: *destinationInternalKey, + Amount: 5, + ChainParams: &address.RegressionNetTap, + } + + tests := []struct { + name string + addressVersion address.Version + packetVersion tappsbt.VPacketVersion + }{ + { + name: "v0 address", addressVersion: address.V0, + packetVersion: tappsbt.V0, + }, + { + name: "v1 address", addressVersion: address.V1, + packetVersion: tappsbt.V1, + }, + } + for _, testCase := range tests { + t.Run(testCase.name, func(t *testing.T) { + addr := addr + addr.Version = testCase.addressVersion + packet, err := CreateOpTrueSweepVpkt( + t.Context(), proofs, &addr, + ) + require.NoError(t, err) + require.Equal(t, testCase.packetVersion, packet.Version) + require.Len(t, packet.Outputs, 2) + require.True(t, packet.Outputs[0].Type.IsSplitRoot()) + require.False(t, packet.Outputs[0].Interactive) + require.False(t, packet.Outputs[1].Interactive) + require.Equal(t, uint32(1), + packet.Outputs[1].AnchorOutputIndex) + require.Same(t, &addr, packet.Outputs[1].Address) + + for _, output := range packet.Outputs { + witnesses, err := output.PrevWitnesses() + require.NoError(t, err) + require.Len(t, witnesses, len(proofs)) + for _, witness := range witnesses { + require.Len(t, witness.TxWitness, 2) + } + } + }) + } + } diff --git a/docs/release-notes/release-notes-next.md b/docs/release-notes/release-notes-next.md index 206d69bc7..f753570ca 100644 --- a/docs/release-notes/release-notes-next.md +++ b/docs/release-notes/release-notes-next.md @@ -13,6 +13,9 @@ #### Bug Fixes +* Shared Taproot Asset sweep packets now preserve the destination address + version and use the required non-interactive split-root layout. + * Instant Out now attempts to cancel server-side swaps when client initialization fails, allowing locked reservations to be released without waiting for the server timeout. From 8d8d89f568ff1cca9c246573b168e137b1eefc90 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Wed, 16 Sep 2026 15:40:48 +0200 Subject: [PATCH 5/8] assets: Harden shared asset validation Reject non-block deposit expiries, unsupported destination and asset versions, and duplicate anchor inputs. Validate the prepared OP_TRUE witnesses with the asset VM before returning a sweep packet. Cover malformed proof keys at the shared HTLC and deposit boundaries using the structural proof checks provided by Taproot Assets v0.8.3. --- assets/deposit/kit.go | 3 +++ assets/deposit/kit_test.go | 13 +++++++++ assets/htlc/swapkit_test.go | 17 ++++++++++++ assets/tapkit.go | 53 ++++++++++++++++++++++++++++++++++++- assets/tapkit_test.go | 13 ++++++++- 5 files changed, 97 insertions(+), 2 deletions(-) diff --git a/assets/deposit/kit.go b/assets/deposit/kit.go index 56b58ff5d..7c9383cf1 100644 --- a/assets/deposit/kit.go +++ b/assets/deposit/kit.go @@ -78,6 +78,9 @@ func NewKit(funderKey, coSignerKey *btcec.PublicKey, if csvExpiry == 0 { return nil, fmt.Errorf("CSV expiry must be positive") } + if csvExpiry > wire.SequenceLockTimeMask { + return nil, fmt.Errorf("CSV expiry exceeds block-based BIP68 range") + } if chainParams == nil || chainParams.Params == nil || chainParams.TapHRP == "" { diff --git a/assets/deposit/kit_test.go b/assets/deposit/kit_test.go index f1abb9649..77d54b538 100644 --- a/assets/deposit/kit_test.go +++ b/assets/deposit/kit_test.go @@ -150,6 +150,12 @@ func TestNewKitValidation(t *testing.T) { coSigner: coSignerKey, assetID: assetID, chainParams: params, }, + { + name: "non-block expiry", funder: funderKey, + coSigner: coSignerKey, assetID: assetID, + expiry: wire.SequenceLockTimeMask + 1, + chainParams: params, + }, { name: "nil chain parameters", funder: funderKey, coSigner: coSignerKey, assetID: assetID, expiry: 1, @@ -383,6 +389,13 @@ func TestVerifyProofReturnsAnchorRoot(t *testing.T) { ) require.ErrorContains(t, err, "deposit asset amount mismatch") + malformedProof := *fixture.proof + malformedProof.InclusionProof.InternalKey = nil + _, err = fixture.kit.AnchorRootFromProofCommitment( + &malformedProof, malformedProof.Asset.Amount, + ) + require.Error(t, err) + outputKey := txscript.ComputeTaprootOutputKey( fixture.kit.muSig2Key.PreTweakedKey, root, ) diff --git a/assets/htlc/swapkit_test.go b/assets/htlc/swapkit_test.go index 5e389e86d..3fc1beefb 100644 --- a/assets/htlc/swapkit_test.go +++ b/assets/htlc/swapkit_test.go @@ -813,6 +813,23 @@ func TestGetPkScriptFromProof(t *testing.T) { } } +// TestMalformedProofReturnsError verifies the Taproot Assets structural proof +// verifier rejects missing keys instead of panicking at this exported boundary. +func TestMalformedProofReturnsError(t *testing.T) { + fixture := newWitnessFixture(t, SuccessSequence) + + missingInternalKey := *fixture.proof + missingInternalKey.InclusionProof.InternalKey = nil + _, err := GenTaprootAssetRootFromProof(&missingInternalKey) + require.ErrorContains(t, err, "internal key") + + missingScriptKey := *fixture.proof + missingScriptKey.Asset = *fixture.proof.Asset.Copy() + missingScriptKey.Asset.ScriptKey.PubKey = nil + _, err = GenTaprootAssetRootFromProof(&missingScriptKey) + require.Error(t, err) +} + // executeWitness runs the virtual asset VM and Bitcoin script engine against a // generated witness. func (f *witnessFixture) executeWitness(t *testing.T, diff --git a/assets/tapkit.go b/assets/tapkit.go index 3e69383c8..dba907d5f 100644 --- a/assets/tapkit.go +++ b/assets/tapkit.go @@ -11,9 +11,11 @@ import ( "github.com/lightninglabs/loop/assets/htlc" "github.com/lightninglabs/taproot-assets/address" "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/commitment" "github.com/lightninglabs/taproot-assets/proof" "github.com/lightninglabs/taproot-assets/tappsbt" "github.com/lightninglabs/taproot-assets/tapsend" + "github.com/lightninglabs/taproot-assets/vm" ) // CreateOpTrueSweepVpkt creates a virtual packet that spends proof-bound @@ -38,13 +40,25 @@ func CreateOpTrueSweepVpkt(ctx context.Context, proofs []*proof.Proof, if addr.AssetID == asset.ZeroID { return nil, fmt.Errorf("group sweep addresses are unsupported") } + if address.IsUnknownVersion(addr.Version) { + return nil, fmt.Errorf("unsupported sweep address version") + } + if addr.Version >= address.V2 { + return nil, fmt.Errorf("version 2 sweep addresses are unsupported") + } + if addr.AssetVersion != asset.V0 && addr.AssetVersion != asset.V1 { + return nil, fmt.Errorf("unsupported sweep asset version") + } opTrueScriptKey, _, _, controlBlock, err := htlc.CreateOpTrueLeaf() if err != nil { return nil, err } opTrueScriptKey = asset.NewScriptKey(opTrueScriptKey.PubKey) - total := uint64(0) + var ( + total uint64 + seenOutpoints = make(map[wire.OutPoint]struct{}, len(proofs)) + ) for idx, assetProof := range proofs { if assetProof == nil { return nil, fmt.Errorf("asset proof %d is nil", idx) @@ -69,6 +83,13 @@ func CreateOpTrueSweepVpkt(ctx context.Context, proofs []*proof.Proof, if math.MaxUint64-total < assetProof.Asset.Amount { return nil, fmt.Errorf("asset proof amount overflow") } + outpoint := assetProof.OutPoint() + if _, ok := seenOutpoints[outpoint]; ok { + return nil, fmt.Errorf( + "asset proof %d duplicates an input outpoint", idx, + ) + } + seenOutpoints[outpoint] = struct{}{} total += assetProof.Asset.Amount } if total != addr.Amount { @@ -164,5 +185,35 @@ func CreateOpTrueSweepVpkt(ctx context.Context, proofs []*proof.Proof, } } + prevAssets := make(commitment.InputSet, len(sweepVpkt.Inputs)) + for _, input := range sweepVpkt.Inputs { + prevAssets[input.PrevID] = input.Asset() + } + splitRootOutput, err := sweepVpkt.SplitRootOutput() + if err != nil { + return nil, err + } + splitAssets := make([]*commitment.SplitAsset, len(sweepVpkt.Outputs)) + for idx, output := range sweepVpkt.Outputs { + splitAsset := output.Asset + if output.Type.IsSplitRoot() { + splitAsset = output.SplitAsset + } + if splitAsset == nil { + return nil, fmt.Errorf( + "prepared split asset output %d is incomplete", idx, + ) + } + splitAssets[idx] = &commitment.SplitAsset{ + Asset: *splitAsset, + OutputIndex: output.AnchorOutputIndex, + } + } + if err := vm.ValidateWitnesses( + splitRootOutput.Asset, splitAssets, prevAssets, + ); err != nil { + return nil, fmt.Errorf("invalid OP_TRUE asset witnesses: %w", err) + } + return sweepVpkt, nil } diff --git a/assets/tapkit_test.go b/assets/tapkit_test.go index e11601f93..c07fca83d 100644 --- a/assets/tapkit_test.go +++ b/assets/tapkit_test.go @@ -159,10 +159,15 @@ func TestCreateOpTrueSweepVpktValidation(t *testing.T) { ) require.ErrorContains(t, err, "is not an OP_TRUE asset") + matchingAddr.Version = address.V2 + _, err = CreateOpTrueSweepVpkt( + t.Context(), []*proof.Proof{nonOpTrueProof}, matchingAddr, + ) + require.ErrorContains(t, err, "version 2") } // TestCreateOpTrueSweepVpktMultipleInputs verifies every asset input receives -// its OP_TRUE virtual witness with the address-selected output layout. +// its OP_TRUE virtual witness and duplicate anchor inputs are rejected. func TestCreateOpTrueSweepVpktMultipleInputs(t *testing.T) { genesis := asset.Genesis{ FirstPrevOut: wire.OutPoint{ @@ -228,4 +233,10 @@ func TestCreateOpTrueSweepVpktMultipleInputs(t *testing.T) { }) } + addr.Version = address.V1 + addr.Amount = 4 + _, err := CreateOpTrueSweepVpkt( + t.Context(), []*proof.Proof{proofs[0], proofs[0]}, &addr, + ) + require.ErrorContains(t, err, "duplicates an input outpoint") } From 7ebf28c246a2c9102dcbd12090810c41bb2ae933 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Wed, 16 Sep 2026 16:07:19 +0200 Subject: [PATCH 6/8] assets: Tighten shared kit input boundaries Verify and decode the same bounded proof snapshot and isolate asset IDs passed to RPC clients. Reject unencodable HTLC amounts and keys that collapse to the same x-only signing identity. Document the HTLC funding template and shared OP_TRUE key limitations. Add regression coverage without changing valid legacy contract vectors. --- assets/deposit/boundary_test.go | 143 +++++++++++++++++++++++ assets/deposit/kit.go | 22 ++-- assets/deposit/kit_test.go | 6 +- assets/htlc/boundary_test.go | 42 +++++++ assets/htlc/script.go | 4 + assets/htlc/swapkit.go | 16 ++- docs/release-notes/release-notes-next.md | 3 + 7 files changed, 223 insertions(+), 13 deletions(-) create mode 100644 assets/deposit/boundary_test.go create mode 100644 assets/htlc/boundary_test.go diff --git a/assets/deposit/boundary_test.go b/assets/deposit/boundary_test.go new file mode 100644 index 000000000..e7cf434c3 --- /dev/null +++ b/assets/deposit/boundary_test.go @@ -0,0 +1,143 @@ +package deposit + +import ( + "bytes" + "context" + "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/taproot-assets/address" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/taprpc" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +// TestNewKitRejectsOppositeKeys ensures the two script roles cannot use the +// same x-only key under different compressed encodings. +func TestNewKitRejectsOppositeKeys(t *testing.T) { + _, funder := scalarKey(t, 1) + encoded := funder.SerializeCompressed() + encoded[0] ^= 1 + opposite, err := btcec.ParsePubKey(encoded) + require.NoError(t, err) + require.False(t, funder.IsEqual(opposite)) + + _, err = NewKit( + funder, opposite, keychain.KeyLocator{}, asset.ID{1}, 144, + &address.RegressionNetTap, + ) + require.ErrorContains(t, err, "keys must differ") +} + +// mutatingAddressClient retains and modifies request IDs to exercise the +// ownership boundary between a kit and its RPC client. +type mutatingAddressClient struct { + ids [][]byte +} + +// NewAddr retains and mutates the request's asset ID. +func (m *mutatingAddressClient) NewAddr(_ context.Context, + req *taprpc.NewAddrRequest, _ ...grpc.CallOption) (*taprpc.Addr, error) { + + m.ids = append(m.ids, req.AssetId) + req.AssetId[0] ^= 1 + + return &taprpc.Addr{}, nil +} + +// ExportProof retains and mutates the request's asset ID. +func (m *mutatingAddressClient) ExportProof(_ context.Context, + req *taprpc.ExportProofRequest, _ ...grpc.CallOption) (*taprpc.ProofFile, + error) { + + m.ids = append(m.ids, req.AssetId) + req.AssetId[0] ^= 1 + + return &taprpc.ProofFile{}, nil +} + +// TestRPCRequestsPreserveAssetID verifies that immediate and retained request +// mutations cannot change a kit or a derived HTLC contract. +func TestRPCRequestsPreserveAssetID(t *testing.T) { + fixture := newWitnessFixture(t) + kit := fixture.kit + expectedID := kit.assetID + client := &mutatingAddressClient{} + + _, err := kit.NewAddr(t.Context(), client, 1000) + require.NoError(t, err) + require.Equal(t, expectedID, kit.assetID) + + _, swapKit, err := kit.NewHtlcAddr( + t.Context(), client, 1000, lntypes.Hash{1}, 144, + ) + require.NoError(t, err) + require.Equal(t, expectedID, kit.assetID) + require.Equal(t, expectedID, swapKit.AssetID()) + + outpoint := fixture.proof.OutPoint() + _, err = kit.ExportProof(t.Context(), client, &outpoint) + require.NoError(t, err) + require.Equal(t, expectedID, kit.assetID) + require.Len(t, client.ids, 3) + for _, id := range client.ids { + clear(id) + } + require.Equal(t, expectedID, kit.assetID) + _, err = kit.AnchorRootFromProofCommitment(fixture.proof, 1000) + require.NoError(t, err) +} + +// TestVerifyProofFileSnapshot verifies that changes to caller-owned bytes +// during the RPC cannot replace the proof consumed after verification. +func TestVerifyProofFileSnapshot(t *testing.T) { + fixture := newWitnessFixture(t) + file, err := proof.NewFile(proof.V0, *fixture.proof) + require.NoError(t, err) + var encoded bytes.Buffer + require.NoError(t, file.Encode(&encoded)) + raw := encoded.Bytes() + expectedBytes := bytes.Clone(raw) + rpcFile := &taprpc.ProofFile{RawProofFile: raw} + outpoint := fixture.proof.OutPoint() + verifier := &proofVerifierMock{ + response: &taprpc.VerifyProofResponse{Valid: true}, + onVerify: func(req *taprpc.ProofFile) { + clear(raw) + rpcFile.RawProofFile = []byte{0} + require.Equal(t, expectedBytes, req.RawProofFile) + }, + } + expectedRoot, err := fixture.kit.AnchorRootFromProofCommitment( + fixture.proof, fixture.proof.Asset.Amount, + ) + require.NoError(t, err) + root, err := fixture.kit.VerifyProofFile( + t.Context(), verifier, rpcFile, &outpoint, + fixture.proof.Asset.Amount, + ) + require.NoError(t, err) + require.Equal(t, expectedRoot, root) + require.Equal(t, 1, verifier.calls) +} + +// TestVerifyProofFileSizeLimit verifies oversized input is rejected before +// crossing the verifier boundary. +func TestVerifyProofFileSizeLimit(t *testing.T) { + fixture := newWitnessFixture(t) + verifier := &proofVerifierMock{ + response: &taprpc.VerifyProofResponse{Valid: true}, + } + _, err := fixture.kit.VerifyProofFile( + t.Context(), verifier, &taprpc.ProofFile{ + RawProofFile: make([]byte, proof.FileMaxProofSizeBytes+1), + }, &wire.OutPoint{}, fixture.proof.Asset.Amount, + ) + require.ErrorContains(t, err, "file exceeds maximum size") + require.Zero(t, verifier.calls) +} diff --git a/assets/deposit/kit.go b/assets/deposit/kit.go index 7c9383cf1..387954d01 100644 --- a/assets/deposit/kit.go +++ b/assets/deposit/kit.go @@ -69,9 +69,6 @@ func NewKit(funderKey, coSignerKey *btcec.PublicKey, if coSignerKey == nil { return nil, fmt.Errorf("co-signer public key is required") } - if funderKey.IsEqual(coSignerKey) { - return nil, fmt.Errorf("funder and co-signer keys must differ") - } if assetID == (asset.ID{}) { return nil, fmt.Errorf("asset ID is required") } @@ -99,6 +96,11 @@ func NewKit(funderKey, coSignerKey *btcec.PublicKey, if err != nil { return nil, fmt.Errorf("invalid co-signer public key: %w", err) } + if bytes.Equal(schnorr.SerializePubKey(funderKeyCopy), + schnorr.SerializePubKey(coSignerKeyCopy)) { + + return nil, fmt.Errorf("funder and co-signer keys must differ") + } sortKeys := true muSig2Key, err := input.MuSig2CombineKeys( @@ -210,7 +212,7 @@ func (d *Kit) NewAddr(ctx context.Context, client AddressProofClient, } return client.NewAddr(ctx, &taprpc.NewAddrRequest{ - AssetId: d.assetID[:], + AssetId: bytes.Clone(d.assetID[:]), Amt: amount, ScriptKey: rpcutils.MarshalScriptKey(tapScriptKey), InternalKey: &taprpc.KeyDescriptor{ @@ -255,7 +257,7 @@ func (d *Kit) NewHtlcAddr(ctx context.Context, client AddressProofClient, } htlcAddr, err := client.NewAddr(ctx, &taprpc.NewAddrRequest{ - AssetId: d.assetID[:], + AssetId: bytes.Clone(d.assetID[:]), Amt: amount, ScriptKey: rpcutils.MarshalScriptKey(tapScriptKey), InternalKey: &taprpc.KeyDescriptor{ @@ -314,7 +316,7 @@ func (d *Kit) ExportProof(ctx context.Context, client AddressProofClient, } return client.ExportProof(ctx, &taprpc.ExportProofRequest{ - AssetId: d.assetID[:], + AssetId: bytes.Clone(d.assetID[:]), ScriptKey: scriptKey.PubKey.SerializeCompressed(), Outpoint: &taprpc.OutPoint{ Txid: outpoint.Hash[:], @@ -482,8 +484,12 @@ func (d *Kit) VerifyProofFile(ctx context.Context, verifier ProofVerifier, return nil, fmt.Errorf("expected deposit amount must be positive") } + rawProof := proofFile.RawProofFile + if err := proof.CheckMaxFileSize(rawProof); err != nil { + return nil, fmt.Errorf("invalid deposit proof file: %w", err) + } proofFileCopy := &taprpc.ProofFile{ - RawProofFile: append([]byte(nil), proofFile.RawProofFile...), + RawProofFile: bytes.Clone(rawProof), GenesisPoint: proofFile.GenesisPoint, } verifyResponse, err := verifier.VerifyProof(ctx, proofFileCopy) @@ -494,7 +500,7 @@ func (d *Kit) VerifyProofFile(ctx context.Context, verifier ProofVerifier, return nil, fmt.Errorf("invalid deposit proof file") } - decodedFile, err := proof.DecodeFile(proofFile.RawProofFile) + decodedFile, err := proof.DecodeFile(proofFileCopy.RawProofFile) if err != nil { return nil, fmt.Errorf("unable to decode deposit proof file: %w", err) } diff --git a/assets/deposit/kit_test.go b/assets/deposit/kit_test.go index 77d54b538..ef63af6cf 100644 --- a/assets/deposit/kit_test.go +++ b/assets/deposit/kit_test.go @@ -205,15 +205,19 @@ type proofVerifierMock struct { response *taprpc.VerifyProofResponse err error calls int + onVerify func(*taprpc.ProofFile) } // VerifyProof records a proof verification request and returns the configured // tapd response. func (m *proofVerifierMock) VerifyProof(_ context.Context, - _ *taprpc.ProofFile, _ ...grpc.CallOption) (*taprpc.VerifyProofResponse, + file *taprpc.ProofFile, _ ...grpc.CallOption) (*taprpc.VerifyProofResponse, error) { m.calls++ + if m.onVerify != nil { + m.onVerify(file) + } return m.response, m.err } diff --git a/assets/htlc/boundary_test.go b/assets/htlc/boundary_test.go new file mode 100644 index 000000000..168aeb489 --- /dev/null +++ b/assets/htlc/boundary_test.go @@ -0,0 +1,42 @@ +package htlc + +import ( + "bytes" + "math" + "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/stretchr/testify/require" +) + +// TestNewSwapKitAmountLimit checks that every accepted boundary amount can +// be encoded in the funding template. +func TestNewSwapKitAmountLimit(t *testing.T) { + params := vectorParams(t) + params.Amount = math.MaxInt64 + kit, err := NewSwapKit(LegacyDepositV0, params) + require.NoError(t, err) + packet, err := kit.CreateHtlcVpkt() + require.NoError(t, err) + var encoded bytes.Buffer + require.NoError(t, packet.Serialize(&encoded)) + + params.Amount++ + _, err = NewSwapKit(LegacyDepositV0, params) + require.ErrorContains(t, err, "amount exceeds maximum") +} + +// TestNewSwapKitRejectsOppositeKeys ensures the two script roles cannot use +// the same x-only key under different compressed encodings. +func TestNewSwapKitRejectsOppositeKeys(t *testing.T) { + params := vectorParams(t) + encoded := params.SenderPubKey.SerializeCompressed() + encoded[0] ^= 1 + opposite, err := btcec.ParsePubKey(encoded) + require.NoError(t, err) + require.False(t, params.SenderPubKey.IsEqual(opposite)) + params.ReceiverPubKey = opposite + + _, err = NewSwapKit(LegacyDepositV0, params) + require.ErrorContains(t, err, "keys must differ") +} diff --git a/assets/htlc/script.go b/assets/htlc/script.go index af65296e6..0641ee6bc 100644 --- a/assets/htlc/script.go +++ b/assets/htlc/script.go @@ -69,6 +69,10 @@ func GetOpTrueScript() ([]byte, error) { // CreateOpTrueLeaf creates the legacy Taproot Asset script key whose only // script path is OP_TRUE beneath the public Taproot Assets NUMS key. +// This key is intentionally shared by all legacy contracts, so multiple +// outputs for the same asset cannot be batched in one transaction. Proof +// delivery must also distinguish contracts by more than this script key. +// Contract-specific keys would require a new policy. func CreateOpTrueLeaf() (asset.ScriptKey, txscript.TapLeaf, *txscript.IndexedTapScriptTree, *txscript.ControlBlock, error) { diff --git a/assets/htlc/swapkit.go b/assets/htlc/swapkit.go index 71e1c3600..529eb3a95 100644 --- a/assets/htlc/swapkit.go +++ b/assets/htlc/swapkit.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "math" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" @@ -105,7 +106,9 @@ func NewSwapKit(policy Policy, params Params) (*SwapKit, error) { if err != nil { return nil, fmt.Errorf("invalid receiver public key: %w", err) } - if senderKey.IsEqual(receiverKey) { + if bytes.Equal(schnorr.SerializePubKey(senderKey), + schnorr.SerializePubKey(receiverKey)) { + return nil, fmt.Errorf("sender and receiver keys must differ") } @@ -223,6 +226,9 @@ func (s *SwapKit) Validate() error { if s.amount == 0 { return fmt.Errorf("asset amount must be positive") } + if s.amount > math.MaxInt64 { + return fmt.Errorf("asset amount exceeds maximum virtual output value") + } if s.assetID == (asset.ID{}) { return fmt.Errorf("asset ID is required") } @@ -347,9 +353,11 @@ func (s *SwapKit) GetSiblingPreimage() ( return commitment.NewPreimageFromBranch(branch), nil } -// CreateHtlcVpkt creates the version-one virtual packet for the HTLC. The -// split-root and HTLC output indices and their interactive flags are consensus -// with the existing server implementation. +// CreateHtlcVpkt creates a version-one HTLC funding template. It must pass +// through FundVirtualPsbt before being prepared or committed as a final +// virtual packet: funding replaces or removes the zero-valued interactive +// change placeholder. The output indices and interactive flags preserve the +// existing server contract. func (s *SwapKit) CreateHtlcVpkt() (*tappsbt.VPacket, error) { if err := s.Validate(); err != nil { return nil, err diff --git a/docs/release-notes/release-notes-next.md b/docs/release-notes/release-notes-next.md index f753570ca..09b5edb30 100644 --- a/docs/release-notes/release-notes-next.md +++ b/docs/release-notes/release-notes-next.md @@ -13,6 +13,9 @@ #### Bug Fixes +* Shared asset kits now isolate proof and RPC buffers, reject oversized proofs + and unencodable amounts, and enforce distinct x-only signing keys. + * Shared Taproot Asset sweep packets now preserve the destination address version and use the required non-interactive split-root layout. From 201d8caef38c1cb4d9a221701216480347454f09 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Wed, 16 Sep 2026 16:26:02 +0200 Subject: [PATCH 7/8] assets: Require complete deposit proof provenance Reject empty, non-genesis-rooted, and ownership-challenge histories before asking tapd to verify a deposit proof file. Apply the same checks to every nested additional input without recursive stack growth. Retain tapd verification and exact terminal contract bindings. Cover valid genesis and transfer histories, an inflated OP_TRUE ownership claim accepted by the upstream verifier, nested shortcuts, and invalid transition witnesses. --- assets/deposit/kit.go | 59 +++++- assets/deposit/kit_test.go | 8 +- assets/deposit/provenance_test.go | 250 +++++++++++++++++++++++ docs/release-notes/release-notes-next.md | 3 + 4 files changed, 309 insertions(+), 11 deletions(-) create mode 100644 assets/deposit/provenance_test.go diff --git a/assets/deposit/kit.go b/assets/deposit/kit.go index 387954d01..8ef065e8d 100644 --- a/assets/deposit/kit.go +++ b/assets/deposit/kit.go @@ -460,10 +460,11 @@ func (d *Kit) AnchorRootFromProofCommitment(depositProof *proof.Proof, return d.anchorRootFromProofCommitment(depositProof) } -// VerifyProofFile asks tapd to fully verify a proof file, binds its terminal -// proof to the expected deposit outpoint and amount, and returns the complete -// anchor Taproot Merkle root. The caller must still enforce confirmation depth -// and verify that the anchor outpoint remains unspent. +// VerifyProofFile requires genesis-rooted histories without ownership +// challenges, including all additional inputs, then asks tapd to fully verify +// the proof file. It binds the terminal proof to the expected deposit outpoint +// and amount and returns the complete anchor Taproot Merkle root. The caller +// must still enforce confirmation depth and check that the outpoint is unspent. func (d *Kit) VerifyProofFile(ctx context.Context, verifier ProofVerifier, proofFile *taprpc.ProofFile, expectedOutpoint *wire.OutPoint, expectedAmount uint64) ([]byte, error) { @@ -492,6 +493,13 @@ func (d *Kit) VerifyProofFile(ctx context.Context, verifier ProofVerifier, RawProofFile: bytes.Clone(rawProof), GenesisPoint: proofFile.GenesisPoint, } + decodedFile, err := proof.DecodeFile(proofFileCopy.RawProofFile) + if err != nil { + return nil, fmt.Errorf("unable to decode deposit proof file: %w", err) + } + if err := validateProofProvenance(ctx, decodedFile); err != nil { + return nil, err + } verifyResponse, err := verifier.VerifyProof(ctx, proofFileCopy) if err != nil { return nil, fmt.Errorf("unable to verify deposit proof file: %w", err) @@ -500,10 +508,6 @@ func (d *Kit) VerifyProofFile(ctx context.Context, verifier ProofVerifier, return nil, fmt.Errorf("invalid deposit proof file") } - decodedFile, err := proof.DecodeFile(proofFileCopy.RawProofFile) - if err != nil { - return nil, fmt.Errorf("unable to decode deposit proof file: %w", err) - } depositProof, err := decodedFile.LastProof() if err != nil { return nil, fmt.Errorf("unable to read terminal deposit proof: %w", err) @@ -518,6 +522,45 @@ func (d *Kit) VerifyProofFile(ctx context.Context, verifier ProofVerifier, return d.AnchorRootFromProofCommitment(depositProof, expectedAmount) } +// validateProofProvenance excludes ownership-only histories before tapd +// verifies issuance and transitions. Walk nested files iteratively so peer +// input cannot determine the call-stack depth. These structural checks alone +// do not establish that an asset is valid. +func validateProofProvenance(ctx context.Context, file *proof.File) error { + pending := []*proof.File{file} + for len(pending) > 0 { + last := len(pending) - 1 + current := pending[last] + pending[last] = nil + pending = pending[:last] + if current.NumProofs() == 0 { + return fmt.Errorf("deposit proof history is empty") + } + for idx := range current.NumProofs() { + if err := ctx.Err(); err != nil { + return err + } + p, err := current.ProofAt(uint32(idx)) + if err != nil { + return fmt.Errorf("unable to decode deposit proof: %w", err) + } + if p.ChallengeWitness != nil { + return fmt.Errorf("deposit proof history contains an " + + "ownership challenge") + } + if idx == 0 && !p.Asset.IsGenesisAsset() { + return fmt.Errorf("deposit proof history must start " + + "at genesis") + } + for inputIdx := range p.AdditionalInputs { + pending = append(pending, &p.AdditionalInputs[inputIdx]) + } + } + } + + return nil +} + // VerifyProof verifies only the proof commitment and binds it to this // deposit's asset, internal key, and timeout sibling. It does not validate the // expected amount, provenance, chain inclusion, or unspent status. diff --git a/assets/deposit/kit_test.go b/assets/deposit/kit_test.go index ef63af6cf..3e2034321 100644 --- a/assets/deposit/kit_test.go +++ b/assets/deposit/kit_test.go @@ -284,7 +284,7 @@ func newWitnessFixture(t *testing.T) *witnessFixture { FirstPrevOut: wire.OutPoint{ Hash: chainhash.Hash{0x31}, Index: 3, }, - Tag: "asset deposit witness test", OutputIndex: 0, + Tag: "asset deposit witness test", OutputIndex: 1, Type: asset.Normal, } kit, err := NewKit( @@ -329,8 +329,10 @@ func newWitnessFixture(t *testing.T) *witnessFixture { Value: anchorValue, PkScript: anchorPkScript, }) assetProof := &proof.Proof{ - AnchorTx: *anchorTx, - Asset: *depositAsset, + PrevOut: genesis.FirstPrevOut, + GenesisReveal: &genesis, + AnchorTx: *anchorTx, + Asset: *depositAsset, InclusionProof: proof.TaprootProof{ OutputIndex: 1, InternalKey: kit.muSig2Key.PreTweakedKey, diff --git a/assets/deposit/provenance_test.go b/assets/deposit/provenance_test.go new file mode 100644 index 000000000..ee0a75665 --- /dev/null +++ b/assets/deposit/provenance_test.go @@ -0,0 +1,250 @@ +package deposit + +import ( + "bytes" + "context" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/loop/assets/htlc" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/commitment" + "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/taprpc" + "github.com/lightninglabs/taproot-assets/tapscript" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +// encodeProofFile serializes proof histories for the RPC boundary tests. +func encodeProofFile(t *testing.T, proofs ...proof.Proof) *taprpc.ProofFile { + t.Helper() + file, err := proof.NewFile(proof.V0, proofs...) + require.NoError(t, err) + var encoded bytes.Buffer + require.NoError(t, file.Encode(&encoded)) + + return &taprpc.ProofFile{RawProofFile: encoded.Bytes()} +} + +// opTrueWitness returns a valid virtual script-path ownership/spend witness. +func opTrueWitness(t *testing.T) wire.TxWitness { + t.Helper() + _, leaf, _, control, err := htlc.CreateOpTrueLeaf() + require.NoError(t, err) + encoded, err := control.ToBytes() + require.NoError(t, err) + + return wire.TxWitness{leaf.Script, encoded} +} + +// reanchorDepositProof rebuilds a proof's commitment after changing its asset +// leaf, preserving the deposit's Bitcoin contract. +func reanchorDepositProof(t *testing.T, kit *Kit, p *proof.Proof) { + t.Helper() + version := commitment.TapCommitmentV2 + tapCommitment, err := commitment.FromAssets(&version, &p.Asset) + require.NoError(t, err) + _, inclusion, err := tapCommitment.Proof( + p.Asset.TapCommitmentKey(), p.Asset.AssetCommitmentKey(), + ) + require.NoError(t, err) + sibling, err := kit.timeoutPathSibling() + require.NoError(t, err) + siblingHash, err := sibling.TapHash() + require.NoError(t, err) + script, err := tapscript.PayToAddrScript( + *kit.muSig2Key.PreTweakedKey, siblingHash, *tapCommitment, + ) + require.NoError(t, err) + p.InclusionProof.CommitmentProof = &proof.CommitmentProof{ + Proof: *inclusion, TapSiblingPreimage: sibling, + } + p.AnchorTx = *p.AnchorTx.Copy() + p.AnchorTx.TxOut[p.InclusionProof.OutputIndex].PkScript = script +} + +// newTransferProof creates an OP_TRUE transfer from a fixture's genesis asset. +func newTransferProof(t *testing.T, fixture *witnessFixture) *proof.Proof { + t.Helper() + p := *fixture.proof + p.Asset = *fixture.proof.Asset.Copy() + p.Asset.PrevWitnesses = []asset.Witness{{ + PrevID: &asset.PrevID{ + OutPoint: fixture.proof.OutPoint(), + ID: p.Asset.ID(), + ScriptKey: asset.ToSerialized( + p.Asset.ScriptKey.PubKey, + ), + }, + TxWitness: opTrueWitness(t), + }} + p.GenesisReveal = nil + p.PrevOut = fixture.proof.OutPoint() + p.AnchorTx = *p.AnchorTx.Copy() + p.AnchorTx.TxIn[0].PreviousOutPoint = p.PrevOut + reanchorDepositProof(t, fixture.kit, &p) + + return &p +} + +// localProofVerifier runs tapd's proof-file verification path with only chain +// lookups mocked; commitment and asset VM verification remain real. +type localProofVerifier struct{} + +// VerifyProof verifies a decoded file with the upstream proof verifier. +func (localProofVerifier) VerifyProof(ctx context.Context, + req *taprpc.ProofFile, _ ...grpc.CallOption) (*taprpc.VerifyProofResponse, + error) { + + file, err := proof.DecodeFile(req.RawProofFile) + if err != nil { + return nil, err + } + _, err = file.Verify(ctx, proof.MockVerifierCtx) + + return &taprpc.VerifyProofResponse{Valid: err == nil}, nil +} + +// TestVerifyProofFileProvenance accepts real genesis and transfer proofs while +// rejecting standalone and nested ownership histories before invoking tapd. +func TestVerifyProofFileProvenance(t *testing.T) { + fixture := newWitnessFixture(t) + transfer := newTransferProof(t, fixture) + for _, history := range [][]proof.Proof{ + {*fixture.proof}, + {*fixture.proof, *transfer}, + } { + terminal := history[len(history)-1] + outpoint := terminal.OutPoint() + _, err := fixture.kit.VerifyProofFile( + t.Context(), localProofVerifier{}, + encodeProofFile(t, history...), &outpoint, + terminal.Asset.Amount, + ) + require.NoError(t, err) + } + + // Structural provenance must not replace cryptographic verification. + invalidTransfer := *transfer + invalidTransfer.Asset = *transfer.Asset.Copy() + invalidTransfer.Asset.PrevWitnesses[0].TxWitness = nil + outpoint := invalidTransfer.OutPoint() + _, err := fixture.kit.VerifyProofFile( + t.Context(), localProofVerifier{}, + encodeProofFile(t, *fixture.proof, invalidTransfer), + &outpoint, invalidTransfer.Asset.Amount, + ) + require.ErrorContains(t, err, "invalid deposit proof file") + + // This leaf has no issuance history and claims an inflated amount, + // yet the real verifier accepts its public OP_TRUE ownership challenge. + ownership := *transfer + ownership.Asset = *transfer.Asset.Copy() + ownership.Asset.Amount++ + ownership.ChallengeWitness = opTrueWitness(t) + reanchorDepositProof(t, fixture.kit, &ownership) + response, err := (localProofVerifier{}).VerifyProof( + t.Context(), encodeProofFile(t, ownership), + ) + require.NoError(t, err) + require.True(t, response.Valid) + + // Wrap a file in AdditionalInputs, including more than one level to + // ensure the restriction applies throughout the input tree. + wrap := func(history []proof.Proof) []proof.Proof { + inputFile, err := proof.NewFile(proof.V0, history...) + require.NoError(t, err) + parent := *fixture.proof + parent.AdditionalInputs = []proof.File{*inputFile} + return []proof.Proof{parent} + } + emptyChallenge := ownership + emptyChallenge.ChallengeWitness = wire.TxWitness{} + tests := []struct { + name string + history []proof.Proof + err string + }{ + { + name: "empty file", err: "history is empty", + }, + { + name: "ownership", history: []proof.Proof{ownership}, + err: "ownership challenge", + }, + { + name: "empty challenge witness", + history: []proof.Proof{emptyChallenge}, + err: "ownership challenge", + }, + { + name: "missing genesis", history: []proof.Proof{*transfer}, + err: "must start at genesis", + }, + { + name: "later ownership challenge", + history: []proof.Proof{*fixture.proof, ownership}, + err: "ownership challenge", + }, + { + name: "ownership additional input", + history: wrap([]proof.Proof{ownership}), + err: "ownership challenge", + }, + { + name: "deep ownership additional input", + history: wrap(wrap([]proof.Proof{ownership})), + err: "ownership challenge", + }, + { + name: "additional input missing genesis", + history: wrap([]proof.Proof{*transfer}), + err: "must start at genesis", + }, + { + name: "empty additional input", + history: wrap(nil), err: "history is empty", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + verifier := &proofVerifierMock{ + response: &taprpc.VerifyProofResponse{Valid: true}, + } + outpoint := fixture.proof.OutPoint() + amount := fixture.proof.Asset.Amount + if len(test.history) > 0 { + terminal := test.history[len(test.history)-1] + outpoint = terminal.OutPoint() + amount = terminal.Asset.Amount + } + _, err := fixture.kit.VerifyProofFile( + t.Context(), verifier, + encodeProofFile(t, test.history...), + &outpoint, amount, + ) + require.ErrorContains(t, err, test.err) + require.Zero(t, verifier.calls) + }) + } +} + +// TestValidateProofProvenanceNested verifies that complete input histories +// pass the structural gate and that traversal respects cancellation. Tapd +// remains responsible for validating the transitions connecting these files. +func TestValidateProofProvenanceNested(t *testing.T) { + fixture := newWitnessFixture(t) + file, err := proof.NewFile(proof.V0, *fixture.proof) + require.NoError(t, err) + for range 3 { + parent := *fixture.proof + parent.AdditionalInputs = []proof.File{*file} + file, err = proof.NewFile(proof.V0, parent) + require.NoError(t, err) + } + require.NoError(t, validateProofProvenance(t.Context(), file)) + ctx, cancel := context.WithCancel(t.Context()) + cancel() + require.ErrorIs(t, validateProofProvenance(ctx, file), context.Canceled) +} diff --git a/docs/release-notes/release-notes-next.md b/docs/release-notes/release-notes-next.md index 09b5edb30..c7d94ea3f 100644 --- a/docs/release-notes/release-notes-next.md +++ b/docs/release-notes/release-notes-next.md @@ -13,6 +13,9 @@ #### Bug Fixes +* Deposit proof verification now requires genesis-rooted histories and rejects + ownership-only proofs, including those hidden in additional inputs. + * Shared asset kits now isolate proof and RPC buffers, reject oversized proofs and unencodable amounts, and enforce distinct x-only signing keys. From dcd5af1f75d3c6e1fe842fee414d57e164671724 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Thu, 17 Sep 2026 10:43:21 +0200 Subject: [PATCH 8/8] assets: Validate shared kit transfers before signing Require caller-approved virtual packets when signing asset anchors. Validate asset conservation, virtual witnesses, complete input commitments and the exact output commitments, including change and passive assets. Normalize split witnesses only for input anchor reconstruction so non-interactive proofs and legacy commitments remain supported. Pin new deposit and HTLC addresses to asset V1 and address V1. Require the deposit CSV sequence before signing to preserve existing fee signatures, and return an independent internal key in deposit control blocks. Add regression coverage for invalid transfers, passive assets, pruned tombstones, legacy and non-interactive inputs, address versions, fee signatures and key isolation. Document the signing API changes. --- assets/deposit/kit.go | 51 ++++- assets/deposit/kit_test.go | 27 ++- assets/deposit/signing_test.go | 154 +++++++++++++ assets/htlc/swapkit.go | 21 +- assets/htlc/swapkit_test.go | 27 ++- assets/htlc/sweep_test.go | 227 +++++++++++++++++++ assets/internal/test/sweep.go | 83 +++++++ assets/sweep/transfer.go | 270 +++++++++++++++++++++++ assets/tapkit_test.go | 88 ++++++++ docs/release-notes/release-notes-next.md | 9 + 10 files changed, 928 insertions(+), 29 deletions(-) create mode 100644 assets/deposit/signing_test.go create mode 100644 assets/htlc/sweep_test.go create mode 100644 assets/internal/test/sweep.go create mode 100644 assets/sweep/transfer.go diff --git a/assets/deposit/kit.go b/assets/deposit/kit.go index 8ef065e8d..f3d03363e 100644 --- a/assets/deposit/kit.go +++ b/assets/deposit/kit.go @@ -15,6 +15,7 @@ import ( "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/assets/htlc" + assetsweep "github.com/lightninglabs/loop/assets/sweep" "github.com/lightninglabs/taproot-assets/address" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" @@ -188,7 +189,8 @@ func (d *Kit) encodedTimeoutPathSibling() ([]byte, error) { } // NewAddr creates a two-party MuSig2 deposit address with a unilateral funder -// timeout path. +// timeout path. New addresses use asset V1 and address V1 (commitment V2), as +// does the HTLC funding template; legacy proofs remain accepted when spending. func (d *Kit) NewAddr(ctx context.Context, client AddressProofClient, amount uint64) (*taprpc.Addr, error) { @@ -219,11 +221,14 @@ func (d *Kit) NewAddr(ctx context.Context, client AddressProofClient, RawKeyBytes: d.muSig2Key.PreTweakedKey.SerializeCompressed(), }, TapscriptSibling: siblingBytes, + AssetVersion: taprpc.AssetVersion_ASSET_VERSION_V1, + AddressVersion: taprpc.AddrVersion_ADDR_VERSION_V1, }) } // NewHtlcAddr creates an HTLC address using the deposit parties as sender and -// receiver under the immutable legacy deposit policy. +// receiver under the immutable legacy deposit policy. Asset and address versions +// are pinned to match CreateHtlcVpkt rather than relying on RPC defaults. func (d *Kit) NewHtlcAddr(ctx context.Context, client AddressProofClient, amount uint64, swapHash lntypes.Hash, csvExpiry uint32) ( *taprpc.Addr, *htlc.SwapKit, error) { @@ -264,6 +269,8 @@ func (d *Kit) NewHtlcAddr(ctx context.Context, client AddressProofClient, RawKeyBytes: btcInternalKey.SerializeCompressed(), }, TapscriptSibling: siblingBytes, + AssetVersion: taprpc.AssetVersion_ASSET_VERSION_V1, + AddressVersion: taprpc.AddrVersion_ADDR_VERSION_V1, }) if err != nil { return nil, nil, err @@ -582,8 +589,9 @@ func (d *Kit) GenTimeoutBtcControlBlock(taprootAssetRoot []byte) ( return nil, fmt.Errorf("asset root must be %d bytes", chainhash.HashSize) } + internalKey := *d.muSig2Key.PreTweakedKey controlBlock := &txscript.ControlBlock{ - InternalKey: d.muSig2Key.PreTweakedKey, + InternalKey: &internalKey, LeafVersion: txscript.BaseLeafVersion, InclusionProof: append([]byte(nil), taprootAssetRoot...), } @@ -745,13 +753,29 @@ func verifyTapscriptSignature(tx *wire.MsgTx, sweep *validatedSweep, return nil } +// AssetInputIndex returns the unique proof-bound deposit input after validating +// the proof and PSBT prevouts. Callers must set this input's sequence to the +// deposit's CSV expiry before signing any transaction inputs. +func (d *Kit) AssetInputIndex(depositProof *proof.Proof, + sweepPacket *psbt.Packet) (uint32, error) { + + sweep, err := d.validateSweep(depositProof, sweepPacket) + if err != nil { + return 0, err + } + + return uint32(sweep.assetInputIndex), nil +} + // CreateTimeoutWitness signs the exact proof-bound deposit input and returns -// its input index and timeout-path witness. The method sets the required CSV -// sequence on a transaction copy before signing, then writes that sequence to -// the caller's PSBT only after the signature has been verified. +// its input index and timeout-path witness. The caller must set the deposit +// input's sequence to the CSV expiry before calling this method. The PSBT is +// never mutated here, preserving signatures on other inputs. The transfer +// must contain the caller-approved, complete active and passive asset packets. func (d *Kit) CreateTimeoutWitness(ctx context.Context, signer lndclient.SignerClient, depositProof *proof.Proof, - sweepPacket *psbt.Packet) (*htlc.SpendWitness, error) { + sweepPacket *psbt.Packet, transfer *assetsweep.Transfer) ( + *htlc.SpendWitness, error) { if signer == nil { return nil, fmt.Errorf("signer is required") @@ -760,8 +784,15 @@ func (d *Kit) CreateTimeoutWitness(ctx context.Context, if err != nil { return nil, err } - sweepTx := sweepPacket.UnsignedTx.Copy() - sweepTx.TxIn[sweep.assetInputIndex].Sequence = d.csvExpiry + sequence := sweepPacket.UnsignedTx.TxIn[sweep.assetInputIndex].Sequence + if sequence != d.csvExpiry { + return nil, fmt.Errorf("asset input sequence must be %d, got %d", + d.csvExpiry, sequence) + } + if err := transfer.Validate(depositProof, sweepPacket); err != nil { + return nil, err + } + sweepTx := sweepPacket.UnsignedTx timeoutScript, err := d.GenTimeoutPathScript() if err != nil { return nil, err @@ -801,8 +832,6 @@ func (d *Kit) CreateTimeoutWitness(ctx context.Context, if err != nil { return nil, err } - sweepPacket.UnsignedTx.TxIn[sweep.assetInputIndex].Sequence = d.csvExpiry - return &htlc.SpendWitness{ InputIndex: uint32(sweep.assetInputIndex), Witness: wire.TxWitness{ diff --git a/assets/deposit/kit_test.go b/assets/deposit/kit_test.go index 3e2034321..193fcec21 100644 --- a/assets/deposit/kit_test.go +++ b/assets/deposit/kit_test.go @@ -14,6 +14,8 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/assets/htlc" + assettest "github.com/lightninglabs/loop/assets/internal/test" + "github.com/lightninglabs/loop/assets/sweep" "github.com/lightninglabs/taproot-assets/address" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" @@ -266,6 +268,7 @@ func (s *localSigner) SignOutputRaw(_ context.Context, tx *wire.MsgTx, } type witnessFixture struct { + transfer *sweep.Transfer kit *Kit proof *proof.Proof packet *psbt.Packet @@ -354,7 +357,7 @@ func newWitnessFixture(t *testing.T) *witnessFixture { }) sweepTx.AddTxIn(&wire.TxIn{ PreviousOutPoint: assetProof.OutPoint(), - Sequence: wire.MaxTxInSequenceNum, + Sequence: kit.csvExpiry, }) sweepTx.AddTxOut(&wire.TxOut{ Value: anchorValue - 2000, PkScript: []byte{txscript.OP_TRUE}, @@ -369,8 +372,18 @@ func newWitnessFixture(t *testing.T) *witnessFixture { packet.Inputs[idx].WitnessUtxo = prevOutputs[idx] } + opTrueScript, err := htlc.GetOpTrueScript() + require.NoError(t, err) + _, _, _, opTrueControl, err := htlc.CreateOpTrueLeaf() + require.NoError(t, err) + controlBytes, err := opTrueControl.ToBytes() + require.NoError(t, err) + transfer := assettest.Sweep(t, []*proof.Proof{assetProof}, packet, + wire.TxWitness{opTrueScript, controlBytes}) + return &witnessFixture{ - kit: kit, proof: assetProof, packet: packet, + transfer: transfer, + kit: kit, proof: assetProof, packet: packet, prevOutputs: prevOutputs, funderKey: funderKey, assetInIndex: assetInIndex, } @@ -458,7 +471,7 @@ func TestCreateTimeoutWitness(t *testing.T) { privateKey: fixture.funderKey, expectedInput: fixture.assetInIndex, } spend, err := fixture.kit.CreateTimeoutWitness( - t.Context(), signer, fixture.proof, fixture.packet, + t.Context(), signer, fixture.proof, fixture.packet, fixture.transfer, ) require.NoError(t, err) require.Equal(t, uint32(fixture.assetInIndex), spend.InputIndex) @@ -548,11 +561,11 @@ func TestCreateTimeoutWitnessRejectsInvalidInputs(t *testing.T) { testCase.mutate(fixture, signer) _, err := fixture.kit.CreateTimeoutWitness( t.Context(), signer, fixture.proof, - fixture.packet, + fixture.packet, fixture.transfer, ) require.Error(t, err) require.Equal( - t, wire.MaxTxInSequenceNum, + t, fixture.kit.csvExpiry, fixture.packet.UnsignedTx. TxIn[fixture.assetInIndex].Sequence, ) @@ -561,11 +574,11 @@ func TestCreateTimeoutWitnessRejectsInvalidInputs(t *testing.T) { fixture := newWitnessFixture(t) _, err := fixture.kit.CreateTimeoutWitness( - t.Context(), nil, fixture.proof, fixture.packet, + t.Context(), nil, fixture.proof, fixture.packet, fixture.transfer, ) require.Error(t, err) _, err = fixture.kit.CreateTimeoutWitness( - t.Context(), &localSigner{}, nil, fixture.packet, + t.Context(), &localSigner{}, nil, fixture.packet, fixture.transfer, ) require.Error(t, err) } diff --git a/assets/deposit/signing_test.go b/assets/deposit/signing_test.go new file mode 100644 index 000000000..7d8912cda --- /dev/null +++ b/assets/deposit/signing_test.go @@ -0,0 +1,154 @@ +package deposit + +import ( + "context" + "testing" + + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/txscript" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/rpcutils" + "github.com/lightninglabs/taproot-assets/taprpc" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +// TestControlBlockKeyIsolation prevents a caller from changing the deposit's +// contract by mutating a returned control block. +func TestControlBlockKeyIsolation(t *testing.T) { + f := newWitnessFixture(t) + original := f.kit.muSig2Key.PreTweakedKey.SerializeCompressed() + root := make([]byte, 32) + block, err := f.kit.GenTimeoutBtcControlBlock(root) + require.NoError(t, err) + _, other := scalarKey(t, 42) + *block.InternalKey = *other + next, err := f.kit.GenTimeoutBtcControlBlock(root) + require.NoError(t, err) + require.Equal(t, original, next.InternalKey.SerializeCompressed()) + require.NotEqual(t, other.SerializeCompressed(), + next.InternalKey.SerializeCompressed()) +} + +// TestTimeoutPreservesFeeSignature exercises refund signing after a wallet has +// already signed a fee input. The transaction ID and fee signature stay valid. +func TestTimeoutPreservesFeeSignature(t *testing.T) { + f := newWitnessFixture(t) + feePrivateKey, feePubKey := scalarKey(t, 42) + feeOutputKey := txscript.ComputeTaprootKeyNoScript(feePubKey) + feeScript, err := txscript.PayToTaprootScript(feeOutputKey) + require.NoError(t, err) + f.prevOutputs[0].PkScript = feeScript + tx := f.packet.UnsignedTx + fetcher := txscript.NewMultiPrevOutFetcher(nil) + for i, input := range tx.TxIn { + fetcher.AddPrevOut(input.PreviousOutPoint, f.prevOutputs[i]) + } + sig, err := txscript.RawTxInTaprootSignature( + tx, txscript.NewTxSigHashes(tx, fetcher), 0, + f.prevOutputs[0].Value, feeScript, nil, txscript.SigHashDefault, + feePrivateKey, + ) + require.NoError(t, err) + f.packet.Inputs[0].TaprootKeySpendSig = sig + parsed, err := schnorr.ParseSignature(sig) + require.NoError(t, err) + signatureValid := func() bool { + digest, err := txscript.CalcTaprootSignatureHash( + txscript.NewTxSigHashes(tx, fetcher), txscript.SigHashDefault, + tx, 0, fetcher, + ) + require.NoError(t, err) + return parsed.Verify(digest, feeOutputKey) + } + require.True(t, signatureValid()) + originalTxid := tx.TxHash() + signer := &localSigner{ + privateKey: f.funderKey, expectedInput: f.assetInIndex, + } + _, err = f.kit.CreateTimeoutWitness( + t.Context(), signer, f.proof, f.packet, f.transfer, + ) + require.NoError(t, err) + require.True(t, signatureValid()) + require.Equal(t, originalTxid, tx.TxHash()) +} + +type versionAddressClient struct { + AddressProofClient + + request *taprpc.NewAddrRequest +} + +func (m *versionAddressClient) NewAddr(_ context.Context, + req *taprpc.NewAddrRequest, _ ...grpc.CallOption) (*taprpc.Addr, error) { + + m.request = req + return &taprpc.Addr{}, nil +} + +// TestAddressAndPacketVersionsMatch freezes the RPC versions so addresses and +// funding packets do not drift when tapd defaults change. +func TestAddressAndPacketVersionsMatch(t *testing.T) { + f := newWitnessFixture(t) + client := &versionAddressClient{} + _, kit, err := f.kit.NewHtlcAddr( + t.Context(), client, 1000, lntypes.Hash{1}, 144, + ) + require.NoError(t, err) + version, err := rpcutils.UnmarshalAssetVersion(client.request.AssetVersion) + require.NoError(t, err) + require.Equal(t, taprpc.AddrVersion_ADDR_VERSION_V1, + client.request.AddressVersion) + packet, err := kit.CreateHtlcVpkt() + require.NoError(t, err) + require.Equal(t, asset.V1, version) + require.Equal(t, asset.V1, packet.Outputs[1].AssetVersion) + _, err = f.kit.NewAddr(t.Context(), client, 1000) + require.NoError(t, err) + require.Equal(t, taprpc.AssetVersion_ASSET_VERSION_V1, + client.request.AssetVersion) + require.Equal(t, taprpc.AddrVersion_ADDR_VERSION_V1, + client.request.AddressVersion) +} + +// TestTimeoutRejectsUnsetSequence prevents signing a transaction that would need +// to change after other inputs have already been signed. +func TestTimeoutRejectsUnsetSequence(t *testing.T) { + f := newWitnessFixture(t) + index, err := f.kit.AssetInputIndex(f.proof, f.packet) + require.NoError(t, err) + require.Equal(t, uint32(f.assetInIndex), index) + f.packet.UnsignedTx.TxIn[index].Sequence = 0 + originalTxid := f.packet.UnsignedTx.TxHash() + signer := &localSigner{privateKey: f.funderKey, expectedInput: f.assetInIndex} + _, err = f.kit.CreateTimeoutWitness( + t.Context(), signer, f.proof, f.packet, f.transfer, + ) + require.ErrorContains(t, err, "asset input sequence must be") + require.Zero(t, signer.calls) + require.Equal(t, originalTxid, f.packet.UnsignedTx.TxHash()) +} + +// TestTimeoutRejectsInvalidTransfer ensures the deposit signing entry point +// also enforces asset preservation before requesting a signature. +func TestTimeoutRejectsInvalidTransfer(t *testing.T) { + for _, missing := range []bool{false, true} { + f := newWitnessFixture(t) + if missing { + f.transfer = nil + } else { + f.packet.UnsignedTx.TxOut[1].PkScript = + []byte{txscript.OP_TRUE} + } + signer := &localSigner{ + privateKey: f.funderKey, expectedInput: f.assetInIndex, + } + _, err := f.kit.CreateTimeoutWitness( + t.Context(), signer, f.proof, f.packet, f.transfer, + ) + require.Error(t, err) + require.Zero(t, signer.calls) + } +} diff --git a/assets/htlc/swapkit.go b/assets/htlc/swapkit.go index 529eb3a95..91f990414 100644 --- a/assets/htlc/swapkit.go +++ b/assets/htlc/swapkit.go @@ -13,6 +13,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/lightninglabs/lndclient" + assetsweep "github.com/lightninglabs/loop/assets/sweep" "github.com/lightninglabs/taproot-assets/address" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" @@ -782,11 +783,13 @@ func verifyTapscriptSignature(tx *wire.MsgTx, sweep *validatedSweep, // CreatePreimageWitness signs the exact proof-bound asset input and returns a // success-path witness in the legacy stack order. The caller must set the // proof-selected input's sequence to SuccessSequence before calling this -// method; the PSBT is never mutated here. +// method; the PSBT is never mutated here. The transfer must contain the +// caller-approved, complete active and passive asset packets. func (s *SwapKit) CreatePreimageWitness(ctx context.Context, signer lndclient.SignerClient, htlcProof *proof.Proof, sweepBtcPacket *psbt.Packet, keyLocator keychain.KeyLocator, - preimage lntypes.Preimage) (*SpendWitness, error) { + preimage lntypes.Preimage, transfer *assetsweep.Transfer) ( + *SpendWitness, error) { if signer == nil { return nil, fmt.Errorf("signer is required") @@ -805,6 +808,10 @@ func (s *SwapKit) CreatePreimageWitness(ctx context.Context, return nil, err } + if err := transfer.Validate(htlcProof, sweepBtcPacket); err != nil { + return nil, err + } + successScript, err := s.GetSuccessScript() if err != nil { return nil, err @@ -856,10 +863,12 @@ func (s *SwapKit) CreatePreimageWitness(ctx context.Context, // CreateTimeoutWitness signs the exact proof-bound asset input and returns a // timeout-path witness in the legacy stack order. The caller must set the // proof-selected input's sequence to CsvExpiry before calling this method; the -// PSBT is never mutated here. +// PSBT is never mutated here. The transfer must contain the caller-approved, +// complete active and passive asset packets. func (s *SwapKit) CreateTimeoutWitness(ctx context.Context, signer lndclient.SignerClient, htlcProof *proof.Proof, - sweepBtcPacket *psbt.Packet, keyLocator keychain.KeyLocator) ( + sweepBtcPacket *psbt.Packet, keyLocator keychain.KeyLocator, + transfer *assetsweep.Transfer) ( *SpendWitness, error) { if signer == nil { @@ -876,6 +885,10 @@ func (s *SwapKit) CreateTimeoutWitness(ctx context.Context, return nil, err } + if err := transfer.Validate(htlcProof, sweepBtcPacket); err != nil { + return nil, err + } + timeoutScript, err := s.GetTimeoutScript() if err != nil { return nil, err diff --git a/assets/htlc/swapkit_test.go b/assets/htlc/swapkit_test.go index 3fc1beefb..68440a9b8 100644 --- a/assets/htlc/swapkit_test.go +++ b/assets/htlc/swapkit_test.go @@ -13,6 +13,8 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/lndclient" + assettest "github.com/lightninglabs/loop/assets/internal/test" + "github.com/lightninglabs/loop/assets/sweep" "github.com/lightninglabs/taproot-assets/address" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" @@ -616,6 +618,7 @@ func (s *localSigner) SignOutputRaw(_ context.Context, tx *wire.MsgTx, } type witnessFixture struct { + transfer *sweep.Transfer kit *SwapKit proof *proof.Proof packet *psbt.Packet @@ -761,8 +764,18 @@ func newWitnessFixtureWithVersions(t *testing.T, sequence uint32, sweepPacket.Inputs[idx].WitnessUtxo = prevOutputs[idx] } + opTrueScript, err := GetOpTrueScript() + require.NoError(t, err) + _, _, _, opTrueControl, err := CreateOpTrueLeaf() + require.NoError(t, err) + controlBytes, err := opTrueControl.ToBytes() + require.NoError(t, err) + transfer := assettest.Sweep(t, []*proof.Proof{assetProof}, sweepPacket, + wire.TxWitness{opTrueScript, controlBytes}) + return &witnessFixture{ - kit: kit, proof: assetProof, packet: sweepPacket, + transfer: transfer, + kit: kit, proof: assetProof, packet: sweepPacket, prevOutputs: prevOutputs, senderKey: senderKey, receiverKey: receiverKey, preimage: preimage, assetInIndex: assetInIndex, @@ -875,7 +888,7 @@ func TestCreateProofBoundWitnesses(t *testing.T) { } witness, err := fixture.kit.CreatePreimageWitness( t.Context(), signer, fixture.proof, fixture.packet, - keychain.KeyLocator{}, fixture.preimage, + keychain.KeyLocator{}, fixture.preimage, fixture.transfer, ) require.NoError(t, err) require.Equal(t, 1, signer.calls) @@ -891,7 +904,7 @@ func TestCreateProofBoundWitnesses(t *testing.T) { } witness, err := fixture.kit.CreateTimeoutWitness( t.Context(), signer, fixture.proof, fixture.packet, - keychain.KeyLocator{}, + keychain.KeyLocator{}, fixture.transfer, ) require.NoError(t, err) require.Equal(t, 1, signer.calls) @@ -922,7 +935,7 @@ func TestProofBoundWitnessValidation(t *testing.T) { _, err := fixture.kit.CreatePreimageWitness( t.Context(), signer, fixture.proof, fixture.packet, keychain.KeyLocator{}, - lntypes.Preimage{1}, + lntypes.Preimage{1}, fixture.transfer, ) return err }, @@ -937,7 +950,7 @@ func TestProofBoundWitnessValidation(t *testing.T) { _, err := fixture.kit.CreateTimeoutWitness( t.Context(), signer, fixture.proof, - fixture.packet, keychain.KeyLocator{}, + fixture.packet, keychain.KeyLocator{}, fixture.transfer, ) return err }, @@ -957,7 +970,7 @@ func TestProofBoundWitnessValidation(t *testing.T) { fixture.kit.CsvExpiry() _, err := fixture.kit.CreateTimeoutWitness( t.Context(), signer, fixture.proof, - fixture.packet, keychain.KeyLocator{}, + fixture.packet, keychain.KeyLocator{}, fixture.transfer, ) return err }, @@ -1060,7 +1073,7 @@ func TestProofBoundWitnessValidation(t *testing.T) { _, err := fixture.kit.CreatePreimageWitness( t.Context(), signer, fixture.proof, fixture.packet, keychain.KeyLocator{}, - fixture.preimage, + fixture.preimage, fixture.transfer, ) require.Error(t, err) }) diff --git a/assets/htlc/sweep_test.go b/assets/htlc/sweep_test.go new file mode 100644 index 000000000..aa0e7f0c5 --- /dev/null +++ b/assets/htlc/sweep_test.go @@ -0,0 +1,227 @@ +package htlc + +import ( + "bytes" + "testing" + + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + assettest "github.com/lightninglabs/loop/assets/internal/test" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/commitment" + "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/tapscript" + "github.com/lightningnetwork/lnd/keychain" + "github.com/stretchr/testify/require" +) + +// TestWitnessRejectsInvalidTransfers checks both signing paths reject omitted, +// corrupted and unanchored virtual transfers before asking for a signature. +func TestWitnessRejectsInvalidTransfers(t *testing.T) { + tests := []struct { + name string + mutate func(*witnessFixture) + }{ + {"missing transfer", func(f *witnessFixture) { + f.transfer = nil + }}, + {"empty transfer", func(f *witnessFixture) { + f.transfer.Packets = nil + }}, + {"nil packet", func(f *witnessFixture) { + f.transfer.Packets[0] = nil + }}, + {"nil input", func(f *witnessFixture) { + f.transfer.Packets[0].Inputs[0] = nil + }}, + {"nil output", func(f *witnessFixture) { + f.transfer.Packets[0].Outputs[0] = nil + }}, + {"missing change", func(f *witnessFixture) { + f.transfer.Packets[0].Outputs = + f.transfer.Packets[0].Outputs[1:] + }}, + {"Bitcoin-only output", func(f *witnessFixture) { + f.packet.UnsignedTx.TxOut[1].PkScript = + []byte{txscript.OP_TRUE} + }}, + {"missing Bitcoin output", func(f *witnessFixture) { + f.packet.UnsignedTx.TxOut = f.packet.UnsignedTx.TxOut[:1] + f.packet.Outputs = f.packet.Outputs[:1] + }}, + {"output metadata", func(f *witnessFixture) { + f.packet.Outputs = nil + }}, + {"recipient mismatch", func(f *witnessFixture) { + f.transfer.Packets[0].Outputs[1].ScriptKey = + asset.NUMSScriptKey + }}, + {"amount loss", func(f *witnessFixture) { + out := f.transfer.Packets[0].Outputs[1] + out.Amount-- + out.Asset.Amount-- + }}, + {"inflation", func(f *witnessFixture) { + out := f.transfer.Packets[0].Outputs[1] + out.Amount++ + out.Asset.Amount++ + }}, + {"invalid virtual witness", func(f *witnessFixture) { + f.transfer.Packets[0].Outputs[0].Asset. + PrevWitnesses[0].TxWitness = nil + }}, + {"wrong input anchor", func(f *witnessFixture) { + f.transfer.Packets[0].Inputs[0].PrevID.OutPoint.Index++ + }}, + {"duplicate packet", func(f *witnessFixture) { + f.transfer.Packets = append( + f.transfer.Packets, f.transfer.Packets[0], + ) + }}, + {"prune live asset", func(f *witnessFixture) { + f.transfer.PrunedAssets = map[wire.OutPoint][]*asset.Asset{ + f.proof.OutPoint(): {&f.proof.Asset}, + } + }}, + } + for _, timeout := range []bool{false, true} { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + f := newWitnessFixture(t, SuccessSequence) + signer := &localSigner{ + privateKey: f.receiverKey, + expectedInput: f.assetInIndex, + } + test.mutate(f) + var err error + if timeout { + f.packet.UnsignedTx.TxIn[1].Sequence = 144 + signer.privateKey = f.senderKey + _, err = f.kit.CreateTimeoutWitness( + t.Context(), signer, f.proof, f.packet, + keychain.KeyLocator{}, f.transfer, + ) + } else { + _, err = f.kit.CreatePreimageWitness( + t.Context(), signer, f.proof, f.packet, + keychain.KeyLocator{}, f.preimage, + f.transfer, + ) + } + require.Error(t, err) + require.Zero(t, signer.calls) + }) + } + } +} + +// TestSweepPreservesPassiveAssets builds two assets in the same HTLC anchor. +// Both must be accounted for even though the kit describes only one of them. +func TestSweepPreservesPassiveAssets(t *testing.T) { + f := newWitnessFixture(t, SuccessSequence) + passive := f.proof.Asset.Copy() + passive.Genesis.Tag = "passive asset" + passive.Amount = 500 + tombstone := f.proof.Asset.Copy() + tombstone.Amount = 0 + tombstone.ScriptKey = asset.NUMSScriptKey + version := commitment.TapCommitmentV2 + inputCommitment, err := commitment.FromAssets( + &version, &f.proof.Asset, passive, tombstone, + ) + require.NoError(t, err) + sibling, err := f.kit.GetSiblingPreimage() + require.NoError(t, err) + siblingHash, err := sibling.TapHash() + require.NoError(t, err) + internalKey, err := f.kit.GetAggregateKey() + require.NoError(t, err) + script, err := tapscript.PayToAddrScript( + *internalKey, siblingHash, *inputCommitment, + ) + require.NoError(t, err) + f.proof.AnchorTx.TxOut[1].PkScript = script + f.prevOutputs[1].PkScript = script + f.packet.UnsignedTx.TxIn[1].PreviousOutPoint = f.proof.OutPoint() + passiveProof := *f.proof + passiveProof.Asset = *passive + proofs := []*proof.Proof{f.proof, &passiveProof} + for _, p := range proofs { + _, cp, err := inputCommitment.Proof( + p.Asset.TapCommitmentKey(), p.Asset.AssetCommitmentKey(), + ) + require.NoError(t, err) + p.InclusionProof.CommitmentProof = &proof.CommitmentProof{ + Proof: *cp, TapSiblingPreimage: &sibling, + } + } + opTrue, err := GetOpTrueScript() + require.NoError(t, err) + _, _, _, cb, err := CreateOpTrueLeaf() + require.NoError(t, err) + control, err := cb.ToBytes() + require.NoError(t, err) + f.transfer = assettest.Sweep( + t, proofs, f.packet, wire.TxWitness{opTrue, control}, + ) + f.transfer.PrunedAssets = map[wire.OutPoint][]*asset.Asset{ + f.proof.OutPoint(): {tombstone}, + } + + before := f.packet.UnsignedTx.TxHash() + encodePackets := func() []byte { + var buf bytes.Buffer + for _, pkt := range f.transfer.Packets { + require.NoError(t, pkt.Serialize(&buf)) + } + return buf.Bytes() + } + packets := encodePackets() + for range 2 { + require.NoError(t, f.transfer.Validate(f.proof, f.packet)) + } + require.Equal(t, before, f.packet.UnsignedTx.TxHash()) + require.Equal(t, packets, encodePackets()) + signer := &localSigner{ + privateKey: f.receiverKey, expectedInput: f.assetInIndex, + } + witness, err := f.kit.CreatePreimageWitness( + t.Context(), signer, f.proof, f.packet, keychain.KeyLocator{}, + f.preimage, f.transfer, + ) + require.NoError(t, err) + f.executeWitness(t, witness) + + // Rebuilding both Bitcoin outputs without the passive transfer still + // fails: the complete input commitment cannot be reconstructed. + omitted := assettest.Sweep( + t, proofs[:1], f.packet, wire.TxWitness{opTrue, control}, + ) + omitted.PrunedAssets = f.transfer.PrunedAssets + require.ErrorContains(t, omitted.Validate(f.proof, f.packet), + "invalid input commitments") + omitted.PrunedAssets = map[wire.OutPoint][]*asset.Asset{ + f.proof.OutPoint(): {passive}, + } + require.ErrorContains(t, omitted.Validate(f.proof, f.packet), + "cannot prune spendable assets") +} + +// TestSweepLegacyInputs retains refund support for existing commitment +// versions while new transfers use version-two commitments. +func TestSweepLegacyInputs(t *testing.T) { + for _, version := range []asset.Version{asset.V0, asset.V1} { + f := newWitnessFixtureWithVersions( + t, 144, version, commitment.TapCommitmentVersion(version), + ) + signer := &localSigner{ + privateKey: f.senderKey, expectedInput: f.assetInIndex, + } + witness, err := f.kit.CreateTimeoutWitness( + t.Context(), signer, f.proof, f.packet, + keychain.KeyLocator{}, f.transfer, + ) + require.NoError(t, err) + f.executeWitness(t, witness) + } +} diff --git a/assets/internal/test/sweep.go b/assets/internal/test/sweep.go new file mode 100644 index 000000000..f3dec7b49 --- /dev/null +++ b/assets/internal/test/sweep.go @@ -0,0 +1,83 @@ +package test + +import ( + "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcutil/psbt" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/loop/assets/sweep" + "github.com/lightninglabs/taproot-assets/address" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/tappsbt" + "github.com/lightninglabs/taproot-assets/tapsend" + "github.com/stretchr/testify/require" +) + +// Sweep anchors complete non-interactive transfers with change into btc. Each +// proof is a separate asset transfer, potentially sharing its Bitcoin anchor. +// The witness must spend each input's asset script key. +func Sweep(t testing.TB, proofs []*proof.Proof, btc *psbt.Packet, + witness wire.TxWitness) *sweep.Transfer { + + t.Helper() + transfer := &sweep.Transfer{} + for idx, p := range proofs { + pkt, err := tappsbt.FromProofs( + []*proof.Proof{p}, &address.RegressionNetTap, tappsbt.V1, + ) + require.NoError(t, err) + _, dest := btcec.PrivKeyFromBytes([]byte{byte(20 + idx*2)}) + _, change := btcec.PrivKeyFromBytes([]byte{byte(21 + idx*2)}) + _, anchor := btcec.PrivKeyFromBytes([]byte{30}) + changeAmount := p.Asset.Amount / 5 + pkt.Outputs = []*tappsbt.VOutput{ + { + Type: tappsbt.TypeSplitRoot, + Amount: changeAmount, AssetVersion: asset.V1, + ScriptKey: asset.NewScriptKey(change), + AnchorOutputInternalKey: anchor, + AnchorOutputIndex: 0, + }, + { + Amount: p.Asset.Amount - changeAmount, + AssetVersion: asset.V1, + ScriptKey: asset.NewScriptKey(dest), + AnchorOutputInternalKey: anchor, + AnchorOutputIndex: 1, + }, + } + require.NoError(t, tapsend.PrepareOutputAssets(t.Context(), pkt)) + for _, out := range pkt.Outputs { + require.NoError(t, out.Asset.UpdateTxWitness(0, witness)) + if out.SplitAsset != nil { + require.NoError(t, out.SplitAsset.UpdateTxWitness( + 0, witness, + )) + } + } + transfer.Packets = append(transfer.Packets, pkt) + for i, in := range btc.UnsignedTx.TxIn { + if in.PreviousOutPoint == p.OutPoint() { + btc.Inputs[i].TaprootInternalKey = + schnorr.SerializePubKey( + p.InclusionProof.InternalKey, + ) + } + } + } + commitments, err := tapsend.CreateOutputCommitments(transfer.Packets) + require.NoError(t, err) + anchor, err := tapsend.CreateAnchorTx(transfer.Packets) + require.NoError(t, err) + for _, pkt := range transfer.Packets { + require.NoError(t, tapsend.UpdateTaprootOutputKeys( + anchor, pkt, commitments, + )) + } + btc.UnsignedTx.TxOut = anchor.UnsignedTx.TxOut + btc.Outputs = anchor.Outputs + return transfer +} diff --git a/assets/sweep/transfer.go b/assets/sweep/transfer.go new file mode 100644 index 000000000..4bf34ad5d --- /dev/null +++ b/assets/sweep/transfer.go @@ -0,0 +1,270 @@ +// Package sweep validates asset transfers before signing their Bitcoin anchor. +package sweep + +import ( + "bytes" + "fmt" + "math" + + "github.com/btcsuite/btcd/btcutil/psbt" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/commitment" + "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/tappsbt" + "github.com/lightninglabs/taproot-assets/tapsend" + "github.com/lightninglabs/taproot-assets/vm" +) + +// Transfer is the caller-approved asset transfer to anchor. The caller must +// construct or check the destinations, amounts and anchor keys independently +// of an untrusted Bitcoin PSBT. All active, change and passive assets must be +// included, with their virtual witnesses complete. Validation does not establish +// ownership of destination keys, proof provenance or chain confirmation status. +type Transfer struct { + Packets []*tappsbt.VPacket + + // PrunedAssets contains only existing tombstones and provable burns + // omitted from the virtual packets, keyed by their input anchor outpoint. + PrunedAssets map[wire.OutPoint][]*asset.Asset +} + +// Validate binds the proof-selected asset and the complete input commitments to +// valid virtual transitions, then binds every resulting asset commitment to the +// exact Bitcoin outputs being signed. Neither the PSBT nor Transfer is mutated. +func (t *Transfer) Validate(p *proof.Proof, btc *psbt.Packet) error { + if t == nil || len(t.Packets) == 0 { + return fmt.Errorf("asset transfer packets are required") + } + if p == nil || btc == nil || btc.UnsignedTx == nil { + return fmt.Errorf("asset proof and Bitcoin PSBT are required") + } + tx := btc.UnsignedTx + if len(tx.TxIn) != len(btc.Inputs) || + len(tx.TxOut) != len(btc.Outputs) { + + return fmt.Errorf("Bitcoin PSBT metadata is incomplete") + } + btcInputs := make(map[wire.OutPoint]bool, len(tx.TxIn)) + for _, in := range tx.TxIn { + if in == nil || btcInputs[in.PreviousOutPoint] { + return fmt.Errorf("nil or duplicate Bitcoin input") + } + btcInputs[in.PreviousOutPoint] = true + } + for _, out := range tx.TxOut { + if out == nil || out.Value < 0 { + return fmt.Errorf("invalid Bitcoin output") + } + } + + var found bool + anchors := make(map[wire.OutPoint]bool) + anchorPackets := make([]*tappsbt.VPacket, len(t.Packets)) + proofAsset := anchorAsset(&p.Asset) + for idx, pkt := range t.Packets { + if err := validatePacket(pkt, len(tx.TxOut)); err != nil { + return fmt.Errorf("asset transfer %d: %w", idx, err) + } + anchorPackets[idx] = &tappsbt.VPacket{} + for _, in := range pkt.Inputs { + anchorInput := in.Copy() + trimSplitWitness(anchorInput.Asset()) + anchorPackets[idx].Inputs = append( + anchorPackets[idx].Inputs, anchorInput, + ) + anchors[in.PrevID.OutPoint] = true + if in.PrevID.OutPoint == p.OutPoint() && + anchorInput.Asset(). + DeepEqualAllowSegWitIgnoreTxWitness(proofAsset) { + + found = true + } + } + } + if !found { + return fmt.Errorf("asset transfer does not spend proof asset") + } + prunedAssets := make(map[wire.OutPoint][]*asset.Asset) + for outpoint, pruned := range t.PrunedAssets { + if !anchors[outpoint] { + return fmt.Errorf("pruned assets have no input anchor") + } + for _, a := range pruned { + if a == nil || a.ScriptKey.PubKey == nil || + (!a.IsUnSpendable() && !a.IsBurn()) { + + return fmt.Errorf("cannot prune spendable assets") + } + prunedAssets[outpoint] = append( + prunedAssets[outpoint], anchorAsset(a), + ) + } + } + if err := tapsend.ValidateVPacketVersions(t.Packets); err != nil { + return err + } + + // Rebuild the outputs from the supplied complete packets. STXO leaves + // are already part of AltLeaves on anchored packets; adding them again + // would both mutate the caller's packets and duplicate those leaves. + outputs, err := tapsend.CreateOutputCommitments( + t.Packets, tapsend.WithNoSTXOProofs(), + ) + if err != nil { + return fmt.Errorf("invalid output commitments: %w", err) + } + if err := tapsend.ValidateAnchorInputs( + btc, anchorPackets, prunedAssets, + ); err != nil { + return fmt.Errorf("invalid input commitments: %w", err) + } + for _, pkt := range t.Packets { + for _, out := range pkt.Outputs { + script, _, _, err := tapsend.AnchorOutputScript( + out.AnchorOutputInternalKey, + out.AnchorOutputTapscriptSibling, + outputs[out.AnchorOutputIndex], + ) + if err != nil { + return err + } + btcOut := tx.TxOut[out.AnchorOutputIndex] + if !bytes.Equal(script, btcOut.PkScript) { + return fmt.Errorf("asset anchor output %d mismatch", + out.AnchorOutputIndex) + } + } + } + + return nil +} + +// anchorAsset returns the on-chain representation of an asset. Non-interactive +// input proofs also carry the split witness, which is not in the anchor leaf. +func anchorAsset(a *asset.Asset) *asset.Asset { + result := a.Copy() + trimSplitWitness(result) + return result +} + +func trimSplitWitness(a *asset.Asset) { + if a.HasSplitCommitmentWitness() && + *a.PrevWitnesses[0].PrevID == asset.ZeroPrevID { + + a.PrevWitnesses[0].SplitCommitment = nil + } +} + +// validatePacket checks complete transfers, including conservation across every +// split output. A valid split proof alone does not establish that all parts of +// the split are present in the Bitcoin transaction. +func validatePacket(pkt *tappsbt.VPacket, numOutputs int) error { + if pkt == nil || len(pkt.Inputs) == 0 || len(pkt.Outputs) == 0 { + return fmt.Errorf("incomplete virtual packet") + } + prevAssets := make(commitment.InputSet, len(pkt.Inputs)) + var inputAmount, outputAmount uint64 + for _, in := range pkt.Inputs { + if in == nil || in.Asset() == nil || + in.Asset().ScriptKey.PubKey == nil { + + return fmt.Errorf("incomplete virtual input") + } + a := in.Asset() + if in.PrevID.ID != a.ID() || in.PrevID.ScriptKey != + asset.ToSerialized(a.ScriptKey.PubKey) { + + return fmt.Errorf("virtual input identity mismatch") + } + if _, ok := prevAssets[in.PrevID]; ok { + return fmt.Errorf("duplicate virtual input") + } + if a.Amount > math.MaxInt64-inputAmount { + return fmt.Errorf("virtual input amount overflow") + } + inputAmount += a.Amount + prevAssets[in.PrevID] = a + } + for _, out := range pkt.Outputs { + if out == nil || out.Asset == nil || + out.Asset.ScriptKey.PubKey == nil || + out.ScriptKey.PubKey == nil || + out.AnchorOutputInternalKey == nil || + uint64(out.AnchorOutputIndex) >= uint64(numOutputs) { + + return fmt.Errorf("incomplete virtual output") + } + a := out.Asset + if a.ID() != pkt.Inputs[0].Asset().ID() || + out.Amount != a.Amount || out.AssetVersion != a.Version || + !out.ScriptKey.PubKey.IsEqual(a.ScriptKey.PubKey) || + out.LockTime != a.LockTime || + out.RelativeLockTime != a.RelativeLockTime { + + return fmt.Errorf("virtual output intent mismatch") + } + if a.IsBurn() || (a.Amount > 0 && + a.ScriptKey.PubKey.IsEqual(asset.NUMSPubKey)) { + + return fmt.Errorf("asset sweep cannot burn assets") + } + if a.Amount > math.MaxInt64-outputAmount { + return fmt.Errorf("virtual output amount overflow") + } + outputAmount += a.Amount + } + if inputAmount == 0 || inputAmount != outputAmount { + return fmt.Errorf("asset transfer does not conserve amounts") + } + + isSplit, err := pkt.HasSplitCommitment() + if err != nil { + return err + } + root := pkt.Outputs[0].Asset + var splits []*commitment.SplitAsset + if isSplit { + splitRoot, err := pkt.SplitRootOutput() + if err != nil { + return err + } + root = splitRoot.Asset + for _, out := range pkt.Outputs { + a := out.Asset + if out.Type.IsSplitRoot() { + a = out.SplitAsset + } + if a == nil || a.ScriptKey.PubKey == nil || + !a.HasSplitCommitmentWitness() { + + return fmt.Errorf("missing split asset") + } + if a.Amount != out.Amount || a.ID() != root.ID() || + a.Version != out.AssetVersion || + !a.ScriptKey.PubKey.IsEqual(out.ScriptKey.PubKey) { + + return fmt.Errorf("split output intent mismatch") + } + splitRoot := &a.PrevWitnesses[0].SplitCommitment.RootAsset + if !splitRoot.DeepEqual(root) { + return fmt.Errorf("split root asset mismatch") + } + splits = append(splits, &commitment.SplitAsset{ + Asset: *a, OutputIndex: out.AnchorOutputIndex, + }) + } + } else if len(pkt.Outputs) != 1 { + return fmt.Errorf("non-split transfer requires one output") + } + + // The anchor may be prepared before its Bitcoin CSV lock matures. This + // validates asset conservation and witnesses, not chain maturity. + engine, err := vm.New( + root, splits, prevAssets, vm.WithSkipTimeLockValidation(), + ) + if err != nil { + return err + } + return engine.Execute() +} diff --git a/assets/tapkit_test.go b/assets/tapkit_test.go index c07fca83d..a7bb7809b 100644 --- a/assets/tapkit_test.go +++ b/assets/tapkit_test.go @@ -4,18 +4,106 @@ import ( "testing" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/loop/assets/htlc" + assettest "github.com/lightninglabs/loop/assets/internal/test" "github.com/lightninglabs/taproot-assets/address" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" "github.com/lightninglabs/taproot-assets/proof" "github.com/lightninglabs/taproot-assets/tappsbt" "github.com/lightninglabs/taproot-assets/tapscript" + "github.com/lightninglabs/taproot-assets/tapsend" "github.com/stretchr/testify/require" ) +// TestSweepNonInteractiveInput verifies assets received through an address can +// be swept with their full split proofs, even though only the trimmed asset +// leaf was committed to on chain. +func TestSweepNonInteractiveInput(t *testing.T) { + genesis := asset.Genesis{ + FirstPrevOut: wire.OutPoint{Hash: chainhash.Hash{1}}, + Tag: "non-interactive sweep", Type: asset.Normal, + } + initial := validOpTrueProof(t, genesis, 1000, 3) + opTrueKey, _, _, cb, err := htlc.CreateOpTrueLeaf() + require.NoError(t, err) + _, internalKey := btcec.PrivKeyFromBytes([]byte{4}) + addr := &address.Tap{ + Version: address.V1, AssetVersion: asset.V1, + AssetID: genesis.ID(), Amount: 1000, + ScriptKey: *opTrueKey.PubKey, InternalKey: *internalKey, + ChainParams: &address.RegressionNetTap, + } + funding, err := CreateOpTrueSweepVpkt( + t.Context(), []*proof.Proof{initial}, addr, + ) + require.NoError(t, err) + funding.Outputs[0].AnchorOutputInternalKey = internalKey + packets := []*tappsbt.VPacket{funding} + commitments, err := tapsend.CreateOutputCommitments(packets) + require.NoError(t, err) + anchor, err := tapsend.CreateAnchorTx(packets) + require.NoError(t, err) + anchor.UnsignedTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: initial.OutPoint(), + }) + require.NoError(t, tapsend.UpdateTaprootOutputKeys( + anchor, funding, commitments, + )) + received := &proof.Proof{ + AnchorTx: *anchor.UnsignedTx, + Asset: *funding.Outputs[1].Asset.Copy(), + AltLeaves: funding.Outputs[1].AltLeaves, + } + for idx := range 2 { + _, cp, err := commitments[uint32(idx)].Proof( + received.Asset.TapCommitmentKey(), + received.Asset.AssetCommitmentKey(), + ) + require.NoError(t, err) + tapProof := proof.TaprootProof{ + OutputIndex: uint32(idx), InternalKey: internalKey, + CommitmentProof: &proof.CommitmentProof{Proof: *cp}, + } + if idx == 1 { + received.InclusionProof = tapProof + } else { + received.ExclusionProofs = []proof.TaprootProof{tapProof} + } + } + rootAsset := funding.Outputs[0].Asset + _, rootProof, err := commitments[0].Proof( + rootAsset.TapCommitmentKey(), rootAsset.AssetCommitmentKey(), + ) + require.NoError(t, err) + received.SplitRootProof = &proof.TaprootProof{ + OutputIndex: 0, InternalKey: internalKey, + CommitmentProof: &proof.CommitmentProof{Proof: *rootProof}, + } + _, err = received.VerifyProofs() + require.NoError(t, err) + require.True(t, received.Asset.HasSplitCommitmentWitness()) + + tx := wire.NewMsgTx(2) + tx.AddTxIn(&wire.TxIn{PreviousOutPoint: received.OutPoint()}) + btc, err := psbt.NewFromUnsignedTx(tx) + require.NoError(t, err) + btc.Inputs[0].WitnessUtxo = anchor.UnsignedTx.TxOut[1] + control, err := cb.ToBytes() + require.NoError(t, err) + script, err := htlc.GetOpTrueScript() + require.NoError(t, err) + transfer := assettest.Sweep( + t, []*proof.Proof{received}, btc, wire.TxWitness{script, control}, + ) + require.NoError(t, transfer.Validate(received, btc)) + require.True(t, transfer.Packets[0].Inputs[0].Asset(). + HasSplitCommitmentWitness()) +} + // validNonOpTrueProof creates a structurally valid proof whose asset cannot be // spent through the OP_TRUE sweep path. func validNonOpTrueProof(t *testing.T) (*proof.Proof, *address.Tap) { diff --git a/docs/release-notes/release-notes-next.md b/docs/release-notes/release-notes-next.md index c7d94ea3f..de07c0584 100644 --- a/docs/release-notes/release-notes-next.md +++ b/docs/release-notes/release-notes-next.md @@ -7,12 +7,21 @@ #### Breaking Changes +* Shared asset witness builders now require complete, caller-approved virtual + transfers. Deposit refunds also require the CSV sequence to be set before + signing any inputs. + * Instant Out requests must now set `max_swap_fee_sat`. Requests that omit the fee cap are rejected; an explicit zero cap remains valid. Direct users of `Manager.NewInstantOut` must pass the fee cap as a required argument. #### Bug Fixes +* Shared asset sweeps validate asset conservation and Bitcoin output commitments, + including change and passive assets, before signing. New deposit addresses use + explicit asset V1 and address V1 versions, refund signing preserves existing + signatures, and returned control blocks no longer expose mutable kit keys. + * Deposit proof verification now requires genesis-rooted histories and rejects ownership-only proofs, including those hidden in additional inputs.