diff --git a/client/asset/driver.go b/client/asset/driver.go index 29dea271ff..71cae01778 100644 --- a/client/asset/driver.go +++ b/client/asset/driver.go @@ -7,6 +7,8 @@ import ( "context" "errors" "fmt" + "slices" + "strings" "sync" "decred.org/dcrdex/dex" @@ -354,3 +356,29 @@ func SPVWithdrawTx(ctx context.Context, assetID uint32, walletPW []byte, recipie } return f(ctx, walletPW, recipient, dataDir, net, log) } + +func Tickers() []string { + assets := Assets() + tickers := make([]string, 0, len(assets)/2 /* generous rough guess accounting for dupes i.e. tokens */) + for _, a := range assets { + ui, _ := UnitInfo(a.ID) + ticker := ui.Ticker() + if !slices.Contains(tickers, ticker) { + tickers = append(tickers, ticker) + } + } + return tickers +} + +func NetworkTickers() map[string]uint32 { + assets := Assets() + netTickers := make(map[string]uint32) + for _, a := range assets { + parts := strings.Split(a.Symbol, ".") + if len(parts) > 1 { + continue + } + netTickers[a.Info.UnitInfo.Conventional.Unit] = a.ID + } + return netTickers +} diff --git a/client/cmd/bisonw-desktop/go.mod b/client/cmd/bisonw-desktop/go.mod index 64452dbde2..7a91208141 100644 --- a/client/cmd/bisonw-desktop/go.mod +++ b/client/cmd/bisonw-desktop/go.mod @@ -1,9 +1,11 @@ module decred.org/dcrdex/client/cmd/bisonw-desktop -go 1.24.0 +go 1.24.9 replace decred.org/dcrdex => ../../.. +replace github.com/bisoncraft/mesh => github.com/buck54321/mesh v0.0.0-20260310125835-501f40b7413d + require ( decred.org/dcrdex v0.6.3 fyne.io/systray v1.10.1-0.20230403195833-7dc3c09283d6 @@ -19,8 +21,11 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/athanorlabs/go-dleq v0.1.0 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect github.com/bisoncraft/bchwallet v1.0.2 // indirect github.com/bisoncraft/go-bip39 v1.0.1 // indirect + github.com/bisoncraft/mesh v0.0.0-00010101000000-000000000000 // indirect github.com/bisoncraft/neutrino-bch v1.0.1 // indirect github.com/bisoncraft/op-geth v0.0.0-20250729074358-3cfe4f15e91c // indirect github.com/bits-and-blooms/bitset v1.22.0 // indirect @@ -28,6 +33,7 @@ require ( github.com/consensys/gnark-crypto v0.18.0 // indirect github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect + github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/dcrlabs/ltcwallet v0.0.0-20240823165752-3e026e8da010 // indirect github.com/decred/dcrd/addrmgr/v3 v3.0.0 // indirect github.com/decred/dcrd/blockchain/stake/v3 v3.0.0 // indirect @@ -52,6 +58,8 @@ require ( github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect github.com/ethereum/go-verkle v0.2.2 // indirect github.com/ferranbt/fastssz v0.1.4 // indirect + github.com/flynn/noise v1.1.0 // indirect + github.com/francoispqt/gojay v1.2.13 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect @@ -61,26 +69,92 @@ require ( github.com/gorilla/schema v1.1.0 // indirect github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c // indirect github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/ipfs/go-cid v0.5.0 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/klauspost/compress v1.18.0 // indirect + github.com/koron/go-ssdp v0.0.6 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-flow-metrics v0.2.0 // indirect + github.com/libp2p/go-libp2p v0.44.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect + github.com/libp2p/go-msgio v0.3.0 // indirect + github.com/libp2p/go-netroute v0.3.0 // indirect + github.com/libp2p/go-reuseport v0.4.0 // indirect + github.com/libp2p/go-yamux/v5 v5.0.1 // indirect github.com/ltcsuite/lnd/tlv v0.0.0-20240222214433-454d35886119 // indirect github.com/ltcsuite/ltcd/chaincfg/chainhash v1.0.2 // indirect github.com/marcopeereboom/sbox v1.1.0 // indirect - github.com/minio/sha256-simd v1.0.0 // indirect + github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect + github.com/miekg/dns v1.1.66 // indirect + github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect + github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.16.0 // indirect + github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect + github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.1 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-multistream v0.6.1 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/naoina/go-stringutil v0.1.0 // indirect github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect + github.com/pion/datachannel v1.5.10 // indirect + github.com/pion/dtls/v2 v2.2.12 // indirect + github.com/pion/dtls/v3 v3.0.6 // indirect + github.com/pion/ice/v4 v4.0.10 // indirect + github.com/pion/interceptor v0.1.40 // indirect + github.com/pion/logging v0.2.3 // indirect + github.com/pion/mdns/v2 v2.0.7 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/rtcp v1.2.15 // indirect + github.com/pion/rtp v1.8.19 // indirect + github.com/pion/sctp v1.8.39 // indirect + github.com/pion/sdp/v3 v3.0.13 // indirect + github.com/pion/srtp/v3 v3.0.6 // indirect + github.com/pion/stun v0.6.1 // indirect + github.com/pion/stun/v3 v3.0.0 // indirect + github.com/pion/transport/v2 v2.2.10 // indirect + github.com/pion/transport/v3 v3.0.7 // indirect + github.com/pion/turn/v4 v4.0.2 // indirect + github.com/pion/webrtc/v4 v4.1.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.64.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.55.0 // indirect + github.com/quic-go/webtransport-go v0.9.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.10.0 // indirect github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect + github.com/wlynxg/anet v0.0.5 // indirect github.com/yuin/goldmark v1.7.13 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.37.0 // indirect go.opentelemetry.io/otel/metric v1.37.0 // indirect go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.uber.org/dig v1.19.0 // indirect + go.uber.org/fx v1.24.0 // indirect + go.uber.org/mock v0.5.2 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/mod v0.31.0 // indirect + golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc // indirect + golang.org/x/tools v0.40.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -148,22 +222,22 @@ require ( github.com/gcash/bchlog v0.0.0-20180913005452-b4f036f92fa6 // indirect github.com/gcash/bchutil v0.0.0-20210113190856-6ea28dff4000 // indirect github.com/gen2brain/beeep v0.0.0-20240112042604-c7bb2cd88fea - github.com/go-chi/chi/v5 v5.0.1 // indirect + github.com/go-chi/chi/v5 v5.2.3 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gofrs/flock v0.12.1 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.1 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.3.2 // indirect github.com/huandu/skiplist v1.2.0 // indirect - github.com/jessevdk/go-flags v1.5.0 // indirect + github.com/jessevdk/go-flags v1.6.1 // indirect github.com/jrick/bitset v1.0.0 // indirect - github.com/jrick/logrotate v1.0.0 // indirect + github.com/jrick/logrotate v1.1.2 // indirect github.com/jrick/wsrpc/v2 v2.3.8 // indirect github.com/kkdai/bstream v1.0.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/lib/pq v1.10.4 // indirect github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect github.com/lightninglabs/neutrino v0.16.1-0.20240814152458-81d6cd2d2da5 // indirect @@ -191,14 +265,14 @@ require ( github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 // indirect go.etcd.io/bbolt v1.3.12 // indirect golang.org/x/crypto v0.47.0 // indirect - golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect golang.org/x/net v0.48.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/term v0.39.0 // indirect golang.org/x/text v0.33.0 // indirect - golang.org/x/time v0.9.0 // indirect + golang.org/x/time v0.12.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - lukechampine.com/blake3 v1.3.0 // indirect + lukechampine.com/blake3 v1.4.1 // indirect ) diff --git a/client/cmd/bisonw-desktop/go.sum b/client/cmd/bisonw-desktop/go.sum index d8d3a77f2a..78cf0181b9 100644 --- a/client/cmd/bisonw-desktop/go.sum +++ b/client/cmd/bisonw-desktop/go.sum @@ -4,7 +4,9 @@ bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxo bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= @@ -87,11 +89,16 @@ decred.org/dcrwallet v1.7.0 h1:U/ew00YBdUlx3rJAynt2OdKDgGzBKK4O89FijBq8iVg= decred.org/dcrwallet v1.7.0/go.mod h1:hNOGyvH53gWdgFB601/ubGRzCPfPtWnEVAi9Grs90y4= decred.org/dcrwallet/v5 v5.0.3 h1:nz3oIq06RnIQ8uNKCG8sBa5vsq2WLd5+a7QlErYMxu8= decred.org/dcrwallet/v5 v5.0.3/go.mod h1:yH+3MRx46MzoPNgEARW5K9a3o/uIa0qtlc6az2oZVqY= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= fyne.io/systray v1.10.1-0.20230403195833-7dc3c09283d6 h1:lHt8dm97Uy9ggtnt9N6XOlsp76wXmRAh3SjReWm1e2Q= fyne.io/systray v1.10.1-0.20230403195833-7dc3c09283d6/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU= @@ -189,6 +196,8 @@ github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZw github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/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= @@ -214,6 +223,7 @@ github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAw github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= 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.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08= @@ -260,7 +270,10 @@ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/buck54321/mesh v0.0.0-20260310125835-501f40b7413d h1:DYZYW/jL0G3KxbReLnLpxW0E53vFMpcVB8IN1eyNz1A= +github.com/buck54321/mesh v0.0.0-20260310125835-501f40b7413d/go.mod h1:DxzW6X/J4N6qqPt7ZYDTVx8iTE4xAoTTG9ANKv94f3Y= github.com/bufbuild/buf v0.37.0/go.mod h1:lQ1m2HkIaGOFba6w/aC3KYBHhKEOESP3gaAEpS3dAFM= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -325,6 +338,7 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= @@ -354,6 +368,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/dchest/blake256 v1.0.0/go.mod h1:xXNWCE1jsAP8DAjP+rKw2MbeqLczjI3TRx2VK+9OEYY= github.com/dchest/blake2b v1.0.0 h1:KK9LimVmE0MjRl9095XJmKqZ+iLxWATvlcpVFRtaw6s= github.com/dchest/blake2b v1.0.0/go.mod h1:U034kXgbJpCle2wSk5ybGIVhOSHCVLMDqOzcPEA0F7s= @@ -624,11 +640,15 @@ github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4 github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= +github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -667,10 +687,12 @@ github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49P github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-chi/chi/v5 v5.0.1 h1:ALxjCrTf1aflOlkhMnCUP86MubbWFrzB3gkRPReLpTo= -github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= +github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-critic/go-critic v0.5.6/go.mod h1:cVjj0DfqewQVIlIAGexPCaGaZDAqGE29PYDDADIVNEo= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 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= @@ -747,6 +769,7 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er 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= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -824,6 +847,7 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.8/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/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -870,7 +894,9 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= 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= @@ -898,8 +924,8 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +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/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw= @@ -913,12 +939,14 @@ github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -991,16 +1019,21 @@ github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSH github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 h1:vilfsDSy7TDxedi9gyBkMvAirat/oRcL0lFdJBf6tdM= github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= +github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= +github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v0.0.0-20181221193153-c0795c8afcf4/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.1-0.20200711081900-c17162fe8fd7/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= +github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/jgautheron/goconst v1.4.0/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= github.com/jhump/protoreflect v1.8.1/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= @@ -1023,8 +1056,9 @@ github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgb github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/bitset v1.0.0 h1:Ws0PXV3PwXqWK2n7Vz6idCdrV/9OrBXgHEJi27ZB9Dw= github.com/jrick/bitset v1.0.0/go.mod h1:ZOYB5Uvkla7wIEY4FEssPVi3IQXa02arznRaYaAEPe4= -github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/jrick/logrotate v1.1.2 h1:6ePk462NCX7TfKtNp5JJ7MbA2YIslkpfgP03TlTYMN0= +github.com/jrick/logrotate v1.1.2/go.mod h1:f9tdWggSVK3iqavGpyvegq5IhNois7KXmasU6/N96OQ= github.com/jrick/wsrpc/v2 v2.3.2/go.mod h1:XPYs8BnRWl99lCvXRM5SLpZmTPqWpSOPkDIqYTwDPfU= github.com/jrick/wsrpc/v2 v2.3.4/go.mod h1:XPYs8BnRWl99lCvXRM5SLpZmTPqWpSOPkDIqYTwDPfU= github.com/jrick/wsrpc/v2 v2.3.8 h1:9vfM8o9g00HXQb/3D6+Y9Cy1uybjD7K1272vtdXXBps= @@ -1061,14 +1095,15 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= -github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 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.2/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/koron/go-ssdp v0.0.6 h1:Jb0h04599eq/CY7rB5YEqPS83HmRfHP2azkxMN2rFtU= +github.com/koron/go-ssdp v0.0.6/go.mod h1:0R9LfRJGek1zWTjN3JUNlm5INCDYGpRDfAptnct63fI= 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= @@ -1078,6 +1113,7 @@ 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= github.com/kr/pty v1.1.2/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -1098,6 +1134,24 @@ github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw= +github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc= +github.com/libp2p/go-libp2p v0.44.0 h1:5Gtt8OrF8yiXmH+Mx4+/iBeFRMK1TY3a8OrEBDEqAvs= +github.com/libp2p/go-libp2p v0.44.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc= +github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= +github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= +github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= +github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= +github.com/libp2p/go-netroute v0.3.0 h1:nqPCXHmeNmgTJnktosJ/sIef9hvwYCrsLxXmfNks/oc= +github.com/libp2p/go-netroute v0.3.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= +github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= +github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= +github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfYRSg= +github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= github.com/lightninglabs/neutrino v0.16.1-0.20240814152458-81d6cd2d2da5 h1:0jNLTYmzZDAJSGTOGqayvRlYpOM/gWtXIJZvtU/8zp0= @@ -1136,13 +1190,19 @@ github.com/ltcsuite/ltcd/ltcutil v1.1.4-0.20240131072528-64dfa402637a h1:fQ9S26c github.com/ltcsuite/ltcd/ltcutil v1.1.4-0.20240131072528-64dfa402637a/go.mod h1:z8txd/ohBFrOMBUT70K8iZvHJD/Vc3gzx+6BP6cBxQw= github.com/ltcsuite/ltcd/ltcutil/psbt v1.1.1-0.20240131072528-64dfa402637a h1:vGmxYaHruD6GcoT6ui/4CulFqdnwfYoKZ+At84Uy10w= github.com/ltcsuite/ltcd/ltcutil/psbt v1.1.1-0.20240131072528-64dfa402637a/go.mod h1:zcnG/wmZrTcQGTJIBgrG4Fu2ljZcV0DvZ8tzfCvU91o= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= github.com/marcopeereboom/sbox v1.1.0 h1:IiVHCi5f+nGRiMX551wnDk5ce+IEd3dWVH7ycf2uU2M= github.com/marcopeereboom/sbox v1.1.0/go.mod h1:u2fh4EbQDXQXXzGypWkf2nMn2TnsqA23t224mii7oog= +github.com/marcopolo/simnet v0.0.1 h1:rSMslhPz6q9IvJeFWDoMGxMIrlsbXau3NkuIXHGJxfg= +github.com/marcopolo/simnet v0.0.1/go.mod h1:WDaQkgLAjqDUEBAOXz22+1j6wXKfGlC5sD5XWt3ddOs= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/martonp/btcd/btcec/v2 v2.0.0-20250528172049-6b252bb1b6a1 h1:B5wUyPCVOFp6GIQ9IyUYAWPC34t9ej8q8Y739IRhpSo= github.com/martonp/btcd/btcec/v2 v2.0.0-20250528172049-6b252bb1b6a1/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= @@ -1177,18 +1237,27 @@ github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOq github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= github.com/mgechev/revive v1.0.6/go.mod h1:Lj5gIVxjBlH8REa3icEOkdfchwYc291nShzZ4QYWyMo= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= +github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -1215,6 +1284,33 @@ github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8q github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mozilla/tls-observatory v0.0.0-20210209181001-cf43108d6880/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= +github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= +github.com/multiformats/go-multiaddr v0.16.0 h1:oGWEVKioVQcdIOBlYM8BH1rZDWOGJSqr9/BKl6zQ4qc= +github.com/multiformats/go-multiaddr v0.16.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= +github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= +github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= +github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.9.1 h1:x/Fuxr7ZuR4jJV4Os5g444F7xC4XmyUaT/FWtE+9Zjo= +github.com/multiformats/go-multicodec v0.9.1/go.mod h1:LLWNMtyV5ithSBUo3vFIMaeDy+h3EbkMTek1m+Fybbo= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-multistream v0.6.1 h1:4aoX5v6T+yWmc2raBHsTvzmFhOI8WVOer28DeBBEYdQ= +github.com/multiformats/go-multistream v0.6.1/go.mod h1:ksQf6kqHAb6zIsyw7Zm+gAuVo57Qbq84E27YlYqavqw= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= @@ -1233,6 +1329,8 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nightlyone/lockfile v1.0.0/go.mod h1:rywoIealpdNse2r832aiD9jRk8ErCatROs6LzC841CI= github.com/nishanths/exhaustive v0.1.0/go.mod h1:S1j9110vxV1ECdCudXRkeMnFQ/DQk9ajLT0Uf2MYZQQ= @@ -1276,6 +1374,7 @@ github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= @@ -1286,6 +1385,8 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9 github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -1298,16 +1399,50 @@ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssy github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= +github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= +github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= +github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= +github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= +github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4= +github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= +github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4= +github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= +github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= +github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= +github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= +github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= +github.com/pion/rtp v1.8.19 h1:jhdO/3XhL/aKm/wARFVmvTfq0lC/CvN1xwYKmduly3c= +github.com/pion/rtp v1.8.19/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= +github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE= +github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= +github.com/pion/sdp/v3 v3.0.13 h1:uN3SS2b+QDZnWXgdr69SM8KB4EbcnPnPf2Laxhty/l4= +github.com/pion/sdp/v3 v3.0.13/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= +github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4= +github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY= +github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= +github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= -github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= +github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= +github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= -github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= -github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= +github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= +github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= +github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= +github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= +github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= +github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= +github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54= +github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1326,6 +1461,7 @@ github.com/polyfloyd/go-errorlint v0.0.0-20210510181950-ab96adb96fea/go.mod h1:w github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/progrium/darwinkit v0.5.0 h1:SwchcMbTOG1py3CQsINmGlsRmYKdlFrbnv3dE4aXA0s= github.com/progrium/darwinkit v0.5.0/go.mod h1:PxQhZuftnALLkCVaR8LaHtUOfoo4pm8qUDG+3C/sXNs= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -1337,16 +1473,17 @@ github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3e github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1359,8 +1496,9 @@ github.com/prometheus/common v0.25.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMD github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4= +github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -1370,8 +1508,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/prometheus/prometheus v2.5.0+incompatible/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= @@ -1391,6 +1529,12 @@ github.com/quasilyte/go-ruleguard/rules v0.0.0-20210203162857-b223e0831f88/go.mo github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/regex/syntax v0.0.0-20200805063351-8f842688393c/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= +github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= +github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70= +github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -1430,9 +1574,29 @@ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil/v3 v3.21.4/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/simpleledgerinc/goslp v0.0.0-20210310142058-5920ead5c7a0/go.mod h1:H95uvOA9ea+6KkX647VlNLbV+UFBV/i+vPktgnXiQJw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -1451,7 +1615,9 @@ github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5/go.mod h1:T7TcVDs9 github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1487,6 +1653,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1499,6 +1666,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -1510,6 +1679,7 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= @@ -1556,7 +1726,12 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= github.com/valyala/quicktemplate v1.6.3/go.mod h1:fwPzK2fHuYEODzJ9pkw0ipCPNHZ2tD5KW4lOuSdPKzY= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= +github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1577,6 +1752,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de 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/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 h1:1qKTeMTSIEvRIjvVYzgcRp0xVp0eoiRTTiHSncb5gD8= @@ -1615,6 +1791,7 @@ go.etcd.io/etcd/v3 v3.5.0-alpha.0/go.mod h1:JZ79d3LV6NUfPjUxXrpiFAYcjhT+06qqw+i2 go.etcd.io/etcd/v3 v3.5.4/go.mod h1:c6jK4IfuWwJU26FD9SeI4cAtvlfu9Iacaxu0vRses1k= go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1651,30 +1828,46 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= +go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= +go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180718160520-a2144134853f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180808211826-de0752318171/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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= @@ -1687,15 +1880,20 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1709,10 +1907,11 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 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-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4= +golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= 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/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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= @@ -1737,6 +1936,10 @@ 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.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1744,6 +1947,8 @@ golang.org/x/net v0.0.0-20180808004115-f9ce57c11b24/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1753,6 +1958,7 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1808,10 +2014,18 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su 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-20220421235706-1d1ef9303861/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1835,6 +2049,7 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ 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/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 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= @@ -1847,6 +2062,8 @@ 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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1856,6 +2073,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1864,6 +2082,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1959,17 +2178,28 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc 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-20220422013727-9388b58f7150/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc h1:bH6xUXay0AIFMElXG2rQ4uiE+7ncwtiOdPfYK1NK2XA= +golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= 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= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1982,6 +2212,10 @@ 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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1992,13 +2226,14 @@ golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2106,11 +2341,18 @@ 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.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= 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= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -2170,7 +2412,11 @@ google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180808183934-383e8b2c3b9e/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -2270,6 +2516,7 @@ google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -2343,6 +2590,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= @@ -2376,6 +2624,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2386,8 +2635,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.4/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= -lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= +lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= @@ -2400,4 +2649,5 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/client/core/core.go b/client/core/core.go index bcd9da6ed1..51410e08d4 100644 --- a/client/core/core.go +++ b/client/core/core.go @@ -32,6 +32,7 @@ import ( "decred.org/dcrdex/client/comms" "decred.org/dcrdex/client/db" "decred.org/dcrdex/client/db/bolt" + "decred.org/dcrdex/client/mesh" "decred.org/dcrdex/client/mnemonic" "decred.org/dcrdex/client/orderbook" "decred.org/dcrdex/dex" @@ -44,8 +45,7 @@ import ( "decred.org/dcrdex/dex/wait" "decred.org/dcrdex/server/account" serverdex "decred.org/dcrdex/server/dex" - "decred.org/dcrdex/tatanka/client/mesh" - "decred.org/dcrdex/tatanka/tanka" + "github.com/bisoncraft/mesh/bond" "github.com/decred/dcrd/crypto/blake256" "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" @@ -1541,7 +1541,7 @@ type Core struct { requestedActions map[string]*asset.ActionRequiredNote meshMtx sync.RWMutex - mesh *mesh.Mesh + mesh *mesh.Client meshCM *dex.ConnectionMaster } @@ -4643,7 +4643,6 @@ func (c *Core) Login(pw []byte) error { } } - var meshPriv *secp256k1.PrivateKey login := func() (needInit bool, err error) { c.loginMtx.Lock() defer c.loginMtx.Unlock() @@ -4658,26 +4657,19 @@ func (c *Core) Login(pw []byte) error { if err != nil { return false, fmt.Errorf("error deriving bond private key: %w", err) } - meshPriv, err = deriveMeshPriv(seed) - if err != nil { - return false, fmt.Errorf("error deriving mesh private key: %w", err) - } c.multisigXPriv, err = deriveMultisigXPriv(seed) if err != nil { return false, fmt.Errorf("error deriving multisig private key: %w", err) } if c.cfg.Mesh && c.net == dex.Simnet { - mesh, err := mesh.New(&mesh.Config{ - DataDir: filepath.Join(filepath.Dir(c.cfg.DBPath), "mesh"), - PrivateKey: meshPriv, - Logger: c.log.SubLogger("MESH"), - EntryNode: &mesh.TatankaCredentials{ - PeerID: tanka.SimnetTatankaPeerID, - Addr: "127.0.0.1:7323", - // Cert: , - NoTLS: true, - }, + const simnetMeshAddr = "/ip4/127.0.0.1/tcp/12347" + todoBonds := []*bond.BondParams{} + mesh, err := mesh.NewClient(&mesh.ClientConfig{ + Seed: seed, + BootstrapPeer: simnetMeshAddr, + Bonds: todoBonds, + Log: c.log.SubLogger("MC"), }) if err != nil { return false, err @@ -4686,16 +4678,9 @@ func (c *Core) Login(pw []byte) error { c.mesh = mesh c.meshCM = dex.NewConnectionMaster(c.mesh) c.meshMtx.Unlock() - go func() { - for { - select { - case n := <-mesh.Next(): - c.handleMeshNotification(n) - case <-c.ctx.Done(): - return - } - } - }() + if err := c.meshCM.Connect(c.ctx); err != nil { + c.log.Errorf("Error establishing connection with Mesh. Will continue trying: %v", err) + } } c.loggedIn = true @@ -4716,7 +4701,7 @@ func (c *Core) Login(pw []byte) error { c.connectWallets(crypter) // initialize reserves c.notify(newLoginNote("Resuming active trades...")) c.resolveActiveTrades(crypter) - c.connectMesh() + c.connectMesh(c.ctx) c.notify(newLoginNote("Connecting to DEX servers...")) c.initializeDEXConnections(crypter) } diff --git a/client/core/mesh.go b/client/core/mesh.go index 139c42f16d..5797d43de6 100644 --- a/client/core/mesh.go +++ b/client/core/mesh.go @@ -1,37 +1,11 @@ package core import ( + "context" + "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/keygen" - "decred.org/dcrdex/tatanka/mj" - "github.com/decred/dcrd/dcrec/secp256k1/v4" ) -func (c *Core) handleMeshNotification(noteI any) { - switch n := noteI.(type) { - case *mj.Broadcast: - c.handleMeshBroadcast(n) - } -} - -func (c *Core) handleMeshBroadcast(bcast *mj.Broadcast) { - c.log.Tracef("Received mesh broadcast: %s", bcast.Subject) - - switch bcast.Topic { - case mj.TopicMarket: - // c.handleMarketBroadcast(bcast) - case mj.TopicFeeRateEstimate: - // c.handleFeeRateEstimateBroadcast(bcast) - case mj.TopicFiatRate: - // c.handleFiatRateBroadcast(bcast) - default: - c.log.Warnf("Unknown broadcast topic: %s", bcast.Topic) - } - - // // Emit the broadcast to the subscribers. - // c.meshEmit(bcast) -} - func (c *Core) coreMesh() *Mesh { c.meshMtx.RLock() mesh, meshCM := c.mesh, c.meshCM @@ -43,7 +17,7 @@ func (c *Core) coreMesh() *Mesh { } -func (c *Core) connectMesh() { +func (c *Core) connectMesh(ctx context.Context) { if c.net != dex.Simnet || !c.cfg.Mesh { return } @@ -64,24 +38,12 @@ func (c *Core) connectMesh() { c.log.Infof("Connected to Mesh") - if err = mesh.SubscribeToFeeRateEstimates(); err != nil { + if err = mesh.SubscribeToFeeRateEstimates(ctx); err != nil { c.log.Errorf("error subscribing to mesh fee rate estimates: %v", err) return } - if err = mesh.SubscribeToFiatRates(); err != nil { + if err = mesh.SubscribeToTickerPrices(ctx); err != nil { c.log.Error("error subscribing to mesh fiat rates: %v", err) return } } - -func deriveMeshPriv(seed []byte) (*secp256k1.PrivateKey, error) { - xKey, err := keygen.GenDeepChild(seed, []uint32{hdKeyPurposeMesh}) - if err != nil { - return nil, err - } - privB, err := xKey.SerializedPrivKey() - if err != nil { - return nil, err - } - return secp256k1.PrivKeyFromBytes(privB), nil -} diff --git a/client/mesh/meshclient.go b/client/mesh/meshclient.go new file mode 100644 index 0000000000..c7c5276f4e --- /dev/null +++ b/client/mesh/meshclient.go @@ -0,0 +1,169 @@ +// This code is available on the terms of the project LICENSE.md file, +// also available online at https://blueoakcouncil.org/license/1.0.0. + +package mesh + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "math" + "math/big" + "strings" + "sync" + "time" + + "decred.org/dcrdex/client/asset" + "decred.org/dcrdex/dex" + "decred.org/dcrdex/dex/encode" + "github.com/bisoncraft/mesh/bond" + mc "github.com/bisoncraft/mesh/client" + "github.com/bisoncraft/mesh/protocols" + "github.com/decred/dcrd/crypto/blake256" + "github.com/decred/dcrd/hdkeychain/v3" + libp2pcrypto "github.com/libp2p/go-libp2p/core/crypto" +) + +const hdKeyPurposeMesh uint32 = hdkeychain.HardenedKeyStart + 0x6d657368 // ASCII "mesh" + +type Client struct { + *mc.Client + log dex.Logger + ch chan any + netTickerMap map[string]uint32 +} + +type ClientConfig struct { + Seed []byte + BootstrapPeer string + Bonds []*bond.BondParams + Log dex.Logger +} + +func NewClient(cfg *ClientConfig) (*Client, error) { + defer encode.ClearBytes(cfg.Seed) + + meshPriv, err := deriveMeshPriv(cfg.Seed) + if err != nil { + return nil, fmt.Errorf("error deriving mesh private key: %w", err) + } + cl, err := mc.NewClient(&mc.Config{ + PrivateKey: meshPriv, + RemotePeerAddrs: []string{cfg.BootstrapPeer}, + Bonds: cfg.Bonds, + Logger: cfg.Log.SubLogger("mc"), + }) + if err != nil { + return nil, err + } + + return &Client{ + Client: cl, + log: cfg.Log, + ch: make(chan interface{}, 128), + netTickerMap: asset.NetworkTickers(), + }, nil +} + +func (c *Client) Connect(ctx context.Context) (*sync.WaitGroup, error) { + var wg sync.WaitGroup + wg.Add(1) + go func() { + c.Client.Run(ctx) + wg.Done() + }() + return &wg, nil +} + +type TickerPriceUpdate struct { + Ticker string + Price float64 +} + +func (c *Client) SubscribeToTickerPrices(ctx context.Context) error { + tickers := asset.Tickers() + topics := make([]string, len(tickers)) + for i, ticker := range tickers { + topics[i] = protocols.PriceTopicPrefix + ":" + ticker + } + return c.Client.Subscribe(ctx, topics, func(topic string, ev mc.TopicEvent) { + ticker, ok := extractSubtopic(topic, protocols.PriceTopicPrefix) + if !ok { + c.log.Warnf("Invalid price topic format: %s", topic) + return + } + c.emit(ctx, &TickerPriceUpdate{ + Ticker: ticker, + Price: decodeFloat64(ev.Data), + }) + }) +} + +type FeeRateUpdate struct { + NetAssetID uint32 + FeeRate uint64 +} + +func (c *Client) SubscribeToFeeRateEstimates(ctx context.Context) error { + netTickers := make([]string, 0, 10) + for _, a := range asset.Assets() { + // Base Network assets don't have a "." in their symbol. + if len(strings.Split(a.Symbol, ".")) != 1 { + continue + } + netTickers = append(netTickers, a.Info.UnitInfo.Conventional.Unit) + } + topics := make([]string, len(netTickers)) + for i, net := range netTickers { + topics[i] = protocols.FeeRateTopicPrefix + ":" + net + } + return c.Client.Subscribe(ctx, topics, func(topic string, ev mc.TopicEvent) { + netTicker, ok := extractSubtopic(topic, protocols.FeeRateTopicPrefix) + if !ok { + c.log.Warnf("Invalid price topic format: %s", topic) + return + } + netAssetID, ok := c.netTickerMap[netTicker] + if !ok { + c.log.Warnf("Unknown network identifier %q", netTicker) + return + } + c.emit(ctx, &FeeRateUpdate{ + NetAssetID: netAssetID, + FeeRate: bytesToBigInt(ev.Data).Uint64(), + }) + }) +} + +func (c *Client) emit(ctx context.Context, thing any) { + select { + case c.ch <- thing: + case <-time.After(time.Second * 10): + c.log.Errorf("Mesh event channel blocking for > 10 seconds: %v", thing) + case <-ctx.Done(): + } +} + +func extractSubtopic(topic, namespace string) (subtopic string, ok bool) { + parts := strings.SplitN(topic, ":", 2) + if len(parts) != 2 || parts[0] != namespace { + return "", false + } + return parts[1], true +} + +func decodeFloat64(buf []byte) float64 { + bits := binary.BigEndian.Uint64(buf) + return math.Float64frombits(bits) +} + +func bytesToBigInt(b []byte) *big.Int { + return new(big.Int).SetBytes(b) +} + +func deriveMeshPriv(seed []byte) (libp2pcrypto.PrivKey, error) { + entropy := blake256.Sum256(append(seed, encode.Uint32Bytes(hdKeyPurposeMesh)...)) + priv, _, err := libp2pcrypto.GenerateEd25519Key(bytes.NewReader(entropy[:])) + return priv, err +} diff --git a/dex/asset.go b/dex/asset.go index eb22dfd7a7..3dc23faa26 100644 --- a/dex/asset.go +++ b/dex/asset.go @@ -207,6 +207,10 @@ func (ui *UnitInfo) FormatSignedAtoms(v int64, plusSign ...bool) string { return "-" + ui.FormatAtoms(uint64(-v)) } +func (ui *UnitInfo) Ticker() string { + return ui.Conventional.Unit +} + // Token is a generic representation of a token-type asset. type Token struct { // ParentID is the asset ID of the token's parent asset. diff --git a/dex/testing/loadbot/go.mod b/dex/testing/loadbot/go.mod index 18b9ef31c7..13d6cb781f 100644 --- a/dex/testing/loadbot/go.mod +++ b/dex/testing/loadbot/go.mod @@ -1,9 +1,11 @@ module decred.org/dcrdex/dex/testing/loadbot -go 1.24.0 +go 1.24.9 replace decred.org/dcrdex => ../../../ +replace github.com/bisoncraft/mesh => github.com/buck54321/mesh v0.0.0-20260310125835-501f40b7413d + require ( decred.org/dcrdex v0.0.0-20230206134810-8a482dd7caf1 github.com/Shopify/toxiproxy/v2 v2.4.0 @@ -32,6 +34,9 @@ require ( github.com/aead/siphash v1.0.1 // indirect github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect github.com/athanorlabs/go-dleq v0.1.0 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bisoncraft/mesh v0.0.0-00010101000000-000000000000 // indirect github.com/bisoncraft/op-geth v0.0.0-20250729074358-3cfe4f15e91c // indirect github.com/bits-and-blooms/bitset v1.22.0 // indirect github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240625142744-cc26860b4026 // indirect @@ -56,6 +61,7 @@ require ( github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/dchest/blake2b v1.0.0 // indirect github.com/dchest/siphash v1.2.3 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect @@ -109,11 +115,13 @@ require ( github.com/ethereum/go-ethereum v1.16.7 // indirect github.com/ethereum/go-verkle v0.2.2 // indirect github.com/ferranbt/fastssz v0.1.4 // indirect + github.com/flynn/noise v1.1.0 // indirect + github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gcash/bchd v0.19.0 // indirect github.com/gcash/bchlog v0.0.0-20180913005452-b4f036f92fa6 // indirect github.com/gcash/bchutil v0.0.0-20210113190856-6ea28dff4000 // indirect - github.com/go-chi/chi/v5 v5.0.1 // indirect + github.com/go-chi/chi/v5 v5.2.3 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -124,18 +132,31 @@ require ( github.com/google/trillian v1.4.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/schema v1.1.0 // indirect - github.com/gorilla/websocket v1.5.1 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c // indirect github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.3.2 // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/ipfs/go-cid v0.5.0 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jrick/bitset v1.0.0 // indirect - github.com/jrick/logrotate v1.0.0 // indirect + github.com/jrick/logrotate v1.1.2 // indirect github.com/jrick/wsrpc/v2 v2.3.8 // indirect github.com/kkdai/bstream v1.0.0 // indirect github.com/klauspost/compress v1.18.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/koron/go-ssdp v0.0.6 // indirect github.com/lib/pq v1.10.4 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-flow-metrics v0.2.0 // indirect + github.com/libp2p/go-libp2p v0.44.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect + github.com/libp2p/go-msgio v0.3.0 // indirect + github.com/libp2p/go-netroute v0.3.0 // indirect + github.com/libp2p/go-reuseport v0.4.0 // indirect + github.com/libp2p/go-yamux/v5 v5.0.1 // indirect github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect github.com/lightninglabs/neutrino v0.16.1-0.20240814152458-81d6cd2d2da5 // indirect github.com/lightninglabs/neutrino/cache v1.1.2 // indirect @@ -151,40 +172,93 @@ require ( github.com/ltcsuite/ltcd/ltcutil v1.1.4-0.20240131072528-64dfa402637a // indirect github.com/ltcsuite/ltcd/ltcutil/psbt v1.1.1-0.20240131072528-64dfa402637a // indirect github.com/marcopeereboom/sbox v1.1.0 // indirect + github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/minio/sha256-simd v1.0.0 // indirect + github.com/miekg/dns v1.1.66 // indirect + github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect + github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.16.0 // indirect + github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect + github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.1 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-multistream v0.6.1 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/naoina/go-stringutil v0.1.0 // indirect github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect + github.com/pion/datachannel v1.5.10 // indirect + github.com/pion/dtls/v2 v2.2.12 // indirect + github.com/pion/dtls/v3 v3.0.6 // indirect + github.com/pion/ice/v4 v4.0.10 // indirect + github.com/pion/interceptor v0.1.40 // indirect + github.com/pion/logging v0.2.3 // indirect + github.com/pion/mdns/v2 v2.0.7 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/rtcp v1.2.15 // indirect + github.com/pion/rtp v1.8.19 // indirect + github.com/pion/sctp v1.8.39 // indirect + github.com/pion/sdp/v3 v3.0.13 // indirect + github.com/pion/srtp/v3 v3.0.6 // indirect + github.com/pion/stun v0.6.1 // indirect + github.com/pion/stun/v3 v3.0.0 // indirect + github.com/pion/transport/v2 v2.2.10 // indirect + github.com/pion/transport/v3 v3.0.7 // indirect + github.com/pion/turn/v4 v4.0.2 // indirect + github.com/pion/webrtc/v4 v4.1.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.64.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.55.0 // indirect + github.com/quic-go/webtransport-go v0.9.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.10.0 // indirect github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/wlynxg/anet v0.0.5 // indirect github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 // indirect go.etcd.io/bbolt v1.3.12 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.37.0 // indirect go.opentelemetry.io/otel/metric v1.37.0 // indirect go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.uber.org/dig v1.19.0 // indirect + go.uber.org/fx v1.24.0 // indirect + go.uber.org/mock v0.5.2 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.47.0 // indirect - golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect + golang.org/x/mod v0.31.0 // indirect golang.org/x/net v0.48.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect + golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc // indirect golang.org/x/term v0.39.0 // indirect golang.org/x/text v0.33.0 // indirect - golang.org/x/time v0.9.0 // indirect + golang.org/x/time v0.12.0 // indirect + golang.org/x/tools v0.40.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/blake3 v1.3.0 // indirect + lukechampine.com/blake3 v1.4.1 // indirect ) replace github.com/btcsuite/btcd/btcec/v2 v2.3.4 => github.com/martonp/btcd/btcec/v2 v2.0.0-20250528172049-6b252bb1b6a1 diff --git a/dex/testing/loadbot/go.sum b/dex/testing/loadbot/go.sum index 7100b17a56..5e8c59eb84 100644 --- a/dex/testing/loadbot/go.sum +++ b/dex/testing/loadbot/go.sum @@ -4,7 +4,9 @@ bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxo bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= @@ -87,9 +89,14 @@ decred.org/dcrwallet v1.7.0 h1:U/ew00YBdUlx3rJAynt2OdKDgGzBKK4O89FijBq8iVg= decred.org/dcrwallet v1.7.0/go.mod h1:hNOGyvH53gWdgFB601/ubGRzCPfPtWnEVAi9Grs90y4= decred.org/dcrwallet/v5 v5.0.3 h1:nz3oIq06RnIQ8uNKCG8sBa5vsq2WLd5+a7QlErYMxu8= decred.org/dcrwallet/v5 v5.0.3/go.mod h1:yH+3MRx46MzoPNgEARW5K9a3o/uIa0qtlc6az2oZVqY= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU= @@ -189,6 +196,8 @@ github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZw github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/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= @@ -212,6 +221,7 @@ github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAw github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= 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.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08= @@ -258,7 +268,10 @@ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/buck54321/mesh v0.0.0-20260310125835-501f40b7413d h1:DYZYW/jL0G3KxbReLnLpxW0E53vFMpcVB8IN1eyNz1A= +github.com/buck54321/mesh v0.0.0-20260310125835-501f40b7413d/go.mod h1:DxzW6X/J4N6qqPt7ZYDTVx8iTE4xAoTTG9ANKv94f3Y= github.com/bufbuild/buf v0.37.0/go.mod h1:lQ1m2HkIaGOFba6w/aC3KYBHhKEOESP3gaAEpS3dAFM= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -323,6 +336,7 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= @@ -352,6 +366,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/dchest/blake256 v1.0.0/go.mod h1:xXNWCE1jsAP8DAjP+rKw2MbeqLczjI3TRx2VK+9OEYY= github.com/dchest/blake2b v1.0.0 h1:KK9LimVmE0MjRl9095XJmKqZ+iLxWATvlcpVFRtaw6s= github.com/dchest/blake2b v1.0.0/go.mod h1:U034kXgbJpCle2wSk5ybGIVhOSHCVLMDqOzcPEA0F7s= @@ -622,11 +638,15 @@ github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4 github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= +github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -663,10 +683,12 @@ github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49P github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-chi/chi/v5 v5.0.1 h1:ALxjCrTf1aflOlkhMnCUP86MubbWFrzB3gkRPReLpTo= -github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= +github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-critic/go-critic v0.5.6/go.mod h1:cVjj0DfqewQVIlIAGexPCaGaZDAqGE29PYDDADIVNEo= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 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= @@ -739,6 +761,7 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er 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= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -816,6 +839,7 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.8/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/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -862,7 +886,9 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= 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= @@ -890,8 +916,8 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +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/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw= @@ -905,12 +931,14 @@ github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -979,10 +1007,15 @@ github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSH github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 h1:vilfsDSy7TDxedi9gyBkMvAirat/oRcL0lFdJBf6tdM= github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= +github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= +github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v0.0.0-20181221193153-c0795c8afcf4/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -1009,8 +1042,9 @@ github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgb github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/bitset v1.0.0 h1:Ws0PXV3PwXqWK2n7Vz6idCdrV/9OrBXgHEJi27ZB9Dw= github.com/jrick/bitset v1.0.0/go.mod h1:ZOYB5Uvkla7wIEY4FEssPVi3IQXa02arznRaYaAEPe4= -github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/jrick/logrotate v1.1.2 h1:6ePk462NCX7TfKtNp5JJ7MbA2YIslkpfgP03TlTYMN0= +github.com/jrick/logrotate v1.1.2/go.mod h1:f9tdWggSVK3iqavGpyvegq5IhNois7KXmasU6/N96OQ= github.com/jrick/wsrpc/v2 v2.3.2/go.mod h1:XPYs8BnRWl99lCvXRM5SLpZmTPqWpSOPkDIqYTwDPfU= github.com/jrick/wsrpc/v2 v2.3.4/go.mod h1:XPYs8BnRWl99lCvXRM5SLpZmTPqWpSOPkDIqYTwDPfU= github.com/jrick/wsrpc/v2 v2.3.8 h1:9vfM8o9g00HXQb/3D6+Y9Cy1uybjD7K1272vtdXXBps= @@ -1047,14 +1081,15 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= -github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 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.2/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/koron/go-ssdp v0.0.6 h1:Jb0h04599eq/CY7rB5YEqPS83HmRfHP2azkxMN2rFtU= +github.com/koron/go-ssdp v0.0.6/go.mod h1:0R9LfRJGek1zWTjN3JUNlm5INCDYGpRDfAptnct63fI= 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= @@ -1064,6 +1099,7 @@ 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= github.com/kr/pty v1.1.2/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -1084,6 +1120,24 @@ github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw= +github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc= +github.com/libp2p/go-libp2p v0.44.0 h1:5Gtt8OrF8yiXmH+Mx4+/iBeFRMK1TY3a8OrEBDEqAvs= +github.com/libp2p/go-libp2p v0.44.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc= +github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= +github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= +github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= +github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= +github.com/libp2p/go-netroute v0.3.0 h1:nqPCXHmeNmgTJnktosJ/sIef9hvwYCrsLxXmfNks/oc= +github.com/libp2p/go-netroute v0.3.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= +github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= +github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= +github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfYRSg= +github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= github.com/lightninglabs/neutrino v0.16.1-0.20240814152458-81d6cd2d2da5 h1:0jNLTYmzZDAJSGTOGqayvRlYpOM/gWtXIJZvtU/8zp0= @@ -1122,13 +1176,19 @@ github.com/ltcsuite/ltcd/ltcutil v1.1.4-0.20240131072528-64dfa402637a h1:fQ9S26c github.com/ltcsuite/ltcd/ltcutil v1.1.4-0.20240131072528-64dfa402637a/go.mod h1:z8txd/ohBFrOMBUT70K8iZvHJD/Vc3gzx+6BP6cBxQw= github.com/ltcsuite/ltcd/ltcutil/psbt v1.1.1-0.20240131072528-64dfa402637a h1:vGmxYaHruD6GcoT6ui/4CulFqdnwfYoKZ+At84Uy10w= github.com/ltcsuite/ltcd/ltcutil/psbt v1.1.1-0.20240131072528-64dfa402637a/go.mod h1:zcnG/wmZrTcQGTJIBgrG4Fu2ljZcV0DvZ8tzfCvU91o= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= github.com/marcopeereboom/sbox v1.1.0 h1:IiVHCi5f+nGRiMX551wnDk5ce+IEd3dWVH7ycf2uU2M= github.com/marcopeereboom/sbox v1.1.0/go.mod h1:u2fh4EbQDXQXXzGypWkf2nMn2TnsqA23t224mii7oog= +github.com/marcopolo/simnet v0.0.1 h1:rSMslhPz6q9IvJeFWDoMGxMIrlsbXau3NkuIXHGJxfg= +github.com/marcopolo/simnet v0.0.1/go.mod h1:WDaQkgLAjqDUEBAOXz22+1j6wXKfGlC5sD5XWt3ddOs= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/martonp/btcd/btcec/v2 v2.0.0-20250528172049-6b252bb1b6a1 h1:B5wUyPCVOFp6GIQ9IyUYAWPC34t9ej8q8Y739IRhpSo= github.com/martonp/btcd/btcec/v2 v2.0.0-20250528172049-6b252bb1b6a1/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= @@ -1163,18 +1223,27 @@ github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOq github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= github.com/mgechev/revive v1.0.6/go.mod h1:Lj5gIVxjBlH8REa3icEOkdfchwYc291nShzZ4QYWyMo= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= +github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -1201,6 +1270,33 @@ github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8q github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mozilla/tls-observatory v0.0.0-20210209181001-cf43108d6880/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= +github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= +github.com/multiformats/go-multiaddr v0.16.0 h1:oGWEVKioVQcdIOBlYM8BH1rZDWOGJSqr9/BKl6zQ4qc= +github.com/multiformats/go-multiaddr v0.16.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= +github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= +github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= +github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.9.1 h1:x/Fuxr7ZuR4jJV4Os5g444F7xC4XmyUaT/FWtE+9Zjo= +github.com/multiformats/go-multicodec v0.9.1/go.mod h1:LLWNMtyV5ithSBUo3vFIMaeDy+h3EbkMTek1m+Fybbo= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-multistream v0.6.1 h1:4aoX5v6T+yWmc2raBHsTvzmFhOI8WVOer28DeBBEYdQ= +github.com/multiformats/go-multistream v0.6.1/go.mod h1:ksQf6kqHAb6zIsyw7Zm+gAuVo57Qbq84E27YlYqavqw= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= @@ -1219,6 +1315,8 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nightlyone/lockfile v1.0.0/go.mod h1:rywoIealpdNse2r832aiD9jRk8ErCatROs6LzC841CI= github.com/nishanths/exhaustive v0.1.0/go.mod h1:S1j9110vxV1ECdCudXRkeMnFQ/DQk9ajLT0Uf2MYZQQ= @@ -1260,6 +1358,7 @@ github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= @@ -1270,6 +1369,8 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9 github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -1282,16 +1383,50 @@ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssy github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= +github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= +github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= +github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= +github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= +github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4= +github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= +github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4= +github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= +github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= +github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= +github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= +github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= +github.com/pion/rtp v1.8.19 h1:jhdO/3XhL/aKm/wARFVmvTfq0lC/CvN1xwYKmduly3c= +github.com/pion/rtp v1.8.19/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= +github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE= +github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= +github.com/pion/sdp/v3 v3.0.13 h1:uN3SS2b+QDZnWXgdr69SM8KB4EbcnPnPf2Laxhty/l4= +github.com/pion/sdp/v3 v3.0.13/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= +github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4= +github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY= +github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= +github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= -github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= +github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= +github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= -github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= -github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= +github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= +github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= +github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= +github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= +github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= +github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= +github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54= +github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U= 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= @@ -1306,6 +1441,7 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/polyfloyd/go-errorlint v0.0.0-20210418123303-74da32850375/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= github.com/polyfloyd/go-errorlint v0.0.0-20210510181950-ab96adb96fea/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -1317,16 +1453,17 @@ github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3e github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1339,8 +1476,9 @@ github.com/prometheus/common v0.25.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMD github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4= +github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -1350,8 +1488,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/prometheus/prometheus v2.5.0+incompatible/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= @@ -1371,6 +1509,12 @@ github.com/quasilyte/go-ruleguard/rules v0.0.0-20210203162857-b223e0831f88/go.mo github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/regex/syntax v0.0.0-20200805063351-8f842688393c/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= +github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= +github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70= +github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -1410,9 +1554,29 @@ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil/v3 v3.21.4/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/simpleledgerinc/goslp v0.0.0-20210310142058-5920ead5c7a0/go.mod h1:H95uvOA9ea+6KkX647VlNLbV+UFBV/i+vPktgnXiQJw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -1429,7 +1593,9 @@ github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5/go.mod h1:T7TcVDs9 github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1465,6 +1631,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1477,6 +1644,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -1486,6 +1655,7 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= @@ -1530,7 +1700,12 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= github.com/valyala/quicktemplate v1.6.3/go.mod h1:fwPzK2fHuYEODzJ9pkw0ipCPNHZ2tD5KW4lOuSdPKzY= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= +github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1551,6 +1726,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de 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/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 h1:1qKTeMTSIEvRIjvVYzgcRp0xVp0eoiRTTiHSncb5gD8= github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1/go.mod h1:bslhAiUxakrA6z6CHmVyvkfpnxx18RJBwVyx2TluJWw= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1587,6 +1763,7 @@ go.etcd.io/etcd/v3 v3.5.0-alpha.0/go.mod h1:JZ79d3LV6NUfPjUxXrpiFAYcjhT+06qqw+i2 go.etcd.io/etcd/v3 v3.5.4/go.mod h1:c6jK4IfuWwJU26FD9SeI4cAtvlfu9Iacaxu0vRses1k= go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1623,30 +1800,46 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= +go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= +go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180718160520-a2144134853f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180808211826-de0752318171/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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= @@ -1659,15 +1852,20 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1681,10 +1879,11 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 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-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4= +golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= 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/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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= @@ -1709,6 +1908,10 @@ 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.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1716,6 +1919,8 @@ golang.org/x/net v0.0.0-20180808004115-f9ce57c11b24/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1725,6 +1930,7 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1780,10 +1986,18 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su 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-20220421235706-1d1ef9303861/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1807,6 +2021,7 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ 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/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 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= @@ -1819,6 +2034,8 @@ 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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1828,6 +2045,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1836,6 +2054,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1930,16 +2149,27 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc 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-20220422013727-9388b58f7150/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.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.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc h1:bH6xUXay0AIFMElXG2rQ4uiE+7ncwtiOdPfYK1NK2XA= +golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= 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= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1952,6 +2182,10 @@ 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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1962,13 +2196,14 @@ golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2076,11 +2311,18 @@ 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.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= 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= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -2140,7 +2382,11 @@ google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180808183934-383e8b2c3b9e/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -2240,6 +2486,7 @@ google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -2313,6 +2560,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= @@ -2344,6 +2592,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2354,8 +2603,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.4/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= -lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= +lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= @@ -2368,4 +2617,5 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/dex/testing/tatanka/harness.sh b/dex/testing/tatanka/harness.sh deleted file mode 100755 index 6a2e5a95c0..0000000000 --- a/dex/testing/tatanka/harness.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash -# Tmux script that sets up a simnet harness. -set -ex -SESSION="tatanka-harness" -ROOT=~/dextest/tatanka -if [ -d "${ROOT}" ]; then - rm -fR "${ROOT}" -fi -mkdir -p "${ROOT}" - -HARNESS_DIR=$( - cd $(dirname "$0") - pwd -) - -CMD_DIR=$(realpath "${HARNESS_DIR}/../../../tatanka/cmd/tatanka") - -# Build script -cat > "${ROOT}/build" < "${HARNESS_DIR}/quit" < "${ROOT}/tatanka.conf" < "${ROOT}/chains.json" < github.com/buck54321/mesh v0.0.0-20260312171757-88530d61c463 require ( decred.org/dcrwallet/v5 v5.0.3 @@ -11,6 +13,7 @@ require ( github.com/bisoncraft/bchwallet v1.0.2 github.com/bisoncraft/go-bip39 v1.0.1 github.com/bisoncraft/go-monero v0.1.1 + github.com/bisoncraft/mesh v0.0.0-00010101000000-000000000000 github.com/bisoncraft/neutrino-bch v1.0.1 github.com/bisoncraft/op-geth v0.0.0-20250729074358-3cfe4f15e91c github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240625142744-cc26860b4026 @@ -58,13 +61,14 @@ require ( github.com/gcash/bchd v0.19.0 github.com/gcash/bchlog v0.0.0-20180913005452-b4f036f92fa6 github.com/gcash/bchutil v0.0.0-20210113190856-6ea28dff4000 - github.com/go-chi/chi/v5 v5.0.1 - github.com/gorilla/websocket v1.5.1 + github.com/go-chi/chi/v5 v5.2.3 + github.com/gorilla/websocket v1.5.3 github.com/haven-protocol-org/monero-go-utils v0.0.0-20211126154105-058b2666f217 github.com/huandu/skiplist v1.2.0 - github.com/jessevdk/go-flags v1.5.0 - github.com/jrick/logrotate v1.0.0 + github.com/jessevdk/go-flags v1.6.1 + github.com/jrick/logrotate v1.1.2 github.com/lib/pq v1.10.4 + github.com/libp2p/go-libp2p v0.44.0 github.com/lightninglabs/neutrino v0.16.1-0.20240814152458-81d6cd2d2da5 github.com/ltcsuite/ltcd v0.23.6-0.20240131072528-64dfa402637a github.com/ltcsuite/ltcd/chaincfg/chainhash v1.0.2 @@ -74,15 +78,15 @@ require ( github.com/yuin/goldmark v1.7.13 go.etcd.io/bbolt v1.3.12 golang.org/x/crypto v0.47.0 - golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa + golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 golang.org/x/sync v0.19.0 golang.org/x/term v0.39.0 golang.org/x/text v0.33.0 - golang.org/x/time v0.9.0 + golang.org/x/time v0.12.0 google.golang.org/protobuf v1.36.10 gopkg.in/ini.v1 v1.67.0 gopkg.in/square/go-jose.v2 v2.6.0 - lukechampine.com/blake3 v1.3.0 + lukechampine.com/blake3 v1.4.1 ) require ( @@ -90,11 +94,14 @@ require ( github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect github.com/charmbracelet/x/ansi v0.10.1 // indirect github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/decred/dcrd/blockchain/stake/v3 v3.0.0 // indirect github.com/decred/dcrd/database/v2 v2.0.2 // indirect github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect @@ -107,6 +114,8 @@ require ( github.com/emicklei/dot v1.6.2 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect + github.com/flynn/noise v1.1.0 // indirect + github.com/francoispqt/gojay v1.2.13 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -114,17 +123,82 @@ require ( github.com/google/trillian v1.4.1 // indirect github.com/gorilla/schema v1.1.0 // indirect github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/ipfs/go-cid v0.5.0 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect + github.com/koron/go-ssdp v0.0.6 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-flow-metrics v0.2.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect + github.com/libp2p/go-msgio v0.3.0 // indirect + github.com/libp2p/go-netroute v0.3.0 // indirect + github.com/libp2p/go-reuseport v0.4.0 // indirect + github.com/libp2p/go-yamux/v5 v5.0.1 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/marcopeereboom/sbox v1.1.0 // indirect + github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-localereader v0.0.1 // indirect + github.com/miekg/dns v1.1.66 // indirect + github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect + github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect + github.com/mr-tron/base58 v1.2.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.16.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.16.0 // indirect + github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect + github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.1 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-multistream v0.6.1 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect + github.com/pion/datachannel v1.5.10 // indirect + github.com/pion/dtls/v2 v2.2.12 // indirect + github.com/pion/dtls/v3 v3.0.6 // indirect + github.com/pion/ice/v4 v4.0.10 // indirect + github.com/pion/interceptor v0.1.40 // indirect + github.com/pion/logging v0.2.3 // indirect + github.com/pion/mdns/v2 v2.0.7 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/rtcp v1.2.15 // indirect + github.com/pion/rtp v1.8.19 // indirect + github.com/pion/sctp v1.8.39 // indirect + github.com/pion/sdp/v3 v3.0.13 // indirect + github.com/pion/srtp/v3 v3.0.6 // indirect + github.com/pion/stun v0.6.1 // indirect + github.com/pion/stun/v3 v3.0.0 // indirect + github.com/pion/transport/v2 v2.2.10 // indirect + github.com/pion/transport/v3 v3.0.7 // indirect + github.com/pion/turn/v4 v4.0.2 // indirect + github.com/pion/webrtc/v4 v4.1.2 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.64.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.55.0 // indirect + github.com/quic-go/webtransport-go v0.9.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/wlynxg/anet v0.0.5 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.37.0 // indirect go.opentelemetry.io/otel/metric v1.37.0 // indirect go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.uber.org/dig v1.19.0 // indirect + go.uber.org/fx v1.24.0 // indirect + go.uber.org/mock v0.5.2 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/mod v0.31.0 // indirect + golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc // indirect + golang.org/x/tools v0.40.0 // indirect ) replace github.com/btcsuite/btcd/btcec/v2 v2.3.4 => github.com/martonp/btcd/btcec/v2 v2.0.0-20250528172049-6b252bb1b6a1 @@ -175,7 +249,7 @@ require ( github.com/jrick/wsrpc/v2 v2.3.8 // indirect github.com/kkdai/bstream v1.0.0 // indirect github.com/klauspost/compress v1.18.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect github.com/lightninglabs/neutrino/cache v1.1.2 // indirect github.com/lightningnetwork/lnd/clock v1.0.1 // indirect @@ -191,7 +265,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/minio/sha256-simd v1.0.0 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/naoina/go-stringutil v0.1.0 // indirect github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 // indirect diff --git a/go.sum b/go.sum index 04db942050..34f7dbb915 100644 --- a/go.sum +++ b/go.sum @@ -4,7 +4,9 @@ bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxo bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= @@ -87,11 +89,16 @@ decred.org/dcrwallet v1.7.0 h1:U/ew00YBdUlx3rJAynt2OdKDgGzBKK4O89FijBq8iVg= decred.org/dcrwallet v1.7.0/go.mod h1:hNOGyvH53gWdgFB601/ubGRzCPfPtWnEVAi9Grs90y4= decred.org/dcrwallet/v5 v5.0.3 h1:nz3oIq06RnIQ8uNKCG8sBa5vsq2WLd5+a7QlErYMxu8= decred.org/dcrwallet/v5 v5.0.3/go.mod h1:yH+3MRx46MzoPNgEARW5K9a3o/uIa0qtlc6az2oZVqY= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93 h1:V2IC9t0Zj9Ur6qDbfhUuzVmIvXKFyxZXRJyigUvovs4= fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU= @@ -191,6 +198,8 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/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= @@ -216,6 +225,7 @@ github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAw github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= 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.22.0-beta.0.20220207191057-4dc4ff7963b4/go.mod h1:7alexyj/lHlOtr2PJK7L/+HDJZpcGDn/pAU98r7DY08= @@ -262,7 +272,10 @@ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/buck54321/mesh v0.0.0-20260312171757-88530d61c463 h1:cTzVglnn0aByPVhCWMy+tZuMEX3tklEM3xhiFebanco= +github.com/buck54321/mesh v0.0.0-20260312171757-88530d61c463/go.mod h1:LjHlWgi2R4nusjXEwZ9AikmfIN39tHZ08nkV7fiNRLI= github.com/bufbuild/buf v0.37.0/go.mod h1:lQ1m2HkIaGOFba6w/aC3KYBHhKEOESP3gaAEpS3dAFM= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw= github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -339,6 +352,7 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= @@ -368,6 +382,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/dchest/blake256 v1.0.0/go.mod h1:xXNWCE1jsAP8DAjP+rKw2MbeqLczjI3TRx2VK+9OEYY= github.com/dchest/blake2b v1.0.0 h1:KK9LimVmE0MjRl9095XJmKqZ+iLxWATvlcpVFRtaw6s= github.com/dchest/blake2b v1.0.0/go.mod h1:U034kXgbJpCle2wSk5ybGIVhOSHCVLMDqOzcPEA0F7s= @@ -642,11 +658,15 @@ github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4 github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= +github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -683,10 +703,12 @@ github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49P github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-chi/chi/v5 v5.0.1 h1:ALxjCrTf1aflOlkhMnCUP86MubbWFrzB3gkRPReLpTo= -github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= +github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-critic/go-critic v0.5.6/go.mod h1:cVjj0DfqewQVIlIAGexPCaGaZDAqGE29PYDDADIVNEo= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 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= @@ -761,6 +783,7 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er 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= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -838,6 +861,7 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.8/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/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -884,7 +908,9 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= 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= @@ -912,8 +938,8 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +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/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw= @@ -927,12 +953,14 @@ github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -1007,16 +1035,21 @@ github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSH github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 h1:vilfsDSy7TDxedi9gyBkMvAirat/oRcL0lFdJBf6tdM= github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= +github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= +github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v0.0.0-20181221193153-c0795c8afcf4/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.1-0.20200711081900-c17162fe8fd7/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= +github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/jgautheron/goconst v1.4.0/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= github.com/jhump/protoreflect v1.8.1/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= @@ -1039,8 +1072,9 @@ github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgb github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/bitset v1.0.0 h1:Ws0PXV3PwXqWK2n7Vz6idCdrV/9OrBXgHEJi27ZB9Dw= github.com/jrick/bitset v1.0.0/go.mod h1:ZOYB5Uvkla7wIEY4FEssPVi3IQXa02arznRaYaAEPe4= -github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/jrick/logrotate v1.1.2 h1:6ePk462NCX7TfKtNp5JJ7MbA2YIslkpfgP03TlTYMN0= +github.com/jrick/logrotate v1.1.2/go.mod h1:f9tdWggSVK3iqavGpyvegq5IhNois7KXmasU6/N96OQ= github.com/jrick/wsrpc/v2 v2.3.2/go.mod h1:XPYs8BnRWl99lCvXRM5SLpZmTPqWpSOPkDIqYTwDPfU= github.com/jrick/wsrpc/v2 v2.3.4/go.mod h1:XPYs8BnRWl99lCvXRM5SLpZmTPqWpSOPkDIqYTwDPfU= github.com/jrick/wsrpc/v2 v2.3.8 h1:9vfM8o9g00HXQb/3D6+Y9Cy1uybjD7K1272vtdXXBps= @@ -1077,14 +1111,15 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= -github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 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.2/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/koron/go-ssdp v0.0.6 h1:Jb0h04599eq/CY7rB5YEqPS83HmRfHP2azkxMN2rFtU= +github.com/koron/go-ssdp v0.0.6/go.mod h1:0R9LfRJGek1zWTjN3JUNlm5INCDYGpRDfAptnct63fI= 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= @@ -1094,6 +1129,7 @@ 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= github.com/kr/pty v1.1.2/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -1114,6 +1150,24 @@ github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw= +github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc= +github.com/libp2p/go-libp2p v0.44.0 h1:5Gtt8OrF8yiXmH+Mx4+/iBeFRMK1TY3a8OrEBDEqAvs= +github.com/libp2p/go-libp2p v0.44.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc= +github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= +github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= +github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= +github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= +github.com/libp2p/go-netroute v0.3.0 h1:nqPCXHmeNmgTJnktosJ/sIef9hvwYCrsLxXmfNks/oc= +github.com/libp2p/go-netroute v0.3.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= +github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= +github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= +github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfYRSg= +github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= github.com/lightninglabs/neutrino v0.16.1-0.20240814152458-81d6cd2d2da5 h1:0jNLTYmzZDAJSGTOGqayvRlYpOM/gWtXIJZvtU/8zp0= @@ -1154,13 +1208,19 @@ github.com/ltcsuite/ltcd/ltcutil/psbt v1.1.1-0.20240131072528-64dfa402637a h1:vG github.com/ltcsuite/ltcd/ltcutil/psbt v1.1.1-0.20240131072528-64dfa402637a/go.mod h1:zcnG/wmZrTcQGTJIBgrG4Fu2ljZcV0DvZ8tzfCvU91o= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= github.com/marcopeereboom/sbox v1.1.0 h1:IiVHCi5f+nGRiMX551wnDk5ce+IEd3dWVH7ycf2uU2M= github.com/marcopeereboom/sbox v1.1.0/go.mod h1:u2fh4EbQDXQXXzGypWkf2nMn2TnsqA23t224mii7oog= +github.com/marcopolo/simnet v0.0.1 h1:rSMslhPz6q9IvJeFWDoMGxMIrlsbXau3NkuIXHGJxfg= +github.com/marcopolo/simnet v0.0.1/go.mod h1:WDaQkgLAjqDUEBAOXz22+1j6wXKfGlC5sD5XWt3ddOs= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/martonp/btcd/btcec/v2 v2.0.0-20250528172049-6b252bb1b6a1 h1:B5wUyPCVOFp6GIQ9IyUYAWPC34t9ej8q8Y739IRhpSo= github.com/martonp/btcd/btcec/v2 v2.0.0-20250528172049-6b252bb1b6a1/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= @@ -1198,18 +1258,27 @@ github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOq github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= github.com/mgechev/revive v1.0.6/go.mod h1:Lj5gIVxjBlH8REa3icEOkdfchwYc291nShzZ4QYWyMo= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= +github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -1236,12 +1305,39 @@ github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8q github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mozilla/tls-observatory v0.0.0-20210209181001-cf43108d6880/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= +github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= +github.com/multiformats/go-multiaddr v0.16.0 h1:oGWEVKioVQcdIOBlYM8BH1rZDWOGJSqr9/BKl6zQ4qc= +github.com/multiformats/go-multiaddr v0.16.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= +github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= +github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= +github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.9.1 h1:x/Fuxr7ZuR4jJV4Os5g444F7xC4XmyUaT/FWtE+9Zjo= +github.com/multiformats/go-multicodec v0.9.1/go.mod h1:LLWNMtyV5ithSBUo3vFIMaeDy+h3EbkMTek1m+Fybbo= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-multistream v0.6.1 h1:4aoX5v6T+yWmc2raBHsTvzmFhOI8WVOer28DeBBEYdQ= +github.com/multiformats/go-multistream v0.6.1/go.mod h1:ksQf6kqHAb6zIsyw7Zm+gAuVo57Qbq84E27YlYqavqw= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= @@ -1260,6 +1356,8 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nightlyone/lockfile v1.0.0/go.mod h1:rywoIealpdNse2r832aiD9jRk8ErCatROs6LzC841CI= github.com/nishanths/exhaustive v0.1.0/go.mod h1:S1j9110vxV1ECdCudXRkeMnFQ/DQk9ajLT0Uf2MYZQQ= @@ -1301,6 +1399,7 @@ github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= @@ -1311,6 +1410,8 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9 github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -1323,16 +1424,50 @@ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssy github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= +github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= +github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= +github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= +github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= +github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4= +github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= +github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4= +github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= +github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= +github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= +github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= +github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= +github.com/pion/rtp v1.8.19 h1:jhdO/3XhL/aKm/wARFVmvTfq0lC/CvN1xwYKmduly3c= +github.com/pion/rtp v1.8.19/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= +github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE= +github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= +github.com/pion/sdp/v3 v3.0.13 h1:uN3SS2b+QDZnWXgdr69SM8KB4EbcnPnPf2Laxhty/l4= +github.com/pion/sdp/v3 v3.0.13/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= +github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4= +github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY= +github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= +github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= -github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= +github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= +github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= -github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= -github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= +github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= +github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= +github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= +github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= +github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= +github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= +github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54= +github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1349,6 +1484,7 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/polyfloyd/go-errorlint v0.0.0-20210418123303-74da32850375/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= github.com/polyfloyd/go-errorlint v0.0.0-20210510181950-ab96adb96fea/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -1360,16 +1496,17 @@ github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3e github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1382,8 +1519,9 @@ github.com/prometheus/common v0.25.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMD github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4= +github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -1393,8 +1531,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/prometheus/prometheus v2.5.0+incompatible/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= @@ -1414,6 +1552,12 @@ github.com/quasilyte/go-ruleguard/rules v0.0.0-20210203162857-b223e0831f88/go.mo github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/regex/syntax v0.0.0-20200805063351-8f842688393c/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= +github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= +github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70= +github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -1453,9 +1597,29 @@ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil/v3 v3.21.4/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/simpleledgerinc/goslp v0.0.0-20210310142058-5920ead5c7a0/go.mod h1:H95uvOA9ea+6KkX647VlNLbV+UFBV/i+vPktgnXiQJw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -1474,7 +1638,9 @@ github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5/go.mod h1:T7TcVDs9 github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1510,6 +1676,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1522,6 +1689,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -1531,6 +1700,7 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= @@ -1577,7 +1747,12 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= github.com/valyala/quicktemplate v1.6.3/go.mod h1:fwPzK2fHuYEODzJ9pkw0ipCPNHZ2tD5KW4lOuSdPKzY= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= +github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1600,6 +1775,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de 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/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 h1:1qKTeMTSIEvRIjvVYzgcRp0xVp0eoiRTTiHSncb5gD8= @@ -1638,6 +1814,7 @@ go.etcd.io/etcd/v3 v3.5.0-alpha.0/go.mod h1:JZ79d3LV6NUfPjUxXrpiFAYcjhT+06qqw+i2 go.etcd.io/etcd/v3 v3.5.4/go.mod h1:c6jK4IfuWwJU26FD9SeI4cAtvlfu9Iacaxu0vRses1k= go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1674,30 +1851,46 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= +go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= +go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180718160520-a2144134853f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180808211826-de0752318171/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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= @@ -1710,15 +1903,20 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1732,10 +1930,11 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 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-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4= +golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= 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/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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= @@ -1760,6 +1959,10 @@ 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.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1767,6 +1970,8 @@ golang.org/x/net v0.0.0-20180808004115-f9ce57c11b24/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1776,6 +1981,7 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1831,10 +2037,18 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su 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-20220421235706-1d1ef9303861/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1858,6 +2072,7 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ 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/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 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= @@ -1870,6 +2085,8 @@ 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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1879,6 +2096,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1887,6 +2105,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1983,18 +2202,29 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc 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-20220422013727-9388b58f7150/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-20220722155257-8c9f86f7a55f/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-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc h1:bH6xUXay0AIFMElXG2rQ4uiE+7ncwtiOdPfYK1NK2XA= +golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= 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= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2007,6 +2237,10 @@ 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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2017,13 +2251,14 @@ golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2131,11 +2366,18 @@ 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.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= 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= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -2195,7 +2437,11 @@ google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180808183934-383e8b2c3b9e/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -2295,6 +2541,7 @@ google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -2368,6 +2615,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= @@ -2401,6 +2649,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2411,8 +2660,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.4/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= -lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= +lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= @@ -2425,4 +2674,5 @@ rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/tatanka/chain/chain.go b/tatanka/chain/chain.go deleted file mode 100644 index d6c8b0453d..0000000000 --- a/tatanka/chain/chain.go +++ /dev/null @@ -1,67 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package chain - -import ( - "context" - "encoding/json" - "fmt" - "sync" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/tatanka/tanka" -) - -/* - The chain package describes the interfaces that must be implemented for - Tatatanka node blockchain backends. -*/ - -type BadQueryError error - -type ChainConfig struct { - ConfigPath string -} - -// type Query json.RawMessage -type Result json.RawMessage - -// Chain is an interface that must be implemented by every blockchain backend. -type Chain interface { - Connect(context.Context) (*sync.WaitGroup, error) - // Query may be needed if clients are the auditors, since some clients might - // not have e.g. txindex enabled for utxo-based assets. - // Query(context.Context, Query) (Result, error) - Connected() bool - CheckBond(*tanka.Bond) error - // AuditHTLC will be needed if Tatanka nodes are the auditors. - // AuditHTLC(*tanka.HTLCAudit) (bool, error) -} - -// FeeRater is an optional interface that should be implemented by backends for -// which the network transaction fee rates are variable. The Tatanka Mesh -// provides a oracle service for these chains. -type FeeRater interface { - FeeChannel() <-chan uint64 -} - -// ChainConstructor is a constructor for a Chain. -type ChainConstructor func(config json.RawMessage, log dex.Logger, net dex.Network) (Chain, error) - -var chains = make(map[uint32]ChainConstructor) - -// RegisterChainConstructor is called by chain backends to register their -// ChainConstructors. -func RegisterChainConstructor(chainID uint32, c ChainConstructor) { - chains[chainID] = c -} - -// New is used by the caller to construct a new Chain. -func New(chainID uint32, cfg json.RawMessage, log dex.Logger, net dex.Network) (Chain, error) { - c, found := chains[chainID] - if !found { - return nil, fmt.Errorf("chain %d not known", chainID) - } - return c(cfg, log, net) -} diff --git a/tatanka/chain/utxo/btc.go b/tatanka/chain/utxo/btc.go deleted file mode 100644 index a276f4017f..0000000000 --- a/tatanka/chain/utxo/btc.go +++ /dev/null @@ -1,335 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package utxo - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "math" - "strings" - "sync" - "sync/atomic" - "time" - - "decred.org/dcrdex/dex" - dexbtc "decred.org/dcrdex/dex/networks/btc" - "decred.org/dcrdex/tatanka/chain" - "decred.org/dcrdex/tatanka/tanka" - "github.com/btcsuite/btcd/btcjson" - "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/chaincfg/chainhash" - chainjson "github.com/decred/dcrd/rpc/jsonrpc/types/v4" - "github.com/decred/dcrd/rpcclient/v8" -) - -const ( - BitcoinID = 0 - feeMonitorTick = time.Second * 10 -) - -func init() { - chain.RegisterChainConstructor(0, NewBitcoin) -} - -type BitcoinConfigFile struct { - dexbtc.RPCConfig - NodeRelay string `json:"nodeRelay"` -} - -type bitcoinChain struct { - cfg *BitcoinConfigFile - net dex.Network - log dex.Logger - fees chan uint64 - name string - - cl *rpcclient.Client - connected atomic.Bool -} - -func NewBitcoin(rawConfig json.RawMessage, log dex.Logger, net dex.Network) (chain.Chain, error) { - var cfg BitcoinConfigFile - if err := json.Unmarshal(rawConfig, &cfg); err != nil { - return nil, fmt.Errorf("error parsing configuration: %w", err) - } - if cfg.NodeRelay != "" { - cfg.RPCBind = cfg.NodeRelay - } - - if err := dexbtc.CheckRPCConfig(&cfg.RPCConfig, "Bitcoin", net, dexbtc.RPCPorts); err != nil { - return nil, fmt.Errorf("error validating RPC configuration: %v", err) - } - - return &bitcoinChain{ - cfg: &cfg, - net: net, - log: log, - name: "Bitcoin", - fees: make(chan uint64, 1), - }, nil -} - -func (c *bitcoinChain) Connect(ctx context.Context) (_ *sync.WaitGroup, err error) { - cfg := c.cfg - host := cfg.RPCBind - if cfg.NodeRelay != "" { - host = cfg.NodeRelay - } - c.cl, err = connectLocalHTTP(host, cfg.RPCUser, cfg.RPCPass) - if err != nil { - return nil, fmt.Errorf("error connecting RPC client: %w", err) - } - - if err = c.initialize(ctx); err != nil { - return nil, err - } - - c.connected.Store(true) - defer c.connected.Store(false) - - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - c.monitorFees(ctx) - }() - - return &wg, nil -} - -func (c *bitcoinChain) initialize(ctx context.Context) error { - // TODO: Check min version - - tip, err := c.getBestBlockHash(ctx) - if err != nil { - return fmt.Errorf("error getting best block from rpc: %w", err) - } - if tip == nil { - return fmt.Errorf("nil best block hash?") - } - - txindex, err := c.checkTxIndex(ctx) - if err != nil { - c.log.Warnf(`Please ensure txindex is enabled in the node config and you might need to re-index if txindex was not previously enabled for %s`, c.name) - return fmt.Errorf("error checking txindex for %s: %w", c.name, err) - } - if !txindex { - return fmt.Errorf("%s transaction index is not enabled. Please enable txindex in the node config and you might need to re-index when you enable txindex", c.name) - } - - return nil -} - -// func (c *bitcoinChain) Query(ctx context.Context, rawQuery chain.Query) (chain.Result, error) { -// var q query -// if err := json.Unmarshal(rawQuery, &q); err != nil { -// return nil, chain.BadQueryError(fmt.Errorf("error parsing raw query: %w", err)) -// } - -// if q.Method == "" { -// return nil, chain.BadQueryError(errors.New("invalid query parameters. no method")) -// } - -// res, err := c.cl.RawRequest(ctx, q.Method, q.Args) -// if err != nil { -// // Could potentially try to parse certain errors here - -// return nil, fmt.Errorf("error performing query: %w", err) -// } - -// return chain.Result(res), nil -// } - -func (c *bitcoinChain) Connected() bool { - return c.connected.Load() -} - -func (c *bitcoinChain) FeeChannel() <-chan uint64 { - return c.fees -} - -func (c *bitcoinChain) monitorFees(ctx context.Context) { - tick := time.NewTicker(feeMonitorTick) - var tip *chainhash.Hash - for { - select { - case <-tick.C: - case <-ctx.Done(): - return - } - - newTip, err := c.getBestBlockHash(ctx) - if err != nil { - c.connected.Store(false) - c.log.Errorf("Decred is not connected: %w", err) - continue - } - if newTip == nil { // sanity check - c.log.Error("nil tip hash?") - continue - } - if tip != nil && *tip == *newTip { - continue - } - tip = newTip - c.connected.Store(true) - // estimatesmartfee 1 returns extremely high rates on DCR. - estimateFeeResult, err := c.cl.EstimateSmartFee(ctx, 2, chainjson.EstimateSmartFeeConservative) - if err != nil { - c.log.Errorf("estimatesmartfee error: %w", err) - continue - } - atomsPerKB, err := btcutil.NewAmount(estimateFeeResult.FeeRate) - if err != nil { - c.log.Errorf("NewAmount error: %w", err) - continue - } - atomsPerB := uint64(math.Round(float64(atomsPerKB) / 1000)) - if atomsPerB == 0 { - atomsPerB = 1 - } - select { - case c.fees <- atomsPerB: - case <-time.After(time.Second * 5): - c.log.Errorf("fee channel is blocking") - } - } -} - -func (c *bitcoinChain) callHashGetter(ctx context.Context, method string, args []any) (*chainhash.Hash, error) { - var txid string - err := c.call(ctx, method, args, &txid) - if err != nil { - return nil, err - } - return chainhash.NewHashFromStr(txid) -} - -// GetBestBlockHash returns the hash of the best block in the longest block -// chain. -func (c *bitcoinChain) getBestBlockHash(ctx context.Context) (*chainhash.Hash, error) { - return c.callHashGetter(ctx, "getbestblockhash", nil) -} - -// checkTxIndex checks if bitcoind transaction index is enabled. -func (c *bitcoinChain) checkTxIndex(ctx context.Context) (bool, error) { - var res struct { - TxIndex *struct{} `json:"txindex"` - } - err := c.call(ctx, "getindexinfo", []any{"txindex"}, &res) - if err == nil { - // Return early if there is no error. bitcoind returns an empty json - // object if txindex is not enabled. It is safe to conclude txindex is - // enabled if res.Txindex is not nil. - return res.TxIndex != nil, nil - } - - if !isMethodNotFoundErr(err) { - return false, err - } - - // Using block at index 5 to retrieve a coinbase transaction and ensure - // txindex is enabled for pre 0.21 versions of bitcoind. - const blockIndex = 5 - blockHash, err := c.getBlockHash(ctx, blockIndex) - if err != nil { - return false, err - } - - blockInfo, err := c.getBlockVerbose(ctx, blockHash) - if err != nil { - return false, err - } - - if len(blockInfo.Tx) == 0 { - return false, fmt.Errorf("block %d does not have a coinbase transaction", blockIndex) - } - - txHash, err := chainhash.NewHashFromStr(blockInfo.Tx[0]) - if err != nil { - return false, err - } - - // Retrieve coinbase transaction information. - txBytes, err := c.getRawTransaction(ctx, txHash) - if err != nil { - return false, err - } - - return len(txBytes) != 0, nil -} - -func (c *bitcoinChain) getBlockHash(ctx context.Context, index int64) (*chainhash.Hash, error) { - var blockHashStr string - if err := c.call(ctx, "getblockhash", []any{index}, &blockHashStr); err != nil { - return nil, err - } - return chainhash.NewHashFromStr(blockHashStr) -} - -type getBlockVerboseResult struct { - Hash string `json:"hash"` - Confirmations int64 `json:"confirmations"` - Height int64 `json:"height"` - Tx []string `json:"tx,omitempty"` - PreviousHash string `json:"previousblockhash"` -} - -func (c *bitcoinChain) getBlockVerbose(ctx context.Context, blockHash *chainhash.Hash) (*getBlockVerboseResult, error) { - var res getBlockVerboseResult - return &res, c.call(ctx, "getblock", []any{blockHash.String(), []any{1}}, res) -} - -func (c *bitcoinChain) getRawTransaction(ctx context.Context, txHash *chainhash.Hash) ([]byte, error) { - var txB dex.Bytes - return txB, c.call(ctx, "getrawtransaction", []any{txHash.String(), false}, &txB) -} - -func (c *bitcoinChain) call(ctx context.Context, method string, args []any, thing any) error { - params := make([]json.RawMessage, 0, len(args)) - for i := range args { - p, err := json.Marshal(args[i]) - if err != nil { - return err - } - params = append(params, p) - } - b, err := c.cl.RawRequest(ctx, method, params) - if err != nil { - return fmt.Errorf("rawrequest error: %w", err) - } - - if thing != nil { - return json.Unmarshal(b, thing) - } - return nil -} - -func (c *bitcoinChain) CheckBond(b *tanka.Bond) error { - - // TODO: Validate bond - - return nil -} - -func (c *bitcoinChain) AuditHTLC(*tanka.HTLCAudit) (bool, error) { - - // TODO: Perform the audit - - return true, nil -} - -// isMethodNotFoundErr will return true if the error indicates that the RPC -// method was not found by the RPC server. The error must be dcrjson.RPCError -// with a numeric code equal to btcjson.ErrRPCMethodNotFound.Code or a message -// containing "method not found". -func isMethodNotFoundErr(err error) bool { - var errRPCMethodNotFound = int(btcjson.ErrRPCMethodNotFound.Code) - var rpcErr *btcjson.RPCError - return errors.As(err, &rpcErr) && - (int(rpcErr.Code) == errRPCMethodNotFound || - strings.Contains(strings.ToLower(rpcErr.Message), "method not found")) -} diff --git a/tatanka/chain/utxo/btc_live_test.go b/tatanka/chain/utxo/btc_live_test.go deleted file mode 100644 index 508dea5a50..0000000000 --- a/tatanka/chain/utxo/btc_live_test.go +++ /dev/null @@ -1,115 +0,0 @@ -//go:build harness - -package utxo - -import ( - "context" - "encoding/json" - "fmt" - "os/user" - "path/filepath" - "testing" - "time" - - "decred.org/dcrdex/dex" - dexbtc "decred.org/dcrdex/dex/networks/btc" - "decred.org/dcrdex/tatanka/chain" -) - -func TestSimnetBTC(t *testing.T) { - simnetAlphaHarnessConfig := &BitcoinConfigFile{ - RPCConfig: dexbtc.RPCConfig{ - RPCUser: "user", - RPCPass: "pass", - RPCBind: "127.0.0.1", - RPCPort: 20556, - }, - } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - rawCfg, _ := json.Marshal(simnetAlphaHarnessConfig) - c := prepareSimnetChain(t, ctx, NewBitcoin, rawCfg) - - q := &query{ - Method: "getchaintxstats", - Args: []json.RawMessage{[]byte("5")}, - } - rawQuery, _ := json.Marshal(q) - rawRes, err := c.Query(ctx, chain.Query(rawQuery)) - if err != nil { - t.Fatalf("Query error: %v", err) - } - - var res struct { - Blocks uint `json:"window_block_count"` - } - if err := json.Unmarshal(rawRes, &res); err != nil { - t.Fatalf("Query unmarshal error: %v", err) - } - - if res.Blocks != 5 { - t.Fatalf("Unexpected query results") - } -} -func TestSimnetDCR(t *testing.T) { - usr, _ := user.Current() - dextestDir := filepath.Join(usr.HomeDir, "dextest") - rpcPath := filepath.Join(dextestDir, "dcr", "alpha", "rpc.cert") - simnetAlphaHarnessConfig := &DecredConfigFile{ - RPCUser: "user", - RPCPass: "pass", - RPCListen: "127.0.0.1:19561", - RPCCert: rpcPath, - } - rawCfg, _ := json.Marshal(simnetAlphaHarnessConfig) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - c := prepareSimnetChain(t, ctx, NewDecred, rawCfg) - - q := &query{ - Method: "getvoteinfo", - Args: []json.RawMessage{[]byte("4")}, - } - rawQuery, _ := json.Marshal(q) - rawRes, err := c.Query(ctx, chain.Query(rawQuery)) - if err != nil { - t.Fatalf("Query error: %v", err) - } - - var res struct { - Agendas []struct { - ID string `json:"id"` - } `json:"agendas"` - } - if err := json.Unmarshal(rawRes, &res); err != nil { - t.Fatalf("Query unmarshal error: %v", err) - } - - if len(res.Agendas) != 1 || res.Agendas[0].ID != "maxblocksize" { - t.Fatalf("Unexpected query results") - } -} - -func prepareSimnetChain(t *testing.T, ctx context.Context, newChain chain.ChainConstructor, rawCfg json.RawMessage) chain.Chain { - feeMonitorTick = time.Second - - c, err := newChain(rawCfg, dex.StdOutLogger("T", dex.LevelTrace), dex.Simnet) - if err != nil { - t.Fatalf("NewBTC error: %v", err) - } - - cm := dex.NewConnectionMaster(c) - if err := cm.ConnectOnce(ctx); err != nil { - t.Fatalf("Connect error: %v", err) - } - - fmt.Println("Waiting for fee update") - select { - case fees := <-c.FeeChannel(): - fmt.Println("Fees received over fee channel =", fees) - case <-time.After(time.Second * 30): - t.Fatalf("No fee update seen. Is the miner running?") - } - - return c -} diff --git a/tatanka/chain/utxo/dcr.go b/tatanka/chain/utxo/dcr.go deleted file mode 100644 index 2ef2520d01..0000000000 --- a/tatanka/chain/utxo/dcr.go +++ /dev/null @@ -1,299 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package utxo - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "math" - "os" - "sync" - "sync/atomic" - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/tatanka/chain" - "decred.org/dcrdex/tatanka/tanka" - "github.com/decred/dcrd/chaincfg/chainhash" - "github.com/decred/dcrd/dcrutil/v4" - chainjson "github.com/decred/dcrd/rpc/jsonrpc/types/v4" - "github.com/decred/dcrd/rpcclient/v8" - "github.com/decred/dcrd/wire" -) - -const ( - ChainID = 42 -) - -var ( - compatibleNodeRPCVersions = []dex.Semver{ - {Major: 8, Minor: 0, Patch: 0}, // 1.8-pre, just dropped unused ticket RPCs - {Major: 7, Minor: 0, Patch: 0}, // 1.7 release, new gettxout args - } -) - -func init() { - chain.RegisterChainConstructor(42, NewDecred) -} - -type DecredConfigFile struct { - RPCUser string `json:"rpcuser"` - RPCPass string `json:"rpcpass"` - RPCListen string `json:"rpclisten"` - RPCCert string `json:"rpccert"` - NodeRelay string `json:"nodeRelay"` -} - -type decredChain struct { - cfg *DecredConfigFile - net dex.Network - log dex.Logger - fees chan uint64 - - cl *rpcclient.Client - connected atomic.Bool -} - -func NewDecred(rawConfig json.RawMessage, log dex.Logger, net dex.Network) (chain.Chain, error) { - var cfg DecredConfigFile - if err := json.Unmarshal(rawConfig, &cfg); err != nil { - return nil, fmt.Errorf("error parsing configuration: %w", err) - } - return &decredChain{ - cfg: &cfg, - net: net, - log: log, - fees: make(chan uint64, 1), - }, nil -} - -func (c *decredChain) Connect(ctx context.Context) (_ *sync.WaitGroup, err error) { - cfg := c.cfg - if cfg.NodeRelay == "" { - c.cl, err = connectNodeRPC(cfg.RPCListen, cfg.RPCUser, cfg.RPCPass, cfg.RPCCert) - } else { - c.cl, err = connectLocalHTTP(cfg.NodeRelay, cfg.RPCUser, cfg.RPCPass) - } - if err != nil { - return nil, fmt.Errorf("error connecting RPC client: %w", err) - } - - if err = c.initialize(ctx); err != nil { - return nil, err - } - - c.connected.Store(true) - defer c.connected.Store(false) - - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - c.monitorFees(ctx) - }() - - return &wg, nil -} - -func (c *decredChain) initialize(ctx context.Context) error { - net, err := c.cl.GetCurrentNet(ctx) - if err != nil { - return fmt.Errorf("getcurrentnet failure: %w", err) - } - var wantCurrencyNet wire.CurrencyNet - switch c.net { - case dex.Testnet: - wantCurrencyNet = wire.TestNet3 - case dex.Mainnet: - wantCurrencyNet = wire.MainNet - case dex.Regtest: // dex.Simnet - wantCurrencyNet = wire.SimNet - } - if net != wantCurrencyNet { - return fmt.Errorf("wrong net %v", net.String()) - } - - // Check the required API versions. - versions, err := c.cl.Version(ctx) - if err != nil { - return fmt.Errorf("DCR node version fetch error: %w", err) - } - - ver, exists := versions["dcrdjsonrpcapi"] - if !exists { - return fmt.Errorf("dcrd.Version response missing 'dcrdjsonrpcapi'") - } - nodeSemver := dex.NewSemver(ver.Major, ver.Minor, ver.Patch) - if !dex.SemverCompatibleAny(compatibleNodeRPCVersions, nodeSemver) { - return fmt.Errorf("dcrd has an incompatible JSON-RPC version %s, require one of %s", - nodeSemver, compatibleNodeRPCVersions) - } - - // Verify dcrd has tx index enabled (required for getrawtransaction). - info, err := c.cl.GetInfo(ctx) - if err != nil { - return fmt.Errorf("dcrd getinfo check failed: %w", err) - } - if !info.TxIndex { - return errors.New("dcrd does not have transaction index enabled (specify --txindex)") - } - - // Prime the cache with the best block. - tip, err := c.cl.GetBestBlockHash(ctx) - if err != nil { - return fmt.Errorf("error getting best block from dcrd: %w", err) - } - if tip == nil { - return fmt.Errorf("nil best block hash?") - } - // if bestHash != nil { - // _, err := dcr.getDcrBlock(ctx, bestHash) - // if err != nil { - // return nil, fmt.Errorf("error priming the cache: %w", err) - // } - // } - - // if _, err = c.cl.FeeRate(ctx); err != nil { - // c.log.Warnf("Decred backend started without fee estimation available: %v", err) - // } - return nil -} - -// type query struct { -// Method string `json:"method"` -// Args []json.RawMessage `json:"args"` -// } - -// func (c *decredChain) Query(ctx context.Context, rawQuery chain.Query) (chain.Result, error) { -// var q query -// if err := json.Unmarshal(rawQuery, &q); err != nil { -// return nil, chain.BadQueryError(fmt.Errorf("error parsing raw query: %w", err)) -// } - -// if q.Method == "" { -// return nil, chain.BadQueryError(errors.New("invalid query parameters. no method")) -// } - -// res, err := c.cl.RawRequest(ctx, q.Method, q.Args) -// if err != nil { -// // Could potentially try to parse certain errors here - -// return nil, fmt.Errorf("error performing query: %w", err) -// } - -// return chain.Result(res), nil -// } - -func (c *decredChain) Connected() bool { - return c.connected.Load() -} - -func (c *decredChain) FeeChannel() <-chan uint64 { - return c.fees -} - -func (c *decredChain) monitorFees(ctx context.Context) { - tick := time.NewTicker(feeMonitorTick) - var tip *chainhash.Hash - for { - select { - case <-tick.C: - case <-ctx.Done(): - return - } - - newTip, err := c.cl.GetBestBlockHash(ctx) - if err != nil { - c.connected.Store(false) - c.log.Errorf("Decred is not connected: %w", err) - continue - } - if newTip == nil { // sanity check - c.log.Error("nil tip hash?") - continue - } - if tip != nil && *tip == *newTip { - continue - } - tip = newTip - c.connected.Store(true) - // estimatesmartfee 1 returns extremely high rates on DCR. - estimateFeeResult, err := c.cl.EstimateSmartFee(ctx, 2, chainjson.EstimateSmartFeeConservative) - if err != nil { - c.log.Errorf("estimatesmartfee error: %w", err) - continue - } - atomsPerKB, err := dcrutil.NewAmount(estimateFeeResult.FeeRate) - if err != nil { - c.log.Errorf("NewAmount error: %w", err) - continue - } - atomsPerB := uint64(math.Round(float64(atomsPerKB) / 1000)) - if atomsPerB == 0 { - atomsPerB = 1 - } - select { - case c.fees <- atomsPerB: - case <-time.After(time.Second * 5): - c.log.Errorf("fee channel is blocking") - } - } -} - -func (c *decredChain) CheckBond(b *tanka.Bond) error { - - // TODO: Validate bond - - return nil -} - -func (c *decredChain) AuditHTLC(*tanka.HTLCAudit) (bool, error) { - - // TODO: Perform the audit - - return true, nil -} - -// connectNodeRPC attempts to create a new websocket connection to a dcrd node -// with the given credentials and notification handlers. -func connectNodeRPC(host, user, pass, cert string) (*rpcclient.Client, error) { - dcrdCerts, err := os.ReadFile(cert) - if err != nil { - return nil, fmt.Errorf("TLS certificate read error: %w", err) - } - - config := &rpcclient.ConnConfig{ - Host: host, - Endpoint: "ws", // websocket - User: user, - Pass: pass, - Certificates: dcrdCerts, - } - - dcrdClient, err := rpcclient.New(config, nil) - if err != nil { - return nil, fmt.Errorf("failed to start dcrd RPC client: %w", err) - } - - return dcrdClient, nil -} - -func connectLocalHTTP(host, user, pass string) (*rpcclient.Client, error) { - config := &rpcclient.ConnConfig{ - Host: host, - HTTPPostMode: true, - DisableTLS: true, - User: user, - Pass: pass, - } - - dcrdClient, err := rpcclient.New(config, nil) - if err != nil { - return nil, fmt.Errorf("failed to start dcrd RPC client: %w", err) - } - - return dcrdClient, nil -} diff --git a/tatanka/client/conn/conn.go b/tatanka/client/conn/conn.go deleted file mode 100644 index b1a0b8e3b9..0000000000 --- a/tatanka/client/conn/conn.go +++ /dev/null @@ -1,1093 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package conn - -import ( - "context" - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "crypto/sha256" - "encoding/json" - "errors" - "fmt" - "strings" - "sync" - "sync/atomic" - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/dexnet" - "decred.org/dcrdex/dex/msgjson" - "decred.org/dcrdex/tatanka" - "decred.org/dcrdex/tatanka/mj" - "decred.org/dcrdex/tatanka/tanka" - "decred.org/dcrdex/tatanka/tcp" - tcpclient "decred.org/dcrdex/tatanka/tcp/client" - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -const ( - ErrPeerNeedsReconnect = dex.ErrorKind("peer needs reconnect") - ErrTankaError = dex.ErrorKind("tanka error") - ErrBadPeerResponse = dex.ErrorKind("bad peer response") -) - -// NetworkBackend represents a peer's communication protocol. -type NetworkBackend interface { - Send(*msgjson.Message) error - Request(msg *msgjson.Message, respHandler func(*msgjson.Message)) error -} - -// tatankaNode is a Tatanka mesh server node. -type tatankaNode struct { - NetworkBackend - cm *dex.ConnectionMaster - url string - peerID tanka.PeerID - pub *secp256k1.PublicKey - config atomic.Value // *mj.TatankaConfig -} - -func (tt *tatankaNode) String() string { - return fmt.Sprintf("%s @ %s", tt.peerID, tt.url) -} - -// peer is a network peer with which we have established encrypted -// communication. -type peer struct { - id tanka.PeerID - ephemeralSharedKey []byte -} - -const ( - aesKeySize = 32 - gcmNonceSize = 12 -) - -func decryptAES(key []byte, ciphertext []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, fmt.Errorf("failed to create AES cipher: %v", err) - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, fmt.Errorf("failed to create GCM: %v", err) - } - - if len(ciphertext) < gcmNonceSize { - return nil, errors.New("ciphertext too short") - } - nonce, ciphertext := ciphertext[:gcmNonceSize], ciphertext[gcmNonceSize:] - - plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, fmt.Errorf("decryption failed: %v", err) - } - - return plaintext, nil -} - -func decryptTankagramPayload(key []byte, ciphertext []byte) (*msgjson.Message, error) { - plaintext, err := decryptAES(key, ciphertext) - if err != nil { - return nil, fmt.Errorf("failed to decrypt payload: %v", err) - } - - return msgjson.DecodeMessage(plaintext) -} - -func decryptTankagramResult(key []byte, ciphertext []byte) (*mj.TankagramResultPayload, error) { - plaintext, err := decryptAES(key, ciphertext) - if err != nil { - return nil, fmt.Errorf("failed to decrypt payload: %v", err) - } - - var res mj.TankagramResultPayload - if err := json.Unmarshal(plaintext, &res); err != nil { - return nil, fmt.Errorf("failed to unmarshal payload: %v", err) - } - - return &res, nil -} - -func encryptAES(key []byte, plaintext []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, fmt.Errorf("failed to create AES cipher: %v", err) - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, fmt.Errorf("failed to create GCM: %v", err) - } - - nonce := make([]byte, gcmNonceSize) - if _, err := rand.Read(nonce); err != nil { - return nil, fmt.Errorf("failed to generate nonce: %v", err) - } - - ciphertext := gcm.Seal(nonce, nonce, plaintext, nil) - return ciphertext, nil -} - -func encryptTankagramPayload(key []byte, msg *msgjson.Message) ([]byte, error) { - plaintext, err := json.Marshal(msg) - if err != nil { - return nil, fmt.Errorf("failed to marshal payload: %v", err) - } - - return encryptAES(key, plaintext) -} - -func encryptTankagramResult(result any, tankagramError mj.TankagramError, encKey []byte) (dex.Bytes, error) { - var resB []byte - if result != nil { - var err error - resB, err = json.Marshal(result) - if err != nil { - return nil, fmt.Errorf("error marshalling result: %v", err) - } - } - - tankagramResult := &mj.TankagramResultPayload{ - Error: tankagramError, - Payload: resB, - } - - plainText, err := json.Marshal(tankagramResult) - if err != nil { - return nil, fmt.Errorf("error marshalling tankagram result: %v", err) - } - - return encryptAES(encKey, plainText) -} - -// IncomingTankagram will be emitted when we receive a tankagram from a -// connected peer. -type IncomingTankagram struct { - Msg *msgjson.Message - Respond func(any) error - RespondErr func(mj.TankagramError) error -} - -// NewMarketSubscriber will be emitted when a new client subscribes to a market. -type NewMarketSubscriber struct { - MarketName string - PeerID tanka.PeerID -} - -// MessageHandlers are handlers for different types of messages from the Mesh. -type MessageHandlers struct { - HandleTatankaRequest func(route string, payload json.RawMessage, respond func(any, *msgjson.Error)) - HandleTatankaNotification func(route string, payload json.RawMessage) - HandlePeerMessage func(peerID tanka.PeerID, route string, payload json.RawMessage, respond func(any, mj.TankagramError)) -} - -// Config is the configuration for the MeshConn. -type Config struct { - EntryNode *TatankaCredentials - Logger dex.Logger - Handlers *MessageHandlers - PrivateKey *secp256k1.PrivateKey -} - -// TatankaCredentials are the connection credentials for a Tatanka node. -type TatankaCredentials struct { - PeerID tanka.PeerID - // Addr should not include the protocol prefix. - Addr string - Cert []byte - NoTLS bool -} - -func (c *TatankaCredentials) HttpURL() string { - if c.NoTLS { - return fmt.Sprintf("http://%s", c.Addr) - } - return fmt.Sprintf("https://%s", c.Addr) -} - -func (c *TatankaCredentials) WsURL() string { - if c.NoTLS { - return fmt.Sprintf("ws://%s", c.Addr) - } - return fmt.Sprintf("wss://%s", c.Addr) -} - -// MeshConn is a Tatanka Mesh connection manager. MeshConn handles both tatanka -// nodes and regular peers. -type MeshConn struct { - ctx context.Context - log dex.Logger - handlers *MessageHandlers - entryNode *TatankaCredentials - - subscriptionMtx sync.RWMutex - subscriptions map[tanka.Topic]map[tanka.Subject]bool - - maintainingMeshConnections atomic.Bool - - nodesMtx sync.RWMutex - primaryNode *tatankaNode - secondaryNode *tatankaNode - knownNodes []*TatankaCredentials - - peerID tanka.PeerID - priv *secp256k1.PrivateKey - - peersMtx sync.RWMutex - peers map[tanka.PeerID]*peer -} - -// New is the constructor for a new MeshConn. -func New(cfg *Config) *MeshConn { - var peerID tanka.PeerID - copy(peerID[:], cfg.PrivateKey.PubKey().SerializeCompressed()) - c := &MeshConn{ - log: cfg.Logger, - handlers: cfg.Handlers, - priv: cfg.PrivateKey, - entryNode: cfg.EntryNode, - peerID: peerID, - knownNodes: make([]*TatankaCredentials, 0, 4), - peers: make(map[tanka.PeerID]*peer), - subscriptions: make(map[tanka.Topic]map[tanka.Subject]bool), - } - return c -} - -// handleNewPeerTankagram handles a tankagram received from a new peer. The -// tankagram should be encrypted with the permanent encryption key derived from -// our permanent private key and the peer's peerID. The contents of the -// tankagram should be an EncryptionKeyPayload, used to establish an ephemeral -// encryption key. -func (c *MeshConn) handleNewPeerTankagram(gram *mj.Tankagram, id uint64, sendResponse func(*msgjson.Message)) { - pub, err := secp256k1.ParsePubKey(gram.From[:]) - if err != nil { - c.log.Errorf("Bad peer ID %s", gram.From) - return - } - - sharedSecretPerm := sharedSecret(c.priv, pub) - - sendError := func(tankagramErr mj.TankagramError) { - enc, err := encryptTankagramResult(nil, tankagramErr, sharedSecretPerm) - if err != nil { - c.log.Errorf("Error encrypting tankagram result: %v", err) - return - } - resp := mj.MustResponse(id, enc, nil) - mj.SignMessage(c.priv, resp) - sendResponse(resp) - } - - decryptedPayload, err := decryptTankagramPayload(sharedSecretPerm, gram.EncryptedPayload) - if err != nil { - c.log.Errorf("failed to decrypt new peer tankagram with permanent key: %v", err) - // Assume that they sent a message with an ephemeral key that - // we do not have, possibly due to restarting the client. They - // will need to do a new handshake before further communication. - sendError(mj.TEDecryptionFailed) - return - } - - if decryptedPayload.Route != mj.RouteEncryptionKey { - c.log.Errorf("unexpected route for new peer tankagram %s", decryptedPayload.Route) - sendError(mj.TEEBadRequest) - return - } - - var encryptionKeyPayload mj.EncryptionKeyPayload - if err := json.Unmarshal(decryptedPayload.Payload, &encryptionKeyPayload); err != nil { - c.log.Errorf("Error unmarshalling encryption key payload: %v", err) - sendError(mj.TEEBadRequest) - return - } - - remoteEphPub, err := secp256k1.ParsePubKey(encryptionKeyPayload.EphemeralPubKey) - if err != nil { - c.log.Errorf("Failed to parse ephemeral pubkey: %v", err) - sendError(mj.TEEBadRequest) - return - } - - ephPriv, err := secp256k1.GeneratePrivateKey() - if err != nil { - c.log.Errorf("Failed to generate ephemeral private key: %v", err) - sendError(mj.TEEPeerError) - return - } - - res := mj.EncryptionKeyPayload{ - EphemeralPubKey: ephPriv.PubKey().SerializeCompressed(), - } - enc, err := encryptTankagramResult(res, mj.TEErrNone, sharedSecretPerm) - if err != nil { - c.log.Errorf("Error encrypting tankagram result: %v", err) - return - } - msg := mj.MustResponse(id, enc, nil) - mj.SignMessage(c.priv, msg) - sendResponse(msg) - - // Add the peer to our peers map. - c.peersMtx.Lock() - c.peers[gram.From] = &peer{ - id: gram.From, - ephemeralSharedKey: sharedSecret(ephPriv, remoteEphPub), - } - c.peersMtx.Unlock() -} - -// handleTankagram handles a tankagram from a peer. -func (c *MeshConn) handleTankagram(tankagram *msgjson.Message, sendResponse func(*msgjson.Message)) { - var gram mj.Tankagram - if err := tankagram.Unmarshal(&gram); err != nil { - c.log.Errorf("error unmarshalling tankagram: %v", err) - return - } - - c.peersMtx.Lock() - p, peerExisted := c.peers[gram.From] - c.peersMtx.Unlock() - - // If the peer doesn't exist, we need to do the handshake. - if !peerExisted { - c.handleNewPeerTankagram(&gram, tankagram.ID, sendResponse) - return - } - - sendError := func(tankagramErr mj.TankagramError, encKey []byte) { - enc, err := encryptTankagramResult(nil, tankagramErr, encKey) - if err != nil { - c.log.Errorf("Error encrypting tankagram result: %v", err) - return - } - sendResponse(mj.MustResponse(tankagram.ID, enc, nil)) - } - - // An empty payload is invalid. - if len(gram.EncryptedPayload) == 0 { - sendError(mj.TEEBadRequest, p.ephemeralSharedKey) - return - } - - // If the payload cannot be decrypted with the ephemeral shared key, - // let the peer know that a shared key must be established before - // we can communicate. - decryptedPayload, err := decryptTankagramPayload(p.ephemeralSharedKey, gram.EncryptedPayload) - if err != nil { - sendError(mj.TEDecryptionFailed, p.ephemeralSharedKey) - return - } - - respond := func(resp any, tankagramErr mj.TankagramError) { - respB, err := encryptTankagramResult(resp, tankagramErr, p.ephemeralSharedKey) - if err != nil { - c.log.Errorf("Error encrypting tankagram result: %v", err) - return - } - sendResponse(mj.MustResponse(tankagram.ID, respB, nil)) - } - c.handlers.HandlePeerMessage(gram.From, decryptedPayload.Route, decryptedPayload.Payload, respond) -} - -type numNodesToConnect int - -const ( - numNodesToConnectSecondary numNodesToConnect = 1 - numNodesToConnectPrimarySecondary numNodesToConnect = 2 -) - -func (c *MeshConn) Connect(ctx context.Context) (*sync.WaitGroup, error) { - var wg sync.WaitGroup - - wg.Add(1) - go c.disconnectNodesOnCancel(ctx, &wg) - - c.ctx = ctx - - knownNodes, err := c.fetchKnownNodes(ctx) - if err != nil { - return nil, fmt.Errorf("fetching known nodes: %w", err) - } - - c.nodesMtx.Lock() - c.knownNodes = knownNodes - connectedNodes, err := c.connectToKnownNodes(numNodesToConnectPrimarySecondary, nil) - if err != nil { - c.nodesMtx.Unlock() - return nil, fmt.Errorf("connecting to known nodes: %w", err) - } - for i, node := range connectedNodes { - if i == 0 { - c.primaryNode = node - } else { - c.secondaryNode = node - } - } - c.nodesMtx.Unlock() - - wg.Add(1) - go c.runNodeMaintenance(ctx, &wg) - - return &wg, nil -} - -// disconnectNodesOnCancel disconnects the primary and secondary nodes -// when the context is cancelled. -func (c *MeshConn) disconnectNodesOnCancel(ctx context.Context, wg *sync.WaitGroup) { - defer wg.Done() - <-ctx.Done() - - c.nodesMtx.Lock() - defer c.nodesMtx.Unlock() - - if c.primaryNode != nil { - c.primaryNode.cm.Disconnect() - } - if c.secondaryNode != nil { - c.secondaryNode.cm.Disconnect() - } -} - -func (c *MeshConn) runNodeMaintenance(ctx context.Context, wg *sync.WaitGroup) { - defer wg.Done() - - ticker := time.NewTicker(30 * time.Second) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - c.nodesMtx.Lock() - c.maintainMeshConnections() - c.nodesMtx.Unlock() - case <-ctx.Done(): - return - } - } -} - -func (c *MeshConn) getNodeInfo(ctx context.Context, url string) (*tatanka.NodeInfoResponse, error) { - var resp tatanka.NodeInfoResponse - if err := dexnet.Get(ctx, fmt.Sprintf("%s/%s", url, mj.RouteNodeInfo), &resp); err != nil { - return nil, fmt.Errorf("error getting node info from node: %w", err) - } - return &resp, nil -} - -// fetchKnownNodes queries the entry node for its list of whitelisted nodes -// and returns a list of TatankaCredentials for each node, including the -// entry node itself. -func (c *MeshConn) fetchKnownNodes(ctx context.Context) ([]*TatankaCredentials, error) { - entryNodeInfo, err := c.getNodeInfo(ctx, c.entryNode.HttpURL()) - if err != nil { - return nil, fmt.Errorf("error getting node info from entry node: %w", err) - } - - knownNodes := make([]*TatankaCredentials, 0, len(entryNodeInfo.Whitelist)+1) - knownNodes = append(knownNodes, c.entryNode) - - for _, node := range entryNodeInfo.Whitelist { - var peerID tanka.PeerID - copy(peerID[:], node.PeerID) - - var remoteNodeConfig tcp.RemoteNodeConfig - if err := json.Unmarshal(node.Config, &remoteNodeConfig); err != nil { - return nil, fmt.Errorf("error reading boot node configuration: %w", err) - } - - // Remove protocol from URL - addr := remoteNodeConfig.URL - if idx := strings.Index(addr, "://"); idx >= 0 { - addr = addr[idx+3:] - } - - knownNodes = append(knownNodes, &TatankaCredentials{ - PeerID: peerID, - Addr: addr, - Cert: remoteNodeConfig.Cert, - NoTLS: len(remoteNodeConfig.Cert) == 0, - }) - } - - return knownNodes, nil -} - -// connectToKnownNodes connects to the specified number of nodes from c.knownNodes. -// If two nodes are being connected, the first returned node will have the -// subscriptions updated because this will be the primary node. -// -// The caller must hold c.nodesMtx. -func (c *MeshConn) connectToKnownNodes(numNodes numNodesToConnect, existingNode *tanka.PeerID) ([]*tatankaNode, error) { - connectedNodes := make([]*tatankaNode, 0, int(numNodes)) - - if c.log.Level() == dex.LevelTrace { - c.log.Tracef("Connecting to %d known nodes", len(c.knownNodes)) - } - - for _, node := range c.knownNodes { - if len(connectedNodes) >= int(numNodes) { - break - } - - if existingNode != nil && *existingNode == node.PeerID { - continue - } - - updateSubscriptions := len(connectedNodes) == 0 && numNodes == numNodesToConnectPrimarySecondary - tatankaNode, err := c.connectToTatankaNode(c.ctx, node, updateSubscriptions) - if err != nil { - c.log.Errorf("Failed to connect to tatanka node %s: %v", node.PeerID, err) - continue - } - - connectedNodes = append(connectedNodes, tatankaNode) - } - - if len(connectedNodes) == 0 { - return nil, fmt.Errorf("unable to connect to any tatanka nodes") - } - - return connectedNodes, nil -} - -func (c *MeshConn) sendUpdateSubscriptionsRequest(tt *tatankaNode) error { - subs := c.currentSubscriptions() - updateSubsReq := mj.MustRequest(mj.RouteUpdateSubscriptions, &mj.UpdateSubscriptions{ - Subscriptions: subs, - }) - mj.SignMessage(c.priv, updateSubsReq) - return tt.Send(updateSubsReq) -} - -// failoverToSecondary updates the secondary node to be the primary node. -// It also sends the required subscriptions to the new primary node. The -// function returns true if the failover was successful. -func (c *MeshConn) failoverToSecondary() bool { - c.nodesMtx.Lock() - defer c.nodesMtx.Unlock() - - c.primaryNode = c.secondaryNode - c.secondaryNode = nil - - err := c.sendUpdateSubscriptionsRequest(c.primaryNode) - if err != nil { - c.log.Errorf("Failed to send update subscriptions request to new primary node: %v", err) - c.primaryNode.cm.Disconnect() - c.primaryNode = nil - return false - } - - return true -} - -// maintainMeshConnections updates the secondary node to be the primary if -// there's no primary, and attempts to find new nodes if there is no primary -// or secondary node. -// -// c.nodesMtx MUST be locked when calling this function. -func (c *MeshConn) maintainMeshConnections() { - if !c.maintainingMeshConnections.CompareAndSwap(false, true) { - return - } - defer c.maintainingMeshConnections.Store(false) - - c.nodesMtx.RLock() - if c.primaryNode != nil && c.secondaryNode != nil { - c.nodesMtx.RUnlock() - return - } - - numNodesRequired := numNodesToConnectSecondary - var existingNode *tanka.PeerID - var failoverToSecondary bool - - if c.primaryNode == nil && c.secondaryNode != nil { - // Doing the failover later because we need a write lock to do it. - failoverToSecondary = true - existingNode = &c.secondaryNode.peerID - } else if c.primaryNode != nil && c.secondaryNode == nil { - existingNode = &c.primaryNode.peerID - } else { // c.primaryNode == nil && c.secondaryNode == nil - numNodesRequired = numNodesToConnectPrimarySecondary - } - c.nodesMtx.RUnlock() - - // Attempt to failover to the secondary node if necessary. If it fails, - // we'll try to find a new primary and secondary node. - if failoverToSecondary && !c.failoverToSecondary() { - numNodesRequired = numNodesToConnectPrimarySecondary - existingNode = nil - } - - if numNodesRequired == numNodesToConnectSecondary { - c.log.Infof("Attempting to find a new secondary node.") - } else { - c.log.Infof("Attempting to find a new primary and secondary node.") - } - - newNodes, err := c.connectToKnownNodes(numNodesRequired, existingNode) - if err != nil { - c.log.Errorf("Failed to update node(s): %w", err) - return - } - - c.nodesMtx.Lock() - for _, node := range newNodes { - if c.primaryNode == nil { - c.primaryNode = node - } else { - c.secondaryNode = node - } - } - c.nodesMtx.Unlock() -} - -func (c *MeshConn) handleNodeDisconnected(peerID tanka.PeerID) { - if c.ctx.Err() != nil { - return - } - - c.nodesMtx.Lock() - if c.primaryNode != nil && c.primaryNode.peerID == peerID { - c.primaryNode = nil - c.log.Infof("Primary node (%s) disconnected.", peerID) - } - if c.secondaryNode != nil && c.secondaryNode.peerID == peerID { - c.secondaryNode = nil - c.log.Infof("Secondary node (%s) disconnected.", peerID) - } - c.nodesMtx.Unlock() - - c.maintainMeshConnections() -} - -func (c *MeshConn) currentSubscriptions() map[tanka.Topic][]tanka.Subject { - subs := make(map[tanka.Topic][]tanka.Subject) - - c.subscriptionMtx.RLock() - for topic, subjects := range c.subscriptions { - if len(subjects) == 0 { - continue - } - subs[topic] = make([]tanka.Subject, 0, len(subjects)) - for subject := range subjects { - subs[topic] = append(subs[topic], subject) - } - } - c.subscriptionMtx.RUnlock() - - return subs -} - -func (c *MeshConn) sendConnectRequest(cl *tatankaNode, updateSubscriptions bool) (*mj.TatankaConfig, error) { - var subs map[tanka.Topic][]tanka.Subject - if updateSubscriptions { - subs = c.currentSubscriptions() - } - - connectReq := mj.MustRequest(mj.RouteConnect, &mj.Connect{ - ID: c.peerID, - InitialSubs: subs, - }) - mj.SignMessage(c.priv, connectReq) - - var tatankaConfig *mj.TatankaConfig - if err := c.requestTT(cl, connectReq, &tatankaConfig, DefaultRequestTimeout, true); err != nil { - return nil, fmt.Errorf("error requesting tatanka config: %w", err) - } - - return tatankaConfig, nil -} - -func (c *MeshConn) setupWSConnection(ctx context.Context, creds *TatankaCredentials) (tt *tatankaNode, err error) { - pub, err := secp256k1.ParsePubKey(creds.PeerID[:]) - if err != nil { - return nil, fmt.Errorf("error parsing pubkey from peer ID: %w", err) - } - - url := creds.WsURL() + "/ws" - - initialConnectSuccessful := false - cfg := &tcpclient.Config{ - Logger: c.log.SubLogger("TCP"), - URL: url, - Cert: creds.Cert, - ConnectEventFunc: func(status tcpclient.ConnectionStatus) { - if !initialConnectSuccessful { - return - } - if status == tcpclient.Disconnected { - c.handleNodeDisconnected(creds.PeerID) - } - }, - HandleMessage: func(msg *msgjson.Message, sendResponse func(*msgjson.Message)) { - c.handleTatankaMessage(creds.PeerID, pub, msg, sendResponse) - }, - } - cl, err := tcpclient.New(cfg) - if err != nil { - return nil, fmt.Errorf("error creating connection: %w", err) - } - cm := dex.NewConnectionMaster(cl) - if err := cm.ConnectOnce(ctx); err != nil { - return nil, fmt.Errorf("error connecting to tatanka node %s at %s: %w", creds.PeerID, cfg.URL, err) - } - - initialConnectSuccessful = true - - return &tatankaNode{ - NetworkBackend: cl, - cm: cm, - url: url, - peerID: creds.PeerID, - pub: pub, - }, nil -} - -func (c *MeshConn) connectToTatankaNode(ctx context.Context, creds *TatankaCredentials, updateSubscriptions bool) (*tatankaNode, error) { - tt, err := c.setupWSConnection(ctx, creds) - if err != nil { - return nil, fmt.Errorf("error setting up WS connection: %w", err) - } - - tatankaConfig, err := c.sendConnectRequest(tt, updateSubscriptions) - if err != nil { - return nil, fmt.Errorf("error sending connect request: %w", err) - } - - tt.config.Store(tatankaConfig) - - if c.log.Level() == dex.LevelTrace { - c.log.Tracef("Connected to %s", tt.url) - } - - return tt, nil -} - -func (c *MeshConn) handleTatankaMessage(tatankaID tanka.PeerID, pub *secp256k1.PublicKey, msg *msgjson.Message, sendResponse func(*msgjson.Message)) { - if c.log.Level() == dex.LevelTrace { - c.log.Tracef("Client handling message from tatanka node: route = %s", msg.Route) - } - - if msg.Type == msgjson.Request && msg.Route == mj.RouteTankagram { - c.handleTankagram(msg, sendResponse) - return - } - - switch msg.Type { - case msgjson.Request: - respond := func(payload any, err *msgjson.Error) { - resp := mj.MustResponse(msg.ID, payload, err) - mj.SignMessage(c.priv, resp) - sendResponse(resp) - } - c.handlers.HandleTatankaRequest(msg.Route, msg.Payload, respond) - case msgjson.Notification: - c.handlers.HandleTatankaNotification(msg.Route, msg.Payload) - default: - c.log.Errorf("tatanka node %s send a message with an unhandleable type %d", tatankaID, msg.Type) - } -} - -func (c *MeshConn) sendResult(tt *tatankaNode, msgID uint64, result dex.Bytes) error { - resp, err := msgjson.NewResponse(msgID, result, nil) - if err != nil { - return err - } - return tt.Send(resp) -} - -const DefaultRequestTimeout = 30 * time.Second - -type requestConfig struct { - timeout time.Duration - errCodeFunc func(code int) - examineFunc func(tatankaURL string, tatankaID tanka.PeerID) (ok bool) -} - -// RequestOption is an optional modifier to request behavior. -type RequestOption func(cfg *requestConfig) - -// WithTimeout sets the time out for the request. If no timeout is specified -// DefaultRequestTimeout is used. -func WithTimeout(d time.Duration) RequestOption { - return func(cfg *requestConfig) { - cfg.timeout = d - } -} - -// WithErrorCode enables the caller to inspect the error code when messaging -// fails. -func WithErrorCode(f func(code int)) RequestOption { - return func(cfg *requestConfig) { - cfg.errCodeFunc = f - } -} - -// WithExamination allows the caller to check the result before returning, and -// continue trying other tatanka nodes if necessary. -func WithExamination(f func(tatankaURL string, tatankaID tanka.PeerID) (resultOK bool)) RequestOption { - return func(cfg *requestConfig) { - cfg.examineFunc = f - } -} - -func (c *MeshConn) Subscribe(topic tanka.Topic, subject tanka.Subject) error { - req := mj.MustRequest(mj.RouteSubscribe, &mj.Subscription{ - Topic: topic, - Subject: subject, - }) - mj.SignMessage(c.priv, req) - - // Only possible non-error response is `true`. - var ok bool - err := c.RequestMesh(req, &ok) - if err != nil { - return err - } - - c.subscriptionMtx.Lock() - defer c.subscriptionMtx.Unlock() - - _, ok = c.subscriptions[topic] - if !ok { - c.subscriptions[topic] = make(map[tanka.Subject]bool) - } - c.subscriptions[topic][subject] = true - - return nil -} - -// RequestMesh sends a request to the Mesh. -func (c *MeshConn) RequestMesh(msg *msgjson.Message, thing any, opts ...RequestOption) error { - cfg := &requestConfig{ - timeout: DefaultRequestTimeout, - } - for _, opt := range opts { - opt(cfg) - } - - c.nodesMtx.RLock() - primaryNode := c.primaryNode - c.nodesMtx.RUnlock() - if primaryNode == nil { - return errors.New("not connected to any tatanka nodes") - } - - mj.SignMessage(c.priv, msg) - err := c.requestTT(primaryNode, msg, thing, cfg.timeout) - if err != nil { - return err - } - - if cfg.examineFunc != nil && !cfg.examineFunc(primaryNode.url, primaryNode.peerID) { - return fmt.Errorf("request failed examination") - } - - return nil -} - -func (c *MeshConn) requestTT(tt *tatankaNode, msg *msgjson.Message, thing any, timeout time.Duration, checkSig ...bool) (err error) { - errChan := make(chan error) - if err := tt.Request(msg, func(msg *msgjson.Message) { - if len(checkSig) > 0 && checkSig[0] { - if err := mj.CheckSig(msg, tt.pub); err != nil { - errChan <- fmt.Errorf("signature check failed: %w", err) - return - } - } - if thing != nil { - errChan <- msg.UnmarshalResult(thing) - } else { - errChan <- nil - } - }); err != nil { - errChan <- fmt.Errorf("request error: %w", err) - } - - select { - case err = <-errChan: - case <-time.After(timeout): - return fmt.Errorf("timed out (%s) waiting for response from %s for route %q", timeout, tt, msg.Route) - } - - return err -} - -// sharedSecret computes the shared secret used for encrypting and decrypting -// messages between two peers. The shared secret is the hash of the result of -// an elliptic curve diffie-hellman key exchange. -func sharedSecret(ourPriv *secp256k1.PrivateKey, theirPub *secp256k1.PublicKey) []byte { - sharedSecret := secp256k1.GenerateSharedSecret(ourPriv, theirPub) - hash := sha256.Sum256(sharedSecret) - return hash[:] -} - -// ConnectPeer establishes a connection to a peer. A handhshake is performed -// to establish an ephemeral shared secret, which is used to encrypt and -// decrypt messages between the two peers for the duration of the connection. -// A permanent shared secret based on the peer IDs is used to encrypt the -// handshake messages. -func (c *MeshConn) ConnectPeer(peerID tanka.PeerID) error { - // Parse remote permanent public key from peerID - remotePub, err := secp256k1.ParsePubKey(peerID[:]) - if err != nil { - return fmt.Errorf("error parsing remote pubkey from peer %s: %v", peerID, err) - } - - // Compute permanent shared secret, to use for encrypting the handshake - // messages. - sharedSecretPerm := sharedSecret(c.priv, remotePub) - - // Generate ephemeral key pair for this session - ephPrivKey, err := secp256k1.GeneratePrivateKey() - if err != nil { - return fmt.Errorf("peer %s: failed to generate ephemeral key: %v", peerID, err) - } - - encryptionKeyPayload, err := json.Marshal(mj.EncryptionKeyPayload{ - EphemeralPubKey: ephPrivKey.PubKey().SerializeCompressed(), - }) - if err != nil { - return fmt.Errorf("peer %s: failed to marshal encryption key payload: %v", peerID, err) - } - - msg := msgjson.Message{ - Route: mj.RouteEncryptionKey, - Payload: encryptionKeyPayload, - } - encryptedPayload, err := encryptTankagramPayload(sharedSecretPerm, &msg) - if err != nil { - return fmt.Errorf("peer %s: failed to encrypt ephemeral pubkey: %v", peerID, err) - } - - req := mj.MustRequest(mj.RouteTankagram, &mj.Tankagram{ - From: c.peerID, - To: peerID, - EncryptedPayload: encryptedPayload, - }) - mj.SignMessage(c.priv, req) - - var r mj.TankagramResult - if err := c.RequestMesh(req, &r, WithExamination(func(tatankaURL string, tatankaID tanka.PeerID) bool { - if r.Result == mj.TRTTransmitted { - return true - } - c.log.Errorf("Tankagram transmission failure connecting to %s via %s @ %s: %q", peerID, tatankaID, tatankaURL, r.Result) - return false - })); err != nil { - return fmt.Errorf("peer %s: failed to send ephemeral key: %v", peerID, err) - } - - tankagramResult, err := decryptTankagramResult(sharedSecretPerm, r.EncryptedPayload) - if err != nil { - return fmt.Errorf("peer %s: error decrypting response: %v", peerID, err) - } - - var resp mj.EncryptionKeyPayload - if err := json.Unmarshal(tankagramResult.Payload, &resp); err != nil { - return fmt.Errorf("peer %s: error unmarshalling response: %v", peerID, err) - } - - remoteEphPub, err := secp256k1.ParsePubKey(resp.EphemeralPubKey) - if err != nil { - return fmt.Errorf("peer %s: error decoding ephemeral pubkey: %v", peerID, err) - } - - c.peersMtx.Lock() - defer c.peersMtx.Unlock() - c.peers[peerID] = &peer{ - id: peerID, - ephemeralSharedKey: sharedSecret(ephPrivKey, remoteEphPub), - } - - return nil -} - -// RequestPeer sends a request to an already-connected peer. -func (c *MeshConn) RequestPeer(peerID tanka.PeerID, msg *msgjson.Message, thing any) error { - c.peersMtx.RLock() - p, known := c.peers[peerID] - c.peersMtx.RUnlock() - if !known { - return fmt.Errorf("not connected to peer %s", peerID) - } - - payload, err := json.Marshal(msg) - if err != nil { - return fmt.Errorf("error marshaling payload: %w", err) - } - - encryptedPayload, err := encryptAES(p.ephemeralSharedKey, payload) - if err != nil { - return fmt.Errorf("error encrypting payload for %s: %w", p.id, err) - } - - tankaGram := &mj.Tankagram{ - From: c.peerID, - To: peerID, - EncryptedPayload: encryptedPayload, - } - - var res mj.TankagramResult - wrappedMsg := mj.MustRequest(mj.RouteTankagram, tankaGram) - mj.SignMessage(c.priv, wrappedMsg) - - if err := c.RequestMesh(wrappedMsg, &res, WithExamination(func(tatankaURL string, tatankaID tanka.PeerID) bool { - if res.Result == mj.TRTTransmitted { - return true - } - c.log.Errorf("Tankagram transmission failure sending %s to %s via %s @ %s: %q", msg.Route, peerID, tatankaID, tatankaURL, res.Result) - return false - })); err != nil { - return err - } - - switch res.Result { - case mj.TRTErrFromTanka: - return ErrTankaError - case mj.TRTNoPath: - return ErrTankaError - case mj.TRTErrBadClient: - c.log.Errorf("ErrBadPeerResponse due to bad client") - return ErrBadPeerResponse - } - - decryptedRes, err := decryptTankagramResult(p.ephemeralSharedKey, res.EncryptedPayload) - if err != nil { - remotePub, err := p.id.PublicKey() - if err != nil { - return fmt.Errorf("peer ID %s does not map to a valid public key: %w", p.id, err) - } - permanentSharedKey := sharedSecret(c.priv, remotePub) - decryptedRes, err := decryptTankagramResult(permanentSharedKey, res.EncryptedPayload) - if err != nil { - c.log.Errorf("RequestPeer: could not decrypt tankagram result: %v", err) - return ErrBadPeerResponse - } - if decryptedRes.Error == mj.TEDecryptionFailed { - return ErrPeerNeedsReconnect - } - // If we requested using an ephemeral key, and they responded using the - // permanent key, the only valid response is a TEDecryptionFailed. - return ErrBadPeerResponse - } - - if thing != nil { - if len(decryptedRes.Payload) == 0 { - return ErrBadPeerResponse - } - if err = json.Unmarshal(decryptedRes.Payload, thing); err != nil { - c.log.Errorf("error unmarshalling response: %v", err) - return ErrBadPeerResponse - } - } - - return nil -} diff --git a/tatanka/client/conn/conn_live_test.go b/tatanka/client/conn/conn_live_test.go deleted file mode 100644 index 40f90191be..0000000000 --- a/tatanka/client/conn/conn_live_test.go +++ /dev/null @@ -1,607 +0,0 @@ -//go:build live - -package conn - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net" - "net/url" - "os" - "os/user" - "path/filepath" - "sync" - "testing" - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/msgjson" - dexbtc "decred.org/dcrdex/dex/networks/btc" - "decred.org/dcrdex/server/comms" - "decred.org/dcrdex/tatanka" - "decred.org/dcrdex/tatanka/chain/utxo" - "decred.org/dcrdex/tatanka/mj" - "decred.org/dcrdex/tatanka/tanka" - "decred.org/dcrdex/tatanka/tcp" - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -var ( - logMaker *dex.LoggerMaker - usr, _ = user.Current() - dextestDir = filepath.Join(usr.HomeDir, "dextest") - decredRPCPath = filepath.Join(dextestDir, "dcr", "alpha", "rpc.cert") - chains = []tatanka.ChainConfig{ - { - Symbol: "dcr", - Config: mustEncode(&utxo.DecredConfigFile{ - RPCUser: "user", - RPCPass: "pass", - RPCListen: "127.0.0.1:19561", - RPCCert: decredRPCPath, - }), - }, - { - Symbol: "btc", - Config: mustEncode(&utxo.BitcoinConfigFile{ - RPCConfig: dexbtc.RPCConfig{ - RPCUser: "user", - RPCPass: "pass", - RPCBind: "127.0.0.1", - RPCPort: 20556, - }, - }), - }, - } -) - -// ### Helper Functions - -func newBootNode(addr string, peerID []byte) tatanka.BootNode { - tcpCfg, _ := json.Marshal(&tcp.RemoteNodeConfig{ - URL: "ws://" + addr, - }) - return tatanka.BootNode{ - Protocol: "ws", - Config: tcpCfg, - PeerID: peerID, - } -} - -func genKeyPair() (*secp256k1.PrivateKey, tanka.PeerID) { - priv, _ := secp256k1.GeneratePrivateKey() - var peerID tanka.PeerID - copy(peerID[:], priv.PubKey().SerializeCompressed()) - return priv, peerID -} - -func mustEncode(thing any) json.RawMessage { - b, err := json.Marshal(thing) - if err != nil { - panic("mustEncode: " + err.Error()) - } - return b -} - -// ### Test types - -type tTatankaNodeInfo struct { - priv *secp256k1.PrivateKey - peerID tanka.PeerID - addr net.Addr -} - -type tTatanka struct { - tatanka *tatanka.Tatanka - cm *dex.ConnectionMaster - nodeInfo *tTatankaNodeInfo -} - -type tConn struct { - conn *MeshConn - cm *dex.ConnectionMaster - priv *secp256k1.PrivateKey - peerID tanka.PeerID - incomingTatankaRequests chan *tatankaRequestHandler - incomingTatankaNotifications chan *tatankaNoteHandler - incomingPeerMsgs chan *tankagramHandler -} - -// ### Network Setup Functions - -// meshNodeInfos creates a list of node info for a mesh of n nodes. -func meshNodeInfos(n int) ([]*tTatankaNodeInfo, error) { - nodes := make([]*tTatankaNodeInfo, 0, n) - - for i := 0; i < n; i++ { - priv, peerID := genKeyPair() - - addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - if err != nil { - return nil, err - } - - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return nil, err - } - defer l.Close() - - nodes = append(nodes, &tTatankaNodeInfo{ - priv: priv, - peerID: peerID, - addr: l.Addr(), - }) - } - - return nodes, nil -} - -// runServer starts a new Tatanka node with the given node info and other nodes as -// part of its whitelist. -func runServer(t *testing.T, ctx context.Context, thisNode *tTatankaNodeInfo, otherNodes []*tTatankaNodeInfo, disableMessariFiatRateSource bool) (*tTatanka, error) { - dir := t.TempDir() - os.MkdirAll(dir, 0755) - os.WriteFile(filepath.Join(dir, "priv.key"), thisNode.priv.Serialize(), 0644) - - whiteList := make([]tatanka.BootNode, 0, len(otherNodes)) - for _, node := range otherNodes { - whiteList = append(whiteList, newBootNode(node.addr.String(), node.peerID[:])) - } - - log := logMaker.Logger(fmt.Sprintf("SRV[%s]", thisNode.addr)) - ttCfg := &tatanka.ConfigFile{Chains: chains} - rawCfg, _ := json.Marshal(ttCfg) - cfgPath := filepath.Join(dir, "config.json") - if err := os.WriteFile(cfgPath, rawCfg, 0644); err != nil { - log.Errorf("WriteFile error: %v", err) - return nil, err - } - - cfg := &tatanka.Config{ - Net: dex.Simnet, - DataDir: dir, - Logger: log, - RPC: comms.RPCConfig{ - ListenAddrs: []string{thisNode.addr.String()}, - NoTLS: true, - }, - ConfigPath: cfgPath, - WhiteList: whiteList, - MaxClients: 100, - } - - if disableMessariFiatRateSource { - // Requesting multiple rates from the same IP can trigger a 429 HTTP - // error. - cfg.FiatOracleConfig.DisabledFiatSources = "Messari" - } - - tatanka, err := tatanka.New(cfg) - if err != nil { - log.Errorf("error creating Tatanka node: %v", err) - return nil, err - } - - cm := dex.NewConnectionMaster(tatanka) - if err := cm.ConnectOnce(ctx); err != nil { - log.Errorf("ConnectOnce error: %v", err) - return nil, err - } - - return &tTatanka{ - tatanka: tatanka, - cm: cm, - nodeInfo: thisNode, - }, nil -} - -type tMesh struct { - nodeInfos []*tTatankaNodeInfo - ctx context.Context - t *testing.T - - mtx sync.RWMutex - tatankas []*tTatanka -} - -func (m *tMesh) tatanka(i int) *tTatanka { - m.mtx.RLock() - defer m.mtx.RUnlock() - return m.tatankas[i] -} - -func (m *tMesh) stop(i int) { - m.mtx.Lock() - defer m.mtx.Unlock() - - m.tatankas[i].cm.Disconnect() -} - -// TODO: simply connecting a node after disconnecting does not work. -// There will be the following error: -// unexpected (http.Server).Serve error: accept tcp4 127.0.0.1:57146: use of closed network connection -func (m *tMesh) start(i int) { - m.t.Helper() - - m.mtx.Lock() - defer m.mtx.Unlock() - - if m.tatankas[i].cm.On() { - m.t.Fatalf("tatanka %d already running", i) - } - - thisNode := m.nodeInfos[i] - var otherNodes []*tTatankaNodeInfo - for j := range m.nodeInfos { - if j == i { - continue - } - otherNodes = append(otherNodes, m.nodeInfos[j]) - } - - tt, err := runServer(m.t, m.ctx, thisNode, otherNodes, true) - if err != nil { - panic(err) - } - - m.tatankas[i] = tt -} - -// setupMesh creates a mesh of numNode tatankas, with each node having all -// other nodes in the mesh as its whitelist. -func setupMesh(t *testing.T, ctx context.Context, numNodes int) (*tMesh, error) { - nodes, err := meshNodeInfos(numNodes) - if err != nil { - return nil, fmt.Errorf("meshNodeInfos error: %w", err) - } - - tatankas := make([]*tTatanka, 0, numNodes) - for i := 0; i < numNodes; i++ { - thisNode := nodes[i] - otherNodes := make([]*tTatankaNodeInfo, 0, numNodes-1) - otherNodes = append(otherNodes, nodes[:i]...) - otherNodes = append(otherNodes, nodes[i+1:]...) - - tt, err := runServer(t, ctx, thisNode, otherNodes, true) - if err != nil { - return nil, fmt.Errorf("runServer error: %w", err) - } - - tatankas = append(tatankas, tt) - } - - return &tMesh{ - nodeInfos: nodes, - ctx: ctx, - t: t, - tatankas: tatankas, - }, nil -} - -type tatankaRequestHandler struct { - route string - payload json.RawMessage - respond func(any, *msgjson.Error) -} - -type tatankaNoteHandler struct { - route string - payload json.RawMessage -} - -type tankagramHandler struct { - peerID tanka.PeerID - route string - payload json.RawMessage - respond func(any, mj.TankagramError) -} - -// newTestConn creates a new client connection to a tatanka node. -func newTestConn(t *testing.T, i int, ctx context.Context, tatankaNodeInfo *tTatankaNodeInfo) (*tConn, error) { - priv, peerID := genKeyPair() - - // Setup channels for incoming messages - incomingTatankaRequests := make(chan *tatankaRequestHandler, 10) - incomingTatankaNotifications := make(chan *tatankaNoteHandler, 10) - incomingPeerMsgs := make(chan *tankagramHandler, 10) - - // Configure connection - cfg := &Config{ - EntryNode: &TatankaCredentials{ - PeerID: tatankaNodeInfo.peerID, - Addr: tatankaNodeInfo.addr.String(), - NoTLS: true, - }, - Handlers: &MessageHandlers{ - HandleTatankaRequest: func(route string, payload json.RawMessage, respond func(any, *msgjson.Error)) { - incomingTatankaRequests <- &tatankaRequestHandler{route: route, payload: payload, respond: respond} - }, - HandleTatankaNotification: func(route string, payload json.RawMessage) { - incomingTatankaNotifications <- &tatankaNoteHandler{route: route, payload: payload} - }, - HandlePeerMessage: func(peerID tanka.PeerID, route string, payload json.RawMessage, respond func(any, mj.TankagramError)) { - incomingPeerMsgs <- &tankagramHandler{peerID: peerID, route: route, payload: payload, respond: respond} - }, - }, - Logger: logMaker.NewLogger(fmt.Sprintf("CONN%d", i), dex.LevelTrace), - PrivateKey: priv, - } - - // Create and connect - conn := New(cfg) - cm := dex.NewConnectionMaster(conn) - if err := cm.ConnectOnce(ctx); err != nil { - return nil, fmt.Errorf("ConnectOnce error: %w", err) - } - - return &tConn{ - conn: conn, - cm: cm, - priv: priv, - peerID: peerID, - incomingTatankaRequests: incomingTatankaRequests, - incomingTatankaNotifications: incomingTatankaNotifications, - incomingPeerMsgs: incomingPeerMsgs, - }, nil -} - -func TestMain(m *testing.M) { - var err error - logMaker, err = dex.NewLoggerMaker(os.Stdout, dex.LevelTrace.String()) - if err != nil { - panic("failed to create custom logger: " + err.Error()) - } - comms.UseLogger(logMaker.NewLogger("COMMS", dex.LevelTrace)) - os.Exit(m.Run()) -} - -// TestConnectPeer tests that a client can connect to another client -// and send a tankagram request to it. -func TestConnectPeer(t *testing.T) { - ctx, cc := context.WithCancel(context.Background()) - fmt.Println(cc) - // defer cancel() - - // Create a two node mesh - mesh, err := setupMesh(t, ctx, 2) - if err != nil { - t.Fatalf("setupMesh error: %v", err) - } - - // Create two clients, one connected to each node - conn1, err := newTestConn(t, 1, ctx, mesh.nodeInfos[0]) - if err != nil { - t.Fatalf("newTestConn error: %v", err) - } - - conn2, err := newTestConn(t, 2, ctx, mesh.nodeInfos[1]) - if err != nil { - t.Fatalf("newTestConn error: %v", err) - } - - // Connect client 1 to client 2 - err = conn1.conn.ConnectPeer(conn2.peerID) - if err != nil { - t.Fatalf("ConnectPeer error: %v", err) - } - - // Create a test request and expected response - type testRequest struct { - Test string `json:"test"` - } - testReq, err := msgjson.NewRequest(1, "Test", testRequest{Test: "testingRequest"}) - if err != nil { - t.Fatalf("NewRequest error: %v", err) - } - expResponse := testRequest{Test: "testingResponse"} - - // Send the request and check the response - wg := sync.WaitGroup{} - wg.Add(1) - errChan := make(chan error, 1) - go func() { - defer wg.Done() - - var res testRequest - err = conn1.conn.RequestPeer(conn2.peerID, testReq, &res) - if err != nil { - errChan <- fmt.Errorf("RequestPeer error: %w", err) - } - if res != expResponse { - errChan <- fmt.Errorf("expected %+v, got %+v", expResponse, res) - } - }() - - // Use client 2 to respond to the request - select { - case msg := <-conn2.incomingPeerMsgs: - msg.respond(expResponse, mj.TEErrNone) - case <-time.After(time.Second): - t.Fatalf("timeout waiting for incoming tatanka request peer 2") - } - - wg.Wait() - - select { - case err := <-errChan: - t.Fatal(err) - default: - } - - // Simulate client 2 restarting and no longer knowing about the - // original ephemeral key. Ensure that ErrPeerNeedsReconnect is returned - // when client 1 makes a request to client 2. - conn2.conn.peers = make(map[tanka.PeerID]*peer) - var res testRequest - err = conn1.conn.RequestPeer(conn2.peerID, testReq, &res) - if err != ErrPeerNeedsReconnect { - t.Fatalf("expected ErrPeerNeedsReconnect, got %v", err) - } -} - -// TestFailover tests that if nodes go down, the connection to the mesh -// will switch to other nodes and the subscriptions are restored. -func TestFailover(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - mesh, err := setupMesh(t, ctx, 4) - if err != nil { - t.Fatalf("setupMesh error: %v", err) - } - - mesh.stop(2) - mesh.stop(3) - - conn1, err := newTestConn(t, 1, ctx, mesh.nodeInfos[0]) - if err != nil { - t.Fatalf("newTestConn error: %v", err) - } - defer conn1.cm.Disconnect() - - conn2, err := newTestConn(t, 2, ctx, mesh.nodeInfos[1]) - if err != nil { - t.Fatalf("newTestConn error: %v", err) - } - defer conn2.cm.Disconnect() - - marketsTopic := tanka.Topic("market") - dcrBtcSubject := tanka.Subject("dcr/btc") - conn1.conn.Subscribe(marketsTopic, dcrBtcSubject) - conn2.conn.Subscribe(marketsTopic, dcrBtcSubject) - - // testBroadcast sends a broadcast on the source client and makes sure that - // the destination client receives it. - testBroadcast := func(bcast *mj.Broadcast, source, dest *tConn) { - t.Helper() - - var ok bool - err = source.conn.RequestMesh(mj.MustRequest(mj.RouteBroadcast, bcast), &ok) - if err != nil { - t.Fatalf("RequestMesh error: %v", err) - } - - wg := sync.WaitGroup{} - wg.Add(1) - errChan := make(chan error, 1) - go func() { - defer wg.Done() - for { - select { - case msg := <-dest.incomingTatankaNotifications: - if msg.route == mj.RouteBroadcast { - var broadcast mj.Broadcast - err := json.Unmarshal(msg.payload, &broadcast) - if err != nil { - errChan <- fmt.Errorf("Unmarshal error: %w", err) - return - } - if bytes.Equal(broadcast.Payload, bcast.Payload) { - return - } - } - case <-time.After(time.Second * 10): - errChan <- fmt.Errorf("timed out waiting for broadcast") - } - } - }() - - wg.Wait() - - select { - case err := <-errChan: - t.Fatal(err) - default: - } - } - - connectedNodes := make(map[tanka.PeerID]map[string]bool) - updateConnectedNodes := func(conn *tConn) { - connectedNodes[conn.conn.peerID] = make(map[string]bool) - hostOnly := func(uri string) string { - parsedURL, _ := url.Parse(uri) - return parsedURL.Host - } - if conn.conn.primaryNode != nil { - connectedNodes[conn.conn.peerID][hostOnly(conn.conn.primaryNode.url)] = true - } - if conn.conn.secondaryNode != nil { - connectedNodes[conn.conn.peerID][hostOnly(conn.conn.secondaryNode.url)] = true - } - } - - // checkConnected that the connections are connected to the expected nodes - // on both conn1 and conn2. - checkConnected := func(expected []string) { - t.Helper() - updateConnectedNodes(conn1) - updateConnectedNodes(conn2) - for _, node := range expected { - if !connectedNodes[conn1.conn.peerID][node] { - t.Fatalf("node %s not connected to conn1", node) - } - if !connectedNodes[conn2.conn.peerID][node] { - t.Fatalf("node %s not connected to conn2", node) - } - } - } - - checkConnected([]string{ - mesh.nodeInfos[0].addr.String(), - mesh.nodeInfos[1].addr.String(), - }) - - testBroadcast(&mj.Broadcast{ - PeerID: conn1.peerID, - Topic: marketsTopic, - Subject: dcrBtcSubject, - MessageType: mj.MessageTypeTrollBox, - Payload: []byte("hello"), - Stamp: time.Now(), - }, conn1, conn2) - - testBroadcast(&mj.Broadcast{ - PeerID: conn2.peerID, - Topic: marketsTopic, - Subject: dcrBtcSubject, - MessageType: mj.MessageTypeTrollBox, - Payload: []byte("hello2"), - Stamp: time.Now(), - }, conn2, conn1) - - // Stop the two nodes that the connections were using, and start the two - // that were previously shut down. - mesh.stop(0) - mesh.stop(1) - mesh.start(2) - mesh.start(3) - - conn1.conn.maintainMeshConnections() - conn2.conn.maintainMeshConnections() - - checkConnected([]string{ - mesh.nodeInfos[2].addr.String(), - mesh.nodeInfos[3].addr.String(), - }) - - testBroadcast(&mj.Broadcast{ - PeerID: conn1.peerID, - Topic: marketsTopic, - Subject: dcrBtcSubject, - MessageType: mj.MessageTypeTrollBox, - Payload: []byte("hello3"), - Stamp: time.Now(), - }, conn1, conn2) - - testBroadcast(&mj.Broadcast{ - PeerID: conn2.peerID, - Topic: marketsTopic, - Subject: dcrBtcSubject, - MessageType: mj.MessageTypeTrollBox, - Payload: []byte("hello4"), - Stamp: time.Now(), - }, conn2, conn1) -} diff --git a/tatanka/client/conn/conn_test.go b/tatanka/client/conn/conn_test.go deleted file mode 100644 index 929e2a33eb..0000000000 --- a/tatanka/client/conn/conn_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package conn - -import ( - "bytes" - "testing" - - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -func TestEncryption(t *testing.T) { - alicePriv, err := secp256k1.GeneratePrivateKey() - if err != nil { - t.Fatalf("error generating private key: %v", err) - } - - bobPrivKey, err := secp256k1.GeneratePrivateKey() - if err != nil { - t.Fatalf("error generating private key: %v", err) - } - - aliceSharedSecret := sharedSecret(alicePriv, bobPrivKey.PubKey()) - bobSharedSecret := sharedSecret(bobPrivKey, alicePriv.PubKey()) - - msg := []byte( - "this is an unencrypted message. " + - "AES chunk size is 16 bytes. We will create a message that is over 16 bytes, " + - "So to make it that long, we'll just continue jabbering about nothing. " + - "Nothing, nothing, nothing, nothing, nothing.", - ) - - enc, err := encryptAES(aliceSharedSecret, msg) - if err != nil { - t.Fatalf("encryptAES error: %v", err) - } - - reMsg, err := decryptAES(bobSharedSecret, enc) - if err != nil { - t.Fatalf("decryptAES error: %v", err) - } - - if !bytes.Equal(reMsg, msg) { - t.Fatalf("wrong message. %q != %q", string(reMsg), string(msg)) - } -} diff --git a/tatanka/client/mesh/mesh.go b/tatanka/client/mesh/mesh.go deleted file mode 100644 index f331591f5f..0000000000 --- a/tatanka/client/mesh/mesh.go +++ /dev/null @@ -1,380 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package mesh - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strings" - "sync" - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/feerates" - "decred.org/dcrdex/dex/fiatrates" - "decred.org/dcrdex/dex/lexi" - "decred.org/dcrdex/dex/msgjson" - "decred.org/dcrdex/tatanka/client/conn" - "decred.org/dcrdex/tatanka/mj" - "decred.org/dcrdex/tatanka/tanka" - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -// Config is the configuration settings for Mesh. -type Config struct { - DataDir string - PrivateKey *secp256k1.PrivateKey - Logger dex.Logger - EntryNode *TatankaCredentials -} - -// Mesh is a manager for operations on the Tatanka Mesh Network. -type Mesh struct { - priv *secp256k1.PrivateKey - peerID tanka.PeerID - - // cfg *Config - log dex.Logger - entryNode *TatankaCredentials - conn *meshConn - payloads chan any - - dataDir string - db *lexi.DB - dbCM *dex.ConnectionMaster - bondTable *lexi.Table - - marketsMtx sync.RWMutex - markets map[string]*market - - fiatRatesMtx sync.RWMutex - fiatRates map[string]*fiatrates.FiatRateInfo - - feeRateEstimateMtx sync.RWMutex - feeRateEstimates map[uint32]*feerates.Estimate -} - -// New is the constructor for a new Mesh. -func New(cfg *Config) (*Mesh, error) { - var peerID tanka.PeerID - copy(peerID[:], cfg.PrivateKey.PubKey().SerializeCompressed()) - - if cfg.DataDir == "" { - return nil, errors.New("no data directory provided") - } - - mesh := &Mesh{ - priv: cfg.PrivateKey, - peerID: peerID, - log: cfg.Logger, - dataDir: cfg.DataDir, - entryNode: cfg.EntryNode, - payloads: make(chan any, 128), - markets: make(map[string]*market), - fiatRates: make(map[string]*fiatrates.FiatRateInfo), - } - - if err := mesh.initializeDB(); err != nil { - return nil, fmt.Errorf("failed to initialize database: %w", err) - } - - return mesh, nil -} - -// Connect initializes the Mesh. -func (m *Mesh) Connect(ctx context.Context) (*sync.WaitGroup, error) { - var wg sync.WaitGroup - - dbCM := dex.NewConnectionMaster(m.db) - if err := dbCM.ConnectOnce(ctx); err != nil { - return nil, fmt.Errorf("couldn't start database: %w", err) - } - m.dbCM = dbCM - - mesh := conn.New(&conn.Config{ - EntryNode: m.entryNode, - Logger: m.log.SubLogger("tTC"), - Handlers: &conn.MessageHandlers{ - HandleTatankaRequest: m.handleTatankaRequest, - HandleTatankaNotification: m.handleTatankaNotification, - HandlePeerMessage: m.handlePeerRequest, - }, - PrivateKey: m.priv, - }) - - meshCM := dex.NewConnectionMaster(mesh) - if err := meshCM.ConnectOnce(ctx); err != nil { - return nil, fmt.Errorf("ConnectOnce error: %w", err) - } - - m.conn = &meshConn{mesh, meshCM} - - wg.Add(1) - go func() { - <-dbCM.Done() - <-meshCM.Done() - wg.Done() - }() - - return &wg, nil -} - -// ID returns our peer ID on Mesh. -func (m *Mesh) ID() tanka.PeerID { - return m.peerID -} - -func (m *Mesh) initializeDB() error { - db, err := lexi.New(&lexi.Config{ - Path: m.dataDir, - Log: m.log.SubLogger("DB"), - }) - if err != nil { - return err - } - m.db = db - - m.bondTable, err = db.Table("bond") - return err -} - -// Next emits certain types of messages. -func (m *Mesh) Next() <-chan any { - return m.payloads -} - -func (m *Mesh) emit(thing any) { - select { - case m.payloads <- thing: - default: - m.log.Errorf("payload channel is blocking") - } -} - -func (m *Mesh) handleTatankaRequest(route string, payload json.RawMessage, respond func(any, *msgjson.Error)) { - switch route { - default: - m.log.Debugf("Received a request for an unknown route %q", route) - } -} - -func (m *Mesh) handleTatankaNotification(route string, payload json.RawMessage) { - switch route { - case mj.RouteBroadcast: - m.handleBroadcast(payload) - case mj.RouteRates: - m.handleRates(payload) - case mj.RouteFeeRateEstimate: - m.handleFeeEstimates(payload) - default: - m.emit(payload) - } -} - -func (m *Mesh) handlePeerRequest(peerID tanka.PeerID, route string, payload json.RawMessage, respond func(any, mj.TankagramError)) { - switch route { - case mj.RouteNegotiate: - // TODO: Reputation check. - m.handleNegotiate(peerID, payload, respond) - default: - m.log.Debugf("Received a peer request for an unknown route %q", route) - } -} - -func (m *Mesh) Broadcast(topic tanka.Topic, subject tanka.Subject, msgType mj.BroadcastMessageType, thing any) error { - payload, err := json.Marshal(thing) - if err != nil { - return fmt.Errorf("error marshaling broadcast payload: %v", err) - } - req := mj.MustRequest(mj.RouteBroadcast, &mj.Broadcast{ - PeerID: m.peerID, - Topic: topic, - Subject: subject, - MessageType: msgType, - Payload: payload, - Stamp: time.Now(), - }) - // Only possible non-error response is `true`. - var ok bool - return m.conn.RequestMesh(req, &ok) -} - -func (m *Mesh) SubscribeToFiatRates() error { - req := mj.MustRequest(mj.RouteSubscribe, &mj.Subscription{ - Topic: mj.TopicFiatRate, - }) - - // Only possible non-error response is `true`. - var ok bool - return m.conn.RequestMesh(req, &ok) -} - -// PostBond stores the bond in the database and sends it to the mesh. -func (m *Mesh) PostBond(bond *tanka.Bond) error { - k := bond.ID() - if err := m.bondTable.Set(k[:], lexi.JSON(bond)); err != nil { - return fmt.Errorf("error storing bond in DB: %w", err) - } - req := mj.MustRequest(mj.RoutePostBond, []*tanka.Bond{bond}) - var res bool - return m.conn.RequestMesh(req, &res) -} - -// ActiveBonds retrieves the active bonds from the database. -func (m *Mesh) ActiveBonds() ([]*tanka.Bond, error) { - bonds := make([]*tanka.Bond, 0, 1) - return bonds, m.bondTable.Iterate(nil, func(it *lexi.Iter) error { - var bond tanka.Bond - if err := it.V(func(vB []byte) error { - return json.Unmarshal(vB, &bond) - }); err != nil { - return err - } - bonds = append(bonds, &bond) - return nil - }) -} - -func (m *Mesh) SubscribeMarket(baseID, quoteID uint32) error { - mktName, err := dex.MarketName(baseID, quoteID) - if err != nil { - return fmt.Errorf("error constructing market name: %w", err) - } - - m.marketsMtx.Lock() - defer m.marketsMtx.Unlock() - - if err = m.conn.Subscribe(mj.TopicMarket, tanka.Subject(mktName)); err != nil { - return fmt.Errorf("error subscribing to market: %w", err) - } - - m.markets[mktName] = &market{ - log: m.log.SubLogger(mktName), - peerID: m.peerID, - baseID: baseID, - quoteID: quoteID, - conn: m.conn, - ords: make(map[tanka.ID40]*order), - } - - return nil -} - -func (m *Mesh) handleBroadcast(payload json.RawMessage) { - var bcast mj.Broadcast - if err := json.Unmarshal(payload, &bcast); err != nil { - m.log.Errorf("%s broadcast unmarshal error: %w", err) - return - } - switch bcast.Topic { - case mj.TopicMarket: - m.handleMarketBroadcast(&bcast) - } - - m.emit(&bcast) -} - -func (m *Mesh) handleNegotiate(peerID tanka.PeerID, payload json.RawMessage, respond func(any, mj.TankagramError)) { - var match *tanka.Match - if err := json.Unmarshal(payload, match); err != nil { - m.log.Debugf("handleNegotiate: unable to unmarshal match from peer %v: %v", peerID, err) - respond(false, mj.TEEBadRequest) - return - } - mktName, err := dex.MarketName(match.BaseID, match.QuoteID) - if err != nil { - m.log.Debugf("handleNegotiate: error constructing market name in request from peer %v: %v", peerID, err) - respond(false, mj.TEEPeerError) - return - } - m.marketsMtx.Lock() - market, found := m.markets[mktName] - m.marketsMtx.Unlock() - if !found { - m.log.Debugf("handleNegotiate: unable to find market %v in request from peer %v", mktName, peerID) - respond(false, mj.TEEPeerError) - return - } - respond(market.handleNegotiate(match), mj.TEErrNone) -} - -func (m *Mesh) handleRates(payload json.RawMessage) { - var rm mj.RateMessage - if err := json.Unmarshal(payload, &rm); err != nil { - m.log.Errorf("%s rate message unmarshal error: %w", err) - return - } - - switch rm.Topic { - case mj.TopicFiatRate: - m.fiatRatesMtx.Lock() - for ticker, rateInfo := range rm.Rates { - m.fiatRates[strings.ToLower(ticker)] = &fiatrates.FiatRateInfo{ - Value: rateInfo.Value, - LastUpdate: time.Now(), - } - } - m.fiatRatesMtx.Unlock() - } - m.emit(&rm) -} - -func (m *Mesh) ConnectPeer(peerID tanka.PeerID) error { - return m.conn.ConnectPeer(peerID) -} - -func (m *Mesh) RequestPeer(peerID tanka.PeerID, msg *msgjson.Message, thing any) error { - return m.conn.RequestPeer(peerID, msg, thing) -} - -type TatankaCredentials = conn.TatankaCredentials - -// meshConn is our representation of the connection to the mesh network. -type meshConn struct { - *conn.MeshConn - cm *dex.ConnectionMaster -} - -func (m *Mesh) handleFeeEstimates(payload json.RawMessage) { - var rm mj.FeeRateEstimateMessage - if err := json.Unmarshal(payload, &rm); err != nil { - m.log.Errorf("%s rate message unmarshal error: %w", err) - return - } - - switch rm.Topic { - case mj.TopicFeeRateEstimate: - m.feeRateEstimateMtx.Lock() - for chainID, feeInfo := range rm.FeeRateEstimates { - m.feeRateEstimates[chainID] = &feerates.Estimate{ - Value: feeInfo.Value, - LastUpdated: time.Now(), - } - } - m.feeRateEstimateMtx.Unlock() - } - m.emit(&rm) -} - -func (m *Mesh) FeeRateEstimate(chainID uint32) uint64 { - m.feeRateEstimateMtx.RLock() - defer m.feeRateEstimateMtx.RUnlock() - feeRate, ok := m.feeRateEstimates[chainID] - if ok && time.Since(feeRate.LastUpdated) < feerates.FeeRateEstimateExpiry { - return feeRate.Value - } - return 0 -} - -func (m *Mesh) SubscribeToFeeRateEstimates() error { - req := mj.MustRequest(mj.RouteSubscribe, &mj.Subscription{ - Topic: mj.TopicFeeRateEstimate, - }) - - // Only possible non-error response is `true`. - var ok bool - return m.conn.RequestMesh(req, &ok) -} diff --git a/tatanka/client/mesh/trade.go b/tatanka/client/mesh/trade.go deleted file mode 100644 index 52e65fd355..0000000000 --- a/tatanka/client/mesh/trade.go +++ /dev/null @@ -1,266 +0,0 @@ -package mesh - -import ( - "encoding/json" - "fmt" - "sync" - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/fiatrates" - "decred.org/dcrdex/dex/msgjson" - "decred.org/dcrdex/tatanka/client/orderbook" - "decred.org/dcrdex/tatanka/client/trade" - "decred.org/dcrdex/tatanka/mj" - "decred.org/dcrdex/tatanka/tanka" -) - -type order struct { - *tanka.Order - oid tanka.ID40 - - // matches are accepted matches where the counterparty has responded to - // our bid positively. - matchesMtx sync.RWMutex - matches map[tanka.ID32]*tanka.Match - remain uint64 -} - -type market struct { - log dex.Logger - peerID tanka.PeerID - conn *meshConn - baseID uint32 - quoteID uint32 - - // ords are our orders and matches. - ordsMtx sync.RWMutex - ords map[tanka.ID40]*order - - // book is known market orders minus ours. - book *orderbook.Book -} - -func (m *market) addOwnOrder(ord *tanka.Order) { - oid := ord.ID() - m.ordsMtx.RLock() - if _, exists := m.ords[oid]; exists { - // ignore it then - return - } - m.ordsMtx.RUnlock() - o := &order{ - Order: ord, - oid: oid, - matches: make(map[tanka.ID32]*tanka.Match), - } - desire := &trade.DesiredTrade{ - Qty: ord.Qty, - Rate: ord.Rate, - Sell: ord.Sell, - } - matches, _ := trade.MatchBook(desire, m.feeParams(), m.book.Find) - // TODO: Do asyncronously. - o.remain = ord.Qty - for _, match := range matches { - now := time.Now() - matchThem := &tanka.Match{ - From: m.peerID, - OrderID: ord.ID(), - Qty: match.Qty, - BaseID: m.baseID, - QuoteID: m.quoteID, - Stamp: now, - } - ok, err := m.negotiate(ord.From, matchThem) - if err != nil { - m.log.Errorf("unable to negotiate match with peer %s: %v", ord.From, err) - continue - } - if !ok { - continue - } - // They agree. - matchUs := &tanka.Match{ - From: match.Order.From, - OrderID: match.Order.ID(), - Qty: match.Qty, - BaseID: m.baseID, - QuoteID: m.quoteID, - Stamp: now, - } - o.matches[matchUs.ID()] = matchUs - o.remain -= match.Qty - } - // TODO: Retry with orders from the book if some were not accepted but - // more compatible orders exist. We need a way to mark tried orders - // first. - m.ordsMtx.Lock() - m.ords[oid] = o - m.ordsMtx.Unlock() -} - -// TODO: Correct these. -func (m *market) feeParams() *trade.FeeParameters { - return &trade.FeeParameters{ - MaxFeeExposure: 1, - BaseFeesPerMatch: 1, - QuoteFeesPerMatch: 1, - } -} - -func (m *market) addOrder(ord *tanka.Order) { - // TODO: Validate order. - m.book.Add(ord) - check := func(o *order) { - o.matchesMtx.Lock() - defer o.matchesMtx.Unlock() - if o.remain == 0 { - return - } - if ord.Sell == o.Order.Sell { - return - } - if o.Sell { - if ord.Rate < o.Order.Rate { - return - } - } else if ord.Rate > o.Order.Rate { - return - } - if compat, _ := trade.OrderIsMatchable(o.remain, ord, m.feeParams()); !compat { - return - } - maxQty := ord.Qty - if ord.Qty > o.remain { - maxQty = o.remain - } - lots := maxQty / ord.LotSize - qty := lots * ord.LotSize - o.remain -= qty - now := time.Now() - matchThem := &tanka.Match{ - From: m.peerID, - OrderID: o.Order.ID(), - Qty: qty, - BaseID: m.baseID, - QuoteID: m.quoteID, - Stamp: now, - } - ok, err := m.negotiate(ord.From, matchThem) - if err != nil { - m.log.Errorf("unable to negotiate match with peer %s: %v", ord.From, err) - return - } - if !ok { - return - } - // They agree. - matchUs := &tanka.Match{ - From: ord.From, - OrderID: ord.ID(), - Qty: qty, - BaseID: m.baseID, - QuoteID: m.quoteID, - Stamp: now, - } - o.matches[matchUs.ID()] = matchUs - } - m.ordsMtx.RLock() - defer m.ordsMtx.RUnlock() - for _, ord := range m.ords { - check(ord) - } -} - -func (m *market) negotiate(to tanka.PeerID, match *tanka.Match) (bool, error) { - b, err := json.Marshal(*match) - if err != nil { - return false, err - } - msg := &msgjson.Message{ - Route: mj.RouteNegotiate, - Payload: b, - } - // Is this necessary or will request try the connection first? - if err := m.conn.ConnectPeer(to); err != nil { - return false, err - } - var ok bool - return ok, m.conn.RequestPeer(to, msg, &ok) -} - -func (m *market) handleNegotiate(match *tanka.Match) bool { - mid := match.ID() - m.ordsMtx.RLock() - ord, found := m.ords[match.OrderID] - m.ordsMtx.RUnlock() - // Order is finished or never existed. - if !found { - m.log.Debugf("ignoring match proposal for unknown order %s", match.OrderID) - return false - } - ord.matchesMtx.Lock() - defer ord.matchesMtx.Unlock() - // We already confirmed this match. - if ord.matches[mid] != nil { - return true - } - // We no longer have the quantity necessary. - // TODO: negotiate a lower quantity. - if ord.remain < match.Qty { - return false - } - ord.matches[mid] = match - ord.remain -= match.Qty - return true -} - -func (m *Mesh) handleMarketBroadcast(bcast *mj.Broadcast) { - mktName := string(bcast.Subject) - m.marketsMtx.RLock() - mkt, found := m.markets[mktName] - m.marketsMtx.RUnlock() - if !found { - m.log.Debugf("received order notification for unknown market %q", mktName) - return - } - switch bcast.MessageType { - case mj.MessageTypeTrollBox: - var troll mj.Troll - if err := json.Unmarshal(bcast.Payload, &troll); err != nil { - m.log.Errorf("error unmarshaling trollbox message: %v", err) - return - } - fmt.Printf("trollbox message for market %s: %s\n", mktName, troll.Msg) - case mj.MessageTypeNewOrder: - var ord tanka.Order - if err := json.Unmarshal(bcast.Payload, &ord); err != nil { - m.log.Errorf("error unmarshaling new order: %v", err) - return - } - mkt.addOrder(&ord) - case mj.MessageTypeNewSubscriber: - var ns mj.NewSubscriber - if err := json.Unmarshal(bcast.Payload, &ns); err != nil { - m.log.Errorf("error decoding new_subscriber payload: %v", err) - } - // c.emit(&NewMarketSubscriber{ - // MarketName: mktName, - // PeerID: bcast.PeerID, - // }) - default: - m.log.Errorf("received broadcast on %s -> %s with unknown message type %s", bcast.Topic, bcast.Subject) - } -} - -func (m *Mesh) FiatRate(assetID uint32) float64 { - m.fiatRatesMtx.RLock() - defer m.fiatRatesMtx.RUnlock() - sym := dex.BipIDSymbol(assetID) - rateInfo := m.fiatRates[sym] - if rateInfo != nil && time.Since(rateInfo.LastUpdate) < fiatrates.FiatRateDataExpiry && rateInfo.Value > 0 { - return rateInfo.Value - } - return 0 -} diff --git a/tatanka/client/orderbook/orderbook.go b/tatanka/client/orderbook/orderbook.go deleted file mode 100644 index 4af4974aba..0000000000 --- a/tatanka/client/orderbook/orderbook.go +++ /dev/null @@ -1,158 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package orderbook - -import ( - "fmt" - "sort" - "sync" - - "decred.org/dcrdex/tatanka/tanka" -) - -// Booker specifies the orderbook interface. -type Booker interface { - Order(id tanka.ID40) *tanka.Order - Orders(ids []tanka.ID40) []*tanka.Order - Find(filter *Filter) - Add(*tanka.Order) - Update(ou *tanka.OrderUpdate) error - Delete(id tanka.ID40) -} - -// Filter is used when searching for orders. -type Filter struct { - IsSell *bool - Check func(*tanka.Order) (done bool) -} - -var _ Booker = (*Book)(nil) - -// Book holds the orderbook. -type Book struct { - mtx sync.RWMutex - book map[tanka.ID40]*tanka.Order - // buys and sells use the same pointers as the book and are sorted - // with the best trade first. - buys, sells []*tanka.Order -} - -// NewBook created a new orderbook. -func New() *Book { - return &Book{ - book: make(map[tanka.ID40]*tanka.Order), - } -} - -// Order returns one order by id. -func (ob *Book) Order(id tanka.ID40) *tanka.Order { - ob.mtx.RLock() - defer ob.mtx.RUnlock() - return ob.book[id] -} - -// Orders returns all orders for the supplied ids. -func (ob *Book) Orders(ids []tanka.ID40) []*tanka.Order { - ob.mtx.RLock() - defer ob.mtx.RUnlock() - ords := make([]*tanka.Order, 0, len(ids)) - for _, id := range ids { - if o, has := ob.book[id]; has { - ords = append(ords, o) - } - } - return ords -} - -// Find filters through all orders with the filter.Check function until done is -// true. They are processed best orders first. -func (ob *Book) Find(filter *Filter) { - if filter == nil || filter.Check == nil { - return - } - find := func(isSell bool) { - ords := ob.buys - if isSell { - ords = ob.sells - } - for _, o := range ords { - if filter.Check(o) { - break - } - } - } - ob.mtx.RLock() - defer ob.mtx.RUnlock() - if filter.IsSell != nil { - find(*filter.IsSell) - } else { - find(false) - find(true) - } -} - -// addOrderAndSort must be called with the mtx held for writes. -func (ob *Book) addOrderAndSort(o *tanka.Order) { - ords := &ob.buys - sortFn := func(i, j int) bool { return (*ords)[i].Rate > (*ords)[j].Rate } - if o.Sell { - ords = &ob.sells - sortFn = func(i, j int) bool { return (*ords)[i].Rate < (*ords)[j].Rate } - } - *ords = append(*ords, o) - sort.Slice(*ords, sortFn) -} - -// deleteSortedOrder must be called with the mtx held for writes. -func (ob *Book) deleteSortedOrder(to *tanka.Order) { - ords := &ob.buys - if to.Sell { - ords = &ob.sells - } - for i, o := range *ords { - // Comparing pointers. - if o == to { - *ords = append((*ords)[:i], (*ords)[i+1:]...) - break - } - } -} - -// Add adds an order. -func (ob *Book) Add(o *tanka.Order) { - id := o.ID() - ob.mtx.Lock() - defer ob.mtx.Unlock() - _, has := ob.book[id] - if has { - return - } - ob.book[id] = o - ob.addOrderAndSort(o) -} - -// Update updates an order. -func (ob *Book) Update(ou *tanka.OrderUpdate) error { - ob.mtx.Lock() - defer ob.mtx.Unlock() - id := ou.ID() - o, has := ob.book[id] - if !has { - return fmt.Errorf("order %x not found", id) - } - o.Qty = ou.Qty - o.Stamp = ou.Stamp - return nil -} - -// Delete deletes an order from the books. -func (ob *Book) Delete(id tanka.ID40) { - ob.mtx.Lock() - defer ob.mtx.Unlock() - o, has := ob.book[id] - if has { - delete(ob.book, id) - ob.deleteSortedOrder(o) - } -} diff --git a/tatanka/client/orderbook/orderbook_test.go b/tatanka/client/orderbook/orderbook_test.go deleted file mode 100644 index 17552c86ec..0000000000 --- a/tatanka/client/orderbook/orderbook_test.go +++ /dev/null @@ -1,262 +0,0 @@ -package orderbook - -import ( - "testing" - "time" - - "decred.org/dcrdex/dex/encode" - "decred.org/dcrdex/tatanka/tanka" -) - -func mustParseTime(s string) time.Time { - t, err := time.Parse(time.RFC1123, s) - if err != nil { - panic(err) - } - return t -} - -func testOrders() []*tanka.Order { - var peer tanka.PeerID - copy(peer[:], encode.RandomBytes(32)) - baseID, quoteID := uint32(0), uint32(42) - lowbuy := &tanka.Order{ - From: peer, - BaseID: baseID, - QuoteID: quoteID, - Sell: false, - Qty: 1000, - Rate: 123, - LotSize: 3, - Nonce: 0, - Stamp: mustParseTime("Sun, 12 Dec 2024 12:23:00 UTC"), - } - highbuy := &tanka.Order{ - From: peer, - BaseID: baseID, - QuoteID: quoteID, - Sell: false, - Qty: 1000, - Rate: 1234, - LotSize: 2, - Nonce: 1, - Stamp: mustParseTime("Sun, 12 Dec 2024 12:23:00 UTC"), - } - lowsell := &tanka.Order{ - From: peer, - BaseID: baseID, - QuoteID: quoteID, - Sell: true, - Qty: 1000, - Rate: 12345, - LotSize: 2, - Nonce: 2, - Stamp: mustParseTime("Sun, 12 Dec 2024 12:23:00 UTC"), - } - highsell := &tanka.Order{ - From: peer, - BaseID: baseID, - QuoteID: quoteID, - Sell: true, - Qty: 1000, - Rate: 123456, - LotSize: 4, - Nonce: 3, - Stamp: mustParseTime("Sun, 12 Dec 2024 12:23:00 UTC"), - } - return []*tanka.Order{lowbuy, highbuy, lowsell, highsell} -} - -func TestOrders(t *testing.T) { - ob := New() - var oids []tanka.ID40 - for _, o := range testOrders() { - ob.Add(o) - oids = append(oids, o.ID()) - } - ords := ob.Orders(oids) - if len(ords) != 4 { - t.Fatalf("wanted 4 but got %d orders", len(ords)) - } -} - -func TestFindOrders(t *testing.T) { - ob := New() - tOrds := testOrders() - for _, o := range tOrds { - ob.Add(o) - } - var ords []*tanka.Order - yes, no := true, false - - tests := []struct { - name string - filter *Filter - wantOrderLen int - wantOrderIdx []int - }{{ - name: "all orders", - filter: &Filter{ - Check: func(o *tanka.Order) (done bool) { - ords = append(ords, o) - return false - }, - }, - wantOrderLen: 4, - wantOrderIdx: []int{1, 0, 2, 3}, - }, { - name: "sells", - filter: &Filter{ - IsSell: &yes, - Check: func(o *tanka.Order) (done bool) { - ords = append(ords, o) - return false - }, - }, - wantOrderLen: 2, - wantOrderIdx: []int{2, 3}, - }, { - name: "buys", - filter: &Filter{ - IsSell: &no, - Check: func(o *tanka.Order) (done bool) { - ords = append(ords, o) - return false - }, - }, - wantOrderLen: 2, - wantOrderIdx: []int{1, 0}, - }, { - name: "lot size over 2", - filter: &Filter{ - Check: func(o *tanka.Order) (done bool) { - if o.LotSize > 2 { - ords = append(ords, o) - } - return false - }, - }, - wantOrderLen: 2, - wantOrderIdx: []int{0, 3}, - }, { - name: "lot size over 2 and sell", - filter: &Filter{ - IsSell: &yes, - Check: func(o *tanka.Order) (done bool) { - if o.LotSize > 2 { - ords = append(ords, o) - } - return false - }, - }, - wantOrderLen: 1, - wantOrderIdx: []int{3}, - }, { - name: "buy done after one", - filter: &Filter{ - IsSell: &no, - Check: func(o *tanka.Order) (done bool) { - ords = append(ords, o) - return true - }, - }, - wantOrderLen: 1, - wantOrderIdx: []int{1}, - }, { - name: "nil filter", - }} - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - ords = nil - ob.Find(test.filter) - if len(ords) != test.wantOrderLen { - t.Fatalf("wanted %d but got %d orders", test.wantOrderLen, len(ords)) - } - for i, idx := range test.wantOrderIdx { - if tOrds[idx].ID() != ords[i].ID() { - t.Fatalf("returned order at idx %d not equal to test order at %d", idx, i) - } - } - }) - } -} - -func TestUpdate(t *testing.T) { - ob := New() - ords := testOrders() - for _, o := range ords { - ob.Add(o) - } - o := ords[0] - updateTime := mustParseTime("Sun, 28 Dec 2025 12:00:00 UTC") - tests := []struct { - name string - update *tanka.OrderUpdate - wantErr bool - }{{ - name: "ok", - update: &tanka.OrderUpdate{ - From: o.From, - Nonce: o.Nonce, - Qty: 7, - Stamp: updateTime, - }, - }, { - name: "order does not exist", - update: &tanka.OrderUpdate{ - From: o.From, - Nonce: 100, - Qty: 7, - Stamp: updateTime, - }, - wantErr: true, - }} - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - err := ob.Update(test.update) - if test.wantErr { - if err == nil { - t.Fatal("expected error") - } - return - } - if err != nil { - t.Fatalf("unexpected error %v", err) - } - ord := ob.Order(test.update.ID()) - if ord.Qty != test.update.Qty { - t.Fatalf("expected qty %d but got %d", test.update.Qty, ord.Qty) - } - if ord.Stamp != test.update.Stamp { - t.Fatalf("expected stamp %s but got %s", test.update.Stamp, ord.Stamp) - } - }) - } -} - -func TestDeleteOrder(t *testing.T) { - ob := New() - var oids []tanka.ID40 - for _, o := range testOrders() { - ob.Add(o) - oids = append(oids, o.ID()) - } - ob.Delete(oids[0]) - ob.Delete(oids[3]) - ous := ob.Orders(oids) - if len(ous) != 2 { - t.Fatalf("wanted 2 but got %d orders", len(oids)) - } - if ob.buys[0].ID() != oids[1] { - t.Fatal("incorrect order deleted") - } - if ob.sells[0].ID() != oids[2] { - t.Fatal("incorrect order deleted") - } - if len(ob.sells) != 1 { - t.Fatalf("wanted 1 but got %d orders", len(oids)) - } - if len(ob.buys) != 1 { - t.Fatalf("wanted 1 but got %d orders", len(oids)) - } -} diff --git a/tatanka/client/trade/README.md b/tatanka/client/trade/README.md deleted file mode 100644 index 6aef0f2ecc..0000000000 --- a/tatanka/client/trade/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Trading Sequence - -1. User initiates an order by specifying a desired quantity that they want to -buy or sell and an acceptable rate limit. This is the `DesiredTrade`. -2. The backend receives the `DesiredTrade` and checks whether there are any -orders on the order book to satisfy the trade using `MatchBook`. This generates -a set of potential matches (`[]*MatchProposal`), but there might be some -remaining quantity that we couldn't fulfill with the existing standing orders, -the `remain`. -3. Any `[]*MatchProposal` from `MatchBook` will generate match requests to -the owners of the matched standing orders. -4. If there is `remain`, we will generate our own standing order and broadcast -it to all market subscribers. \ No newline at end of file diff --git a/tatanka/client/trade/compat.go b/tatanka/client/trade/compat.go deleted file mode 100644 index f6bacbd322..0000000000 --- a/tatanka/client/trade/compat.go +++ /dev/null @@ -1,68 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package trade - -import ( - "math" - - "decred.org/dcrdex/dex/calc" - "decred.org/dcrdex/tatanka/tanka" -) - -// FeeParameters combines the user's fee exposure settings with the fees per -// lot, which are based on current on-chain fee rates and other asset-specific -// parameters e.g. swap tx size for utxo-based assets. -type FeeParameters struct { - // MaxFeeExposure is the maximum fee losses we are willing to incur from a - // trade, as a ratio of the trade size. - MaxFeeExposure float64 - BaseFeesPerMatch uint64 - QuoteFeesPerMatch uint64 -} - -const ( - ReasonOurQtyTooSmall = "our order size is less than their lot size" - ReasonTheirQtyTooSmall = "their order size is less than our lot size" -) - -// OrderIsMatchable determines whether a given standing limit order is -// matchable for our desired quantity and fee parameters. -func OrderIsMatchable(desiredQty uint64, theirOrd *tanka.Order, p *FeeParameters) (_ bool, reason string) { - // Can we satisfy their lot size? - if desiredQty < theirOrd.LotSize { - return false, ReasonOurQtyTooSmall - } - // Can they satisfy our lot size? - minLotSize := MinimumLotSize(theirOrd.Rate, p) - if theirOrd.Qty < minLotSize { - return false, ReasonTheirQtyTooSmall - } - return true, "" -} - -// MinimumLotSize calculates the smallest lot size that satisfies our -// desired maximum fee exposure. -func MinimumLotSize(msgRate uint64, p *FeeParameters) uint64 { - // fee_exposure = (lots * base_fees_per_lot / qty) + (lots * quote_fees_per_lot / quote_qty) - // quote_qty = qty * rate - // ## We want fee_exposure < max_fee_exposure - // (lots * base_fees_per_lot / qty) + (lots * quote_fees_per_lot / (qty * rate)) < max_fee_exposure - // ## multiplying both sides by qty - // (lots * base_fees_per_lot) + (lots * quote_fees_per_lot / rate) < max_fee_exposure * qty - // ## Factoring out lots - // lots * (base_fees_per_lot + (quote_fees_per_lot / rate)) < max_fee_exposure * qty - // ## isolating lots - // lots < (max_fee_exposure * qty) / (base_fees_per_lot + (quote_fees_per_lot / rate)) - // ## Noting that lots = qty / lot_size - // qty / lot_size < (max_fee_exposure * qty) / (base_fees_per_lot + (quote_fees_per_lot / rate)) - // ## Dividing both size by qty - // 1 / lot_size < max_fee_exposure / (base_fees_per_lot + (quote_fees_per_lot / rate)) - // ## Fliparoo - // lot_size > (base_fees_per_lot + (quote_fees_per_lot / rate)) / max_fee_exposure - atomicRate := float64(msgRate) / calc.RateEncodingFactor - perfectLotSize := math.Ceil((float64(p.BaseFeesPerMatch) + float64(p.QuoteFeesPerMatch)/atomicRate) / float64(p.MaxFeeExposure)) - // How many powers of 2? - n := math.Ceil(math.Log2(perfectLotSize)) - return uint64(math.Round(math.Pow(2, n))) -} diff --git a/tatanka/client/trade/compat_test.go b/tatanka/client/trade/compat_test.go deleted file mode 100644 index e075195eb9..0000000000 --- a/tatanka/client/trade/compat_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package trade - -import ( - "testing" - - "decred.org/dcrdex/dex/calc" - "decred.org/dcrdex/tatanka/tanka" -) - -func TestMinimumLotSize(t *testing.T) { - // Exchange rate of 1. 10 atoms lost to fees in the match. To stay under 1%, - // the lot size needs to be >= 1000, so 1024 is the closest power of 2. - p := &FeeParameters{ - MaxFeeExposure: 0.01, - BaseFeesPerMatch: 5, - QuoteFeesPerMatch: 5, - } - var atomicRate uint64 = 1 - msgRate := atomicRate * calc.RateEncodingFactor - if l := MinimumLotSize(msgRate, p); l != 1024 { - t.Fatal(p, l) - } - // Doubling the fees should double the lot size. - p.QuoteFeesPerMatch *= 2 - p.BaseFeesPerMatch *= 2 - if l := MinimumLotSize(msgRate, p); l != 2048 { - t.Fatal(p, l) - } - // Double the quote fees, but also double the rate (to halve the quote qty). - // The effects should offset. - p.QuoteFeesPerMatch *= 2 - msgRate *= 2 - if l := MinimumLotSize(msgRate, p); l != 2048 { - t.Fatal(p, l) - } -} - -func TestOrderIsMatchable(t *testing.T) { - // Set up fee parameters with a min lot size of 1024. - p := &FeeParameters{ - MaxFeeExposure: 0.01, - BaseFeesPerMatch: 5, - QuoteFeesPerMatch: 5, - } - var desiredQty uint64 = 1024 - // Create a standing order that matches our lot size and has 2 lots - // available. - var atomicRate uint64 = 1 - msgRate := atomicRate * calc.RateEncodingFactor - var lotSize uint64 = 1024 - theirOrd := &tanka.Order{ - LotSize: lotSize, - Rate: msgRate, - Qty: lotSize * 2, - } - // Should be matchable. - if matchable, reason := OrderIsMatchable(desiredQty, theirOrd, p); !matchable { - t.Fatal(reason) - } - // Quadrupling our min lot size should cause unmatchability. - p.MaxFeeExposure /= 4 - matchable, reason := OrderIsMatchable(desiredQty, theirOrd, p) - if matchable { - t.Fatal("Their remaining qty should be too small") - } - if reason != ReasonTheirQtyTooSmall { - t.Fatal(reason) - } - p.MaxFeeExposure *= 4 // undoing - // Make our desired qty too small. - desiredQty /= 2 - matchable, reason = OrderIsMatchable(desiredQty, theirOrd, p) - if matchable { - t.Fatal("Our desired qty should be too small") - } - if reason != ReasonOurQtyTooSmall { - t.Fatal(reason) - } -} diff --git a/tatanka/client/trade/match.go b/tatanka/client/trade/match.go deleted file mode 100644 index c2512019dd..0000000000 --- a/tatanka/client/trade/match.go +++ /dev/null @@ -1,58 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package trade - -import ( - "decred.org/dcrdex/tatanka/client/orderbook" - "decred.org/dcrdex/tatanka/tanka" -) - -// DesiredTrade is the parameters of a trade that the user wants to make. -type DesiredTrade struct { - Qty uint64 - Rate uint64 - Sell bool -} - -// MatchProposal is a potential match based on our desired trade and the -// current standing orders. -type MatchProposal struct { - Order *tanka.Order - Qty uint64 -} - -// MatchBook matches our desired trade with the order book side. It is assumed -// that the order book side is correct for our choice of buy/sell, and that -// the orders are ordered by rate, with low-to-high for sell orders, and -// high-to-low for buy orders. -func MatchBook(desire *DesiredTrade, p *FeeParameters, findOrders func(filter *orderbook.Filter)) (matches []*MatchProposal, remain uint64) { - remain = desire.Qty - check := func(ord *tanka.Order) (done bool) { - // Check rate compatibility. - if desire.Sell { - if ord.Rate < desire.Rate { - return true - } - } else if ord.Rate > desire.Rate { - return true - } - // Check lot size compatibility. - if compat, _ := OrderIsMatchable(desire.Qty, ord, p); !compat { - return - } - // How much can we match? - maxQty := ord.Qty - if ord.Qty > remain { - maxQty = remain - } - lots := maxQty / ord.LotSize - qty := lots * ord.LotSize - matches = append(matches, &MatchProposal{Order: ord, Qty: qty}) - remain -= qty - return remain == 0 - } - wantSell := !desire.Sell - findOrders(&orderbook.Filter{IsSell: &wantSell, Check: check}) - return matches, remain -} diff --git a/tatanka/client/trade/match_test.go b/tatanka/client/trade/match_test.go deleted file mode 100644 index 93971ca2e8..0000000000 --- a/tatanka/client/trade/match_test.go +++ /dev/null @@ -1,137 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package trade - -import ( - "testing" - - "decred.org/dcrdex/dex/calc" - "decred.org/dcrdex/tatanka/client/orderbook" - "decred.org/dcrdex/tatanka/tanka" -) - -func TestMatchBook(t *testing.T) { - // These can be varied before resetting. - weSell := false - var weWantLots uint64 = 1 - - // These will be initialized by reset. - var lotSize, atomicRate, msgRate, baseQty uint64 - var desire *DesiredTrade - var ords []*tanka.Order - var p *FeeParameters - var book *orderbook.Book - - // A function to reset everything - reset := func() { - atomicRate = 1 - lotSize = 1024 - msgRate = atomicRate * calc.RateEncodingFactor - baseQty = weWantLots * lotSize - p = &FeeParameters{ - MaxFeeExposure: 0.01, - BaseFeesPerMatch: 5, - QuoteFeesPerMatch: 5, - } - desire = &DesiredTrade{ - Qty: baseQty, - Rate: msgRate, - Sell: weSell, - } - ords = []*tanka.Order{ - { - Rate: msgRate, - Qty: baseQty, - LotSize: lotSize, - Sell: !weSell, - }, - } - book = orderbook.New() - for _, o := range ords { - book.Add(o) - } - } - - // Our testing function - testMatches := func(expRemain uint64, expQtys []uint64) { - t.Helper() - matches, remain := MatchBook(desire, p, book.Find) - if remain != expRemain { - t.Fatal("wrong remain", remain, expRemain) - } - if len(matches) != len(expQtys) { - t.Fatal("wrong number of matches", matches, len(expQtys)) - } - for i, m := range matches { - if m.Qty != expQtys[i] { - t.Fatal("wrong qty", i, m.Qty, expQtys[i]) - } - } - } - - // Basic 1-lot buy order that perfectly matches the 1 order on the book, - // leaving no remainder. - reset() - testMatches(0, []uint64{baseQty}) - // Double the first order's qty. Shouldn't change anything. - ords[0].Qty *= 2 - testMatches(0, []uint64{baseQty}) - // Triple our ask. We'll get the whole (now-doubled) order, with some - // remainder. - desire.Qty *= 3 - testMatches(baseQty, []uint64{2 * baseQty}) - // Add another order to satisfy our needs. - ords = append(ords, &tanka.Order{ - Rate: msgRate, - Qty: baseQty, - LotSize: lotSize, - Nonce: 1, - Sell: !weSell, - }) - book.Add(ords[1]) - testMatches(0, []uint64{2 * baseQty, baseQty}) - // Double the rate of the new order though, and we're back to only getting - // the first order. - ords[1].Rate *= 2 - testMatches(baseQty, []uint64{2 * baseQty}) - - // Make sure it works with multiple lots too. - weWantLots = 4 - reset() - testMatches(0, []uint64{baseQty}) - - // Basic 1-lot sell order now. - weSell = true - reset() - testMatches(0, []uint64{baseQty}) - // Doubling our ask should leave a remainder. - desire.Qty *= 2 - testMatches(baseQty, []uint64{baseQty}) - // Add another order to satisfy our needs. - ords = append(ords, &tanka.Order{ - Rate: msgRate, - Qty: baseQty, - LotSize: lotSize, - Nonce: 1, - Sell: !weSell, - }) - book.Add(ords[1]) - testMatches(0, []uint64{baseQty, baseQty}) - // But if the second order isn't offering enough, we can't match it. - ords[1].Rate -= 1 - testMatches(baseQty, []uint64{baseQty}) - - // Back to buying 1 lot - weSell, weWantLots = false, 1 - reset() - testMatches(0, []uint64{baseQty}) - - // Make the order incompatible because our maximum fee exposure is too low. - p.MaxFeeExposure /= 2 - testMatches(baseQty, nil) - - // Sanity check - reset() - testMatches(0, []uint64{baseQty}) -} diff --git a/tatanka/client_messages.go b/tatanka/client_messages.go deleted file mode 100644 index 08b0bd1d74..0000000000 --- a/tatanka/client_messages.go +++ /dev/null @@ -1,866 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package tatanka - -import ( - "context" - "encoding/json" - "errors" - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/msgjson" - "decred.org/dcrdex/dex/utils" - "decred.org/dcrdex/tatanka/mj" - "decred.org/dcrdex/tatanka/tanka" - "decred.org/dcrdex/tatanka/tcp" -) - -// clientJob is a job for the remote clients loop. -type clientJob struct { - task any - res chan any -} - -// clientJobNewRemote is a clientJob task that adds a remote client to the -// remoteClients map. -type clientJobNewRemote struct { - tankaID tanka.PeerID - clientID tanka.PeerID -} - -// clientJobRemoteDisconnect is a clientJob task that removes a remote client -// from the remoteClients map. -type clientJobRemoteDisconnect clientJobNewRemote - -// clientJobFindRemotes is a clientJob that produces a list of remote tatanka -// nodes to which the client is thought to be connected. -type clientJobFindRemotes struct { - clientID tanka.PeerID -} - -// runRemoteClientsLoop is a loop for reading and writing to the remoteClients -// map. I don't know if it's more performant, I'm just tired of mutex patterns. -func (t *Tatanka) runRemoteClientsLoop(ctx context.Context) { - for { - select { - case job := <-t.clientJobs: - switch task := job.task.(type) { - case *clientJobNewRemote: - srvs, found := t.remoteClients[task.clientID] - if !found { - srvs = make(map[tanka.PeerID]struct{}) - t.remoteClients[task.clientID] = srvs - } - if _, found = srvs[task.tankaID]; !found { - srvs[task.tankaID] = struct{}{} - if t.log.Level() <= dex.LevelTrace { - t.log.Tracef("Indexing new remote client %s from %s", task.clientID, task.tankaID) - } - } - job.res <- true - case *clientJobRemoteDisconnect: - srvs, found := t.remoteClients[task.clientID] - if !found { - job.res <- true - continue - } - delete(srvs, task.tankaID) - if len(srvs) == 0 { - delete(t.remoteClients, task.clientID) - } - job.res <- true - case *clientJobFindRemotes: - job.res <- utils.CopyMap(t.remoteClients[task.clientID]) - } - case <-ctx.Done(): - return - } - } -} - -// registerRemoteClient ensures the remote client is in the remoteClients map. -func (t *Tatanka) registerRemoteClient(tankaID, clientID tanka.PeerID) { - job := &clientJob{ - task: &clientJobNewRemote{ - clientID: clientID, - tankaID: tankaID, - }, - res: make(chan any, 1), - } - t.clientJobs <- job - select { - case <-job.res: - case <-t.ctx.Done(): - } -} - -// Tatanka.specialHandlers - -// setSubscriptions sets the topic for which a client is subscribed. -// TODO: This function currently only adds subscriptions, but it should also remove -// the subscriptions that are not in the set. -func (t *Tatanka) setSubscriptions(peerID tanka.PeerID, subs map[tanka.Topic][]tanka.Subject) { - for topic, subjects := range subs { - for _, subject := range subjects { - t.storeSubscription(peerID, topic, subject, &mj.Broadcast{ - Topic: topic, - Subject: subject, - MessageType: mj.MessageTypeNewSubscriber, - PeerID: peerID, - Stamp: time.Now(), - }) - } - } -} - -// handleClientConnect handles a new locally-connected client. checking -// reputation before adding the client to the map. -func (t *Tatanka) handleClientConnect(cl tanka.Sender, msg *msgjson.Message) *msgjson.Error { - var conn *mj.Connect - if err := msg.Unmarshal(&conn); err != nil { - return msgjson.NewError(mj.ErrBadRequest, "error unmarshaling client connection configuration from %q: %v", cl.PeerID(), err) - } - - p, err := t.db.Peer(conn.ID) - if err != nil { - return msgjson.NewError(mj.ErrInternal, "error getting peer info for peer %q: %v", conn.ID, err) - } - - if err := mj.CheckSig(msg, p.PubKey); err != nil { - return msgjson.NewError(mj.ErrAuth, "signature error: %v", err) - } - - cl.SetPeerID(p.ID) - - pp := &peer{Peer: p, Sender: cl, rrs: make(map[tanka.PeerID]*tanka.Reputation)} - - // TODO: this is temporarily removed until a future change - // implements how bonds will be communicated between the client and - // server. - /*if pp.banned() { - return msgjson.NewError(mj.ErrBannned, "your tier is <= 0. post some bonds") - }*/ - - // Since this is ultimately a comms.Link, we definitely have a Done channel. - dunner, is := cl.(interface{ Done() <-chan struct{} }) - if !is { - return msgjson.NewError(mj.ErrInternal, "internal error: client does not implement Done") - } - - bondTier := p.BondTier() - - t.clientMtx.Lock() - oldClient := t.clients[conn.ID] - t.clients[conn.ID] = &client{peer: pp} - numClients := len(t.clients) - if len(conn.InitialSubs) > 0 { - t.setSubscriptions(conn.ID, conn.InitialSubs) - } - t.clientMtx.Unlock() - - go func() { - <-dunner.Done() - t.handleClientDisconnect(conn.ID) - }() - - if oldClient != nil { - t.log.Debugf("new connection for already connected client %q", conn.ID) - oldClient.Disconnect() - } else if numClients >= t.maxClients { - return msgjson.NewError(mj.ErrCapacity, "node is at capacity") - } - - t.sendResult(cl, msg.ID, t.generateConfig(bondTier)) - - req := mj.MustRequest(mj.RouteNewClient, conn) - for tt, s := range t.tatankaNodes() { - if err := t.request(s, req, func(m *msgjson.Message) { - var rep *tanka.Reputation - if err := m.UnmarshalResult(&rep); err != nil { - t.log.Errorf("error parsing response for new client request to %s: %w", tt, err) - return - } - pp.mtx.Lock() - pp.rrs[conn.ID] = rep - pp.mtx.Unlock() - }); err != nil { - t.log.Errorf("error sharing new client info with tatanka node %q", s.ID) - } - } - - return nil -} - -// Tatanka.clientHandlers - -type clientRequestHandler = func(c *client, msg *msgjson.Message) *msgjson.Error -type clientNotificationHandler = func(c *client, msg *msgjson.Message) - -// handleClientMessage handles incoming messages from locally-connected clients. -// All messages except for handleClientConnect and handlePostBond are handled -// here, with some common pre-processing and validation done before the -// subsequent route handler is called. -func (t *Tatanka) handleClientMessage(cl tanka.Sender, msg *msgjson.Message) *msgjson.Error { - peerID := cl.PeerID() - c := t.clientNode(peerID) - if c == nil { - t.log.Errorf("Ignoring message from unknown client %s", peerID) - cl.Disconnect() - return nil - } - - if err := mj.CheckSig(msg, c.PubKey); err != nil { - t.log.Errorf("Signature error for %q message from %q: %v", msg.Route, c.ID, err) - return msgjson.NewError(mj.ErrSig, "signature doesn't check") - } - - switch handle := t.clientHandlers[msg.Route].(type) { - case clientRequestHandler: - return handle(c, msg) - case clientNotificationHandler: - handle(c, msg) - } - return nil // Notification -} - -// handlePostBond handles a new bond sent from a locally connected client. -// handlePostBond is the only client route than can be invoked before the user -// is bonded. -func (t *Tatanka) handlePostBond(cl tanka.Sender, msg *msgjson.Message) *msgjson.Error { - var bonds []*tanka.Bond - if err := msg.Unmarshal(&bonds); err != nil { - t.log.Errorf("Bond-posting client sent a bad bond message: %v", err) - return msgjson.NewError(mj.ErrBadRequest, "bad request") - } - - if len(bonds) == 0 { - t.log.Errorf("Bond-posting client sent zero bonds") - return msgjson.NewError(mj.ErrBadRequest, "no bonds sent") - } - - peerID := bonds[0].PeerID - if peerID == (tanka.PeerID{}) { - t.log.Errorf("Bond-posting client didn't provide a peer ID") - return msgjson.NewError(mj.ErrBadRequest, "no peer ID") - } - - for i := 1; i < len(bonds); i++ { - if bonds[i].PeerID != bonds[0].PeerID { - t.log.Errorf("Bond-posting client provided non uniform peer IDs") - return msgjson.NewError(mj.ErrBadRequest, "mismatched peer IDs") - } - } - - for _, b := range bonds { - if b == nil { - t.log.Errorf("Bond-posting client %s sent a nil bond", peerID) - return msgjson.NewError(mj.ErrBadRequest, "nil bond") - } - - if len(b.CoinID) == 0 { - t.log.Errorf("Bond-posting client %q sent a bond with no coin ID", peerID) - return msgjson.NewError(mj.ErrBadRequest, "no coin ID") - } - - if b.Expiration.Before(time.Now()) { - t.log.Errorf("Bond-posting client %q sent an expired bond", peerID) - return msgjson.NewError(mj.ErrBadRequest, "bond already expired") - } - - t.chainMtx.RLock() - ch := t.chains[b.AssetID] - t.chainMtx.RUnlock() - if ch == nil { - t.log.Errorf("Bond-posting client %q sent a bond for an unknown chain %d", peerID, b.AssetID) - return msgjson.NewError(mj.ErrBadRequest, "unsupported asset") - } - - if err := ch.CheckBond(b); err != nil { - t.log.Errorf("Bond-posting client %q with bond %s didn't pass validation for chain %d: %v", peerID, b.CoinID, b.AssetID, err) - return msgjson.NewError(mj.ErrBadRequest, "failed validation") - } - - if err := t.db.StoreBond(b); err != nil { - t.log.Errorf("Error storing bond for client %s in db: %v", peerID, err) - return msgjson.NewError(mj.ErrInternal, "internal error") - } - } - - liveBonds, err := t.db.GetBonds(peerID) - if err != nil { - t.log.Errorf("Error retrieving bonds for client %s in db: %v", peerID, err) - msgjson.NewError(mj.ErrInternal, "internal error") - } - - if len(liveBonds) > 0 { // Probably no way to get here with empty liveBonds, but checking anyway. - if c := t.clientNode(peerID); c != nil { - c.updateBonds(liveBonds) - } - } - - t.sendResult(cl, msg.ID, true) - - return nil -} - -// NodeInfoResponse is the response to a NodeInfo request. -type NodeInfoResponse struct { - Capacity uint64 `json:"capacity"` - Chains []uint32 `json:"chains"` - Whitelist []*BootNode `json:"whitelist"` -} - -func (t *Tatanka) handleNodeInfo(any) (any, error) { - t.clientMtx.RLock() - numClients := len(t.clients) - t.clientMtx.RUnlock() - - var remainingCapacity uint64 - if t.maxClients > numClients { - remainingCapacity = uint64(t.maxClients - numClients) - } - - t.chainMtx.RLock() - chains := make([]uint32, 0, len(t.chains)) - for assetID := range t.chains { - chains = append(chains, assetID) - } - t.chainMtx.RUnlock() - - whitelist := make([]*BootNode, 0, len(t.whitelist)) - - for peerID, node := range t.whitelist { - var n tcp.RemoteNodeConfig - if err := json.Unmarshal(node.cfg, &n); err != nil { - t.log.Errorf("error reading boot node configuration: %w", err) - continue - } - whitelist = append(whitelist, &BootNode{ - PeerID: peerID[:], - Config: node.cfg, - Protocol: node.protocol, - }) - } - - return NodeInfoResponse{remainingCapacity, chains, whitelist}, nil -} - -func (t *Tatanka) notifySubscribersOfNewSubscriber( - subs map[tanka.PeerID]struct{}, - topicName tanka.Topic, - subjectName tanka.Subject, - bcast *mj.Broadcast, -) { - clientMsg := mj.MustNotification(mj.RouteBroadcast, bcast) - mj.SignMessage(t.priv, clientMsg) - clientMsgB, _ := json.Marshal(clientMsg) - for peerID := range subs { - subscriber, found := t.clients[peerID] - if !found { - t.log.Errorf("client not found for subscriber %s on topic %q, subject %q", peerID, topicName, subjectName) - continue - } - - if err := subscriber.Sender.SendRaw(clientMsgB); err != nil { - // DRAFT TODO: Remove subscriber and client and disconnect? - // Or do that in (*Tatanka).send? - t.log.Errorf("Error relaying broadcast: %v", err) - continue - } - } -} - -// storeSubscription adds a client to the subscriptions map. -// -// t.clientsMtx must be held when calling this function. -func (t *Tatanka) storeSubscription( - peerID tanka.PeerID, - topicName tanka.Topic, - subjectName tanka.Subject, - bcast *mj.Broadcast, -) { - topic, exists := t.topics[topicName] - if exists { - // We have the topic. Do we have the subject? - topic.subscribers[peerID] = struct{}{} - subs, exists := topic.subjects[subjectName] - if exists { - // We already have the subject, distribute the broadcast to existing - // subscribers. - t.notifySubscribersOfNewSubscriber(subs, topicName, subjectName, bcast) - subs[peerID] = struct{}{} - } else { - // Add the subject. Nothing to broadcast. - topic.subjects[subjectName] = map[tanka.PeerID]struct{}{ - peerID: {}, - } - } - } else { - // New topic and subject. - t.log.Tracef("Adding new subscription topic and subject %s -> %s", topicName, subjectName) - t.topics[topicName] = &Topic{ - subjects: map[tanka.Subject]map[tanka.PeerID]struct{}{ - subjectName: { - peerID: {}, - }, - }, - subscribers: map[tanka.PeerID]struct{}{ - peerID: {}, - }, - } - } -} - -func (t *Tatanka) handleUpdateSubscriptions(c *client, msg *msgjson.Message) *msgjson.Error { - var updateSubs *mj.UpdateSubscriptions - if err := msg.Unmarshal(&updateSubs); err != nil || updateSubs == nil { - t.log.Errorf("error unmarshaling update subscriptions from %s: %w", c.ID, err) - return msgjson.NewError(mj.ErrBadRequest, "is this payload an update subscriptions?") - } - - t.clientMtx.Lock() - t.setSubscriptions(c.ID, updateSubs.Subscriptions) - t.clientMtx.Unlock() - - t.sendResult(c, msg.ID, true) - return nil -} - -// handleSubscription handles a new subscription, adding the subject to the -// map if it doesn't exist. It then distributes a NewSubscriber broadcast -// to all current subscribers and remote tatankas. -func (t *Tatanka) handleSubscription(c *client, msg *msgjson.Message) *msgjson.Error { - var sub *mj.Subscription - if err := msg.Unmarshal(&sub); err != nil || sub == nil || sub.Topic == "" { - t.log.Errorf("error unmarshaling subscription from %s: %w", c.ID, err) - return msgjson.NewError(mj.ErrBadRequest, "is this payload a subscription?") - } - - newSubB, err := json.Marshal(&mj.NewSubscriber{ - PeerID: c.ID, - Topic: sub.Topic, - Subject: sub.Subject, - }) - if err != nil { - t.log.Errorf("error marshaling subscription from %s: %w", c.ID, err) - return msgjson.NewError(mj.ErrInternal, "why didn't the NewSubscriber marshal?") - } - - bcast := &mj.Broadcast{ - Topic: sub.Topic, - Subject: sub.Subject, - MessageType: mj.MessageTypeNewSubscriber, - PeerID: c.ID, - Stamp: time.Now(), - Payload: newSubB, - } - - // Send it to all other remote tatankas. - t.relayBroadcast(bcast, c.ID) - - // Find it and broadcast to locally-connected clients, or add the subject if - // it doesn't exist. - // - // TODO: clientMtx here is locked while a message is sent to all subscribers. - // This needs to be avoided. - t.clientMtx.Lock() - defer t.clientMtx.Unlock() - - t.storeSubscription(c.ID, sub.Topic, sub.Subject, bcast) - - t.sendResult(c, msg.ID, true) - t.replySubscription(c, sub.Topic) - return nil -} - -// replySubscription sends a follow up reply to a sender's subscription after -// their message has been processed successfully. -func (t *Tatanka) replySubscription(cl tanka.Sender, topic tanka.Topic) { - switch topic { - case mj.TopicFiatRate: - if t.fiatOracleEnabled() { - rates := t.fiatRateOracle.Rates() - if len(rates) == 0 { // no data to send - return - } - - reply := mj.MustNotification(mj.RouteRates, &mj.RateMessage{ - Topic: mj.TopicFiatRate, - Rates: rates, - }) - - if err := t.send(cl, reply); err != nil { - peerID := cl.PeerID() - t.log.Errorf("error sending result to %q: %v", dex.Bytes(peerID[:]), err) - } - } - - case mj.TopicFeeRateEstimate: - if t.hasFeeRatesOracle() { - estimates := t.feeRatesOracle.FeeRateEstimates() - if len(estimates) == 0 { // no data to send - return - } - - reply := mj.MustNotification(mj.RouteFeeRateEstimate, &mj.FeeRateEstimateMessage{ - Topic: mj.TopicFeeRateEstimate, - FeeRateEstimates: estimates, - }) - - if err := t.send(cl, reply); err != nil { - peerID := cl.PeerID() - t.log.Errorf("error sending result to %q: %v", dex.Bytes(peerID[:]), err) - } - } - } -} - -func (t *Tatanka) unsub(peerID tanka.PeerID, topicID tanka.Topic, subjectID tanka.Subject) *msgjson.Error { - t.clientMtx.Lock() - defer t.clientMtx.Unlock() - topic, exists := t.topics[topicID] - if !exists { - t.log.Errorf("client %q unsubscribed from an unknown topic %q", peerID, topicID) - return msgjson.NewError(mj.ErrBadRequest, "unknown topic") - } - if _, found := topic.subscribers[peerID]; !found { - t.log.Errorf("client %q unsubscribed from topic %q, to which they were not suscribed", peerID, topicID) - return msgjson.NewError(mj.ErrBadRequest, "unknown topic") - } - if subjectID == "" { - // Unsubbing all subjects. - for subID, subs := range topic.subjects { - delete(subs, peerID) - if len(subs) == 0 { - delete(topic.subjects, subID) - } - } - } else { - subs, exists := topic.subjects[subjectID] - if !exists { - t.log.Errorf("client %q unsubscribed subject %q, topic %q, to which they were not suscribed", peerID, subjectID, topicID) - return msgjson.NewError(mj.ErrBadRequest, "unknown subject") - } - delete(subs, peerID) - if len(subs) == 0 { - delete(topic.subjects, subjectID) - } - } - - if len(topic.subscribers) == 0 { - delete(t.topics, topicID) - } - return nil -} - -// skipRelay checks whether the message has already been handled. This function -// may not be necessary with the version 0 whitelisted mesh net, since -// it is expected to be highly connected and relays only go one hop. -func (t *Tatanka) skipRelay(msg *msgjson.Message) bool { - bcastID := mj.MessageDigest(msg) - t.relayMtx.Lock() - defer t.relayMtx.Unlock() - _, exists := t.recentRelays[bcastID] - if !exists { - t.recentRelays[bcastID] = time.Now() - } - return exists -} - -// distributeBroadcastedMessage distributes the broadcast to any -// locally-connected subscribers. -func (t *Tatanka) distributeBroadcastedMessage(bcast *mj.Broadcast, mustExist bool) *msgjson.Error { - relayedMsg := mj.MustNotification(mj.RouteBroadcast, bcast) - mj.SignMessage(t.priv, relayedMsg) - relayedMsgB, _ := json.Marshal(relayedMsg) - - t.clientMtx.RLock() - defer t.clientMtx.RUnlock() - topic, found := t.topics[bcast.Topic] - if !found { - if mustExist { - t.log.Errorf("client %q broadcasted to an unknown topic %q", bcast.PeerID, bcast.Topic) - return msgjson.NewError(mj.ErrBadRequest, "unknown topic") - } - return nil - } - - relay := func(subs map[tanka.PeerID]struct{}) { - for peerID := range subs { - subscriber, found := t.clients[peerID] - if !found { - t.log.Errorf("client not found for subscriber %s on topic %q, subject %q", peerID, bcast.Topic, bcast.Subject) - continue - } - - if err := subscriber.Sender.SendRaw(relayedMsgB); err != nil { - // DRAFT TODO: Remove subscriber and client and disconnect? - // Or do that in (*Tatanka).send? - t.log.Errorf("Error relaying broadcast: %v", err) - continue - } - } - } - - if bcast.Subject == "" { - relay(topic.subscribers) - } else { - subs, found := topic.subjects[bcast.Subject] - if !found { - if mustExist { - t.log.Errorf("client %s broadcasted to an unknown subject %q on topic %s", bcast.PeerID, bcast.Subject, bcast.Topic) - } - return msgjson.NewError(mj.ErrBadRequest, "unknown subject") - } - relay(subs) - } - return nil -} - -// handleBroadcast handles a broadcast from a locally connected client, -// forwarding the message to all remote tatankas and local subscribers. -func (t *Tatanka) handleBroadcast(c *client, msg *msgjson.Message) *msgjson.Error { - p := c.peer - if t.skipRelay(msg) { - return nil - } - - var bcast *mj.Broadcast - if err := msg.Unmarshal(&bcast); err != nil || bcast == nil || bcast.Topic == "" { - t.log.Errorf("error unmarshaling broadcast from %s: %w", p.ID, err) - return msgjson.NewError(mj.ErrBadRequest, "is this payload a broadcast?") - } - - if bcast.PeerID != p.ID { - t.log.Errorf("broadcast peer ID does not match connected client: %s != %s", bcast.PeerID, p.ID) - return msgjson.NewError(mj.ErrBadRequest, "who's broadcast is this?") - } - - if time.Since(bcast.Stamp) > tanka.EpochLength || time.Until(bcast.Stamp) > tanka.EpochLength { - t.log.Errorf("Ignoring broadcast with old stamp received from %s", p.ID) - return msgjson.NewError(mj.ErrBadRequest, "too old") - } - - // Relay to remote tatankas first. - t.relayBroadcast(bcast, p.ID) - - // Send to local subscribers. - if msgErr := t.distributeBroadcastedMessage(bcast, true); msgErr != nil { - return msgErr - } - - t.sendResult(p, msg.ID, true) - - // Handle unsubs. - switch bcast.MessageType { - case mj.MessageTypeUnsubTopic: - t.unsub(p.ID, bcast.Topic, "") - case mj.MessageTypeUnsubSubject: - t.unsub(p.ID, bcast.Topic, bcast.Subject) - } - - return nil -} - -// relayBroadcast sends a relay_broadcast message to all remote tatankas. -func (t *Tatanka) relayBroadcast(bcast *mj.Broadcast, from tanka.PeerID) { - relayedMsg := mj.MustNotification(mj.RouteRelayBroadcast, bcast) - mj.SignMessage(t.priv, relayedMsg) - relayedMsgB, _ := json.Marshal(relayedMsg) - - for _, tt := range t.tatankaNodes() { - if tt.ID == from { - // don't send back to sender - continue - } - if err := tt.Sender.SendRaw(relayedMsgB); err != nil { - t.log.Errorf("Error relaying broadcast to %s: %v", tt.ID, err) - } - } -} - -// findPath finds remote tatankas that are hosting the specified peer. -func (t *Tatanka) findPath(peerID tanka.PeerID) []*remoteTatanka { - job := &clientJob{ - task: &clientJobFindRemotes{ - clientID: peerID, - }, - res: make(chan any), - } - t.clientJobs <- job - ttIDs := (<-job.res).(map[tanka.PeerID]struct{}) - - nodes := make([]*remoteTatanka, 0, len(ttIDs)) - t.tatankasMtx.RLock() - for ttID := range ttIDs { - if tt, found := t.tatankas[ttID]; found { - nodes = append(nodes, tt) - } - } - t.tatankasMtx.RUnlock() - return nodes -} - -// handleTankagram forwards a tankagram from a locally connected client, sending -// it to the recipient if the recipient is also locally connected, or else -// bouncing it off of a remote tatanka if one is known. -func (t *Tatanka) handleTankagram(c *client, msg *msgjson.Message) *msgjson.Error { - var gram mj.Tankagram - if err := msg.Unmarshal(&gram); err != nil { - t.log.Errorf("Error unmarshaling tankagram from %s: %w", c.ID, err) - return msgjson.NewError(mj.ErrBadRequest, "bad tankagram") - } - - if gram.From != c.ID { - t.log.Errorf("Tankagram from %s has wrong sender %s", c.ID, gram.From) - return msgjson.NewError(mj.ErrBadRequest, "wrong sender") - } - - // The TankagramResult is signed separately. - sendTankagramResult := func(r *mj.TankagramResult) { - t.sendResult(c, msg.ID, r) - } - - t.clientMtx.RLock() - recip, foundLocally := t.clients[gram.To] - t.clientMtx.RUnlock() - if foundLocally { - var resB dex.Bytes - relayedMsg := mj.MustRequest(mj.RouteTankagram, gram) - sent, clientErr, err := t.requestAnyOne([]tanka.Sender{recip}, relayedMsg, &resB) - if sent { - sendTankagramResult(&mj.TankagramResult{Result: mj.TRTTransmitted, EncryptedPayload: resB}) - return nil - } - if clientErr != nil { - t.log.Errorf("Error sending to local client clientErr %s: %v", recip.ID, clientErr) - sendTankagramResult(&mj.TankagramResult{Result: mj.TRTErrBadClient}) - return nil - } - if err != nil { - t.log.Errorf("Error sending to local client err %s: %v", recip.ID, err) - } - } - - // Either it's not a locally-connected client, or the send attempt failed. - // See if we have any other routes. - - tts := t.findPath(gram.To) - if len(tts) == 0 { - // TODO: Disconnect client? - t.log.Errorf("No local or remote client %s for tankagram from %s", gram.To, c.ID) - sendTankagramResult(&mj.TankagramResult{Result: mj.TRTNoPath}) - return nil - } - - relayedMsg := mj.MustRequest(mj.RouteRelayTankagram, gram) - var r mj.TankagramResult - sent, clientErr, err := t.requestAnyOne(tankasToSenders(tts), relayedMsg, &r) - if sent { - sendTankagramResult(&r) - return nil - } - if clientErr != nil { - // TODO: This means weren't able to communicate with the server - // node. We should probably disconnect this server. - sendTankagramResult(&mj.TankagramResult{Result: mj.TRTErrFromTanka}) - return nil - } - if err != nil { - if errors.Is(err, ErrNoPath) { - sendTankagramResult(&mj.TankagramResult{Result: mj.TRTNoPath}) - } else { - t.log.Errorf("Error relaying tankagram: %v", err) - sendTankagramResult(&mj.TankagramResult{Result: mj.TRTErrFromTanka}) - } - } - return nil -} - -func (t *Tatanka) handleSetScore(c *client, msg *msgjson.Message) { - scorer := c.peer.ID - var score *mj.ScoreReport - if err := msg.Unmarshal(&score); err != nil { - t.log.Errorf("error unmarshaling set_score from %s: %v", scorer, err) - return - } - if err := t.db.SetScore(score.PeerID, scorer, score.Score, time.Now()); err != nil { - t.log.Errorf("error adding score from %s for %s to db: %v", scorer, score.PeerID, err) - } - rep, err := t.db.Reputation(score.PeerID) - if err != nil { - t.log.Errorf("error getting reputation after score update from %s for %s: %v", score.PeerID, scorer, err) - return - } - t.clientMtx.RLock() - c, found := t.clients[score.PeerID] - t.clientMtx.RUnlock() - if found { - c.mtx.Lock() - c.Reputation = rep - c.mtx.Unlock() - } - - note := mj.MustNotification(mj.RouteShareScore, &mj.SharedScore{ - Scorer: scorer, - Scored: score.PeerID, - Score: score.Score, - Reputation: rep, - }) - for _, tt := range t.tatankaNodes() { - if err := t.send(tt, note); err != nil { - t.log.Errorf("error notifying %s of new score: %v", tt.ID, err) - } - } -} - -const ErrNoPath = dex.ErrorKind("no path") - -// requestAnyOne tries to request from the senders in order until one succeeds. -func (t *Tatanka) requestAnyOne(senders []tanka.Sender, msg *msgjson.Message, resp any) (sent bool, clientErr, err error) { - mj.SignMessage(t.priv, msg) - rawMsg, err := json.Marshal(msg) - if err != nil { - return false, nil, err - } - return t.requestAnyOneRaw(senders, msg.ID, rawMsg, resp) -} - -func (t *Tatanka) requestAnyOneRaw(senders []tanka.Sender, msgID uint64, rawMsg []byte, resp any) (sent bool, clientErr, err error) { - for _, sender := range senders { - var errChan = make(chan error) - if err := sender.RequestRaw(msgID, rawMsg, func(msg *msgjson.Message) { - if err := msg.UnmarshalResult(&resp); err != nil { - errChan <- err - return - } - errChan <- nil - }); err != nil { - peerID := sender.PeerID() - t.log.Errorf("error sending message to %s. msg = %s, err = %v", dex.Bytes(peerID[:]), mj.Truncate(rawMsg), err) - continue - } - select { - case err := <-errChan: - if err == nil { - return true, nil, nil - } - // If we get here, that means we got a result from the client, but - // it didn't parse. This is a client error. - return false, err, nil - case <-t.ctx.Done(): - return false, nil, nil - } - } - return false, nil, ErrNoPath -} - -func tankasToSenders(tts []*remoteTatanka) []tanka.Sender { - senders := make([]tanka.Sender, len(tts)) - for i, tt := range tts { - senders[i] = tt - } - return senders -} diff --git a/tatanka/cmd/tatanka/main.go b/tatanka/cmd/tatanka/main.go deleted file mode 100644 index f98f030b02..0000000000 --- a/tatanka/cmd/tatanka/main.go +++ /dev/null @@ -1,334 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package main - -import ( - "context" - "errors" - "fmt" - "net" - "os" - "os/signal" - "path/filepath" - "runtime" - "strings" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/feerates" - "decred.org/dcrdex/dex/fiatrates" - "decred.org/dcrdex/server/comms" - "decred.org/dcrdex/tatanka" - _ "decred.org/dcrdex/tatanka/chain/utxo" - "github.com/jessevdk/go-flags" - "github.com/jrick/logrotate/rotator" -) - -const ( - Version = 0 - defaultConfigFilename = "tatanka.conf" - defaultChainConfigFilename = "chains.json" - defaultHost = "127.0.0.1" - defaultPort = "7323" - missingPort = "missing port in address" - defaultHSHost = defaultHost // should be a loopback address - defaultHSPort = "7525" - defaultLogLevel = "debug" - defaultMaxClients = 1000 -) - -var ( - log = dex.Disabled -) - -func main() { - if err := mainErr(); err != nil { - fmt.Fprint(os.Stderr, err, "\n") - os.Exit(1) - } - os.Exit(0) -} - -func mainErr() (err error) { - logMaker, cfg := config() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - killChan := make(chan os.Signal, 1) - signal.Notify(killChan, os.Interrupt) - go func() { - <-killChan - fmt.Println("Shutting down...") - cancel() - }() - - net := dex.Mainnet - switch { - case cfg.Simnet: - net = dex.Simnet - case cfg.Testnet: - net = dex.Testnet - } - - maxClients := defaultMaxClients - if cfg.MaxClients > 0 { - maxClients = cfg.MaxClients - } - - t, err := tatanka.New(&tatanka.Config{ - Net: net, - DataDir: cfg.AppDataDir, - Logger: logMaker.Logger("🦬"), - ChainConfig: cfg.ChainConfig, - MaxClients: maxClients, - RPC: comms.RPCConfig{ - HiddenServiceAddr: cfg.HiddenService, - ListenAddrs: cfg.Listeners, - RPCKey: cfg.KeyPath, - RPCCert: cfg.CertPath, - NoTLS: cfg.NoTLS, - AltDNSNames: cfg.AltDNSNames, - }, - }) - if err != nil { - return fmt.Errorf("tatanka.New error: %w", err) - } - - tc := dex.NewConnectionMaster(t) - if err = tc.ConnectOnce(ctx); err != nil { - return fmt.Errorf("ConnectOnce error: %w", err) - } - - tc.Wait() - return nil -} - -type Config struct { - AppDataDir string `short:"A" long:"appdata" description:"Path to application home directory."` - ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file."` - ChainConfig string `long:"chainconfig" description:"Path to chain configuration file."` - DebugLevel string `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}."` - LocalLogs bool `long:"loglocal" description:"Use local time zone time stamps in log entries."` - ShowVersion bool `short:"v" long:"version" description:"Display version information and exit."` - - Testnet bool `long:"testnet" description:"Use the test network (default mainnet)."` - Simnet bool `long:"simnet" description:"Use the simulation test network (default mainnet)."` - - CertPath string `long:"tlscert" description:"TLS certificate file."` - KeyPath string `long:"tlskey" description:"TLS private key file."` - Listeners []string `long:"listen" description:"IP addresses on which the RPC server should listen for incoming connections."` - NoTLS bool `long:"notls" description:"Run without TLS encryption."` - AltDNSNames []string `long:"altdnsnames" description:"A list of hostnames to include in the RPC certificate (X509v3 Subject Alternative Name)."` - HiddenService string `long:"hiddenservice" description:"A host:port on which the RPC server should listen for incoming hidden service connections. No TLS is used for these connections."` - feerateOracleCfg feerates.Config - - WebAddr string `long:"webaddr" description:"The public facing address by which peers should connect."` - MaxClients int `long:"maxclients" description:"The maximum number of clients that can connect to this node."` - - FiatOracleConfig fiatrates.Config `group:"Fiat Oracle Config"` -} - -func config() (*dex.LoggerMaker, *Config) { - emitConfigError := func(s string, a ...any) { - fmt.Fprintln(os.Stderr, fmt.Errorf(s, a...)) - os.Exit(0) - } - - cfg := Config{} - var preCfg Config // zero values as defaults - preParser := flags.NewParser(&preCfg, flags.HelpFlag) - _, err := preParser.Parse() - if err != nil { - if e, ok := err.(*flags.Error); ok && e.Type != flags.ErrHelp { - emitConfigError("flag pre-parse error: %v", err) - } else if ok && e.Type == flags.ErrHelp { - fmt.Fprintln(os.Stdout, err) - os.Exit(0) - } - } - - // Show the version and exit if the version flag was specified. - if preCfg.ShowVersion { - fmt.Printf("Tatanka version %d (Go version %s %s/%s)\n", - Version, runtime.Version(), runtime.GOOS, runtime.GOARCH) - os.Exit(0) - } - - // If a non-default appdata folder is specified on the command line, it may - // be necessary adjust the config file location. If the config file - // location was not specified on the command line, the default location - // should be under the non-default appdata directory. However, if the config - // file was specified on the command line, it should be used regardless of - // the appdata directory. - if preCfg.AppDataDir != "" { - // appdata was set on the command line. If it is not absolute, make it - // relative to cwd. - cfg.AppDataDir, err = filepath.Abs(preCfg.AppDataDir) - if err != nil { - emitConfigError("Unable to determine working directory: %v", err) - } - } - isDefaultConfigFile := preCfg.ConfigFile == "" - if isDefaultConfigFile { - preCfg.ConfigFile = filepath.Join(cfg.AppDataDir, defaultConfigFilename) - } else if !filepath.IsAbs(preCfg.ConfigFile) { - preCfg.ConfigFile = filepath.Join(cfg.AppDataDir, preCfg.ConfigFile) - } - - // Config file name for logging. - configFile := "NONE (defaults)" - - // Load additional config from file. - parser := flags.NewParser(&cfg, flags.Default) - // Do not error default config file is missing. - if _, err := os.Stat(preCfg.ConfigFile); os.IsNotExist(err) { - // Non-default config file must exist. - if !isDefaultConfigFile { - emitConfigError("error characterizing file %q: %v", preCfg.ConfigFile, err) - } - // Warn about missing default config file, but continue. - fmt.Printf("Config file (%s) does not exist. Using defaults.\n", - preCfg.ConfigFile) - } else { - // The config file exists, so attempt to parse it. - err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile) - if err != nil { - if _, ok := err.(*os.PathError); !ok { - emitConfigError("INI file parsing error: %v", err) - } - } - configFile = preCfg.ConfigFile - } - - isDefaultChainConfigFile := preCfg.ChainConfig == "" - if isDefaultChainConfigFile { - cfg.ChainConfig = filepath.Join(cfg.AppDataDir, defaultChainConfigFilename) - } else if !filepath.IsAbs(preCfg.ChainConfig) { - cfg.ChainConfig = filepath.Join(cfg.AppDataDir, preCfg.ChainConfig) - } - - // Parse command line options again to ensure they take precedence. - _, err = parser.Parse() - if err != nil { - if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { - parser.WriteHelp(os.Stderr) - } - emitConfigError("CLI re-parsing error: %v", err) - } - - if len(cfg.Listeners) == 0 { - cfg.Listeners = []string{defaultHost + ":" + defaultPort} - } - for i := range cfg.Listeners { - listen, err := normalizeNetworkAddress(cfg.Listeners[i], defaultHost, defaultPort) - if err != nil { - emitConfigError("error parsing Listeners: %v", err.Error()) - } - cfg.Listeners[i] = listen - } - if cfg.HiddenService != "" { - cfg.HiddenService, err = normalizeNetworkAddress(cfg.HiddenService, defaultHSHost, defaultHSPort) - if err != nil { - emitConfigError("error parsing HiddenService: %v", err.Error()) - } - } - - if cfg.DebugLevel == "" { - cfg.DebugLevel = defaultLogLevel - } - - // Create the loggers: Parse and validate the debug level string, create the - // subsystem loggers, and set package level loggers. The generated - // LoggerMaker is used by other subsystems to create new loggers with the - // same backend. - logMaker, err := parseAndSetDebugLevels(cfg.DebugLevel, !cfg.LocalLogs) - if err != nil { - emitConfigError("parseAndSetDebugLevels error: %v", err) - } - - initLogRotator(filepath.Join(cfg.AppDataDir, "logs")) - - log.Infof("App data folder: %s", cfg.AppDataDir) - log.Infof("Config file: %s", configFile) - - return logMaker, &cfg -} - -// parseAndSetDebugLevels attempts to parse the specified debug level and set -// the levels accordingly. An appropriate error is returned if anything is -// invalid. -func parseAndSetDebugLevels(debugLevel string, UTC bool) (*dex.LoggerMaker, error) { - // Create a LoggerMaker with the level string. - lm, err := dex.NewLoggerMaker(logWriter{}, debugLevel, UTC) - if err != nil { - return nil, err - } - - // Set main's Logger. - log = lm.Logger("TANKA") - comms.UseLogger(lm.Logger("COMMS")) - - return lm, nil -} - -// logWriter implements an io.Writer that outputs to both standard output and -// the write-end pipe of an initialized log rotator. -type logWriter struct { - logRotator *rotator.Rotator -} - -// Write writes the data in p to standard out and the log rotator. -func (w logWriter) Write(p []byte) (n int, err error) { - if w.logRotator == nil { - return os.Stdout.Write(p) - } - os.Stdout.Write(p) - return w.logRotator.Write(p) // not safe concurrent writes, so only one logWriter{} allowed! -} - -// initLogRotator initializes the logging rotater to write logs to logFile and -// create roll files in the same directory. It must be called before the -// package-global log rotater variables are used. -func initLogRotator(dir string) (*rotator.Rotator, error) { - logFile := filepath.Join(dir, "tatanka.log") - err := os.MkdirAll(dir, 0700) - if err != nil { - return nil, err - } - const maxLogRolls = 5 - return rotator.New(logFile, 32*1024, false, maxLogRolls) -} - -// normalizeNetworkAddress checks for a valid local network address format and -// adds default host and port if not present. Invalidates addresses that include -// a protocol identifier. -func normalizeNetworkAddress(a, defaultHost, defaultPort string) (string, error) { - if strings.Contains(a, "://") { - return a, fmt.Errorf("address %s contains a protocol identifier, which is not allowed", a) - } - if a == "" { - return net.JoinHostPort(defaultHost, defaultPort), nil - } - host, port, err := net.SplitHostPort(a) - if err != nil { - var addrErr *net.AddrError - if errors.As(err, &addrErr) && addrErr.Err == missingPort { - host = strings.Trim(addrErr.Addr, "[]") // JoinHostPort expects no brackets for ipv6 hosts - normalized := net.JoinHostPort(host, defaultPort) - host, port, err = net.SplitHostPort(normalized) - if err != nil { - return a, fmt.Errorf("unable to address %s after port resolution: %w", normalized, err) - } - } else { - return a, fmt.Errorf("unable to normalize address %s: %w", a, err) - } - } - if host == "" { - host = defaultHost - } - if port == "" { - port = defaultPort - } - return net.JoinHostPort(host, port), nil -} diff --git a/tatanka/db/bonds.go b/tatanka/db/bonds.go deleted file mode 100644 index ed5f359afd..0000000000 --- a/tatanka/db/bonds.go +++ /dev/null @@ -1,73 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package db - -import ( - "fmt" - "time" - - "decred.org/dcrdex/dex/encode" - "decred.org/dcrdex/dex/lexi" - "decred.org/dcrdex/tatanka/tanka" -) - -type dbBond struct { - *tanka.Bond -} - -func (bond *dbBond) MarshalBinary() ([]byte, error) { - const bondVer = 0 - var b encode.BuildyBytes = make([]byte, 1, 1+tanka.PeerIDLength+4+len(bond.CoinID)+8+8) - b[0] = bondVer - b = b.AddData(bond.PeerID[:]). - AddData(encode.Uint32Bytes(bond.AssetID)). - AddData(bond.CoinID). - AddData(encode.Uint64Bytes(bond.Strength)). - AddData(encode.Uint64Bytes(uint64(bond.Expiration.Unix()))) - return b, nil -} - -func (bond *dbBond) UnmarshalBinary(b []byte) error { - const bondVer = 0 - bond.Bond = new(tanka.Bond) - ver, pushes, err := encode.DecodeBlob(b, 5) - if err != nil { - return fmt.Errorf("error decoding bond blob: %w", err) - } - if ver != bondVer { - return fmt.Errorf("unknown bond version %d", ver) - } - if len(pushes) != 5 { - return fmt.Errorf("unknown number of bond blob pushes %d", len(pushes)) - } - copy(bond.PeerID[:], pushes[0]) - bond.AssetID = encode.BytesToUint32(pushes[1]) - bond.CoinID = pushes[2] - bond.Strength = encode.BytesToUint64(pushes[3]) - bond.Expiration = time.Unix(int64(encode.BytesToUint64(pushes[4])), 0) - return nil -} - -func (d *DB) StoreBond(newBond *tanka.Bond) error { - return d.bonds.Set(newBond.CoinID[:], &dbBond{newBond}, lexi.WithReplace()) -} - -func (d *DB) GetBonds(peerID tanka.PeerID) ([]*tanka.Bond, error) { - var bonds []*tanka.Bond - now := time.Now() - return bonds, d.bonderIdx.Iterate(peerID[:], func(it *lexi.Iter) error { - return it.V(func(vB []byte) error { - var bond dbBond - if err := bond.UnmarshalBinary(vB); err != nil { - return fmt.Errorf("error unmarshaling bond: %w", err) - } - if bond.Expiration.Before(now) { - it.Delete() - return nil - } - bonds = append(bonds, bond.Bond) - return nil - }) - }) -} diff --git a/tatanka/db/db.go b/tatanka/db/db.go deleted file mode 100644 index 2bae50518e..0000000000 --- a/tatanka/db/db.go +++ /dev/null @@ -1,158 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package db - -import ( - "context" - "encoding/binary" - "errors" - "fmt" - "os" - "path/filepath" - "sync" - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/encode" - "decred.org/dcrdex/dex/lexi" -) - -const DBVersion = 0 - -var ( - versionKey = []byte("dbver") -) - -type DB struct { - *lexi.DB - log dex.Logger - scores *lexi.Table - scoredIdx *lexi.Index - bonds *lexi.Table - bonderIdx *lexi.Index - bondStampIdx *lexi.Index -} - -func New(dir string, log dex.Logger) (*DB, error) { - if err := os.MkdirAll(dir, 0755); err != nil { - return nil, fmt.Errorf("error creating db dir: %w", err) - } - db, err := lexi.New(&lexi.Config{ - Path: filepath.Join(dir, "reputation.db"), - Log: log, - }) - if err != nil { - return nil, fmt.Errorf("error initializing db: %w", err) - } - - metaTable, err := db.Table("meta") - if err != nil { - return nil, fmt.Errorf("error initializing meta table: %w", err) - } - verB, err := metaTable.GetRaw(versionKey) - if err != nil { - if errors.Is(err, lexi.ErrKeyNotFound) { - // fresh install - verB = []byte{DBVersion} - metaTable.Set(versionKey, verB) - } else { - return nil, fmt.Errorf("error getting version") - } - } - if len(verB) != 1 { - return nil, fmt.Errorf("bad version length. bad") - } - ver := verB[0] - if ver > DBVersion { - return nil, fmt.Errorf("database reporting version from the future") - } - if ver < DBVersion { - log.Warn("database version is older than current version. Upgrading...") - // TODO: implement lexi upgrade logic - } - - // Scores. Keyed on (scorer peer ID, scored peer ID). - scoreTable, err := db.Table("scores") - if err != nil { - return nil, fmt.Errorf("error constructing reputation table: %w", err) - } - // Scored peer index with timestamp sorting. - scoredIdx, err := scoreTable.AddIndex("scored-stamp", func(_, v lexi.KV) ([]byte, error) { - s, is := v.(*dbScore) - if !is { - return nil, fmt.Errorf("wrong type %T", v) - } - tB := make([]byte, 8) - binary.BigEndian.PutUint64(tB, uint64(s.stamp.UnixMilli())) - return append(s.scored[:], tB...), nil - }) - if err != nil { - return nil, fmt.Errorf("error initializing reputation index: %w", err) - } - scoredIdx.UseDefaultIterationOptions(lexi.WithReverse()) - - // Bonds. Keyed on coin ID - bondsTable, err := db.Table("bonds") - if err != nil { - return nil, fmt.Errorf("error initializing bonds table: %w", err) - } - // Retrieve bonds by peer ID. - bonderIdx, err := bondsTable.AddIndex("bonder", func(_, v lexi.KV) ([]byte, error) { - b, is := v.(*dbBond) - if !is { - return nil, fmt.Errorf("wrong type %T", v) - } - return b.PeerID[:], nil - }) - if err != nil { - return nil, fmt.Errorf("error initializing bonder index: %w", err) - } - // We'll periodically prune expired bonds. - bondStampIdx, err := bondsTable.AddIndex("bond-stamp", func(_, v lexi.KV) ([]byte, error) { - b, is := v.(*dbBond) - if !is { - return nil, fmt.Errorf("wrong type %T", v) - } - return encode.Uint64Bytes(uint64(b.Expiration.Unix())), nil - }) - bondStampIdx.UseDefaultIterationOptions(lexi.WithReverse()) - if err != nil { - return nil, fmt.Errorf("error initializing bond stamp index: %w", err) - } - return &DB{ - DB: db, - scores: scoreTable, - scoredIdx: scoredIdx, - log: log, - bonds: bondsTable, - bonderIdx: bonderIdx, - bondStampIdx: bondStampIdx, - }, nil -} - -func (db *DB) Connect(ctx context.Context) (*sync.WaitGroup, error) { - var wg sync.WaitGroup - go func() { - for { - select { - case <-time.After(time.Hour): - wg.Add(1) - db.pruneOldBonds() - wg.Done() - case <-ctx.Done(): - return - } - } - }() - - return &wg, nil -} - -func (db *DB) pruneOldBonds() { - if err := db.bondStampIdx.Iterate(nil, func(it *lexi.Iter) error { - return it.Delete() - }, lexi.WithSeek(encode.Uint64Bytes(uint64(time.Now().Unix()))), lexi.WithUpdate()); err != nil { - db.log.Errorf("Error pruning bonds: %v", err) - } -} diff --git a/tatanka/db/db_test.go b/tatanka/db/db_test.go deleted file mode 100644 index 0dccf43eee..0000000000 --- a/tatanka/db/db_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package db - -import ( - "bytes" - "os" - "testing" - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/lexi" - "decred.org/dcrdex/tatanka/tanka" -) - -func tNewDB() (*DB, func()) { - tempDir, _ := os.MkdirTemp("", "") - db, err := New(tempDir, dex.StdOutLogger("T", dex.LevelInfo)) - if err != nil { - panic(err.Error()) - } - return db, func() { - os.RemoveAll(tempDir) - } -} - -func newBond(peer tanka.PeerID, n byte, timeOffset ...time.Duration) *tanka.Bond { - expiration := time.Now().Add(time.Minute) - if len(timeOffset) > 0 { - expiration = time.Now().Add(timeOffset[0]) - } - return &tanka.Bond{ - PeerID: peer, - AssetID: uint32(n), - CoinID: []byte{n}, - Strength: uint64(n), - Expiration: expiration, - } -} - -func TestBonds(t *testing.T) { - // Testing basic bond insertion and retrieval, and binary marshaling. - db, shutdown := tNewDB() - defer shutdown() - - peer := tanka.PeerID{0x01} - b1 := newBond(peer, 1) - if err := db.StoreBond(b1); err != nil { - t.Fatalf("StoreBond error: %v", err) - } - bs, err := db.GetBonds(peer) - if err != nil { - t.Fatalf("GetBonds error: %v", err) - } - if len(bs) != 1 { - t.Fatalf("Expected 1 bond, got %d", len(bs)) - } - reB := bs[0] - if b1.PeerID != reB.PeerID { - t.Fatalf("Wrong peer ID. %s != %s", b1.PeerID, reB.PeerID) - } - if b1.AssetID != reB.AssetID { - t.Fatalf("Wrong asset ID. %d != %d", b1.AssetID, reB.AssetID) - } - if !bytes.Equal(b1.CoinID, reB.CoinID) { - t.Fatalf("Wrong coin ID, %v != %v", b1.CoinID, reB.CoinID) - } - if b1.Strength != reB.Strength { - t.Fatalf("Wrong strength. %d != %d", b1.Strength, reB.Strength) - } - if b1.Expiration.Unix() != reB.Expiration.Unix() { - t.Fatalf("Wrong time. %d != %d", b1.Expiration.Unix(), reB.Expiration.Unix()) - } - - // Works with > 1 bond too - db.StoreBond(newBond(peer, 2)) - bs, _ = db.GetBonds(peer) - if len(bs) != 2 { - t.Fatalf("Expected 2 bonds, got %d", len(bs)) - } - - // Expired bonds are not returned - db.StoreBond(newBond(peer, 3, -time.Second)) - bs, _ = db.GetBonds(peer) - if len(bs) != 2 { - t.Fatalf("Expected 2 bonds after pruning, got %d", len(bs)) - } -} - -func TestPruneBonds(t *testing.T) { - db, shutdown := tNewDB() - defer shutdown() - - peer := tanka.PeerID{0x01} - db.StoreBond(newBond(peer, 0)) - db.StoreBond(newBond(peer, 1)) - db.StoreBond(newBond(peer, 2)) - db.StoreBond(newBond(peer, 3, -time.Second)) - db.StoreBond(newBond(peer, 4, -time.Second)) - db.StoreBond(newBond(peer, 5, -time.Second)) - // Make sure they're all there. - var n int - db.bonderIdx.Iterate(nil, func(it *lexi.Iter) error { - n++ - return nil - }) - if n != 6 { - t.Fatalf("Where'd the bonds go?") - } - db.pruneOldBonds() - n = 0 - db.bonderIdx.Iterate(nil, func(it *lexi.Iter) error { - n++ - return nil - }) - if n != 3 { - t.Fatalf("Expected 3 bonds, got %d", n) - } -} - -func TestReputation(t *testing.T) { - db, shutdown := tNewDB() - defer shutdown() - - var scored tanka.PeerID - const outdatedN = 5 // won't be included in score. Will be deleted. - // Note: If MaxReputationEntries is increased to > 122, this won't work - // any more. - for i := 0; i < tanka.MaxReputationEntries+outdatedN; i++ { - if err := db.SetScore(scored, tanka.PeerID{byte(i + 1)}, int8(i), time.Now().Add(-time.Duration(i)*time.Second)); err != nil { - t.Fatalf("SetScore(%d) error: %v", i, err) - } - } - // Sum of positive integers from 1 to N is (N(N+1))/2 - var scoreMax int64 = tanka.MaxReputationEntries - 1 // We started with 0. - var expAggScore int64 = (scoreMax * (scoreMax + 1)) / 2 - rep, err := db.Reputation(scored) - if err != nil { - t.Fatalf("Reputation error: %v", err) - } - if rep.Score != expAggScore { - t.Fatalf("Wrong aggregate score. Expected %d, got %d", expAggScore, rep.Score) - } - if rep.Depth != tanka.MaxReputationEntries { - t.Fatalf("Wrong aggregate depth. Expected %d, got %d", tanka.MaxReputationEntries, rep.Depth) - } - // Max sure old scores were deleted. - var n int - db.scoredIdx.Iterate(scored[:], func(it *lexi.Iter) error { - n++ - return nil - }) - if n != tanka.MaxReputationEntries { - t.Fatalf("Wrong number of remaining entries. Expected %d, got %d", tanka.MaxReputationEntries, n) - } -} diff --git a/tatanka/db/peers.go b/tatanka/db/peers.go deleted file mode 100644 index f145e2a166..0000000000 --- a/tatanka/db/peers.go +++ /dev/null @@ -1,30 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package db - -import ( - "fmt" - - "decred.org/dcrdex/tatanka/tanka" - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -func (d *DB) Peer(peerID tanka.PeerID) (_ *tanka.Peer, err error) { - p := &tanka.Peer{ - ID: peerID, - } - if p.PubKey, err = secp256k1.ParsePubKey(peerID[:]); err != nil { - return nil, fmt.Errorf("ParsePubKey error: %w", err) - } - - if p.Bonds, err = d.GetBonds(peerID); err != nil { - return nil, fmt.Errorf("GetBonds error: %w", err) - } - - if p.Reputation, err = d.Reputation(peerID); err != nil { - return nil, fmt.Errorf("error getting Reputation: %w", err) - } - - return p, nil -} diff --git a/tatanka/db/reputation.go b/tatanka/db/reputation.go deleted file mode 100644 index 7c09b60906..0000000000 --- a/tatanka/db/reputation.go +++ /dev/null @@ -1,56 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package db - -import ( - "fmt" - "time" - - "decred.org/dcrdex/dex/lexi" - "decred.org/dcrdex/tatanka/tanka" -) - -type dbScore struct { - scorer tanka.PeerID - scored tanka.PeerID - score int8 - stamp time.Time -} - -func (s *dbScore) MarshalBinary() ([]byte, error) { - return []byte{byte(s.score)}, nil -} - -func (d *DB) SetScore(scored, scorer tanka.PeerID, score int8, stamp time.Time) error { - k := append(scored[:], scorer[:]...) - s := &dbScore{ - scorer: scorer, - scored: scored, - score: score, - stamp: stamp, - } - return d.scores.Set(k, s, lexi.WithReplace()) -} - -func (d *DB) Reputation(scored tanka.PeerID) (*tanka.Reputation, error) { - agg := new(tanka.Reputation) - var i int - return agg, d.scoredIdx.Iterate(scored[:], func(it *lexi.Iter) error { - if i >= tanka.MaxReputationEntries { - return it.Delete() - } - if err := it.V(func(vB []byte) error { - if len(vB) != 1 { - return fmt.Errorf("score not a single byte. length = %d", len(vB)) - } - agg.Score += int64(vB[0]) - return nil - }); err != nil { - return err - } - agg.Depth++ - i++ - return nil - }, lexi.WithUpdate()) -} diff --git a/tatanka/mj/auth.go b/tatanka/mj/auth.go deleted file mode 100644 index d4d799bd3f..0000000000 --- a/tatanka/mj/auth.go +++ /dev/null @@ -1,44 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package mj - -import ( - "crypto/sha256" - "encoding/binary" - "fmt" - - "decred.org/dcrdex/dex/msgjson" - "github.com/btcsuite/btcd/btcec/v2/ecdsa" - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -func MessageDigest(msg *msgjson.Message) [32]byte { - b := make([]byte, 0, 1+len(msg.Route)+8+len(msg.Payload)) - b = append(b, byte(msg.Type)) - b = append(b, []byte(msg.Route)...) - var idB [8]byte - binary.BigEndian.PutUint64(idB[:], msg.ID) - b = append(b, idB[:]...) - b = append(b, msg.Payload...) - return sha256.Sum256(b) -} - -func SignMessage(priv *secp256k1.PrivateKey, msg *msgjson.Message) { - h := MessageDigest(msg) - msg.Sig = ecdsa.Sign(priv, h[:]).Serialize() -} - -// CheckSig checks that the message's signature was created with the private -// key for the provided secp256k1 public key on the sha256 hash of the message. -func CheckSig(msg *msgjson.Message, pubKey *secp256k1.PublicKey) error { - signature, err := ecdsa.ParseDERSignature(msg.Sig) - if err != nil { - return fmt.Errorf("error decoding secp256k1 Signature from bytes: %w", err) - } - h := MessageDigest(msg) - if !signature.Verify(h[:], pubKey) { - return fmt.Errorf("secp256k1 signature verification failed") - } - return nil -} diff --git a/tatanka/mj/types.go b/tatanka/mj/types.go deleted file mode 100644 index c004ceb105..0000000000 --- a/tatanka/mj/types.go +++ /dev/null @@ -1,255 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package mj - -import ( - "encoding/json" - "sync/atomic" - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/feerates" - "decred.org/dcrdex/dex/fiatrates" - "decred.org/dcrdex/dex/msgjson" - "decred.org/dcrdex/tatanka/tanka" -) - -const ( - ErrNone = iota - ErrTimeout - ErrInternal - ErrBadRequest - ErrAuth - ErrSig - ErrNoConfig - ErrBanned - ErrFailedRelay - ErrUnknownSender - ErrCapacity -) - -const ( - // tatanka <=> tatanka - RouteTatankaConfig = "tatanka_config" - RouteTatankaConnect = "tatanka_connect" - RouteNewClient = "new_client" - RouteClientDisconnect = "client_disconnect" - RouteRelayBroadcast = "relay_broadcast" - RouteRelayTankagram = "relay_tankagram" - RoutePathInquiry = "path_inquiry" - RouteShareScore = "share_score" - - // tatanka <=> client - RouteConnect = "connect" - RoutePostBond = "post_bond" - RouteSubscribe = "subscribe" - RouteUnsubscribe = "unsubscribe" - RouteUpdateSubscriptions = "update_subscriptions" - RouteRates = "rates" - RouteSetScore = "set_score" - RouteFeeRateEstimate = "fee_rate_estimate" - - // client1 <=> tatankanode <=> client2 - RouteTankagram = "tankagram" - RouteEncryptionKey = "encryption_key" - RouteNegotiate = "negotiate" - RouteBroadcast = "broadcast" - RouteNewSubscriber = "new_subscriber" - - // HTTP Requests, used before client established WS connection - RouteNodeInfo = "node_info" -) - -const ( - TopicMarket = "market" - TopicFiatRate = "fiat_rate" - TopicFeeRateEstimate = "fee_rate_estimate" -) - -type BroadcastMessageType string - -const ( - MessageTypeTrollBox BroadcastMessageType = "troll_box" - MessageTypeNewOrder BroadcastMessageType = "new_order" - MessageTypeNewSubscriber BroadcastMessageType = "new_subscriber" - MessageTypeUnsubTopic BroadcastMessageType = "unsub_topic" - MessageTypeUnsubSubject BroadcastMessageType = "unsub_subject" -) - -var msgIDCounter atomic.Uint64 - -func NewMessageID() uint64 { - return msgIDCounter.Add(1) -} - -type TatankaConfig struct { - ID tanka.PeerID `json:"id"` - Version uint32 `json:"version"` - Chains []uint32 `json:"chains"` - // BondTier is the senders current view of the receiver's tier. - BondTier uint64 `json:"bondTier"` -} - -type Connect struct { - ID tanka.PeerID `json:"id"` - InitialSubs map[tanka.Topic][]tanka.Subject `json:"initialSubs"` -} - -type UpdateSubscriptions struct { - Subscriptions map[tanka.Topic][]tanka.Subject `json:"subscriptions"` -} - -type Disconnect = Connect - -// EncryptionKeyPayload is the payload of a Tankagram or TankagramResult that -// contains the ephemeral public key of the sender. -type EncryptionKeyPayload struct { - EphemeralPubKey dex.Bytes `json:"ephemeralPubKey"` -} - -type Tankagram struct { - To tanka.PeerID `json:"to"` - From tanka.PeerID `json:"from"` - EncryptedPayload dex.Bytes `json:"encryptedPayload"` -} - -// TankagramError is a standard error that can be returned to a peer. -type TankagramError string - -const ( - TEErrNone TankagramError = "" - // TEEPeerError is returned when a peer encounters an error. - TEEPeerError TankagramError = "error" - TEEBadRequest TankagramError = "bad_request" - // TEDecryptionFailed is returned when a tankagram cannot be decrypted. - // This usually means that the counterparty does not have the same ephemeral - // encryption key, and it needs to be re-established. - TEDecryptionFailed TankagramError = "decryption_failed" -) - -// TankagramResultPayload must be the contents of a decrypted -// TankagramResult EncryptedResponse. -type TankagramResultPayload struct { - Error TankagramError `json:"error"` - Payload dex.Bytes `json:"payload"` -} - -// TankagramResultType is a critical component of the mesh network's reputation -// system. The outcome of self-reported audits of counterparty failures will -// depend heavily on what signed TankagramResult.Result is submitted. -type TankagramResultType string - -const ( - TRTTransmitted TankagramResultType = "transmitted" - TRTNoPath TankagramResultType = "nopath" - TRTErrFromTanka TankagramResultType = "errorfromtanka" - TRTErrBadClient TankagramResultType = "badclient" -) - -type TankagramResult struct { - Result TankagramResultType `json:"result"` - EncryptedPayload dex.Bytes `json:"encryptedPayload"` -} - -type Broadcast struct { - // TOOD: Why is PeerID part of the broadcast message when it can be - // determined by the request? - PeerID tanka.PeerID `json:"peerID"` - Topic tanka.Topic `json:"topic"` - Subject tanka.Subject `json:"subject"` - MessageType BroadcastMessageType `json:"messageType"` - Payload dex.Bytes `json:"payload,omitempty"` - Stamp time.Time `json:"stamp"` -} - -type Subscription struct { - Topic tanka.Topic `json:"topic"` - Subject tanka.Subject `json:"subject"` - // ChannelParameters json.RawMessage `json:"channelParameters,omitempty"` -} - -type Unsubscription struct { - Topic tanka.Topic `json:"topic"` - PeerID tanka.PeerID `json:"peerID"` -} - -type FeeRateEstimateMessage struct { - Topic tanka.Topic `json:"topic"` - FeeRateEstimates map[uint32]*feerates.Estimate `json:"feeRateEstimates"` -} - -type FundedMessage struct { - AssetID uint32 `json:"assetID"` - Funding json.RawMessage `json:"funding"` - Msg msgjson.Message `json:"msg"` -} - -type RateMessage struct { - Topic tanka.Topic `json:"topic"` - Rates map[string]*fiatrates.FiatRateInfo `json:"rates"` -} - -type Troll struct { - Msg string `json:"msg"` -} - -var ellipsis = []byte("...") - -func Truncate(b []byte) string { - const truncationLength = 300 - if len(b) <= truncationLength { - return string(b) - } - truncated := make([]byte, truncationLength+len(ellipsis)) - copy(truncated[:truncationLength], b[:truncationLength]) - copy(truncated[truncationLength:], ellipsis) - return string(truncated) -} - -type PathInquiry = Connect - -type NewSubscriber struct { - PeerID tanka.PeerID `json:"peerID"` - Topic tanka.Topic `json:"topic"` - Subject tanka.Subject `json:"subject"` -} - -// ScoreReport is an update of a client's score of a peer. -type ScoreReport struct { - PeerID tanka.PeerID `json:"peerID"` - Score int8 `json:"score"` -} - -// SharedScore is a scorer-scored tuple shared between tatanka nodes, along -// with the sending tatanka's new view of the scored peer's reputation. -type SharedScore struct { - Scorer tanka.PeerID `json:"scorer"` - Scored tanka.PeerID `json:"scored"` - Score int8 `json:"score"` - Reputation *tanka.Reputation `json:"rep"` -} - -func MustRequest(route string, payload any) *msgjson.Message { - msg, err := msgjson.NewRequest(NewMessageID(), route, payload) - if err != nil { - panic("MustRequest error: " + err.Error()) - } - return msg -} - -func MustNotification(route string, payload any) *msgjson.Message { - msg, err := msgjson.NewNotification(route, payload) - if err != nil { - panic("MustNotification error: " + err.Error()) - } - return msg -} - -func MustResponse(id uint64, payload any, rpcErr *msgjson.Error) *msgjson.Message { - msg, err := msgjson.NewResponse(id, payload, rpcErr) - if err != nil { - panic("MustResponse error: " + err.Error()) - } - return msg -} diff --git a/tatanka/peer.go b/tatanka/peer.go deleted file mode 100644 index 1598683be5..0000000000 --- a/tatanka/peer.go +++ /dev/null @@ -1,46 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package tatanka - -import ( - "sync" - - "decred.org/dcrdex/tatanka/tanka" -) - -// peer represents a locally connected client. -type peer struct { - tanka.Sender - - mtx sync.RWMutex - *tanka.Peer // Bonds and Reputation protected by mtx - rrs map[tanka.PeerID]*tanka.Reputation -} - -// banned checks whether the peer is banned. -func (p *peer) banned() bool { - p.mtx.RLock() - defer p.mtx.RUnlock() - // NEED TO CONSIDER *RemoteReputations.... - tier := calcTier(p.Reputation, p.BondTier()) - return tier <= 0 -} - -// bondTier is the peer's current bonded tier. -func (p *peer) bondTier() uint64 { - p.mtx.RLock() - defer p.mtx.RUnlock() - return p.BondTier() -} - -// updateBonds updates the peers bond set. -func (p *peer) updateBonds(bonds []*tanka.Bond) { - p.mtx.Lock() - defer p.mtx.Unlock() - p.Bonds = bonds -} - -type client struct { - *peer -} diff --git a/tatanka/spec/intro.md b/tatanka/spec/intro.md deleted file mode 100644 index b0b9d83ce4..0000000000 --- a/tatanka/spec/intro.md +++ /dev/null @@ -1,93 +0,0 @@ -# Introduction to Tatanka Mesh - -Tatanka Mesh (TM, or just "the mesh") is a multi-blockchain mesh network offering a suite -of services for clients. This initial specification version may reference the -"toy mesh", which is a proof-of-concept implementation of Tatanka Mesh. - -### Services - -1) Reputation services. The mesh will handle client bonds and reputation in -a fashion similar to a dcrdex server. Bonds provide network access, increased -network usage limits, and can offset penalties for high-volume users. Clients -self-report successes and failures, which are used to build a reputation score. -If a clients reputation score drops too low, they can have their usage limited -or outright suspended. - -1) Subscription channels, or "apps", perform a function similar to a chat room. -Clients subscribe to "topics", which can also have more narrow "subjects" to -which the client might subscribe. Subscription channels can be used to construct -various applications, such as a peer-to-peer decentralized exchange. The mesh -does not prescribe protocols for communications on a topic or subject. Such -protocols are defined by the client software. - -1) Client message routing. The mesh can route messages from client to client, -even when those clients are connected to different mesh nodes. Client messaging -is end-to-end encrypted, so the mesh network itself cannot read the contents. -The encrypted messaging primitive is called a "tankagram". Clients connected -to the same subscription channel might send one another tankagrams to, for -instance, communicate details about an ongoing atomic swap. - -1) Oracle services. The mesh will coordinate distribution of network transaction -fee rates that might be used as part of validation for app actions. The mesh -could potentially distribute fiat exchange rates as well, although such -functionality is not implemented in the toy mesh. See also the -[Outstanding Questions](#oustanding_questions) section. - -### Other Important Features - -- Although the toy mesh uses WebSockets for communications, there is no -prescribed transport protocol, and other communication backends are planned, -especially BisonRelay. If clients are using compatible protocols with the -requisite features (e.g. BisonRelay), client communications (tankagrams) can be -taken out-of-band. - -- Version 0 of Tatanka Mesh will be whitelist-only. This means that the mesh -network itself is a network of trusted nodes. By using a whitelist, we can -initially forego implementation of what will inevitably be a complicated mesh -node reputation system. - -- Tatanka Mesh does not maintain a global state. Mesh nodes need not agree on -client reputation, and the ability to synchronize reputation is extremely -limited. See the [Outstanding Questions](#oustanding_questions) section for more -discussion. - -### Why? - -The mesh architecture is designed in this way specifically to move all DEX -operations to the clients. In the litany of proposed U.S. legislation aimed at -regulating cryptocurrencies, even the most favorable bills indicate that -operating an order book would compel the server operator to collect -know-your-customer (KYC) and anti-money-laundering (AML) information from users. -This is going to be the case regardless of the (non-custodial, trustless) nature -of the underlying exchange protocol. While we believe that the trustless nature -of atomic swaps should preclude an order book operator from these regulations, -that is simply not going to be the case. As such, we're moving towards a fully -P2P model. Tatanka Mesh does not even know what a market is. - -### Outstanding Questions - -- Clients will self-report successes and counter-party failures, but who is in -charge of auditing these reports before inclusion into reputation? For example, -the mesh does not know what an atomic swap is, so how can a mesh node validate a -reported swap failure? Two possiblities are... - 1) The server could audit the reports, but do so in terms of blockchain - primitives that exist outside of the context of particular applications. For - example, the server could audit reports in terms of "HTLC pairs". The - important thing is that the server does not even appear to participate in - any way in the maintenance of an exchange market. - 1) The server could outsource auditing to clients, and accept the majority - result. This might require the server to expose some blockchain functionality - to clients who may not have e.g. txindex enabled on their UTXO-based - blockchain. This also creates perhaps more questions than it answers. What - if there aren't enough clients connected? How do we account for the report - while we are still waiting for client audits? - -- Since the mesh network does not maintain a global state, what happens if a -client is suspended on one node but not another? For the toy mesh, reputation -is only checked by the node(s) to which the client is directly connected. This -simple system might work for a whitelisted network, but is not a long-term -solution. - -### More Info - -[Introduction to Mesh DEX](meshdex.md) \ No newline at end of file diff --git a/tatanka/spec/meshdex.md b/tatanka/spec/meshdex.md deleted file mode 100644 index 0c1875f657..0000000000 --- a/tatanka/spec/meshdex.md +++ /dev/null @@ -1,62 +0,0 @@ -# DEX on Tatanka Mesh - -Implementing a peer-to-peer decentralized exchange on Tatanka Mesh will require -substantial re-thinking of many existing DCRDEX protocol features. The primary -difference between DCRDEX and DEX on Tatanka Mesh is that Tatanka Mesh clients -take over the roll of maintaining order books. - -### Protocol Basics - -1) There are no pre-defined markets. A market is simply a broadcast channel, -which is a topic-subject pair, e.g. topic = "market", subject = "dcr_btc". Any -user can create a new market by simply subscribing to a non-existent channel. -This means we need to eliminate most market configuration. - - Lot size will not be a constant. Instead, it will be chosen by the user - based on their preferences for limiting fee exposure. For example, the user - may set their fee exposure limit to 1%. This will hide any order (in their - local view of the order book) which have a lot size that might result in fee - losses > 1%. Only orders with compatible lot sizes can match. To ensure - orders fill completely, I propose that lot sizes must be a power of 2, - though other schemes are possibly conceivable. - - Max fee rates are similarly set by the user as part of the order, and - orders whose max fee rate falls below the fee rates provided by the mesh - oracle feed will be ignored or unbooked. - - If the mesh provides a fiat exchange rate feed, we can define the rate - step and parcel size as a exchange-wide constant in terms of USD, and these - values will be dynamically re-calculated for each market as rates change. - - We could potentially do away with epoch-based matching, but we may want to - keep it around, albeit in a different form. Our goal should be that clients - are reacting appropriately by creating matches for valid offers. Clients - can't just ignore offers based on their own arbitrary criteria. Ideally - they would accept the first offers that are sent to them, though this would - be very hard to enforce. - - I've considered doing away with order funding validation. The only purpose - of funding validation is to prevent empty wallets from spamming the market - with orders they have no intention to fulfill. This would of course result - in an account suspension, so such spamming cannot go on indefinitely. The - bonding system also requires users to lock up funds before using the - network, so there is still a barrier to such malicious behavior, though this - is not as strong a filter as funding validation would provide. It's - important to remember that the global worst-case scenario for - atomic-swap-based exchange is a refund and fees, not lost funds, so a - malicious actor's only incentive to spam would be to grief the network. On - the other hand, the server could still provide funding validation services - without participating in the market. Either way, we're going to do away with - the necessity of split funding transactions. More on this later. - - -1) Markets are user-policed. Successes and counterparty failures are -self-reported. - -1) Match proposals could be either public or private. Public match proposals -could be monitored by all market participants to potentially root out bad -actors, but private match proposals have obvious benefits as well. Either way -matches are self-reported as public broadcasts, so that clients can maintain -their order books. - -1) Swap communications take place in encrypted tankagrams. Unless a swap failure -occurs which requires an audit, the server never knows any swap details. - -1) Orders will have an expiration time that can be only so far in the future. -Clients can extend their expiration time as needed, but still only within a -relatively short window into the future. Expired orders are pruned. diff --git a/tatanka/tanka/peer.go b/tatanka/tanka/peer.go deleted file mode 100644 index 46c0425ac8..0000000000 --- a/tatanka/tanka/peer.go +++ /dev/null @@ -1,83 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package tanka - -import ( - "encoding/hex" - - "decred.org/dcrdex/dex/msgjson" - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -const PeerIDLength = secp256k1.PubKeyBytesLenCompressed - -// PeerID is the primary identifier for both clients and servers on Tatanka -// Mesh. The PeerID is the compressed-format serialized secp256k1.PublicKey. -type PeerID [PeerIDLength]byte - -func (p PeerID) PublicKey() (*secp256k1.PublicKey, error) { - return secp256k1.ParsePubKey(p[:]) -} - -func (p PeerID) String() string { - return hex.EncodeToString(p[:]) -} - -// func (p *PeerID) MarshalJSON() ([]byte, error) { -// return json.Marshal(hex.EncodeToString((*p)[:])) -// } - -// func (p *PeerID) UnmarshalJSON(encHex []byte) (err error) { -// if len(encHex) < 2 { -// return fmt.Errorf("unmarshalled bytes, %q, not valid", string(encHex)) -// } -// if encHex[0] != '"' || encHex[len(encHex)-1] != '"' { -// return fmt.Errorf("unmarshalled bytes, %q, not quoted", string(encHex)) -// } -// // DecodeString over-allocates by at least double, and it makes a copy. -// src := encHex[1 : len(encHex)-1] -// if len(src) != PeerIDLength*2 { -// return fmt.Errorf("unmarshalled bytes of wrong length %d", len(src)) -// } -// dst := make([]byte, len(src)/2) -// _, err = hex.Decode(dst, src) -// if err == nil { -// var id PeerID -// copy(id[:], dst) -// *p = id -// } -// return err -// } - -// Peer is a network peer, which could be a client or a server node. -type Peer struct { - ID PeerID `json:"-"` - PubKey *secp256k1.PublicKey `json:"-"` - Bonds []*Bond `json:"-"` - Reputation *Reputation `json:"reputation"` -} - -// BondTier sums the tier of the current bond set. -func (p *Peer) BondTier() (tier uint64) { - for _, b := range p.Bonds { - tier += b.Strength - } - return -} - -// Sender is an interface that is implemented by all network connections. -type Sender interface { - Send(*msgjson.Message) error - SendRaw([]byte) error - Request(msg *msgjson.Message, respHandler func(*msgjson.Message)) error - RequestRaw(msgID uint64, rawMsg []byte, respHandler func(*msgjson.Message)) error - SetPeerID(PeerID) - PeerID() PeerID - Disconnect() -} - -var ( - SimnetTatankaPriv, _ = hex.DecodeString("4122f61ac48667b3d21c624adca3b390ed0ce17e231cf642e4ac993241fd01af") - SimnetTatankaPeerID = PeerID{0x02, 0x2e, 0x3d, 0x03, 0x9d, 0xd0, 0x48, 0x1b, 0xcd, 0x6e, 0x71, 0xcc, 0x79, 0x0e, 0x47, 0x2e, 0xea, 0xc7, 0x4c, 0x90, 0x0c, 0x5d, 0x96, 0x6a, 0xd0, 0xcd, 0xe1, 0x3e, 0xeb, 0xec, 0xe2, 0xe1, 0xbe} -) diff --git a/tatanka/tanka/peer_test.go b/tatanka/tanka/peer_test.go deleted file mode 100644 index d0a8f282f2..0000000000 --- a/tatanka/tanka/peer_test.go +++ /dev/null @@ -1 +0,0 @@ -package tanka diff --git a/tatanka/tanka/reputation.go b/tatanka/tanka/reputation.go deleted file mode 100644 index 8e6772288a..0000000000 --- a/tatanka/tanka/reputation.go +++ /dev/null @@ -1,46 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package tanka - -import ( - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/encode" - "github.com/decred/dcrd/crypto/blake256" -) - -const ( - MaxSubScore = 256 - MaxReputationEntries = 100 - TierIncrement = 20 - MaxAggregateScore = MaxReputationEntries * MaxSubScore - EpochLength = time.Second * 15 -) - -type Reputation struct { - Score int64 - Depth uint64 -} - -type Bond struct { - PeerID PeerID `json:"peerID"` - AssetID uint32 `json:"assetID"` - CoinID dex.Bytes `json:"coinID"` - Strength uint64 `json:"strength"` - Expiration time.Time `json:"expiration"` - // TODO (buck): Switch to Maturation. - Maturation time.Time `json:"maturation"` -} - -func (bond *Bond) ID() ID32 { - buf := make([]byte, PeerIDLength+4 /* asset ID */ +len(bond.CoinID)) - copy(buf[:PeerIDLength], bond.PeerID[:]) - copy(buf[PeerIDLength:PeerIDLength+4], encode.Uint32Bytes(bond.AssetID)) - copy(buf[PeerIDLength+4:], bond.CoinID[:]) - return blake256.Sum256(buf) - -} - -type HTLCAudit struct{} diff --git a/tatanka/tanka/subscriptions.go b/tatanka/tanka/subscriptions.go deleted file mode 100644 index 957a78727f..0000000000 --- a/tatanka/tanka/subscriptions.go +++ /dev/null @@ -1,7 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package tanka - -type Subject string -type Topic string diff --git a/tatanka/tanka/swaps.go b/tatanka/tanka/swaps.go deleted file mode 100644 index 33aa95754d..0000000000 --- a/tatanka/tanka/swaps.go +++ /dev/null @@ -1,114 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package tanka - -import ( - "encoding/binary" - "encoding/hex" - "errors" - "fmt" - "time" - - "github.com/decred/dcrd/crypto/blake256" -) - -type Order struct { - From PeerID `json:"from"` - BaseID uint32 `json:"baseID"` - QuoteID uint32 `json:"quoteID"` - Sell bool `json:"sell"` - Qty uint64 `json:"qty"` - Rate uint64 `json:"rate"` - // LotSize: Tatankanet does not prescribe a lot size. Instead, users must - // select their own minimum lot size. The user's UI should ignore - // orderbook orders that don't have the requisite lot size. The UI should - // show lot size selection in terms of a sliding scale of fee exposure. - // Lot sizes can only be powers of 2. - LotSize uint64 `json:"lotSize"` - Nonce uint64 `json:"nonce"` - Stamp time.Time `json:"stamp"` -} - -func (ord *Order) ID() ID40 { - var b ID40 - copy(b[:32], ord.From[:]) - binary.BigEndian.PutUint64(b[32:], ord.Nonce) - return b -} - -func (ord *Order) Valid() error { - // Check whether the lot size is a power of 2, using binary jiu-jitsu. - if ord.LotSize&(ord.LotSize-1) != 0 { - return fmt.Errorf("lot size %d is not a power of 2", ord.LotSize) - } - if ord.Qty%ord.LotSize != 0 { - return fmt.Errorf("order quantity %d is not an integer-multiple of the order lot size %d", ord.Qty, ord.LotSize) - } - if ord.BaseID == ord.QuoteID { - return fmt.Errorf("base and quote assets are identical. %d = %d", ord.BaseID, ord.QuoteID) - } - if ord.Qty == 0 { - return errors.New("order quantity is zero") - } - if ord.Rate == 0 { - return errors.New("order rate is zero") - } - return nil -} - -type ID32 [32]byte - -func (i ID32) String() string { - return hex.EncodeToString(i[:]) -} - -type ID40 [40]byte - -func (i ID40) String() string { - return hex.EncodeToString(i[:]) -} - -type Match struct { - From PeerID `json:"from"` - OrderID ID40 `json:"orderID"` - Qty uint64 `json:"qty"` - BaseID uint32 `json:"baseID"` - QuoteID uint32 `json:"quoteID"` - Stamp time.Time `json:"stamp"` -} - -func (m *Match) ID() ID32 { - const msgLen = 32 + 40 + 4 + 4 + 8 - b := make([]byte, msgLen) - copy(b[:32], m.From[:]) - copy(b[32:72], m.OrderID[:]) - binary.BigEndian.PutUint32(b[72:76], m.BaseID) - binary.BigEndian.PutUint32(b[76:80], m.QuoteID) - binary.BigEndian.PutUint64(b[80:88], m.Qty) - return blake256.Sum256(b) -} - -type MatchAcceptance struct { - OrderID ID40 `json:"orderID"` - MatchID ID32 `json:"matchID"` -} - -type MarketParameters struct { - BaseID uint32 `json:"baseID"` - QuoteID uint32 `json:"quoteID"` -} - -type OrderUpdate struct { - From PeerID `json:"from"` - Nonce uint64 `json:"nonce"` - Qty uint64 `json:"qty"` - Stamp time.Time `json:"stamp"` -} - -func (ou *OrderUpdate) ID() ID40 { - var b ID40 - copy(b[:32], ou.From[:]) - binary.BigEndian.PutUint64(b[32:], ou.Nonce) - return b -} diff --git a/tatanka/tatanka.go b/tatanka/tatanka.go deleted file mode 100644 index aaa28c4165..0000000000 --- a/tatanka/tatanka.go +++ /dev/null @@ -1,806 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package tatanka - -import ( - "context" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - "sync" - "sync/atomic" - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/feerates" - "decred.org/dcrdex/dex/fiatrates" - "decred.org/dcrdex/dex/msgjson" - "decred.org/dcrdex/server/comms" - "decred.org/dcrdex/tatanka/chain" - "decred.org/dcrdex/tatanka/db" - "decred.org/dcrdex/tatanka/mj" - "decred.org/dcrdex/tatanka/tanka" - "decred.org/dcrdex/tatanka/tcp" - "github.com/decred/dcrd/dcrec/secp256k1/v4" - "golang.org/x/text/cases" - "golang.org/x/text/language" -) - -const ( - version = 0 - - // tatankaUniqueID is the unique ID used to register a Tatanka as a fiat - // rate listener. - tatankaUniqueID = "Tatanka" -) - -// remoteTatanka is a remote tatanka node. A remote tatanka node can either -// be outgoing (whitelist loop) or incoming via handleInboundTatankaConnect. -type remoteTatanka struct { - *peer - cfg atomic.Value // mj.TatankaConfig -} - -type Topic struct { - subjects map[tanka.Subject]map[tanka.PeerID]struct{} - subscribers map[tanka.PeerID]struct{} -} - -func (topic *Topic) unsubUser(peerID tanka.PeerID) { - if _, found := topic.subscribers[peerID]; !found { - return - } - for subID, subs := range topic.subjects { - delete(subs, peerID) - if len(subs) == 0 { - delete(topic.subjects, subID) - } - } - delete(topic.subscribers, peerID) -} - -// BootNode represents a configured boot node. Tatanka is whitelist only, and -// node operators are responsible for keeping their whitelist up to date. -type BootNode struct { - // Protocol is one of ("ws", "wss"), though other tatanka comms protocols - // may be implemented later. Or we may end up using e.g. go-libp2p. - Protocol string `json:"protocol"` - PeerID dex.Bytes `json:"peer_id"` - // Config can take different forms depending on the comms protocol, but is - // probably a tcp.RemoteNodeConfig. - Config json.RawMessage `json:"config"` -} - -// Draft note: Why do we need a parsedBootNode? Why not just make BootNode -// have a tanka.PeerID instead of a dex.Bytes? Also, instead of having a -// TatankaCredentials in client/conn, can we use BootNode as well? - -// parsedBootNode is the unexported version of BootNode, but with a PeerID -// instead of []byte. -type parsedBootNode struct { - peerID tanka.PeerID - cfg json.RawMessage - protocol string -} - -// Tatanka is a server node on Tatanka Mesh. Tatanka implements two APIs, one -// for fellow tatanka nodes, and one for clients. The primary roles of a -// tatanka node are -// 1. Maintain reputation information about client nodes. -// 2. Distribute broadcasts and relay tankagrams. -// 3. Provide some basic oracle services. -type Tatanka struct { - net dex.Network - log dex.Logger - tcpSrv *tcp.Server - dataDir string - ctx context.Context - wg *sync.WaitGroup - whitelist map[tanka.PeerID]*parsedBootNode - db *db.DB - nets atomic.Value // []uint32 - specialHandlers map[string]func(tanka.Sender, *msgjson.Message) *msgjson.Error - clientHandlers map[string]any // clientRequestHandler | clientNotificationHandler - tatankaHandlers map[string]any // tatankaRequestHandler | tatankaNotificationHandler - routes []string - httpReqHandlers map[string]comms.HTTPHandler - httpRoutes []string - maxClients int - // bondTier atomic.Uint64 - - priv *secp256k1.PrivateKey - id tanka.PeerID - - chainMtx sync.RWMutex - chains map[uint32]chain.Chain - - relayMtx sync.Mutex - recentRelays map[[32]byte]time.Time - - clientMtx sync.RWMutex - clients map[tanka.PeerID]*client - topics map[tanka.Topic]*Topic - - clientJobs chan *clientJob - remoteClients map[tanka.PeerID]map[tanka.PeerID]struct{} - - tatankasMtx sync.RWMutex - tatankas map[tanka.PeerID]*remoteTatanka - - fiatRateOracle *fiatrates.Oracle - fiatRateChan chan map[string]*fiatrates.FiatRateInfo - - feeRatesOracle *feerates.Oracle - feeRateEstimatesChan chan map[uint32]*feerates.Estimate -} - -// Config is the configuration of the Tatanka. -type Config struct { - Net dex.Network - DataDir string - Logger dex.Logger - RPC comms.RPCConfig - ChainConfig string - MaxClients int - - feeRatesOracleCfg feerates.Config - - WhiteList []BootNode - - FiatOracleConfig fiatrates.Config -} - -func New(cfg *Config) (*Tatanka, error) { - chainCfg, err := loadConfig(cfg.ChainConfig) - if err != nil { - return nil, fmt.Errorf("error loading config from %q: %w", cfg.ChainConfig, err) - } - - chains := make(map[uint32]chain.Chain) - nets := make([]uint32, 0, len(chainCfg.Chains)) - for _, c := range chainCfg.Chains { - chainID, found := dex.BipSymbolID(c.Symbol) - if !found { - return nil, fmt.Errorf("no chain ID found for symbol %s", c.Symbol) - } - chains[chainID], err = chain.New(chainID, c.Config, cfg.Logger.SubLogger(c.Symbol), cfg.Net) - if err != nil { - return nil, fmt.Errorf("error creating chain backend: %w", err) - } - nets = append(nets, chainID) - } - - db, err := db.New(filepath.Join(cfg.DataDir, "db"), cfg.Logger.SubLogger("DB")) - if err != nil { - return nil, fmt.Errorf("db.New error: %w", err) - } - - keyPath := filepath.Join(cfg.DataDir, "priv.key") - keyB, err := os.ReadFile(keyPath) - if err != nil { - if !os.IsNotExist(err) { - return nil, fmt.Errorf("error reading key file") - } - cfg.Logger.Infof("No key file found. Generating new identity.") - priv, err := secp256k1.GeneratePrivateKey() - if err != nil { - return nil, fmt.Errorf("GeneratePrivateKey error: %w", err) - } - keyB = priv.Serialize() - if err = os.WriteFile(keyPath, keyB, 0600); err != nil { - return nil, fmt.Errorf("error writing newly-generated key to %q: %v", keyPath, err) - } - } - priv := secp256k1.PrivKeyFromBytes(keyB) - var peerID tanka.PeerID - copy(peerID[:], priv.PubKey().SerializeCompressed()) - - whitelist := make(map[tanka.PeerID]*parsedBootNode, len(cfg.WhiteList)) - for _, n := range cfg.WhiteList { - if len(n.PeerID) != tanka.PeerIDLength { - return nil, fmt.Errorf("invalid peer ID length %d for %s boot node with configuration %q", len(n.PeerID), n.Protocol, n.Config) - } - var peerID tanka.PeerID - copy(peerID[:], n.PeerID) - whitelist[peerID] = &parsedBootNode{ - peerID: peerID, - cfg: n.Config, - protocol: n.Protocol, - } - } - - if cfg.MaxClients < 1 { - return nil, fmt.Errorf("max clients must be greater than 0") - } - - t := &Tatanka{ - net: cfg.Net, - dataDir: cfg.DataDir, - log: cfg.Logger, - whitelist: whitelist, - db: db, - priv: priv, - id: peerID, - chains: chains, - tatankas: make(map[tanka.PeerID]*remoteTatanka), - clients: make(map[tanka.PeerID]*client), - remoteClients: make(map[tanka.PeerID]map[tanka.PeerID]struct{}), - topics: make(map[tanka.Topic]*Topic), - recentRelays: make(map[[32]byte]time.Time), - clientJobs: make(chan *clientJob, 128), - clientHandlers: make(map[string]any), - tatankaHandlers: make(map[string]any), - httpReqHandlers: make(map[string]comms.HTTPHandler), - maxClients: cfg.MaxClients, - } - - if !cfg.FiatOracleConfig.AllFiatSourceDisabled() { - var tickers string - upperCaser := cases.Upper(language.AmericanEnglish) - for _, c := range chainCfg.Chains { - tickers += upperCaser.String(c.Symbol) + "," - } - tickers = strings.Trim(tickers, ",") - - t.fiatRateOracle, err = fiatrates.NewFiatOracle(cfg.FiatOracleConfig, tickers, t.log) - if err != nil { - return nil, fmt.Errorf("error initializing fiat oracle: %w", err) - } - - // Register tatanka as a listener - t.fiatRateChan = make(chan map[string]*fiatrates.FiatRateInfo) - t.fiatRateOracle.AddFiatRateListener(tatankaUniqueID, t.fiatRateChan) - } - - t.nets.Store(nets) - t.prepareHandlers() - t.tcpSrv, err = tcp.NewServer(&cfg.RPC, &tcpCore{t}, cfg.Logger.SubLogger("TCP")) - if err != nil { - return nil, fmt.Errorf("error starting TPC server:: %v", err) - } - - if t.net == dex.Mainnet || t.net == dex.Testnet { - t.feeRateEstimatesChan = make(chan map[uint32]*feerates.Estimate) - t.feeRatesOracle, err = feerates.NewOracle(t.net, cfg.feeRatesOracleCfg, nets, t.feeRateEstimatesChan) - if err != nil { - return nil, fmt.Errorf("feerates.NewOracle error: %w", err) - } - } - - return t, nil -} - -func (t *Tatanka) hasFeeRatesOracle() bool { - return t.feeRatesOracle != nil -} - -func (t *Tatanka) prepareHandlers() { - t.specialHandlers = map[string]func(tanka.Sender, *msgjson.Message) *msgjson.Error{ - // tatanka messages - mj.RouteTatankaConnect: t.handleInboundTatankaConnect, - // client messages - mj.RouteConnect: t.handleClientConnect, - mj.RoutePostBond: t.handlePostBond, - } - // Tatanka routes - registerTatankaHandler := func(route string, handler any) { - if _, is := handler.(tatankaRequestHandler); !is { - if _, is := handler.(tatankaNotificationHandler); !is { - panic("unknown handler type for " + route) - } - } - t.tatankaHandlers[route] = handler - } - for route, handler := range map[string]any{ - mj.RouteTatankaConfig: t.handleTatankaConfig, - mj.RouteRelayBroadcast: t.handleRelayBroadcast, - mj.RouteNewClient: t.handleNewRemoteClientNotification, - mj.RouteClientDisconnect: t.handleRemoteClientDisconnect, - mj.RouteRelayTankagram: t.handleRelayedTankagram, - mj.RoutePathInquiry: t.handlePathInquiry, - mj.RouteShareScore: t.handleShareScore, - } { - registerTatankaHandler(route, handler) - } - // Client routes - registerClientHandler := func(route string, handler any) { - if _, is := handler.(clientRequestHandler); !is { - if _, is := handler.(clientNotificationHandler); !is { - panic("unknown handler type for " + route) - } - } - t.clientHandlers[route] = handler - } - for route, handler := range map[string]any{ - mj.RouteSubscribe: t.handleSubscription, - mj.RouteUpdateSubscriptions: t.handleUpdateSubscriptions, - // mj.RouteUnsubscribe: t.handleUnsubscribe, - mj.RouteBroadcast: t.handleBroadcast, - mj.RouteTankagram: t.handleTankagram, - mj.RouteSetScore: t.handleSetScore, - } { - registerClientHandler(route, handler) - } - for route := range t.tatankaHandlers { - t.routes = append(t.routes, route) - } - for route := range t.specialHandlers { - t.routes = append(t.routes, route) - } - for route := range t.clientHandlers { - t.routes = append(t.routes, route) - } - - // HTTP Requests - registerHTTPRequestHandler := func(route string, handler comms.HTTPHandler) { - t.httpReqHandlers[route] = handler - t.httpRoutes = append(t.httpRoutes, route) - } - for route, handler := range map[string]comms.HTTPHandler{ - mj.RouteNodeInfo: t.handleNodeInfo, - } { - registerHTTPRequestHandler(route, handler) - } -} - -func (t *Tatanka) assets() []uint32 { - return t.nets.Load().([]uint32) -} - -func (t *Tatanka) fiatOracleEnabled() bool { - return t.fiatRateOracle != nil -} - -func (t *Tatanka) tatankaNodes() []*remoteTatanka { - t.tatankasMtx.RLock() - defer t.tatankasMtx.RUnlock() - nodes := make([]*remoteTatanka, 0, len(t.tatankas)) - for _, n := range t.tatankas { - nodes = append(nodes, n) - } - return nodes -} - -func (t *Tatanka) tatankaNode(peerID tanka.PeerID) *remoteTatanka { - t.tatankasMtx.RLock() - defer t.tatankasMtx.RUnlock() - return t.tatankas[peerID] -} - -func (t *Tatanka) clientNode(peerID tanka.PeerID) *client { - t.clientMtx.RLock() - defer t.clientMtx.RUnlock() - return t.clients[peerID] -} - -func (t *Tatanka) Connect(ctx context.Context) (_ *sync.WaitGroup, err error) { - t.ctx = ctx - var wg sync.WaitGroup - t.wg = &wg - - t.log.Infof("Starting Tatanka node with peer ID %s", t.id) - - // Start WebSocket server - cm := dex.NewConnectionMaster(t.tcpSrv) - if err := cm.ConnectOnce(ctx); err != nil { - return nil, fmt.Errorf("error connecting TCP server: %v", err) - } - - wg.Add(1) - go func() { - cm.Wait() - wg.Done() - }() - - // Start a ticker to clean up the recent relays map. - wg.Add(1) - go func() { - defer wg.Done() - tick := time.NewTicker(tanka.EpochLength * 4) // 1 minute - for { - select { - case <-tick.C: - t.relayMtx.Lock() - for bid, stamp := range t.recentRelays { - if time.Since(stamp) > time.Minute { - delete(t.recentRelays, bid) - } - } - t.relayMtx.Unlock() - case <-ctx.Done(): - return - } - } - }() - - wg.Add(1) - go func() { - defer wg.Done() - t.runRemoteClientsLoop(ctx) - }() - - var success bool - defer func() { - if !success { - cm.Disconnect() - cm.Wait() - } - }() - - t.chainMtx.RLock() - for assetID, c := range t.chains { - feeRater, is := c.(chain.FeeRater) - if !is { - continue - } - wg.Add(1) - go func(assetID uint32, feeRater chain.FeeRater) { - defer wg.Done() - t.monitorChainFees(ctx, assetID, feeRater) - }(assetID, feeRater) - } - t.chainMtx.RUnlock() - - wg.Add(1) - go func() { - defer wg.Done() - t.runWhitelistLoop(ctx) - }() - - if t.fiatOracleEnabled() { - wg.Add(2) - go func() { - defer wg.Done() - t.fiatRateOracle.Run(t.ctx) - }() - - go func() { - defer wg.Done() - t.broadcastRates() - }() - } - - if t.hasFeeRatesOracle() { - wg.Add(1) - go func() { - defer wg.Done() - t.feeRatesOracle.Run(t.ctx, t.log) - }() - - wg.Add(1) - go func() { - defer wg.Done() - t.broadcastFeeRateEstimates() - }() - } - - success = true - return &wg, nil -} - -// runWhitelistLoop attempts to connect to the whitelist, and then periodically -// tries again. -func (t *Tatanka) runWhitelistLoop(ctx context.Context) { - connectWhitelist := func() { - for proto, n := range t.whitelist { - t.tatankasMtx.RLock() - _, exists := t.tatankas[n.peerID] - t.tatankasMtx.RUnlock() - if exists { - continue - } - - p, err := t.db.Peer(n.peerID) - if err != nil { - t.log.Errorf("error getting peer info for boot node at %q (proto %q): %v", string(n.cfg), proto, err) - continue - } - - bondTier := p.BondTier() - // TODO: Check Tatanka Node reputation too - // if calcTier(rep, bondTier) <= 0 { - // t.log.Errorf("not attempting to contact banned boot node at %q (proto %q)", string(n.cfg), proto) - // } - - handleDisconnect := func() { - // TODO: schedule a reconnect? - t.tatankasMtx.Lock() - delete(t.tatankas, p.ID) - t.tatankasMtx.Unlock() - } - - handleMessage := func(cl tanka.Sender, msg *msgjson.Message) { - t.handleTatankaMessage(cl, msg) - } - - var cl tanka.Sender - switch n.protocol { - case "ws", "wss": - cl, err = t.tcpSrv.ConnectBootNode(ctx, n.cfg, handleMessage, handleDisconnect) - default: - t.log.Errorf("unknown boot node network protocol: %s", proto) - continue - } - if err != nil { - t.log.Errorf("error connecting boot node with proto = %s, config = %s", proto, string(n.cfg)) - continue - } - - t.log.Infof("Connected to boot node with peer ID %s, config %s", n.peerID, string(n.cfg)) - - cl.SetPeerID(p.ID) - pp := &peer{Peer: p, Sender: cl, rrs: make(map[tanka.PeerID]*tanka.Reputation)} - tt := &remoteTatanka{peer: pp} - t.tatankasMtx.Lock() - t.tatankas[p.ID] = tt - t.tatankasMtx.Unlock() - - cfgMsg := mj.MustRequest(mj.RouteTatankaConnect, t.generateConfig(bondTier)) - if err := t.request(cl, cfgMsg, func(msg *msgjson.Message) { - // Nothing to do. The only non-error result is payload = true. - }); err != nil { - t.log.Errorf("Error sending connect message: %w", err) - cl.Disconnect() - } - } - } - - for { - connectWhitelist() - - select { - case <-time.After(time.Minute * 5): - case <-ctx.Done(): - return - } - } -} - -// monitorChainFees monitors chains for new fee rates, and will distribute them -// as part of the not-yet-implemented oracle services the mesh provides. -func (t *Tatanka) monitorChainFees(ctx context.Context, assetID uint32, c chain.FeeRater) { - feeC := c.FeeChannel() - for { - select { - case feeRate := <-feeC: - // TODO: Distribute the fee rate to other Tatanka nodes, then to - // clients. Should fee rates be averaged across tatankas somehow? - fmt.Printf("new fee rate from %s: %d\n", dex.BipIDSymbol(assetID), feeRate) - case <-ctx.Done(): - return - } - } -} - -// sendResult sends the response to a request and logs errors. -func (t *Tatanka) sendResult(cl tanka.Sender, msgID uint64, result any) { - resp := mj.MustResponse(msgID, result, nil) - if err := t.send(cl, resp); err != nil { - peerID := cl.PeerID() - t.log.Errorf("error sending result to %q: %v", dex.Bytes(peerID[:]), err) - } -} - -// batchSend must be called with the clientMtx >= RLocked. -func (t *Tatanka) batchSend(peers map[tanka.PeerID]struct{}, msg *msgjson.Message) { - mj.SignMessage(t.priv, msg) - msgB, err := json.Marshal(msg) - if err != nil { - t.log.Errorf("error marshaling batch send message: %v", err) - return - } - disconnects := make(map[tanka.PeerID]struct{}) - t.clientMtx.RLock() - for peerID := range peers { - if c, found := t.clients[peerID]; found { - if err := c.SendRaw(msgB); err != nil { - t.log.Tracef("Disconnecting client %s after SendRaw error: %v", peerID, err) - disconnects[peerID] = struct{}{} - } - } else { - t.log.Error("found a subscriber ID without a client") - } - } - t.clientMtx.RUnlock() - if len(disconnects) > 0 { - for peerID := range disconnects { - t.handleClientDisconnect(peerID) - } - } -} - -// send signs and sends the message, returning any errors. -func (t *Tatanka) send(s tanka.Sender, msg *msgjson.Message) error { - mj.SignMessage(t.priv, msg) - err := s.Send(msg) - if err != nil { - t.handleClientDisconnect(s.PeerID()) - } - return err -} - -// request signs and sends the request, returning any errors. -func (t *Tatanka) request(s tanka.Sender, msg *msgjson.Message, respHandler func(*msgjson.Message)) error { - mj.SignMessage(t.priv, msg) - err := s.Request(msg, respHandler) - if err != nil { - t.handleClientDisconnect(s.PeerID()) - } - return err -} - -func (t *Tatanka) generateConfig(bondTier uint64) *mj.TatankaConfig { - return &mj.TatankaConfig{ - ID: t.id, - Version: version, - Chains: t.assets(), - BondTier: bondTier, - } -} - -func calcTier(r *tanka.Reputation, bondTier uint64) int64 { - return int64(bondTier) + r.Score/tanka.TierIncrement -} - -// ChainConfig is how the chain configuration is specified in the Tatanka -// configuration file. -type ChainConfig struct { - Symbol string `json:"symbol"` - Config json.RawMessage `json:"config"` -} - -// ConfigFile represents the JSON Tatanka configuration file. -type ConfigFile struct { - Chains []ChainConfig `json:"chains"` -} - -func loadConfig(configPath string) (*ConfigFile, error) { - var cfg ConfigFile - b, err := os.ReadFile(configPath) - if err != nil { - return nil, fmt.Errorf("OpenFile error: %w", err) - } - return &cfg, json.Unmarshal(b, &cfg) -} - -// tcpCore implements tcp.TankaCore. -type tcpCore struct { - *Tatanka -} - -func (t *tcpCore) Routes() []string { - return t.routes -} - -func (t *tcpCore) HandleMessage(cl tanka.Sender, msg *msgjson.Message) *msgjson.Error { - if t.log.Level() == dex.LevelTrace { - t.log.Tracef("Tatanka node handling message. route = %s, payload = %s", msg.Route, string(msg.Payload)) - } - - if _, found := t.clientHandlers[msg.Route]; found { - return t.handleClientMessage(cl, msg) - } - if _, found := t.tatankaHandlers[msg.Route]; found { - return t.handleTatankaMessage(cl, msg) - } - if handle, found := t.specialHandlers[msg.Route]; found { - return handle(cl, msg) - } - - fmt.Println("no route found", msg.Route) - - return msgjson.NewError(mj.ErrBadRequest, "route %q not known", msg.Route) -} - -func (t *tcpCore) HTTPRoutes() []string { - return t.httpRoutes -} - -func (t *tcpCore) HandleRequest(route string, thing any) (any, error) { - handler, found := t.httpReqHandlers[route] - if !found { - return nil, fmt.Errorf("http route %v not found", route) - } - - return handler(thing) -} - -// handleClientDisconnect handle a client disconnect, removing the client from -// the clients map and unsubscribing from all topics. -func (t *Tatanka) handleClientDisconnect(peerID tanka.PeerID) { - unsubs := make(map[tanka.Topic]*Topic) - - t.clientMtx.Lock() - delete(t.clients, peerID) - for n, topic := range t.topics { - if _, found := topic.subscribers[peerID]; found { - unsubs[n] = topic - delete(topic.subscribers, peerID) - for _, subs := range topic.subjects { - delete(subs, peerID) - } - } - } - t.clientMtx.Unlock() - - if len(unsubs) == 0 { - return - } - - stamp := time.Now() - for n, topic := range unsubs { - note := mj.MustNotification(mj.RouteBroadcast, &mj.Broadcast{ - Topic: n, - PeerID: peerID, - MessageType: mj.MessageTypeUnsubTopic, - Stamp: stamp, - }) - t.batchSend(topic.subscribers, note) - } - - note := mj.MustNotification(mj.RouteClientDisconnect, &mj.Disconnect{ID: peerID}) - mj.SignMessage(t.priv, note) - for _, tt := range t.tatankaNodes() { - tt.Send(note) - } -} - -// broadcastRates sends market rates to all fiat rate subscribers once new rates -// are received from the fiat oracle. -func (t *Tatanka) broadcastRates() { - for { - select { - case <-t.ctx.Done(): - return - case rates, ok := <-t.fiatRateChan: - if !ok { - t.log.Debug("Tatanka stopped listening for fiat rates.") - return - } - - t.clientMtx.RLock() - topic := t.topics[mj.TopicFiatRate] - t.clientMtx.RUnlock() - - // TODO: Is this a race condition on topic.subscribers? - if topic != nil && len(topic.subscribers) > 0 { - t.batchSend(topic.subscribers, mj.MustNotification(mj.RouteRates, &mj.RateMessage{ - Topic: mj.TopicFiatRate, - Rates: rates, - })) - } - } - } -} - -// broadcastFeeRateEstimates broadcasts fee rate estimates to all subscribed clients once -// we receive data from t.feeRateEstimatesChan. -func (t *Tatanka) broadcastFeeRateEstimates() { - for { - select { - case <-t.ctx.Done(): - return - case feeRateEstimates, ok := <-t.feeRateEstimatesChan: - if !ok { - t.log.Debug("Tatanka stopped listening for fee rate estimates.") - return - } - - t.clientMtx.RLock() - topic := t.topics[mj.TopicFeeRateEstimate] - t.clientMtx.RUnlock() - if topic == nil || len(topic.subscribers) == 0 { - continue - } - - note := mj.MustNotification(mj.RouteFeeRateEstimate, &mj.FeeRateEstimateMessage{ - Topic: mj.TopicFeeRateEstimate, - FeeRateEstimates: feeRateEstimates, - }) - t.batchSend(topic.subscribers, note) - } - } -} diff --git a/tatanka/tatanka_messages.go b/tatanka/tatanka_messages.go deleted file mode 100644 index 7e1591c5e1..0000000000 --- a/tatanka/tatanka_messages.go +++ /dev/null @@ -1,313 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package tatanka - -import ( - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/msgjson" - "decred.org/dcrdex/tatanka/mj" - "decred.org/dcrdex/tatanka/tanka" -) - -// handleInboundTatankaConnect handles an inbound tatanka connection. -func (t *Tatanka) handleInboundTatankaConnect(cl tanka.Sender, msg *msgjson.Message) *msgjson.Error { - var cfg mj.TatankaConfig - if err := msg.Unmarshal(&cfg); err != nil { - return msgjson.NewError(mj.ErrBadRequest, "unmarshal error: %v", err) - } - - if _, found := t.whitelist[cfg.ID]; !found { - return msgjson.NewError(mj.ErrAuth, "not whitelisted") - } - - p, err := t.db.Peer(cfg.ID) - if err != nil { - return msgjson.NewError(mj.ErrInternal, "error finding peer: %v", err) - } - - if err := mj.CheckSig(msg, p.PubKey); err != nil { - return msgjson.NewError(mj.ErrAuth, "bad sig") - } - - // if calcTier(rep, p.BondTier()) <= 0 { - // return msgjson.NewError(mj.ErrAuth, "denying inbound banned tatanka node %q", p.ID) - // } - - cfgMsg := mj.MustNotification(mj.RouteTatankaConfig, t.generateConfig(p.BondTier())) - if err := t.send(cl, cfgMsg); err != nil { - peerID := cl.PeerID() - t.log.Errorf("error sending configuration to connecting tatanka %q", dex.Bytes(peerID[:])) - cl.Disconnect() - return nil // don't bother - } - - // TODO: Resolve our own bond tier with the tatanka node. - // myTier := t.bondTier.Load() - // if conn.BondTier != myTier { - // bonds, err := t.db.GetBonds(t.id) - // if err != nil { - // t.log.Errorf("Error getting bonds from DB: %v", err) - // return msgjson.NewError(mj.ErrInternal, "internal error") - // } - // var bondTier uint64 - // for _, b := range bonds { - // bondTier += b.Strength - // } - // if conn.BondTier != myTier { - // bondsUpdate := mj.MustNotification(mj.RouteBonds, bonds) - // if err := cl.Send(bondsUpdate); err != nil { - // t.log.Errorf("Error sending bonds update to %s during connect: %w", p.ID, err) - // cl.Disconnect() - // return nil - // } - // } - - // } - - cl.SetPeerID(cfg.ID) - - t.tatankasMtx.Lock() - // TODO: Track peer reconnections and ban peer if on a runaway. - if _, exists := t.tatankas[p.ID]; exists { - t.log.Debugf("Connecting Tatanka node %s replaces already connected node", cl.PeerID()) - } - - pp := &peer{Peer: p, Sender: cl, rrs: make(map[tanka.PeerID]*tanka.Reputation)} - - // TODO: Check Tatanka Node reputation too - // if pp.banned() { - // t.tatankasMtx.Unlock() - // return msgjson.NewError(mj.ErrAuth, "inbound peer %q is banned", p.ID) - // } - - tt := &remoteTatanka{peer: pp} - tt.cfg.Store(&cfg) - t.tatankas[cfg.ID] = tt - t.tatankasMtx.Unlock() - - t.sendResult(cl, msg.ID, true) - - return nil -} - -type tatankaRequestHandler = func(tt *remoteTatanka, msg *msgjson.Message) *msgjson.Error -type tatankaNotificationHandler = func(tt *remoteTatanka, msg *msgjson.Message) - -// handleTatankaMessage handles all messages from remote tatanka nodes except -// for mj.RouteTatankaConnect. The node is expected to already be connected. -// handleTatankaMessage perfoms some initial handling of the message, like -// fetching the *remoteTatanka and checking signatures, before finding calling -// the appropriate handler. -func (t *Tatanka) handleTatankaMessage(cl tanka.Sender, msg *msgjson.Message) *msgjson.Error { - if t.log.Level() == dex.LevelTrace { - t.log.Tracef("Tatanka node handling message from remote tatanka. route = %s, payload = %s", msg.Route, string(msg.Payload)) - } - - peerID := cl.PeerID() - tt := t.tatankaNode(peerID) - if tt == nil { - t.log.Errorf("%q (%d) message received from unknown outbound tatanka peer %q", msg.Route, msg.ID, peerID) - return msgjson.NewError(mj.ErrAuth, "who even is this?") - } - - if err := mj.CheckSig(msg, tt.PubKey); err != nil { - t.log.Errorf("Signature error for %q message from %q: %v", msg.Route, tt.ID, err) - return msgjson.NewError(mj.ErrSig, "signature doesn't check") - } - - // The first message received after mj.RouteTatankaConnect must be the - // config. - if msg.Route != mj.RouteTatankaConfig && tt.cfg.Load() == nil { - t.log.Errorf("message received from tatanka peer %q before configuration", peerID) - tt.Disconnect() - return msgjson.NewError(mj.ErrNoConfig, "send your configuration first") - } - - switch handle := t.tatankaHandlers[msg.Route].(type) { - case tatankaRequestHandler: - return handle(tt, msg) - case tatankaNotificationHandler: - handle(tt, msg) - } - return nil -} - -// handleRelayedTankagram handles a tankagram relayed from another node. The -// mesh is currently only capable of single-hop relays. -func (t *Tatanka) handleRelayedTankagram(tt *remoteTatanka, msg *msgjson.Message) *msgjson.Error { - var gram *mj.Tankagram - if err := msg.Unmarshal(&gram); err != nil { - t.log.Errorf("Error unmarshaling tankagram from %s: %w", tt.ID, err) - return msgjson.NewError(mj.ErrBadRequest, "unmarshal error") - } - - t.clientMtx.RLock() - c, found := t.clients[gram.To] - t.clientMtx.RUnlock() - if !found { - t.sendResult(tt, msg.ID, &mj.TankagramResult{Result: mj.TRTNoPath}) - t.log.Warnf("Tankagram relay from %s to unknown client %s", tt.ID, gram.To) - return nil - } - - relayedMsg := mj.MustRequest(mj.RouteTankagram, gram) - var resB dex.Bytes - sent, clientErr, err := t.requestAnyOne([]tanka.Sender{c}, relayedMsg, &resB) - if sent { - t.sendResult(tt, msg.ID, &mj.TankagramResult{Result: mj.TRTTransmitted, EncryptedPayload: resB}) - return nil - } - if clientErr != nil { - t.sendResult(tt, msg.ID, &mj.TankagramResult{Result: mj.TRTErrBadClient}) - return nil - } - if err != nil { - t.log.Errorf("Error sending to local client %s: %v", gram.To, err) - t.sendResult(tt, msg.ID, &mj.TankagramResult{Result: mj.TRTErrFromTanka}) - return nil - } - // We might get here if the context expires during the call to requestOne. - return nil -} - -// handlePathInquiry returns whether a particular client is connected to this -// node. -func (t *Tatanka) handlePathInquiry(tt *remoteTatanka, msg *msgjson.Message) *msgjson.Error { - var inq mj.PathInquiry - if err := msg.Unmarshal(&inq); err != nil { - t.log.Errorf("Failed to unmarshal path inquiry from %s: %v", tt.ID, err) - } - - t.clientMtx.RLock() - _, found := t.clients[inq.ID] - t.clientMtx.RUnlock() - t.sendResult(tt, msg.ID, found) - return nil -} - -// handleNewRemoteClientNotification handles an mj.RouteNewClient notification, -// updating the t.remoteClients map. -func (t *Tatanka) handleNewRemoteClientNotification(tt *remoteTatanka, msg *msgjson.Message) { - if t.skipRelay(msg) { - return - } - - var conn mj.Connect - if err := msg.Unmarshal(&conn); err != nil { - t.log.Errorf("error unmarshaling %s notification payload: %q", msg.Route, err) - return - } - t.registerRemoteClient(tt.ID, conn.ID) - t.clientMtx.RLock() - var rep *tanka.Reputation - c, found := t.clients[conn.ID] - if found { - rep = c.Reputation - } - t.clientMtx.RUnlock() - if rep == nil { - var err error - rep, err = t.db.Reputation(conn.ID) - if err != nil { - t.log.Errorf("error getting reputation for %s from DB: %q", conn.ID, err) - return - } - } - t.sendResult(tt, msg.ID, rep) -} - -// handleRemoteClientDisconnect handles an mj.RouteClientDisconnect -// notification, updating the t.remoteClients map. -func (t *Tatanka) handleRemoteClientDisconnect(tt *remoteTatanka, msg *msgjson.Message) { - if t.skipRelay(msg) { - return - } - - var dconn mj.Disconnect - if err := msg.Unmarshal(&dconn); err != nil { - t.log.Errorf("error unmarshaling %s notification payload: %q", msg.Route, err) - return - } - - job := &clientJob{ - task: &clientJobRemoteDisconnect{ - clientID: dconn.ID, - tankaID: tt.ID, - }, - res: make(chan any, 1), - } - t.clientJobs <- job - <-job.res -} - -// handleTatankaConfig handles the mj.RouteTatankaConfig notification, -// storing a remote tatanka node's updated configuration info. -func (t *Tatanka) handleTatankaConfig(tt *remoteTatanka, msg *msgjson.Message) { - peerID := tt.PeerID() - - var cfg mj.TatankaConfig - if err := msg.Unmarshal(&cfg); err != nil { - tt.Disconnect() - t.log.Errorf("failed to parse tatanka config from %q: %w", peerID, err) - return - } - - tt.cfg.Store(&cfg) -} - -// handleRelayBroadcast distributes the broadcast to any locally connected -// subscribers. -func (t *Tatanka) handleRelayBroadcast(tt *remoteTatanka, msg *msgjson.Message) { - if t.skipRelay(msg) { - return - } - - var bcast *mj.Broadcast - if err := msg.Unmarshal(&bcast); err != nil || bcast == nil || bcast.Topic == "" { - t.log.Errorf("error unmarshaling broadcast from %s: %w", tt.ID, err) - return - } - - if time.Since(bcast.Stamp) > tanka.EpochLength || time.Until(bcast.Stamp) > tanka.EpochLength { - t.log.Errorf("Ignoring relayed broadcast with old stamp received from %s", tt.ID) - return - } - - t.registerRemoteClient(tt.ID, tt.ID) - - if msgErr := t.distributeBroadcastedMessage(bcast, false); msgErr != nil { - t.log.Errorf("error distributing broadcast: %v", msgErr) - return - } -} - -func (t *Tatanka) handleShareScore(tt *remoteTatanka, msg *msgjson.Message) { - var ss mj.SharedScore - if err := msg.Unmarshal(&ss); err != nil { - t.log.Errorf("error unmarshaling shared score: %v", err) - return - } - if err := t.db.SetScore(ss.Scored, ss.Scorer, ss.Score, time.Now()); err != nil { - return - } - t.clientMtx.RLock() - c, found := t.clients[ss.Scored] - t.clientMtx.RUnlock() - if !found { - return - } - - rep, err := t.db.Reputation(ss.Scored) - if err != nil { - t.log.Errorf("error updating reputation after shared score update: %v", err) - return - } - - c.mtx.Lock() - c.Reputation = rep - c.rrs[tt.ID] = ss.Reputation - c.mtx.Unlock() -} diff --git a/tatanka/tatanka_test.go b/tatanka/tatanka_test.go deleted file mode 100644 index 5b0008b887..0000000000 --- a/tatanka/tatanka_test.go +++ /dev/null @@ -1,225 +0,0 @@ -package tatanka - -import ( - "bytes" - "context" - "errors" - "fmt" - "testing" - "time" - - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/encode" - "decred.org/dcrdex/dex/msgjson" - "decred.org/dcrdex/server/comms" - "decred.org/dcrdex/tatanka/mj" - "decred.org/dcrdex/tatanka/tanka" - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -type tSender struct { - id tanka.PeerID - disconnected bool - responseErr error - responses []*msgjson.Message - recvd []*msgjson.Message - sendErr error -} - -func tNewSender(peerID tanka.PeerID) *tSender { - return &tSender{id: peerID} -} - -func (s *tSender) queueResponse(resp *msgjson.Message) { - s.responses = append(s.responses, resp) -} - -func (s *tSender) received() *msgjson.Message { - if len(s.recvd) == 0 { - return nil - } - msg := s.recvd[0] - s.recvd = s.recvd[1:] - return msg -} - -func (s *tSender) Send(msg *msgjson.Message) error { - s.recvd = append(s.recvd, msg) - return s.sendErr -} -func (s *tSender) SendRaw(rawMsg []byte) error { - msg, err := msgjson.DecodeMessage(rawMsg) - if err != nil { - return fmt.Errorf("tSender DecodeMessage error: %w", err) - } - s.recvd = append(s.recvd, msg) - return s.sendErr -} -func (s *tSender) Request(msg *msgjson.Message, respHandler func(*msgjson.Message)) error { - return s.RequestRaw(0, nil, respHandler) -} -func (s *tSender) RequestRaw(msgID uint64, rawMsg []byte, respHandler func(*msgjson.Message)) error { - if s.responseErr != nil { - return s.responseErr - } - if len(s.responses) == 0 { - return errors.New("not test responses queued") - } - resp := s.responses[0] - s.responses = s.responses[1:] - go respHandler(resp) // goroutine to simulate Sender impls - return nil -} -func (s *tSender) SetPeerID(tanka.PeerID) {} - -func (s *tSender) PeerID() tanka.PeerID { - return s.id -} -func (s *tSender) Disconnect() { - s.disconnected = true -} - -func tNewTatanka() *Tatanka { - priv, _ := secp256k1.GeneratePrivateKey() - var peerID tanka.PeerID - copy(peerID[:], priv.PubKey().SerializeCompressed()) - - return &Tatanka{ - net: dex.Simnet, - log: dex.StdOutLogger("T", dex.LevelTrace), - // db: db, - priv: priv, - id: peerID, - // chains: chains, - tatankas: make(map[tanka.PeerID]*remoteTatanka), - clients: make(map[tanka.PeerID]*client), - remoteClients: make(map[tanka.PeerID]map[tanka.PeerID]struct{}), - topics: make(map[tanka.Topic]*Topic), - recentRelays: make(map[[32]byte]time.Time), - clientJobs: make(chan *clientJob, 128), - clientHandlers: make(map[string]any), - tatankaHandlers: make(map[string]any), - httpReqHandlers: make(map[string]comms.HTTPHandler), - } -} - -func tNewRunningTatanka() (*Tatanka, func()) { - tt := tNewTatanka() - ctx, cancel := context.WithCancel(context.Background()) - tt.ctx = ctx - go tt.runRemoteClientsLoop(ctx) - - return tt, cancel -} - -func tNewPeer(id byte) (*peer, *tSender) { - var peerID tanka.PeerID - peerID[tanka.PeerIDLength-1] = id - p := &tanka.Peer{ID: peerID} - s := tNewSender(peerID) - return &peer{Peer: p, Sender: s, rrs: make(map[tanka.PeerID]*tanka.Reputation)}, s -} - -func tNewRemoteTatanka(id byte) (*remoteTatanka, *tSender) { - p, s := tNewPeer(id) - return &remoteTatanka{peer: p}, s -} - -func tNewClient(id byte) (*client, *tSender) { - p, s := tNewPeer(id) - return &client{peer: p}, s -} - -func TestTankagrams(t *testing.T) { - srv, shutdown := tNewRunningTatanka() - defer shutdown() - - tt, tts := tNewRemoteTatanka(1) - srv.tatankas[tt.ID] = tt - - c0, c0s := tNewClient(2) - srv.clients[c0.ID] = c0 - - c1, c1s := tNewClient(3) - srv.clients[c1.ID] = c1 - - gram := &mj.Tankagram{ - From: c0.ID, - To: c1.ID, - } - msg := mj.MustRequest(mj.RouteTankagram, gram) - var r mj.TankagramResult - checkResponse := func(expResult mj.TankagramResultType) { - t.Helper() - if msgErr := srv.handleTankagram(c0, msg); msgErr != nil { - t.Fatalf("Initial handleTankagram error: %v", msgErr) - } - respMsg := c0s.received() - if respMsg == nil { - t.Fatalf("No response received") - } - - respMsg.UnmarshalResult(&r) - if r.Result != expResult { - t.Fatalf("Expected result %q, got %q", expResult, r.Result) - } - } - - // tankagram to locally connected client success - responseB := dex.Bytes(encode.RandomBytes(10)) - resp := mj.MustResponse(msg.ID, responseB, nil) - c1s.queueResponse(resp) - checkResponse(mj.TRTTransmitted) - if !bytes.Equal(r.EncryptedPayload, responseB) { - t.Fatalf("wrong response") - } - - // Bad response from client. - resp, _ = msgjson.NewResponse(msg.ID, "zz", nil) - c1s.queueResponse(resp) - checkResponse(mj.TRTErrBadClient) - - // Client not responsive locally, but is remotely. - c1s.responseErr = errors.New("test error") - resp, _ = msgjson.NewResponse(msg.ID, &mj.TankagramResult{Result: mj.TRTTransmitted, EncryptedPayload: responseB}, nil) - tts.queueResponse(resp) - srv.remoteClients[c1.ID] = map[tanka.PeerID]struct{}{tt.ID: {}} - checkResponse(mj.TRTTransmitted) - if !bytes.Equal(r.EncryptedPayload, responseB) { - t.Fatalf("wrong response") - } - c1s.responseErr = nil - - // Client not known locally, and is erroneous remotely. - delete(srv.clients, c1.ID) - resp, _ = msgjson.NewResponse(msg.ID, &mj.TankagramResult{Result: mj.TRTErrBadClient}, nil) - tts.queueResponse(resp) - checkResponse(mj.TRTErrBadClient) - - // Client not known locally or remotely - delete(srv.remoteClients, c1.ID) - checkResponse(mj.TRTNoPath) - - // Now we're the remote. - - // We know the client and transmit the relayed tankagram successfully. - srv.clients[c1.ID] = c1 - resp, _ = msgjson.NewResponse(msg.ID, responseB, nil) - c1s.queueResponse(resp) - msg, _ = msgjson.NewRequest(mj.NewMessageID(), mj.RouteRelayTankagram, gram) - srv.handleRelayedTankagram(tt, msg) - respMsg := tts.received() - if respMsg == nil { - t.Fatalf("No response received") - } - respMsg.UnmarshalResult(&r) - if r.Result != mj.TRTTransmitted { - t.Fatalf("Expected result %q, got %q", mj.TRTTransmitted, r.Result) - } -} - -func TestPrepareHandlers(t *testing.T) { - // Really just checking for proper call signatures for the handlers. - // Incorrect call signatures will cause a panic in prepareHandlers. - tNewTatanka().prepareHandlers() -} diff --git a/tatanka/tcp/client/client.go b/tatanka/tcp/client/client.go deleted file mode 100644 index bab0575fc1..0000000000 --- a/tatanka/tcp/client/client.go +++ /dev/null @@ -1,140 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package client - -import ( - "context" - "fmt" - "net/url" - "sync" - "time" - - "decred.org/dcrdex/client/comms" - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/msgjson" - "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -type ConnectionStatus uint32 - -const ( - Disconnected ConnectionStatus = iota - Connected - InvalidCert -) - -type Client struct { - ctx context.Context - log dex.Logger - url *url.URL - cert []byte - cl comms.WsConn - cm *dex.ConnectionMaster - handle func(*msgjson.Message, func(*msgjson.Message)) - connectEventFunc func(ConnectionStatus) -} - -type Config struct { - Logger dex.Logger - URL string - Cert []byte - PrivateKey *secp256k1.PrivateKey - HandleMessage func(*msgjson.Message, func(*msgjson.Message)) - ConnectEventFunc func(ConnectionStatus) -} - -func New(cfg *Config) (*Client, error) { - u, err := url.Parse(cfg.URL) - if err != nil { - return nil, fmt.Errorf("error parsing URL: %w", err) - } - switch u.Scheme { - case "ws", "wss": - default: - return nil, fmt.Errorf("protocol should be 'ws' or 'wss', not %q", u.Scheme) - } - return &Client{ - log: cfg.Logger, - url: u, - cert: cfg.Cert, - handle: cfg.HandleMessage, - connectEventFunc: cfg.ConnectEventFunc, - }, nil -} - -func (c *Client) Connect(ctx context.Context) (_ *sync.WaitGroup, err error) { - c.ctx = ctx - if c.cl, err = comms.NewWsConn(&comms.WsCfg{ - URL: c.url.String(), - PingWait: 20 * time.Second, - Cert: c.cert, - DisableAutoReconnect: true, - ReconnectSync: func() { - fmt.Println("## RECONNECTED RECONNECTED RECONNECTED RECONNECTED ") - }, - ConnectEventFunc: func(status comms.ConnectionStatus) { - if c.connectEventFunc == nil { - return - } - - switch status { - case comms.Disconnected: - c.connectEventFunc(Disconnected) - case comms.Connected: - c.connectEventFunc(Connected) - case comms.InvalidCert: - c.connectEventFunc(InvalidCert) - default: - c.log.Errorf("Unknown connection status: %d", status) - } - }, - Logger: c.log.SubLogger("TC"), - }); err != nil { - return nil, fmt.Errorf("error creating websockets connection: %w", err) - } - - var wg sync.WaitGroup - - msgs := c.cl.MessageSource() - - c.cm = dex.NewConnectionMaster(c.cl) - if err := c.cm.ConnectOnce(ctx); err != nil { - return nil, fmt.Errorf("error connecting to %q: %w", c.url, err) - } - - sendResponse := func(msg *msgjson.Message) { - c.log.Infof("Sending response: %v", msg) - err = c.cl.Send(msg) - if err != nil { - c.log.Errorf("Error sending response: %v", err) - } - } - - wg.Add(1) - go func() { - defer wg.Done() - for { - select { - case msg := <-msgs: - if msg == nil { - c.log.Errorf("Received nil message") - continue - } - c.handle(msg, sendResponse) - case <-ctx.Done(): - return - } - } - }() - - return &wg, nil -} - -func (c *Client) Send(msg *msgjson.Message) error { - return c.cl.Send(msg) -} - -func (c *Client) Request(msg *msgjson.Message, respHandler func(*msgjson.Message)) error { - return c.cl.Request(msg, respHandler) -} diff --git a/tatanka/tcp/server.go b/tatanka/tcp/server.go deleted file mode 100644 index 6e5f77b272..0000000000 --- a/tatanka/tcp/server.go +++ /dev/null @@ -1,218 +0,0 @@ -// This code is available on the terms of the project LICENSE.md file, -// also available online at https://blueoakcouncil.org/license/1.0.0. - -package tcp - -import ( - "context" - "encoding/json" - "fmt" - "net/url" - "sync" - "sync/atomic" - "time" - - clientcomms "decred.org/dcrdex/client/comms" - "decred.org/dcrdex/dex" - "decred.org/dcrdex/dex/msgjson" - "decred.org/dcrdex/server/comms" - "decred.org/dcrdex/tatanka/mj" - "decred.org/dcrdex/tatanka/tanka" -) - -// linkWrapper wraps a comms.Link to create a tanka.Sender. -type linkWrapper struct { - comms.Link -} - -func (l *linkWrapper) Request(msg *msgjson.Message, respHandler func(*msgjson.Message)) error { - const requestTimeout = time.Second * 30 - return l.Link.Request(msg, func(_ comms.Link, respMsg *msgjson.Message) { - respHandler(respMsg) - }, requestTimeout, func() { // expire - errMsg := mj.MustResponse(msg.ID, nil, msgjson.NewError(mj.ErrTimeout, "request timed out")) - respHandler(errMsg) - }) -} - -func (l *linkWrapper) RequestRaw(msgID uint64, rawMsg []byte, respHandler func(*msgjson.Message)) error { - const requestTimeout = time.Second * 30 - return l.Link.RequestRaw(msgID, rawMsg, func(_ comms.Link, respMsg *msgjson.Message) { - respHandler(respMsg) - }, requestTimeout, func() { // expire - errMsg := mj.MustResponse(msgID, nil, msgjson.NewError(mj.ErrTimeout, "request timed out")) - respHandler(errMsg) - }) -} - -func (l *linkWrapper) PeerID() (peerID tanka.PeerID) { - copy(peerID[:], []byte(l.CustomID())) - return -} - -func (l *linkWrapper) SetPeerID(peerID tanka.PeerID) { - l.SetCustomID(string(peerID[:])) -} - -type wsConnWrapper struct { - clientcomms.WsConn - cm *dex.ConnectionMaster - id atomic.Value -} - -func newWsConnWrapper(cl clientcomms.WsConn, cm *dex.ConnectionMaster) *wsConnWrapper { - w := &wsConnWrapper{ - WsConn: cl, - cm: cm, - } - w.id.Store(tanka.PeerID{}) - return w -} - -func (cl *wsConnWrapper) Disconnect() { - cl.cm.Disconnect() - cl.cm.Wait() -} - -func (cl *wsConnWrapper) SetPeerID(id tanka.PeerID) { - cl.id.Store(id) -} - -func (cl *wsConnWrapper) PeerID() tanka.PeerID { - return cl.id.Load().(tanka.PeerID) -} - -// Server handles HTTP, HTTPS, WebSockets, and Tor communications protocols. -type Server struct { - t TankaCore - wg *sync.WaitGroup - srv *comms.Server - log dex.Logger -} - -type TankaCore interface { - // Config() *mj.TatankaConfig - Routes() []string - HandleMessage(tanka.Sender, *msgjson.Message) *msgjson.Error - HTTPRoutes() []string - HandleRequest(route string, thing any) (any, error) -} - -func NewServer(cfg *comms.RPCConfig, t TankaCore, log dex.Logger) (*Server, error) { - srv, err := comms.NewServer(cfg) - if err != nil { - return nil, fmt.Errorf("NewServer error: %w", err) - } - - s := &Server{ - t: t, - srv: srv, - log: log, - } - - for _, r := range t.Routes() { - srv.Route(r, func(l comms.Link, msg *msgjson.Message) *msgjson.Error { - return t.HandleMessage(&linkWrapper{l}, msg) - }) - } - - for _, r := range t.HTTPRoutes() { - route := r - srv.RegisterHTTP(r, func(thing any) (any, error) { - return t.HandleRequest(route, thing) - }) - srv.Mux().Get(fmt.Sprintf("/%s", r), srv.NewRouteHandler(r)) - } - - return s, nil -} - -func (s *Server) Connect(ctx context.Context) (*sync.WaitGroup, error) { - var wg sync.WaitGroup - s.wg = &wg - - // Start WebSocket server - runner := dex.NewStartStopWaiter(s.srv) - runner.Start(ctx) // stopped with Stop - - wg.Add(1) - go func() { - <-ctx.Done() - runner.Stop() - wg.Done() - }() - - return &wg, nil -} - -type RemoteNodeConfig struct { - URL string `json:"url"` - Cert []byte `json:"cert"` -} - -func (s *Server) ConnectBootNode( - ctx context.Context, - rawCfg json.RawMessage, - handleMessage func(cl tanka.Sender, msg *msgjson.Message), - disconnect func(), -) (tanka.Sender, error) { - - var n RemoteNodeConfig - if err := json.Unmarshal(rawCfg, &n); err != nil { - return nil, fmt.Errorf("error reading boot node configuration: %w", err) - } - - uri, err := url.Parse(n.URL) - if err != nil { - return nil, fmt.Errorf("url parse error: %w", err) - } - - if uri.Path != "/ws" { - uri.Path = "/ws" - } - - cl, err := clientcomms.NewWsConn(&clientcomms.WsCfg{ - URL: uri.String(), - PingWait: 20 * time.Second, - Cert: n.Cert, - ConnectEventFunc: func(status clientcomms.ConnectionStatus) { - if status == clientcomms.Disconnected { - disconnect() - } - }, - Logger: dex.StdOutLogger(fmt.Sprintf("CL[%s]", n.URL), dex.LevelDebug), - DisableAutoReconnect: true, - }) - if err != nil { - return nil, fmt.Errorf("failed to connect to bootnode %q: %v", n.URL, err) - } - - cm := dex.NewConnectionMaster(cl) - if err := cm.ConnectOnce(ctx); err != nil { - return nil, fmt.Errorf("failed to connect to boot node %q. error: %w", n.URL, err) - } - - sender := newWsConnWrapper(cl, cm) - go s.handleOutboudTatankaConnection(ctx, sender, handleMessage) - - return sender, nil -} - -func (s *Server) handleOutboudTatankaConnection(ctx context.Context, cl *wsConnWrapper, handleMessage func(cl tanka.Sender, msg *msgjson.Message)) { - msgs := cl.MessageSource() - for { - if ctx.Err() != nil { - return - } - select { - case msg, ok := <-msgs: - if !ok { - // Connection has been closed. - return - } - handleMessage(cl, msg) - case <-ctx.Done(): - return - } - } -}