From 20bebca055439a907adcfc80cb98b0d7ffb79f38 Mon Sep 17 00:00:00 2001 From: Brian Stafford Date: Mon, 2 Mar 2026 20:13:16 -0600 Subject: [PATCH 1/2] switch to new mesh repo Deletes tatanka package and testing harness. New repo at github.com/bisoncraft/mesh has its own harness. Updates mesh client to leverage client from new mesh repo, and points simnet mesh client in core at new repo's harness. Relies on some changes that are not merged in new repo, so using a mod replace for now. --- client/asset/driver.go | 28 + client/cmd/bisonw-desktop/go.mod | 94 +- client/cmd/bisonw-desktop/go.sum | 316 +++++- client/core/core.go | 43 +- client/core/mesh.go | 48 +- client/mesh/meshclient.go | 169 +++ dex/asset.go | 4 + dex/testing/loadbot/go.mod | 92 +- dex/testing/loadbot/go.sum | 312 +++++- dex/testing/tatanka/harness.sh | 68 -- dex/testing/tatanka/priv.key | 1 - go.mod | 94 +- go.sum | 316 +++++- tatanka/chain/chain.go | 67 -- tatanka/chain/utxo/btc.go | 335 ------ tatanka/chain/utxo/btc_live_test.go | 115 -- tatanka/chain/utxo/dcr.go | 299 ------ tatanka/client/conn/conn.go | 1093 -------------------- tatanka/client/conn/conn_live_test.go | 607 ----------- tatanka/client/conn/conn_test.go | 44 - tatanka/client/mesh/mesh.go | 380 ------- tatanka/client/mesh/trade.go | 266 ----- tatanka/client/orderbook/orderbook.go | 158 --- tatanka/client/orderbook/orderbook_test.go | 262 ----- tatanka/client/trade/README.md | 13 - tatanka/client/trade/compat.go | 68 -- tatanka/client/trade/compat_test.go | 82 -- tatanka/client/trade/match.go | 58 -- tatanka/client/trade/match_test.go | 137 --- tatanka/client_messages.go | 866 ---------------- tatanka/cmd/tatanka/main.go | 334 ------ tatanka/db/bonds.go | 73 -- tatanka/db/db.go | 158 --- tatanka/db/db_test.go | 154 --- tatanka/db/peers.go | 30 - tatanka/db/reputation.go | 56 - tatanka/mj/auth.go | 44 - tatanka/mj/types.go | 255 ----- tatanka/peer.go | 46 - tatanka/spec/intro.md | 93 -- tatanka/spec/meshdex.md | 62 -- tatanka/tanka/peer.go | 83 -- tatanka/tanka/peer_test.go | 1 - tatanka/tanka/reputation.go | 46 - tatanka/tanka/subscriptions.go | 7 - tatanka/tanka/swaps.go | 114 -- tatanka/tatanka.go | 806 --------------- tatanka/tatanka_messages.go | 313 ------ tatanka/tatanka_test.go | 225 ---- tatanka/tcp/client/client.go | 140 --- tatanka/tcp/server.go | 218 ---- 51 files changed, 1318 insertions(+), 8375 deletions(-) create mode 100644 client/mesh/meshclient.go delete mode 100755 dex/testing/tatanka/harness.sh delete mode 100644 dex/testing/tatanka/priv.key delete mode 100644 tatanka/chain/chain.go delete mode 100644 tatanka/chain/utxo/btc.go delete mode 100644 tatanka/chain/utxo/btc_live_test.go delete mode 100644 tatanka/chain/utxo/dcr.go delete mode 100644 tatanka/client/conn/conn.go delete mode 100644 tatanka/client/conn/conn_live_test.go delete mode 100644 tatanka/client/conn/conn_test.go delete mode 100644 tatanka/client/mesh/mesh.go delete mode 100644 tatanka/client/mesh/trade.go delete mode 100644 tatanka/client/orderbook/orderbook.go delete mode 100644 tatanka/client/orderbook/orderbook_test.go delete mode 100644 tatanka/client/trade/README.md delete mode 100644 tatanka/client/trade/compat.go delete mode 100644 tatanka/client/trade/compat_test.go delete mode 100644 tatanka/client/trade/match.go delete mode 100644 tatanka/client/trade/match_test.go delete mode 100644 tatanka/client_messages.go delete mode 100644 tatanka/cmd/tatanka/main.go delete mode 100644 tatanka/db/bonds.go delete mode 100644 tatanka/db/db.go delete mode 100644 tatanka/db/db_test.go delete mode 100644 tatanka/db/peers.go delete mode 100644 tatanka/db/reputation.go delete mode 100644 tatanka/mj/auth.go delete mode 100644 tatanka/mj/types.go delete mode 100644 tatanka/peer.go delete mode 100644 tatanka/spec/intro.md delete mode 100644 tatanka/spec/meshdex.md delete mode 100644 tatanka/tanka/peer.go delete mode 100644 tatanka/tanka/peer_test.go delete mode 100644 tatanka/tanka/reputation.go delete mode 100644 tatanka/tanka/subscriptions.go delete mode 100644 tatanka/tanka/swaps.go delete mode 100644 tatanka/tatanka.go delete mode 100644 tatanka/tatanka_messages.go delete mode 100644 tatanka/tatanka_test.go delete mode 100644 tatanka/tcp/client/client.go delete mode 100644 tatanka/tcp/server.go 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-20260310125835-501f40b7413d 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..2c136202bd 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-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= @@ -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 - } - } -} From 0401bf788577d9dc0c3c77458ee27f641788f859 Mon Sep 17 00:00:00 2001 From: Brian Stafford Date: Thu, 12 Mar 2026 12:46:25 -0500 Subject: [PATCH 2/2] update replace --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0cbce4e8cb..f37c5bf440 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module decred.org/dcrdex go 1.24.9 -replace github.com/bisoncraft/mesh => github.com/buck54321/mesh v0.0.0-20260310125835-501f40b7413d +replace github.com/bisoncraft/mesh => github.com/buck54321/mesh v0.0.0-20260312171757-88530d61c463 require ( decred.org/dcrwallet/v5 v5.0.3 diff --git a/go.sum b/go.sum index 2c136202bd..34f7dbb915 100644 --- a/go.sum +++ b/go.sum @@ -272,8 +272,8 @@ 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/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=