diff --git a/config.go b/config.go index 3c063d9c0..15504134c 100644 --- a/config.go +++ b/config.go @@ -15,6 +15,7 @@ import ( "github.com/lightninglabs/taproot-assets/rfq" "github.com/lightninglabs/taproot-assets/tapchannel" "github.com/lightninglabs/taproot-assets/tapdb" + "github.com/lightninglabs/taproot-assets/tapfeatures" "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/tapgarden" "github.com/lightninglabs/taproot-assets/universe" @@ -229,6 +230,8 @@ type Config struct { AuxTrafficShaper *tapchannel.AuxTrafficShaper + AuxChanNegotiator *tapfeatures.AuxChannelNegotiator + AuxInvoiceManager *tapchannel.AuxInvoiceManager AuxChanCloser *tapchannel.AuxChanCloser diff --git a/docs/release-notes/release-notes-0.7.0.md b/docs/release-notes/release-notes-0.7.0.md index 6788ff241..ceea845f0 100644 --- a/docs/release-notes/release-notes-0.7.0.md +++ b/docs/release-notes/release-notes-0.7.0.md @@ -124,6 +124,14 @@ controls whether the peer's identity public key is sent to the local price oracle when querying asset price rates. +- Tap channels now use + [feature bits](https://github.com/lightninglabs/taproot-assets/pull/1748), in + order to provide a safe way to upgrade the channels into using new features + without breaking backwards compatibility. This way a new feature will become + active over a channel only when both peers decide to use it. When funding a + channel or upon reconnection we check that the required features are supported + by the remote party before proceeding. + ## RPC Additions - The [price oracle RPC calls now have an intent, optional peer ID and metadata diff --git a/go.mod b/go.mod index 249c949c6..281cc3057 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c github.com/btcsuite/btclog/v2 v2.0.1-0.20250728225537-6090e87c6c5b - github.com/btcsuite/btcwallet v0.16.15-0.20250805011126-a3632ae48ab3 + github.com/btcsuite/btcwallet v0.16.17 github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5 github.com/btcsuite/btcwallet/wtxmgr v1.5.6 github.com/caddyserver/certmagic v0.17.2 @@ -27,10 +27,10 @@ require ( github.com/lib/pq v1.10.9 github.com/lightninglabs/aperture v0.3.13-beta github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.3 - github.com/lightninglabs/lndclient v0.19.0-9 + github.com/lightninglabs/lndclient v0.20.0-1 github.com/lightninglabs/neutrino/cache v1.1.2 github.com/lightninglabs/taproot-assets/taprpc v1.0.9 - github.com/lightningnetwork/lnd v0.19.3-beta.rc1.0.20250812194315-c3226e8c2223 + github.com/lightningnetwork/lnd v0.19.0-beta.rc5.0.20250925062351-f293566849f2 github.com/lightningnetwork/lnd/cert v1.2.2 github.com/lightningnetwork/lnd/clock v1.1.1 github.com/lightningnetwork/lnd/fn/v2 v2.0.8 @@ -87,7 +87,7 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fergusstrange/embedded-postgres v1.25.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/goccy/go-yaml v1.15.23 // indirect @@ -128,11 +128,11 @@ require ( github.com/lightninglabs/lightning-node-connect/gbn v1.0.1 // indirect github.com/lightninglabs/lightning-node-connect/mailbox v1.0.1 // indirect github.com/lightninglabs/neutrino v0.16.1 // indirect - github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb // indirect + github.com/lightningnetwork/lightning-onion v1.2.1-0.20240815225420-8b40adf04ab9 // indirect github.com/lightningnetwork/lnd/healthcheck v1.2.6 // indirect github.com/lightningnetwork/lnd/kvdb v1.4.16 // indirect github.com/lightningnetwork/lnd/queue v1.1.1 // indirect - github.com/lightningnetwork/lnd/sqldb v1.0.10-0.20250812192515-dd1d57d82de1 // indirect + github.com/lightningnetwork/lnd/sqldb v1.0.11-0.20250925062351-f293566849f2 // indirect github.com/lightningnetwork/lnd/ticker v1.1.1 // indirect github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -158,7 +158,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect @@ -169,7 +169,7 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect - go.etcd.io/bbolt v1.3.11 // indirect + go.etcd.io/bbolt v1.4.3 // indirect go.etcd.io/etcd/api/v3 v3.5.12 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect go.etcd.io/etcd/client/v2 v2.305.12 // indirect @@ -179,18 +179,18 @@ require ( go.etcd.io/etcd/server/v3 v3.5.12 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect + go.opentelemetry.io/otel/sdk v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.23.0 // indirect golang.org/x/mod v0.21.0 // indirect - golang.org/x/sys v0.32.0 // indirect + golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.24.0 // indirect golang.org/x/tools v0.24.0 // indirect google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect diff --git a/go.sum b/go.sum index 9161f845e..67fca15da 100644 --- a/go.sum +++ b/go.sum @@ -669,8 +669,8 @@ github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c/go.mod h1:w7xnGOhw 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.15-0.20250805011126-a3632ae48ab3 h1:MAjNRpj3XhCOrhchq4wq0qI34TIBX/DCnT6OLWejx68= -github.com/btcsuite/btcwallet v0.16.15-0.20250805011126-a3632ae48ab3/go.mod h1:H6dfoZcWPonM2wbVsR2ZBY0PKNZKdQyLAmnX8vL9JFA= +github.com/btcsuite/btcwallet v0.16.17 h1:1N6lHznRdcjDopBvcofxaIHknArkJ/EcVKgLKfGL4Dg= +github.com/btcsuite/btcwallet v0.16.17/go.mod h1:YO+W745BAH8n/Rpgj68QsLR6eLlgM4W2do4RejT0buo= 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= @@ -827,8 +827,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= @@ -1140,8 +1140,8 @@ github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.3 h1:NuDp6Z+QNM github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.3/go.mod h1:bDnEKRN1u13NFBuy/C+bFLhxA5bfd3clT25y76QY0AM= github.com/lightninglabs/lightning-node-connect/mailbox v1.0.1 h1:RWmohykp3n/DTMWY8b18RNTEcLDf+KT/AZHKYdOObkM= github.com/lightninglabs/lightning-node-connect/mailbox v1.0.1/go.mod h1:NYtNexZE9gO1eOeegTxmIW9fqanl7eZ9cOrE9yewSAk= -github.com/lightninglabs/lndclient v0.19.0-9 h1:ell27omDoks79upoAsO/7QY40O93ud4tAtBXXZilqok= -github.com/lightninglabs/lndclient v0.19.0-9/go.mod h1:35d50tEMFxlJlKTZGYA6EdOllPsbxS4FUmEVbETUx+Q= +github.com/lightninglabs/lndclient v0.20.0-1 h1:xwDoh7z3bszXc4mkMO6ksEcXhkQw9v0XHJ7fB0LKDNo= +github.com/lightninglabs/lndclient v0.20.0-1/go.mod h1:LcbsTCCd0Qw5C4zlv/YqrPY81XUVA6wN1lA/qEWIs+Y= github.com/lightninglabs/migrate/v4 v4.18.2-9023d66a-fork-pr-2 h1:eFjp1dIB2BhhQp/THKrjLdlYuPugO9UU4kDqu91OX/Q= github.com/lightninglabs/migrate/v4 v4.18.2-9023d66a-fork-pr-2/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY= github.com/lightninglabs/neutrino v0.16.1 h1:5Kz4ToxncEVkpKC6fwUjXKtFKJhuxlG3sBB3MdJTJjs= @@ -1150,10 +1150,10 @@ github.com/lightninglabs/neutrino/cache v1.1.2 h1:C9DY/DAPaPxbFC+xNNEI/z1SJY9GS3 github.com/lightninglabs/neutrino/cache v1.1.2/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo= 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/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb h1:yfM05S8DXKhuCBp5qSMZdtSwvJ+GFzl94KbXMNB1JDY= -github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI= -github.com/lightningnetwork/lnd v0.19.3-beta.rc1.0.20250812194315-c3226e8c2223 h1:CBN1ju+LQL+jTfneGabUTFoHYuAnFeHv3j5+09wtOCo= -github.com/lightningnetwork/lnd v0.19.3-beta.rc1.0.20250812194315-c3226e8c2223/go.mod h1:MNRzea8Yrgk+ohyUhK7JSpoigE4T9JgerMQQUxMbl9I= +github.com/lightningnetwork/lightning-onion v1.2.1-0.20240815225420-8b40adf04ab9 h1:6D3LrdagJweLLdFm1JNodZsBk6iU4TTsBBFLQ4yiXfI= +github.com/lightningnetwork/lightning-onion v1.2.1-0.20240815225420-8b40adf04ab9/go.mod h1:EDqJ3MuZIbMq0QI1czTIKDJ/GS8S14RXPwapHw8cw6w= +github.com/lightningnetwork/lnd v0.19.0-beta.rc5.0.20250925062351-f293566849f2 h1:6R3TIiMAJVf4uCAlu1UsrXlYu/1BEEcHEQ19AQNIaJo= +github.com/lightningnetwork/lnd v0.19.0-beta.rc5.0.20250925062351-f293566849f2/go.mod h1:JTrMWr7r8Itu0td4ApgqsAyux2mAZ41LeqKr8V3aFuc= github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf0d0Uy4qBjI= github.com/lightningnetwork/lnd/cert v1.2.2/go.mod h1:jQmFn/Ez4zhDgq2hnYSw8r35bqGVxViXhX6Cd7HXM6U= github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0= @@ -1166,8 +1166,8 @@ github.com/lightningnetwork/lnd/kvdb v1.4.16 h1:9BZgWdDfjmHRHLS97cz39bVuBAqMc4/p github.com/lightningnetwork/lnd/kvdb v1.4.16/go.mod h1:HW+bvwkxNaopkz3oIgBV6NEnV4jCEZCACFUcNg4xSjM= github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQZznVb18iNMI= github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4= -github.com/lightningnetwork/lnd/sqldb v1.0.10-0.20250812192515-dd1d57d82de1 h1:Qf0lRLvj5xmcJGIRlREPi0c1h9fMxGe2u098Hn13eAc= -github.com/lightningnetwork/lnd/sqldb v1.0.10-0.20250812192515-dd1d57d82de1/go.mod h1:c/vWoQfcxu6FAfHzGajkIQi7CEIeIZFhhH4DYh1BJpc= +github.com/lightningnetwork/lnd/sqldb v1.0.11-0.20250925062351-f293566849f2 h1:HNujNZQwZW1Ve3bflUJ7MWxx2Fe3320TZJvbwd8OAt4= +github.com/lightningnetwork/lnd/sqldb v1.0.11-0.20250925062351-f293566849f2/go.mod h1:oOdZ7vjmAUmI9He+aFHTunnxKVefHZAfJttZdz16hSg= github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM= 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= @@ -1319,8 +1319,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO 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.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -1375,8 +1375,8 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 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.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= -go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= +go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= +go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= go.etcd.io/etcd/api/v3 v3.5.12 h1:W4sw5ZoU2Juc9gBWuLk5U6fHfNVyY1WC5g9uiXZio/c= go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4= go.etcd.io/etcd/client/pkg/v3 v3.5.12 h1:EYDL6pWwyOsylrQyLp2w+HkQ46ATiOvoEdMarindU2A= @@ -1405,18 +1405,18 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= 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= @@ -1751,8 +1751,8 @@ 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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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= diff --git a/lndservices/daemon_adapters.go b/lndservices/daemon_adapters.go index 4b6394d94..585ac864c 100644 --- a/lndservices/daemon_adapters.go +++ b/lndservices/daemon_adapters.go @@ -172,7 +172,7 @@ func (l *LndFsmDaemonAdapters) RegisterConfirmationsNtfn( return &chainntnfs.ConfirmationEvent{ Confirmed: spendDetail, - Updates: make(chan uint32), + Updates: make(chan chainntnfs.TxUpdateInfo), NegativeConf: make(chan int32), Done: make(chan struct{}), Cancel: cancel, diff --git a/rfq/manager.go b/rfq/manager.go index de27084c8..86bd858db 100644 --- a/rfq/manager.go +++ b/rfq/manager.go @@ -19,6 +19,7 @@ import ( "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/rfqmath" "github.com/lightninglabs/taproot-assets/rfqmsg" + "github.com/lightninglabs/taproot-assets/tapfeatures" "github.com/lightninglabs/taproot-assets/taprpc/rfqrpc" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnutils" @@ -112,6 +113,11 @@ type ManagerCfg struct { // into the manager once lnd and tapd are hooked together. AliasManager ScidAliasManager + // AuxChannelNegotiator is responsible for producing the extra tlv blob + // that is encapsulated in the init and reestablish peer messages. This + // helps us communicate custom feature bits with our peer. + AuxChanNegotiator *tapfeatures.AuxChannelNegotiator + // AcceptPriceDeviationPpm is the price deviation in // parts per million that is accepted by the RFQ negotiator. // @@ -251,12 +257,13 @@ func (m *Manager) startSubsystems(ctx context.Context) error { // Initialise and start the order handler. m.orderHandler, err = NewOrderHandler(OrderHandlerCfg{ - CleanupInterval: CacheCleanupInterval, - HtlcInterceptor: m.cfg.HtlcInterceptor, - HtlcSubscriber: m.cfg.HtlcSubscriber, - AcceptHtlcEvents: m.acceptHtlcEvents, - SpecifierChecker: m.AssetMatchesSpecifier, - NoOpHTLCs: m.cfg.NoOpHTLCs, + CleanupInterval: CacheCleanupInterval, + HtlcInterceptor: m.cfg.HtlcInterceptor, + HtlcSubscriber: m.cfg.HtlcSubscriber, + AcceptHtlcEvents: m.acceptHtlcEvents, + SpecifierChecker: m.AssetMatchesSpecifier, + NoOpHTLCs: m.cfg.NoOpHTLCs, + AuxChanNegotiator: m.cfg.AuxChanNegotiator, }) if err != nil { return fmt.Errorf("error initializing RFQ order handler: %w", diff --git a/rfq/order.go b/rfq/order.go index e6c87e618..270a0f735 100644 --- a/rfq/order.go +++ b/rfq/order.go @@ -14,11 +14,13 @@ import ( "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/rfqmath" "github.com/lightninglabs/taproot-assets/rfqmsg" + "github.com/lightninglabs/taproot-assets/tapfeatures" "github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/tlv" ) @@ -125,13 +127,22 @@ type AssetSalePolicy struct { // wants us to produce NoOp HTLCs. NoOpHTLCs bool + // auxChannelNegotiator is used to query the supported feature bits that + // are supported by a peer, or a channel. + auxChanNegotiator *tapfeatures.AuxChannelNegotiator + + // peer is the peer pub key of the peer we established this policy with. + peer route.Vertex + // expiry is the policy's expiry unix timestamp after which the policy // is no longer valid. expiry uint64 } // NewAssetSalePolicy creates a new asset sale policy. -func NewAssetSalePolicy(quote rfqmsg.BuyAccept, noop bool) *AssetSalePolicy { +func NewAssetSalePolicy(quote rfqmsg.BuyAccept, noop bool, + chanNegotiator *tapfeatures.AuxChannelNegotiator) *AssetSalePolicy { + htlcToAmtMap := make(map[models.CircuitKey]lnwire.MilliSatoshi) return &AssetSalePolicy{ @@ -142,6 +153,8 @@ func NewAssetSalePolicy(quote rfqmsg.BuyAccept, noop bool) *AssetSalePolicy { expiry: uint64(quote.AssetRate.Expiry.Unix()), htlcToAmt: htlcToAmtMap, NoOpHTLCs: noop, + auxChanNegotiator: chanNegotiator, + peer: quote.Peer, } } @@ -290,10 +303,13 @@ func (c *AssetSalePolicy) GenerateInterceptorResponse( fn.None[[]rfqmsg.ID](), ) + peerFeatures := c.auxChanNegotiator.GetPeerFeatures(c.peer) + supportNoOp := peerFeatures.HasFeature(tapfeatures.NoOpHTLCsOptional) + // We are about to create an outgoing HTLC that carries assets. Let's // set the noop flag in order to eventually only settle the assets but // never settle the sats anchor amount that will carry them. - if c.NoOpHTLCs { + if c.NoOpHTLCs && supportNoOp { htlcRecord.SetNoopAdd(rfqmsg.UseNoOpHTLCs) } @@ -686,6 +702,11 @@ type OrderHandlerCfg struct { // NoOpHTLCs is a boolean indicating whether the daemon configuration // wants us to produce NoOp HTLCs. NoOpHTLCs bool + + // AuxChannelNegotiator is responsible for producing the extra tlv blob + // that is encapsulated in the init and reestablish peer messages. This + // helps us communicate custom feature bits with our peer. + AuxChanNegotiator *tapfeatures.AuxChannelNegotiator } // OrderHandler orchestrates management of accepted quote bundles. It monitors @@ -940,7 +961,10 @@ func (h *OrderHandler) RegisterAssetSalePolicy(buyAccept rfqmsg.BuyAccept) { log.Debugf("Order handler is registering an asset sale policy given a "+ "buy accept message: %s", buyAccept.String()) - policy := NewAssetSalePolicy(buyAccept, h.cfg.NoOpHTLCs) + policy := NewAssetSalePolicy( + buyAccept, h.cfg.NoOpHTLCs, h.cfg.AuxChanNegotiator, + ) + h.policies.Store(policy.AcceptedQuoteId.Scid(), policy) } diff --git a/server.go b/server.go index 23698a4d7..f7a8d5b22 100644 --- a/server.go +++ b/server.go @@ -1296,3 +1296,67 @@ func (s *Server) NotifyBroadcast(req *sweep.BumpRequest, return s.cfg.AuxSweeper.NotifyBroadcast(req, tx, fee, outpointToTxIndex) } + +// GetInitRecords is called when sending an init message to a peer. It returns +// custom records to include in the init message TLVs. The implementation can +// decide which records to include based on the peer identity. +func (s *Server) GetInitRecords( + peer route.Vertex) (lnwire.CustomRecords, error) { + + srvrLog.Tracef("GetInitRecords called, peer=%s", peer) + + if err := s.waitForReady(); err != nil { + return nil, err + } + + return s.cfg.AuxChanNegotiator.GetInitRecords(peer) +} + +// ProcessInitRecords handles received init records from a peer. The +// implementation can store state internally to affect future channel operations +// with this peer. +func (s *Server) ProcessInitRecords(peer route.Vertex, + customRecords lnwire.CustomRecords) error { + + srvrLog.Tracef("ProcessInitRecords called, peer=%s", peer) + + if err := s.waitForReady(); err != nil { + return err + } + + return s.cfg.AuxChanNegotiator.ProcessInitRecords(peer, customRecords) +} + +// ProcessReestablishFeatures handles received channel_reestablish feature TLVs. +// This is a blocking call - the channel link will wait for this method to +// complete before continuing channel operations. The implementation can modify +// aux channel behavior based on the negotiated features. +func (s *Server) ProcessReestablish(cid lnwire.ChannelID, peer route.Vertex) { + srvrLog.Tracef("ProcessReestablishFeatures called, cid=%s", + cid.String()) + + if err := s.waitForReady(); err != nil { + srvrLog.Errorf("Failed to handle ProcessReestablish, server " + + "not ready") + } + + s.cfg.AuxChanNegotiator.ProcessReestablish( + cid, peer, + ) +} + +// ProcessChannelReady handles the event of marking a channel identified by its +// channel ID as ready to use. We also provide the peer the channel was +// established with. +func (s *Server) ProcessChannelReady(cid lnwire.ChannelID, peer route.Vertex) { + srvrLog.Tracef("ProcessChannelReady called, cid=%s, peer=%s", cid, peer) + + if err := s.waitForReady(); err != nil { + srvrLog.Errorf("ProcessChannelReady got error while waiting " + + "for server ready") + + return + } + + s.cfg.AuxChanNegotiator.ProcessChannelReady(cid, peer) +} diff --git a/tapcfg/server.go b/tapcfg/server.go index d3d8b7327..75d83f549 100644 --- a/tapcfg/server.go +++ b/tapcfg/server.go @@ -20,6 +20,7 @@ import ( "github.com/lightninglabs/taproot-assets/tapchannel" "github.com/lightninglabs/taproot-assets/tapdb" "github.com/lightninglabs/taproot-assets/tapdb/sqlc" + "github.com/lightninglabs/taproot-assets/tapfeatures" "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/tapgarden" "github.com/lightninglabs/taproot-assets/tapscript" @@ -474,6 +475,9 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger, } } + // Construct the AuxChannelNegotiator. + auxChanNegotiator := tapfeatures.NewAuxChannelNegotiator() + // Construct the RFQ manager. rfqManager, err := rfq.NewManager(rfq.ManagerCfg{ PeerMessenger: msgTransportClient, @@ -482,6 +486,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger, PriceOracle: priceOracle, ChannelLister: lndServices.Client, GroupLookup: tapdbAddrBook, + AuxChanNegotiator: auxChanNegotiator, AliasManager: lndRouterClient, AcceptPriceDeviationPpm: rfqCfg.AcceptPriceDeviationPpm, SkipAcceptQuotePriceCheck: rfqCfg.SkipAcceptQuotePriceCheck, @@ -628,9 +633,10 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger, ) auxTrafficShaper := tapchannel.NewAuxTrafficShaper( &tapchannel.TrafficShaperConfig{ - ChainParams: &tapChainParams, - RfqManager: rfqManager, - NoopHTLCs: cfg.Channel.NoopHTLCs, + ChainParams: &tapChainParams, + RfqManager: rfqManager, + NoopHTLCs: cfg.Channel.NoopHTLCs, + AuxChanNegotiator: auxChanNegotiator, }, ) auxInvoiceManager := tapchannel.NewAuxInvoiceManager( @@ -745,6 +751,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger, AuxFundingController: auxFundingController, AuxChanCloser: auxChanCloser, AuxTrafficShaper: auxTrafficShaper, + AuxChanNegotiator: auxChanNegotiator, AuxInvoiceManager: auxInvoiceManager, AuxSweeper: auxSweeper, LogWriter: cfg.LogWriter, diff --git a/tapchannel/aux_traffic_shaper.go b/tapchannel/aux_traffic_shaper.go index 114b5344b..02b3dd628 100644 --- a/tapchannel/aux_traffic_shaper.go +++ b/tapchannel/aux_traffic_shaper.go @@ -14,6 +14,7 @@ import ( "github.com/lightninglabs/taproot-assets/rfqmath" "github.com/lightninglabs/taproot-assets/rfqmsg" cmsg "github.com/lightninglabs/taproot-assets/tapchannelmsg" + "github.com/lightninglabs/taproot-assets/tapfeatures" lfn "github.com/lightningnetwork/lnd/fn/v2" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnutils" @@ -30,6 +31,8 @@ type TrafficShaperConfig struct { RfqManager *rfq.Manager + AuxChanNegotiator *tapfeatures.AuxChannelNegotiator + // NoOpHTLCs is a boolean indicating whether the daemon configuration // wants us to produce NoOp HTLCs. NoopHTLCs bool @@ -464,6 +467,9 @@ func (s *AuxTrafficShaper) ProduceHtlcExtraData(totalAmount lnwire.MilliSatoshi, return totalAmount, nil, nil } + peerFeatures := s.cfg.AuxChanNegotiator.GetPeerFeatures(peer) + supportNoOp := peerFeatures.HasFeature(tapfeatures.NoOpHTLCsOptional) + // We need to do a round trip to convert the custom records to a blob // that we can then parse into the correct struct again. htlc, err := rfqmsg.HtlcFromCustomRecords(htlcCustomRecords) @@ -478,7 +484,7 @@ func (s *AuxTrafficShaper) ProduceHtlcExtraData(totalAmount lnwire.MilliSatoshi, log.Tracef("Already have asset amount (sum %d) in HTLC, not "+ "producing extra data", htlc.Amounts.Val.Sum()) - if s.cfg.NoopHTLCs { + if s.cfg.NoopHTLCs && supportNoOp { htlc.SetNoopAdd(rfqmsg.UseNoOpHTLCs) } @@ -587,7 +593,7 @@ func (s *AuxTrafficShaper) ProduceHtlcExtraData(totalAmount lnwire.MilliSatoshi, // Now we set the flag that marks this HTLC as a noop_add, which means // that the above dust will eventually return to us. This means that // only the assets will be sent and not any btc balance. - if s.cfg.NoopHTLCs { + if s.cfg.NoopHTLCs && supportNoOp { htlc.SetNoopAdd(rfqmsg.UseNoOpHTLCs) } diff --git a/tapfeatures/aux_channel_negotiator.go b/tapfeatures/aux_channel_negotiator.go new file mode 100644 index 000000000..2d7cfbad3 --- /dev/null +++ b/tapfeatures/aux_channel_negotiator.go @@ -0,0 +1,171 @@ +package tapfeatures + +import ( + "bytes" + "fmt" + + "github.com/lightningnetwork/lnd/lnutils" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/tlv" +) + +const ( + // AuxFeatureBitsTLV is the TLV type used to encode auxiliary feature + // bits in the init message. These feature bits allow aux channel + // implementations to negotiate custom channel behavior. + AuxFeatureBitsTLV tlv.Type = 65545 +) + +// AuxFeatureBits is a type alias for a TLV blob that contains custom feature +// bits for auxiliary channel negotiation. +type AuxFeatureBits = tlv.Blob + +// AuxChannelNegotiator is responsible for producing the extra tlv blob that is +// encapsulated in the init and reestablish peer messages. This helps us +// communicate custom feature bits with our peer. +type AuxChannelNegotiator struct { + // peerFeatures keeps track of the supported features of our peers. This + // map will be used for lookups by other subsystems, when some features + // need to be supported by both parties to take effect. + peerFeatures lnutils.SyncMap[route.Vertex, *lnwire.RawFeatureVector] + + // chanFeatures keeps track of the supported features of each channel. + // This map will be used for lookups by other subsystems to check + // whether certain custom channel features are supported. + chanFeatures lnutils.SyncMap[lnwire.ChannelID, *lnwire.RawFeatureVector] +} + +// NewAuxChannelNegotiator returns a new instance of the aux channel negotiator. +func NewAuxChannelNegotiator() *AuxChannelNegotiator { + return &AuxChannelNegotiator{} +} + +// GetInitRecords is called when sending an init message to a peer. It returns +// custom feature bits to include in the init message TLVs. The implementation +// can decide which features to advertise based on the peer's identity. +func (n *AuxChannelNegotiator) GetInitRecords( + _ route.Vertex) (lnwire.CustomRecords, error) { + + var buf bytes.Buffer + + // Grab the "static" feature vector that denotes the supported features + // of our node. If our peer can read this message they will keep track + // of our features just like we do below in `ProcessInitFeatures`. + features := getLocalFeatureVec() + err := features.Encode(&buf) + if err != nil { + return nil, err + } + + tlvMap := make(tlv.TypeMap, 1) + tlvMap[AuxFeatureBitsTLV] = buf.Bytes() + + return lnwire.NewCustomRecords(tlvMap) +} + +// ProcessInitRecords handles received init feature TLVs from a peer. The +// implementation can store state internally to affect future channel operations +// with this peer. +func (n *AuxChannelNegotiator) ProcessInitRecords(peer route.Vertex, + customRecords lnwire.CustomRecords) error { + + auxRecord, ok := customRecords[uint64(AuxFeatureBitsTLV)] + if !ok { + // If the entry was not present, delete the previous entry. Our + // peer did not provide a custom feature bit vector this time. + n.peerFeatures.Delete(peer) + return nil + } + + buf := bytes.NewBuffer(auxRecord) + peerVec := lnwire.NewRawFeatureVector() + err := peerVec.Decode(buf) + if err != nil { + return err + } + + // Before we store this peer's supported features we need to first check + // if our required features are supported by that peer. If a locally + // required feature is not supported by the remote peer we have to + // return an error and drop the connection. Whether we support all of + // the remote required features is a responsibility of the remote peer. + // If we fail to support a remotely required feature they are the ones + // to drop the connection (by returning an error right here). + err = checkRequiredBits(getLocalFeatureVec(), peerVec) + if err != nil { + return err + } + + // Store this peer's features. + n.peerFeatures.Store(peer, peerVec) + + return nil +} + +// ProcessChannelReady handles the reception of the ChannelReady message, which +// signals that a newly established channel is now ready to use. This helps us +// correlate a peer's features with a channel outpoint +func (n *AuxChannelNegotiator) ProcessChannelReady(cid lnwire.ChannelID, + peer route.Vertex) { + + features, ok := n.peerFeatures.Load(peer) + if ok { + n.chanFeatures.Store(cid, features) + } +} + +// ProcessReestablish handles the reception of the ChannelReestablish message, +// which signals that a previously established channel is now ready to use. This +// helps us correlate a peer's features with a channel outpoint. +func (n *AuxChannelNegotiator) ProcessReestablish( + cid lnwire.ChannelID, peer route.Vertex) { + + features, ok := n.peerFeatures.Load(peer) + if ok { + n.chanFeatures.Store(cid, features) + } +} + +// GetPeerFeatures returns the negotiated feature bit vector that was +// established with the given peer. +func (n *AuxChannelNegotiator) GetPeerFeatures( + peer route.Vertex) lnwire.FeatureVector { + + rawfeatures, ok := n.peerFeatures.Load(peer) + if !ok { + rawfeatures = lnwire.NewRawFeatureVector() + } + + return *lnwire.NewFeatureVector(rawfeatures, featureNames) +} + +// GetChannelFeatures returns the negotiated feature bits vector for the channel +// identified by the given channelID. +func (n *AuxChannelNegotiator) GetChannelFeatures( + cid lnwire.ChannelID) lnwire.FeatureVector { + + rawfeatures, ok := n.chanFeatures.Load(cid) + if !ok { + rawfeatures = lnwire.NewRawFeatureVector() + } + + return *lnwire.NewFeatureVector(rawfeatures, featureNames) +} + +// checkRequiredBits is a helper method that checks if all of the required bits +// of the first vector are supported by the second vector. +func checkRequiredBits(local, remote *lnwire.RawFeatureVector) error { + localBits, remoteBits := + lnwire.NewFeatureVector(local, featureNames), + lnwire.NewFeatureVector(remote, featureNames) + + for _, f := range ourFeatures() { + if localBits.RequiresFeature(f) && !remoteBits.HasFeature(f) { + return fmt.Errorf("peer does not support required "+ + "feature: %v", localBits.Name(f)) + } + } + + return nil +} diff --git a/tapfeatures/aux_channel_negotiator_test.go b/tapfeatures/aux_channel_negotiator_test.go new file mode 100644 index 000000000..b810a486e --- /dev/null +++ b/tapfeatures/aux_channel_negotiator_test.go @@ -0,0 +1,58 @@ +package tapfeatures + +import ( + "testing" + + "github.com/lightningnetwork/lnd/lnwire" + "github.com/stretchr/testify/require" +) + +// TestFeatureBits tests that the behavior of the feature vector matches our +// expectations when using the custom feature bits for taproot asset channels. +func TestFeatureBits(t *testing.T) { + featuresA := lnwire.NewFeatureVector( + lnwire.NewRawFeatureVector(NoOpHTLCsOptional), featureNames, + ) + + featuresB := lnwire.NewFeatureVector( + lnwire.NewRawFeatureVector(STXOOptional), featureNames, + ) + + require.True(t, featuresA.HasFeature(NoOpHTLCsOptional)) + require.True(t, featuresB.HasFeature(STXOOptional)) + + require.False(t, featuresA.HasFeature(STXOOptional)) + require.False(t, featuresB.HasFeature(NoOpHTLCsOptional)) + + require.False(t, featuresA.RequiresFeature(NoOpHTLCsOptional)) + require.False(t, featuresB.RequiresFeature(STXOOptional)) + + err := checkRequiredBits( + featuresA.RawFeatureVector, featuresB.RawFeatureVector, + ) + + require.NoError(t, err) + + featuresA = lnwire.NewFeatureVector( + lnwire.NewRawFeatureVector(NoOpHTLCsRequired), featureNames, + ) + + featuresB = lnwire.NewFeatureVector( + lnwire.NewRawFeatureVector(STXORequired), featureNames, + ) + + require.True(t, featuresA.HasFeature(NoOpHTLCsOptional)) + require.True(t, featuresB.HasFeature(STXOOptional)) + + require.False(t, featuresA.HasFeature(STXOOptional)) + require.False(t, featuresB.HasFeature(NoOpHTLCsOptional)) + + require.True(t, featuresA.RequiresFeature(NoOpHTLCsOptional)) + require.True(t, featuresB.RequiresFeature(STXOOptional)) + + err = checkRequiredBits( + featuresA.RawFeatureVector, featuresB.RawFeatureVector, + ) + + require.Error(t, err) +} diff --git a/tapfeatures/aux_feature_bits.go b/tapfeatures/aux_feature_bits.go new file mode 100644 index 000000000..135e1e3a1 --- /dev/null +++ b/tapfeatures/aux_feature_bits.go @@ -0,0 +1,51 @@ +package tapfeatures + +import "github.com/lightningnetwork/lnd/lnwire" + +const ( + // NoOpHTLCsRequired is a feature bit that declares the noop-htlcs as a + // required feature. + NoOpHTLCsRequired lnwire.FeatureBit = 0 + + // NoOpHTLCsOptional is a feature bit that declares the noop-htlcs as an + // optional feature. + NoOpHTLCsOptional lnwire.FeatureBit = 1 + + // STXORequired is a feature bit that declares STXO proofs as a required + // feature. + STXORequired lnwire.FeatureBit = 2 + + // STXOOptional is a feature bit that declares the STXO proofs as an + // optional feature. + STXOOptional lnwire.FeatureBit = 3 +) + +// featureNames keeps track of the string description of known features. +var featureNames = map[lnwire.FeatureBit]string{ + NoOpHTLCsRequired: "noop-htlcs", + NoOpHTLCsOptional: "noop-htlcs", + STXORequired: "stxo-proofs", + STXOOptional: "stxo-proofs", +} + +// ourFeatures returns a slice containing all of the locally supported features. +func ourFeatures() []lnwire.FeatureBit { + // TODO(george): instead of hosting the supported features in the + // following slice we could make something more explicit / modular. + return []lnwire.FeatureBit{ + NoOpHTLCsOptional, + STXOOptional, + } +} + +// getLocalFeatureVec returns the feature vector of the currently supported +// features. This set of features may change between different versions of tapd, +// and that exactly is its purpose. This feature vector denotes which features +// of tap channels are currently supported, in order to maintain compatibility +// with our peers. +func getLocalFeatureVec() *lnwire.RawFeatureVector { + ourFeatures := ourFeatures() + return lnwire.NewRawFeatureVector( + ourFeatures..., + ) +}