From 5ad178b2591bd721d73ebeea5496dc34fcc00ed0 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Tue, 10 Dec 2024 13:09:33 +0530 Subject: [PATCH 01/35] fix --- cmd/staking-expiry-checker/main.go | 10 +- go.mod | 260 ++++- go.sum | 1273 ++++++++++++++++++++++++- internal/btcclient/notifier.go | 91 ++ internal/config/btc.go | 14 + internal/db/delegation.go | 24 + internal/db/interface.go | 4 + internal/db/model/delegation.go | 36 +- internal/db/model/params.go | 32 + internal/db/model/setup.go | 5 +- internal/db/params.go | 29 + internal/services/service.go | 27 +- internal/services/watch_btc_events.go | 625 ++++++++++++ internal/types/error.go | 14 + internal/utils/utils.go | 20 + 15 files changed, 2378 insertions(+), 86 deletions(-) create mode 100644 internal/btcclient/notifier.go create mode 100644 internal/db/model/params.go create mode 100644 internal/db/params.go create mode 100644 internal/services/watch_btc_events.go diff --git a/cmd/staking-expiry-checker/main.go b/cmd/staking-expiry-checker/main.go index 6e60544..62a6772 100644 --- a/cmd/staking-expiry-checker/main.go +++ b/cmd/staking-expiry-checker/main.go @@ -52,7 +52,15 @@ func main() { log.Fatal().Err(err).Msg("error while creating btc client") } - delegationService := services.NewService(dbClient, btcClient) + btcNotifier, err := btcclient.NewBTCNotifier( + &cfg.Btc, + &btcclient.EmptyHintCache{}, + ) + if err != nil { + log.Fatal().Err(err).Msg("error while creating btc notifier") + } + + delegationService := services.NewService(cfg, dbClient, btcNotifier, btcClient) if err != nil { log.Fatal().Err(err).Msg("error while creating delegation service") } diff --git a/go.mod b/go.mod index 412ee36..b17e754 100644 --- a/go.mod +++ b/go.mod @@ -1,40 +1,248 @@ module github.com/babylonlabs-io/staking-expiry-checker -go 1.21.6 +go 1.23.1 require ( - github.com/btcsuite/btcd v0.24.0 - github.com/spf13/viper v1.18.2 + github.com/babylonlabs-io/babylon v0.18.1 + github.com/btcsuite/btcd v0.24.3-0.20241011125836-24eb815168f4 + github.com/btcsuite/btcd/btcec/v2 v2.3.4 + github.com/btcsuite/btcd/btcutil v1.1.6 + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 + github.com/btcsuite/btcwallet v0.16.10-0.20240912233857-ffb143c77cc5 + github.com/lightningnetwork/lnd v0.18.4-beta.rc1 + github.com/spf13/viper v1.19.0 ) require ( + cosmossdk.io/api v0.7.5 // indirect + cosmossdk.io/collections v0.4.0 // indirect + cosmossdk.io/core v0.11.1 // indirect + cosmossdk.io/depinject v1.0.0 // indirect + cosmossdk.io/errors v1.0.1 // indirect + cosmossdk.io/log v1.4.1 // indirect + cosmossdk.io/math v1.4.0 // indirect + cosmossdk.io/store v1.1.0 // indirect + cosmossdk.io/x/tx v0.13.4 // indirect + dario.cat/mergo v1.0.0 // indirect + filippo.io/edwards25519 v1.0.0 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/DataDog/datadog-go v3.2.0+incompatible // indirect + github.com/DataDog/zstd v1.5.5 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect + github.com/aead/siphash v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.1.3 // indirect - github.com/btcsuite/btcd/btcutil v1.1.5 // indirect - github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect + github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect + github.com/btcsuite/btcd/btcutil/psbt v1.1.8 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect + github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 // indirect + github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 // indirect + github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5 // indirect + github.com/btcsuite/btcwallet/walletdb v1.4.4 // indirect + github.com/btcsuite/btcwallet/wtxmgr v1.5.4 // indirect github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/btcsuite/winsvc v1.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cockroachdb/errors v1.11.3 // indirect + github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.2 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/cometbft/cometbft v0.38.15 // indirect + github.com/cometbft/cometbft-db v0.15.0 // indirect + github.com/containerd/continuity v0.3.0 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-db v1.0.2 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect + github.com/cosmos/cosmos-sdk v0.50.9 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/gogoproto v1.7.0 // indirect + github.com/cosmos/iavl v1.2.0 // indirect + github.com/cosmos/ics23/go v0.10.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect - github.com/golang/snappy v0.0.4 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/decred/dcrd/lru v1.1.2 // indirect + github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect + github.com/dgraph-io/badger/v4 v4.3.0 // indirect + github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91 // indirect + github.com/docker/cli v25.0.6+incompatible // indirect + github.com/docker/docker v25.0.6+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dvsekhvalnov/jose2go v1.6.0 // indirect + github.com/emicklei/dot v1.6.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fergusstrange/embedded-postgres v1.25.0 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/go-kit/kit v0.13.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect + github.com/gogo/googleapis v1.4.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-migrate/migrate/v4 v4.17.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/flatbuffers v2.0.8+incompatible // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/handlers v1.5.2 // indirect + github.com/gorilla/mux v1.8.1 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.3 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-uuid v1.0.2 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hdevalence/ed25519consensus v0.1.0 // indirect + github.com/iancoleman/strcase v0.3.0 // indirect + github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.17.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.14.3 // indirect + github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.3.3 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgtype v1.14.0 // indirect + github.com/jackc/pgx/v4 v4.18.2 // indirect + github.com/jackc/pgx/v5 v5.3.1 // indirect + github.com/jessevdk/go-flags v1.4.0 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect + github.com/jonboulle/clockwork v0.2.2 // indirect + github.com/jrick/logrotate v1.1.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kkdai/bstream v1.0.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect + github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd // indirect + github.com/lightninglabs/neutrino/cache v1.1.2 // indirect + github.com/lightningnetwork/lnd/clock v1.1.1 // indirect + github.com/lightningnetwork/lnd/fn v1.2.3 // indirect + github.com/lightningnetwork/lnd/healthcheck v1.2.5 // indirect + github.com/lightningnetwork/lnd/kvdb v1.4.10 // indirect + github.com/lightningnetwork/lnd/queue v1.1.1 // indirect + github.com/lightningnetwork/lnd/sqldb v1.0.4 // indirect + github.com/lightningnetwork/lnd/ticker v1.1.1 // indirect + github.com/lightningnetwork/lnd/tlv v1.2.6 // indirect + github.com/lightningnetwork/lnd/tor v1.1.2 // indirect + github.com/linxGnu/grocksdb v1.9.3 // indirect + github.com/miekg/dns v1.1.43 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect + github.com/mtibben/percent v0.2.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc2 // indirect + github.com/opencontainers/runc v1.1.12 // indirect + github.com/ory/dockertest/v3 v3.10.0 // indirect + github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // 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_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.60.1 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/cors v1.11.1 // indirect + github.com/sasha-s/go-deadlock v0.3.5 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/soheilhy/cmux v0.1.5 // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/tendermint/go-amino v0.16.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect + github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect + github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/sync v0.5.0 // indirect - google.golang.org/protobuf v1.32.0 // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect + go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect + go.etcd.io/etcd/api/v3 v3.5.12 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.12 // indirect + go.etcd.io/etcd/client/v2 v2.305.12 // indirect + go.etcd.io/etcd/client/v3 v3.5.12 // indirect + go.etcd.io/etcd/pkg/v3 v3.5.7 // indirect + go.etcd.io/etcd/raft/v3 v3.5.7 // indirect + go.etcd.io/etcd/server/v3 v3.5.7 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/sdk v1.22.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.opentelemetry.io/proto/otlp v0.9.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.1 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gotest.tools/v3 v3.5.1 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.49.3 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/sqlite v1.29.10 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect + nhooyr.io/websocket v1.8.6 // indirect + pgregory.net/rapid v1.1.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) require ( @@ -44,25 +252,25 @@ require ( github.com/joho/godotenv v1.5.1 github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect - github.com/prometheus/client_golang v1.19.0 - github.com/rs/zerolog v1.32.0 + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/prometheus/client_golang v1.20.5 + github.com/rs/zerolog v1.33.0 github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/cobra v1.8.0 + github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.9.0 github.com/subosito/gotenv v1.6.0 // indirect go.mongodb.org/mongo-driver v1.14.0 go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 82f42ef..788932e 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,134 @@ +cloud.google.com/go v0.26.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.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= +cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg= +cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= +cosmossdk.io/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ= +cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= +cosmossdk.io/client/v2 v2.0.0-beta.1 h1:XkHh1lhrLYIT9zKl7cIOXUXg2hdhtjTPBUfqERNA1/Q= +cosmossdk.io/client/v2 v2.0.0-beta.1/go.mod h1:JEUSu9moNZQ4kU3ir1DKD5eU4bllmAexrGWjmb9k8qU= +cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= +cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= +cosmossdk.io/core v0.11.1 h1:h9WfBey7NAiFfIcUhDVNS503I2P2HdZLebJlUIs8LPA= +cosmossdk.io/core v0.11.1/go.mod h1:OJzxcdC+RPrgGF8NJZR2uoQr56tc7gfBKhiKeDO7hH0= +cosmossdk.io/depinject v1.0.0 h1:dQaTu6+O6askNXO06+jyeUAnF2/ssKwrrszP9t5q050= +cosmossdk.io/depinject v1.0.0/go.mod h1:zxK/h3HgHoA/eJVtiSsoaRaRA2D5U4cJ5thIG4ssbB8= +cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= +cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= +cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM= +cosmossdk.io/log v1.4.1/go.mod h1:k08v0Pyq+gCP6phvdI6RCGhLf/r425UT6Rk/m+o74rU= +cosmossdk.io/math v1.4.0 h1:XbgExXFnXmF/CccPPEto40gOO7FpWu9yWNAZPN3nkNQ= +cosmossdk.io/math v1.4.0/go.mod h1:O5PkD4apz2jZs4zqFdTr16e1dcaQCc5z6lkEnrrppuk= +cosmossdk.io/store v1.1.0 h1:LnKwgYMc9BInn9PhpTFEQVbL9UK475G2H911CGGnWHk= +cosmossdk.io/store v1.1.0/go.mod h1:oZfW/4Fc/zYqu3JmQcQdUJ3fqu5vnYTn3LZFFy8P8ng= +cosmossdk.io/x/circuit v0.1.1 h1:KPJCnLChWrxD4jLwUiuQaf5mFD/1m7Omyo7oooefBVQ= +cosmossdk.io/x/circuit v0.1.1/go.mod h1:B6f/urRuQH8gjt4eLIXfZJucrbreuYrKh5CSjaOxr+Q= +cosmossdk.io/x/evidence v0.1.1 h1:Ks+BLTa3uftFpElLTDp9L76t2b58htjVbSZ86aoK/E4= +cosmossdk.io/x/evidence v0.1.1/go.mod h1:OoDsWlbtuyqS70LY51aX8FBTvguQqvFrt78qL7UzeNc= +cosmossdk.io/x/feegrant v0.1.1 h1:EKFWOeo/pup0yF0svDisWWKAA9Zags6Zd0P3nRvVvw8= +cosmossdk.io/x/feegrant v0.1.1/go.mod h1:2GjVVxX6G2fta8LWj7pC/ytHjryA6MHAJroBWHFNiEQ= +cosmossdk.io/x/nft v0.1.1 h1:pslAVS8P5NkW080+LWOamInjDcq+v2GSCo+BjN9sxZ8= +cosmossdk.io/x/nft v0.1.1/go.mod h1:Kac6F6y2gsKvoxU+fy8uvxRTi4BIhLOor2zgCNQwVgY= +cosmossdk.io/x/tx v0.13.4 h1:Eg0PbJgeO0gM8p5wx6xa0fKR7hIV6+8lC56UrsvSo0Y= +cosmossdk.io/x/tx v0.13.4/go.mod h1:BkFqrnGGgW50Y6cwTy+JvgAhiffbGEKW6KF9ufcDpvk= +cosmossdk.io/x/upgrade v0.1.4 h1:/BWJim24QHoXde8Bc64/2BSEB6W4eTydq0X/2f8+g38= +cosmossdk.io/x/upgrade v0.1.4/go.mod h1:9v0Aj+fs97O+Ztw+tG3/tp5JSlrmT7IcFhAebQHmOPo= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= +github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/CosmWasm/wasmd v0.53.0 h1:kdaoAi20bIb4VCsxw9pRaT2g5PpIp82Wqrr9DRVN9ao= +github.com/CosmWasm/wasmd v0.53.0/go.mod h1:FJl/aWjdpGof3usAMFQpDe07Rkx77PUzp0cygFMOvtw= +github.com/CosmWasm/wasmvm/v2 v2.1.3 h1:CSJTauZqkHyb9yic6JVYCjiGUgxI2MJV2QzfSu8m49c= +github.com/CosmWasm/wasmvm/v2 v2.1.3/go.mod h1:bMhLQL4Yp9CzJi9A83aR7VO9wockOsSlZbT4ztOl6bg= +github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= +github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.49.6 h1:yNldzF5kzLBRvKlKz1S0bkvc2+04R1kt13KfBWQBfFA= +github.com/aws/aws-sdk-go v1.49.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/babylonlabs-io/babylon v0.18.1 h1:ID8BDDHc+snOnW2nqjP7OMf3a//5mvjbEmiAoJAtZlc= +github.com/babylonlabs-io/babylon v0.18.1/go.mod h1:sT+KG2U+M0tDMNZZ2L5CwlXX0OpagGEs56BiWXqaZFw= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d h1:zsO4lp+bjv5XvPTF58Vq+qgmZEYZttJK+CWtSZhKenI= +github.com/boljen/go-bitmap v0.0.0-20151001105940-23cd2fb0ce7d/go.mod h1:f1iKL6ZhUWvbk7PdWVmOaak10o86cqMUYEmn1CZNGEI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= -github.com/btcsuite/btcd v0.24.0 h1:gL3uHE/IaFj6fcZSu03SvqPMSx7s/dPzfpG/atRwWdo= -github.com/btcsuite/btcd v0.24.0/go.mod h1:K4IDc1593s8jKXIF7yS7yCTSxrknB9z0STzc2j6XgE4= +github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= +github.com/btcsuite/btcd v0.24.3-0.20241011125836-24eb815168f4 h1:wks2KaK25+5rr3BBmD2euEhinRViLv3jJpYZImxwCnM= +github.com/btcsuite/btcd v0.24.3-0.20241011125836-24eb815168f4/go.mod h1:zHK7t7sw8XbsCkD64WePHE3r3k9/XoGAcf6mXV14c64= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= -github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c= +github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE= +github.com/btcsuite/btcd/btcutil/psbt v1.1.8 h1:4voqtT8UppT7nmKQkXV+T9K8UyQjKOn2z/ycpmJK8wg= +github.com/btcsuite/btcd/btcutil/psbt v1.1.8/go.mod h1:kA6FLH/JfUx++j9pYU0pyu+Z8XGBQuuTmuKYUf6q7/U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= @@ -20,6 +136,18 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtyd github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcwallet v0.16.10-0.20240912233857-ffb143c77cc5 h1:zYy233eUBvkF3lq2MUkybEhxhDsrRDSgiToIKN57mtk= +github.com/btcsuite/btcwallet v0.16.10-0.20240912233857-ffb143c77cc5/go.mod h1:1HJXYbjJzgumlnxOC2+ViR1U+gnHWoOn7WeK5OfY1eU= +github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 h1:Rr0njWI3r341nhSPesKQ2JF+ugDSzdPoeckS75SeDZk= +github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5/go.mod h1:+tXJ3Ym0nlQc/iHSwW1qzjmPs3ev+UVWMbGgfV1OZqU= +github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 h1:YEO+Lx1ZJJAtdRrjuhXjWrYsmAk26wLTlNzxt2q0lhk= +github.com/btcsuite/btcwallet/wallet/txrules v1.2.2/go.mod h1:4v+grppsDpVn91SJv+mZT7B8hEV4nSmpREM4I8Uohws= +github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5 h1:93o5Xz9dYepBP4RMFUc9RGIFXwqP2volSWRkYJFrNtI= +github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5/go.mod h1:lQ+e9HxZ85QP7r3kdxItkiMSloSLg1PEGis5o5CXUQw= +github.com/btcsuite/btcwallet/walletdb v1.4.4 h1:BDel6iT/ltYSIYKs0YbjwnEDi7xR3yzABIsQxN2F1L8= +github.com/btcsuite/btcwallet/walletdb v1.4.4/go.mod h1:jk/hvpLFINF0C1kfTn0bfx2GbnFT+Nvnj6eblZALfjs= +github.com/btcsuite/btcwallet/wtxmgr v1.5.4 h1:hJjHy1h/dJwSfD9uDsCwcH21D1iOrus6OrI5gR9E/O0= +github.com/btcsuite/btcwallet/wtxmgr v1.5.4/go.mod h1:lAv0b1Vj9Ig5U8QFm0yiJ9WqPl8yGO/6l7JxdHY1PKE= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= @@ -28,219 +156,1272 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 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 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= +github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= +github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= +github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/cometbft/cometbft v0.38.15 h1:5veFd8k1uXM27PBg9sMO3hAfRJ3vbh4OmmLf6cVrqXg= +github.com/cometbft/cometbft v0.38.15/go.mod h1:+wh6ap6xctVG+JOHwbl8pPKZ0GeqdPYqISu7F4b43cQ= +github.com/cometbft/cometbft-db v0.15.0 h1:VLtsRt8udD4jHCyjvrsTBpgz83qne5hnL245AcPJVRk= +github.com/cometbft/cometbft-db v0.15.0/go.mod h1:EBrFs1GDRiTqrWXYi4v90Awf/gcdD5ExzdPbg4X8+mk= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +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-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= +github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= +github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= +github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= +github.com/cosmos/cosmos-sdk v0.50.9 h1:gt2usjz0H0qW6KwAxWw7ZJ3XU8uDwmhN+hYG3nTLeSg= +github.com/cosmos/cosmos-sdk v0.50.9/go.mod h1:TMH6wpoYBcg7Cp5BEg8fneLr+8XloNQkf2MRNF9V6JE= +github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= +github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= +github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= +github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= +github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= +github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= +github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= +github.com/cosmos/iavl v1.2.0 h1:kVxTmjTh4k0Dh1VNL046v6BXqKziqMDzxo93oh3kOfM= +github.com/cosmos/iavl v1.2.0/go.mod h1:HidWWLVAtODJqFD6Hbne2Y0q3SdxByJepHUOeoH4LiI= +github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd h1:Lx+/5dZ/nN6qPXP2Ofog6u1fmlkCFA1ElcOconnofEM= +github.com/cosmos/ibc-go/modules/apps/callbacks v0.2.1-0.20231113120333-342c00b0f8bd/go.mod h1:JWfpWVKJKiKtd53/KbRoKfxWl8FsT2GPcNezTOk0o5Q= +github.com/cosmos/ibc-go/modules/capability v1.0.1 h1:ibwhrpJ3SftEEZRxCRkH0fQZ9svjthrX2+oXdZvzgGI= +github.com/cosmos/ibc-go/modules/capability v1.0.1/go.mod h1:rquyOV262nGJplkumH+/LeYs04P3eV8oB7ZM4Ygqk4E= +github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead h1:QB50+AmrEVqFr2hzvIxMkICziWQ/uuebze0vNYKMnBg= +github.com/cosmos/ibc-go/modules/light-clients/08-wasm v0.0.0-20240429153234-e1e6da7e4ead/go.mod h1:AJeroAXnPKeFpD1AfEfjYBHGEWt5gBfzUjgs4SYn2ZY= +github.com/cosmos/ibc-go/v8 v8.4.0 h1:K2PfX0AZ+1XKZytHGEMuSjQXG/MZshPb83RSTQt2+cE= +github.com/cosmos/ibc-go/v8 v8.4.0/go.mod h1:zh6x1osR0hNvEcFrC/lhGD08sMfQmr9wHVvZ/mRWMCs= +github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= +github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= +github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= +github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/decred/dcrd/lru v1.1.2 h1:KdCzlkxppuoIDGEvCGah1fZRicrDH36IipvlB1ROkFY= +github.com/decred/dcrd/lru v1.1.2/go.mod h1:gEdCVgXs1/YoBvFWt7Scgknbhwik3FgVSzlnCcXL2N8= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/dgraph-io/badger/v4 v4.3.0 h1:lcsCE1/1qrRhqP+zYx6xDZb8n7U+QlwNicpc676Ub40= +github.com/dgraph-io/badger/v4 v4.3.0/go.mod h1:Sc0T595g8zqAQRDf44n+z3wG4BOqLwceaFntt8KPxUM= +github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91 h1:Pux6+xANi0I7RRo5E1gflI4EZ2yx3BGZ75JkAIvGEOA= +github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91/go.mod h1:swkazRqnUf1N62d0Nutz7KIj2UKqsm/H8tD0nBJAXqM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dhui/dktest v0.4.0 h1:z05UmuXZHO/bgj/ds2bGMBu8FI4WA+Ag/m3ghL+om7M= +github.com/dhui/dktest v0.4.0/go.mod h1:v/Dbz1LgCBOi2Uki2nUqLBGa83hWBGFMu5MrgMDCc78= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/cli v25.0.6+incompatible h1:F1mCw1kUGixOkM8WQbcG5kniPvP8XCFxreFxl4b/UnY= +github.com/docker/cli v25.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg= +github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= +github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/emicklei/dot v1.6.1 h1:ujpDlBkkwgWUY+qPId5IwapRW/xEoligRSYjioR6DFI= +github.com/emicklei/dot v1.6.1/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fergusstrange/embedded-postgres v1.25.0 h1:sa+k2Ycrtz40eCRPOzI7Ry7TtkWXXJ+YRsxpKMDhxK0= +github.com/fergusstrange/embedded-postgres v1.25.0/go.mod h1:t/MLs0h9ukYM6FSt99R7InCHs1nW0ordoVCcnzmpTYw= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +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/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE= github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= +github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYND2gB4p7xcaU= +github.com/golang-migrate/migrate/v4 v4.17.0/go.mod h1:+Cp2mtLP4/aXDTKb9wmXYitdrNx2HGs45rbWAo6OsKM= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/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 h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= +github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= +github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/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.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.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.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-getter v1.7.5 h1:dT58k9hQ/vbxNMwoI5+xFYAJuv6152UNvdHokfI5wE4= +github.com/hashicorp/go-getter v1.7.5/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-metrics v0.5.3 h1:M5uADWMOGCTUNU1YuC4hfknOeHNaX54LDm4oYSucoNE= +github.com/hashicorp/go-metrics v0.5.3/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y= +github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= +github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= +github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= +github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= +github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= +github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0= +github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU= +github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU= +github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= +github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 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/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8= +github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +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/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.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.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= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +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.20240425105051-602843d34ffd h1:D8aRocHpoCv43hL8egXEMYyPmyOiefFHZ66338KQB2s= +github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd/go.mod h1:x3OmY2wsA18+Kc3TSV2QpSUewOCiscw2mKpXgZv2kZk= +github.com/lightninglabs/neutrino/cache v1.1.2 h1:C9DY/DAPaPxbFC+xNNEI/z1SJY9GS3shmlu5hIQ798g= +github.com/lightninglabs/neutrino/cache v1.1.2/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo= +github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb h1:yfM05S8DXKhuCBp5qSMZdtSwvJ+GFzl94KbXMNB1JDY= +github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI= +github.com/lightningnetwork/lnd v0.18.4-beta.rc1 h1:z6hFKvtbfo8udPrIb81GbSoKlUWd06d4LRxTkD19IMQ= +github.com/lightningnetwork/lnd v0.18.4-beta.rc1/go.mod h1:nPRQzLla5uHPQFyyZn8r9Vgddkd23PBUDa9rggEPOfY= +github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0= +github.com/lightningnetwork/lnd/clock v1.1.1/go.mod h1:mGnAhPyjYZQJmebS7aevElXKTFDuO+uNFFfMXK1W8xQ= +github.com/lightningnetwork/lnd/fn v1.2.3 h1:Q1OrgNSgQynVheBNa16CsKVov1JI5N2AR6G07x9Mles= +github.com/lightningnetwork/lnd/fn v1.2.3/go.mod h1:SyFohpVrARPKH3XVAJZlXdVe+IwMYc4OMAvrDY32kw0= +github.com/lightningnetwork/lnd/healthcheck v1.2.5 h1:aTJy5xeBpcWgRtW/PGBDe+LMQEmNm/HQewlQx2jt7OA= +github.com/lightningnetwork/lnd/healthcheck v1.2.5/go.mod h1:G7Tst2tVvWo7cx6mSBEToQC5L1XOGxzZTPB29g9Rv2I= +github.com/lightningnetwork/lnd/kvdb v1.4.10 h1:vK89IVv1oVH9ubQWU+EmoCQFeVRaC8kfmOrqHbY5zoY= +github.com/lightningnetwork/lnd/kvdb v1.4.10/go.mod h1:J2diNABOoII9UrMnxXS5w7vZwP7CA1CStrl8MnIrb3A= +github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQZznVb18iNMI= +github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4= +github.com/lightningnetwork/lnd/sqldb v1.0.4 h1:9cMwPxcrLQG8UmyZO4q8SpR7NmxSwBMbj3AispdcwHg= +github.com/lightningnetwork/lnd/sqldb v1.0.4/go.mod h1:4cQOkdymlZ1znnjuRNvMoatQGJkRneTj2CoPSPaQhWo= +github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM= +github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA= +github.com/lightningnetwork/lnd/tlv v1.2.6 h1:icvQG2yDr6k3ZuZzfRdG3EJp6pHurcuh3R6dg0gv/Mw= +github.com/lightningnetwork/lnd/tlv v1.2.6/go.mod h1:/CmY4VbItpOldksocmGT4lxiJqRP9oLxwSZOda2kzNQ= +github.com/lightningnetwork/lnd/tor v1.1.2 h1:3zv9z/EivNFaMF89v3ciBjCS7kvCj4ZFG7XvD2Qq0/k= +github.com/lightningnetwork/lnd/tor v1.1.2/go.mod h1:j7T9uJ2NLMaHwE7GiBGnpYLn4f7NRoTM6qj+ul6/ycA= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/linxGnu/grocksdb v1.9.3 h1:s1cbPcOd0cU2SKXRG1nEqCOWYAELQjdqg3RVI2MH9ik= +github.com/linxGnu/grocksdb v1.9.3/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= +github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw= +github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= +github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= +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 h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a h1:dlRvE5fWabOchtH7znfiFCcOvmIYgOeAS5ifBXBlh9Q= +github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= +github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= +github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= +github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +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.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= +github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= +github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= +github.com/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/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +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/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= -github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +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 v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +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.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +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.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= -github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= +github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shamaton/msgpack/v2 v2.2.0 h1:IP1m01pHwCrMa6ZccP9B3bqxEMKMSmMVAVKk54g3L/Y= +github.com/shamaton/msgpack/v2 v2.2.0/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 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 v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= +github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= +github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vulpine-io/io-test v1.0.0 h1:Ot8vMh+ssm1VWDAwJ3U4C5qG9aRnr5YfQFZPNZBAUGI= +github.com/vulpine-io/io-test v1.0.0/go.mod h1:X1I+p5GCxVX9m4nFd1HBtr2bVX9v1ZE6x8w+Obt36AU= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 h1:qxen9oVGzDdIRP6ejyAJc760RwW4SnVDiTYTzwnXuxo= +go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5/go.mod h1:eW0HG9/oHQhvRCvb1/pIXW4cOvtDqeQK+XSi3TnwaXY= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd/api/v3 v3.5.12 h1:W4sw5ZoU2Juc9gBWuLk5U6fHfNVyY1WC5g9uiXZio/c= +go.etcd.io/etcd/api/v3 v3.5.12/go.mod h1:Ot+o0SWSyT6uHhA56al1oCED0JImsRiU9Dc26+C2a+4= +go.etcd.io/etcd/client/pkg/v3 v3.5.12 h1:EYDL6pWwyOsylrQyLp2w+HkQ46ATiOvoEdMarindU2A= +go.etcd.io/etcd/client/pkg/v3 v3.5.12/go.mod h1:seTzl2d9APP8R5Y2hFL3NVlD6qC/dOT+3kvrqPyTas4= +go.etcd.io/etcd/client/v2 v2.305.12 h1:0m4ovXYo1CHaA/Mp3X/Fak5sRNIWf01wk/X1/G3sGKI= +go.etcd.io/etcd/client/v2 v2.305.12/go.mod h1:aQ/yhsxMu+Oht1FOupSr60oBvcS9cKXHrzBpDsPTf9E= +go.etcd.io/etcd/client/v3 v3.5.12 h1:v5lCPXn1pf1Uu3M4laUE2hp/geOTc5uPcYYsNe1lDxg= +go.etcd.io/etcd/client/v3 v3.5.12/go.mod h1:tSbBCakoWmmddL+BKVAJHa9km+O/E+bumDe9mSbPiqw= +go.etcd.io/etcd/pkg/v3 v3.5.7 h1:obOzeVwerFwZ9trMWapU/VjDcYUJb5OfgC1zqEGWO/0= +go.etcd.io/etcd/pkg/v3 v3.5.7/go.mod h1:kcOfWt3Ov9zgYdOiJ/o1Y9zFfLhQjylTgL4Lru8opRo= +go.etcd.io/etcd/raft/v3 v3.5.7 h1:aN79qxLmV3SvIq84aNTliYGmjwsW6NqJSnqmI1HLJKc= +go.etcd.io/etcd/raft/v3 v3.5.7/go.mod h1:TflkAb/8Uy6JFBxcRaH2Fr6Slm9mCPVdI2efzxY96yU= +go.etcd.io/etcd/server/v3 v3.5.7 h1:BTBD8IJUV7YFgsczZMHhMTS67XuA4KpRquL0MFOJGRk= +go.etcd.io/etcd/server/v3 v3.5.7/go.mod h1:gxBgT84issUVBRpZ3XkW1T55NjOb4vZZRI4wVvNhf4A= go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +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.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1 h1:ofMbch7i29qIUf7VtF+r0HRF6ac0SBaPSziSsKp7wkk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1/go.mod h1:Kv8liBeVNFkkkbilbgWRpV+wWuu+H5xdOT6HAgd30iw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1 h1:CFMFNoz+CGprjFAFy+RJFrfEe4GBia3RRm2a4fREvCA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1/go.mod h1:xOvWoTOrQjxjW61xtOmD/WKGRYb/P4NzRo3bs65U6Rk= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.9.0 h1:C0g6TWmQYvjKRnljRULLWUVJGy8Uvu0NEL/5frY2/t4= +go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +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 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +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/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.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 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.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +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.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8= +golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +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-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= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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= +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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/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-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-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= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190403152447-81d4e9dc473e/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= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/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-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.171.0 h1:w174hnBPqut76FzW5Qaupt7zY8Kql6fiVjgys4f58sU= +google.golang.org/api v0.171.0/go.mod h1:Hnq5AHm4OTMt2BUVjael2CWZFD6vksJdWCWiUAmjC9o= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +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= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +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/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk= +modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA= +modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= +modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg= +modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.29.10 h1:3u93dz83myFnMilBGCOLbr+HjklS6+5rJLx4q86RDAg= +modernc.org/sqlite v1.29.10/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/internal/btcclient/notifier.go b/internal/btcclient/notifier.go new file mode 100644 index 0000000..0fe1666 --- /dev/null +++ b/internal/btcclient/notifier.go @@ -0,0 +1,91 @@ +package btcclient + +import ( + "fmt" + "net" + + "github.com/btcsuite/btcwallet/chain" + "github.com/lightningnetwork/lnd/blockcache" + "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/chainntnfs/bitcoindnotify" + + "github.com/babylonlabs-io/staking-expiry-checker/internal/config" + "github.com/babylonlabs-io/staking-expiry-checker/internal/utils" +) + +type BTCNotifier struct { + *bitcoindnotify.BitcoindNotifier +} + +func NewBTCNotifier( + cfg *config.BtcConfig, + hintCache HintCache, +) (*BTCNotifier, error) { + params := utils.GetBTCParams(cfg.NetParams) + + bitcoindCfg := &chain.BitcoindConfig{ + ChainParams: params, + Host: cfg.Endpoint, + User: cfg.RpcUser, + Pass: cfg.RpcPass, + Dialer: BuildDialer(cfg.Endpoint), + PrunedModeMaxPeers: cfg.PrunedNodeMaxPeers, + PollingConfig: &chain.PollingConfig{ + BlockPollingInterval: cfg.BlockPollingInterval, + TxPollingInterval: cfg.TxPollingInterval, + TxPollingIntervalJitter: cfg.TxPollingIntervalJitter, + }, + } + + bitcoindConn, err := chain.NewBitcoindConn(bitcoindCfg) + if err != nil { + return nil, err + } + + if err := bitcoindConn.Start(); err != nil { + return nil, fmt.Errorf("unable to connect to "+ + "bitcoind: %v", err) + } + + chainNotifier := bitcoindnotify.New( + bitcoindConn, params, hintCache, + hintCache, blockcache.NewBlockCache(cfg.BlockCacheSize), + ) + + return &BTCNotifier{BitcoindNotifier: chainNotifier}, nil +} + +func BuildDialer(rpcHost string) func(string) (net.Conn, error) { + return func(addr string) (net.Conn, error) { + return net.Dial("tcp", rpcHost) + } +} + +type HintCache interface { + chainntnfs.SpendHintCache + chainntnfs.ConfirmHintCache +} + +type EmptyHintCache struct{} + +var _ HintCache = (*EmptyHintCache)(nil) + +func (c *EmptyHintCache) CommitSpendHint(height uint32, spendRequests ...chainntnfs.SpendRequest) error { + return nil +} +func (c *EmptyHintCache) QuerySpendHint(spendRequest chainntnfs.SpendRequest) (uint32, error) { + return 0, nil +} +func (c *EmptyHintCache) PurgeSpendHint(spendRequests ...chainntnfs.SpendRequest) error { + return nil +} + +func (c *EmptyHintCache) CommitConfirmHint(height uint32, confRequests ...chainntnfs.ConfRequest) error { + return nil +} +func (c *EmptyHintCache) QueryConfirmHint(confRequest chainntnfs.ConfRequest) (uint32, error) { + return 0, nil +} +func (c *EmptyHintCache) PurgeConfirmHint(confRequests ...chainntnfs.ConfRequest) error { + return nil +} diff --git a/internal/config/btc.go b/internal/config/btc.go index b474f29..eb63357 100644 --- a/internal/config/btc.go +++ b/internal/config/btc.go @@ -2,6 +2,7 @@ package config import ( "fmt" + "time" "github.com/babylonlabs-io/staking-expiry-checker/internal/utils" ) @@ -20,6 +21,19 @@ type BtcConfig struct { RpcUser string `mapstructure:"rpc-user"` // RpcPass is the password for RPC server authentication. RpcPass string `mapstructure:"rpc-pass"` + + // PrunedNodeMaxPeers is the maximum number of peers to connect to when using a pruned node. + PrunedNodeMaxPeers int `mapstructure:"prunednodemaxpeers"` + // BlockPollingInterval is the interval at which to poll for new blocks. + BlockPollingInterval time.Duration `mapstructure:"blockpollinginterval"` + // TxPollingInterval is the interval at which to poll for new transactions. + TxPollingInterval time.Duration `mapstructure:"txpollinginterval"` + // TxPollingIntervalJitter is the jitter factor for the transaction polling interval. + TxPollingIntervalJitter float64 `mapstructure:"txpollingintervaljitter"` + // BlockCacheSize is the size of the block cache. + BlockCacheSize uint64 `mapstructure:"blockcachesize"` + MaxRetryTimes uint `mapstructure:"maxretrytimes"` + RetryInterval time.Duration `mapstructure:"retryinterval"` } func (cfg *BtcConfig) Validate() error { diff --git a/internal/db/delegation.go b/internal/db/delegation.go index 8cbfc9c..b4522f5 100644 --- a/internal/db/delegation.go +++ b/internal/db/delegation.go @@ -64,3 +64,27 @@ func (db *Database) transitionState( } return nil } + +func (db *Database) GetBTCDelegationByStakingTxHash( + ctx context.Context, stakingTxHash string, +) (*model.BTCDelegationDetails, error) { + filter := bson.M{"_id": stakingTxHash} + + res := db.client.Database(db.dbName). + Collection(model.DelegationsCollection). + FindOne(ctx, filter) + + var delegationDoc model.BTCDelegationDetails + err := res.Decode(&delegationDoc) + if err != nil { + if errors.Is(err, mongo.ErrNoDocuments) { + return nil, &NotFoundError{ + Key: stakingTxHash, + Message: "BTC delegation not found when getting by staking tx hash", + } + } + return nil, err + } + + return &delegationDoc, nil +} diff --git a/internal/db/interface.go b/internal/db/interface.go index 080b37a..95db0d8 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -25,4 +25,8 @@ type DbInterface interface { stakingTxHashHex string, unbondTxType types.TransactionType, ) error + GetBTCDelegationByStakingTxHash( + ctx context.Context, stakingTxHash string, + ) (*model.BTCDelegationDetails, error) + GetStakingParams(ctx context.Context, version uint32) (*model.StakingParams, error) } diff --git a/internal/db/model/delegation.go b/internal/db/model/delegation.go index 8d6dac1..44b278a 100644 --- a/internal/db/model/delegation.go +++ b/internal/db/model/delegation.go @@ -2,12 +2,6 @@ package model import "fmt" -// We don't care about other fields in the document -type DelegationDocument struct { - StakingTxHashHex string `bson:"_id"` // Primary key - State DelegationState `bson:"state"` -} - type DelegationState string const ( @@ -41,3 +35,33 @@ func FromStringToDelegationState(s string) (DelegationState, error) { return "", fmt.Errorf("invalid delegation state: %s", s) } } + +type CovenantSignature struct { + CovenantBtcPkHex string `bson:"covenant_btc_pk_hex"` + SignatureHex string `bson:"signature_hex"` +} + +type BTCDelegationCreatedBbnBlock struct { + Height int64 `bson:"height"` + Timestamp int64 `bson:"timestamp"` // epoch time in seconds +} + +type BTCDelegationDetails struct { + StakingTxHashHex string `bson:"_id"` // Primary key + StakingTxHex string `bson:"staking_tx_hex"` + StakingTime uint32 `bson:"staking_time"` + StakingAmount uint64 `bson:"staking_amount"` + StakingOutputIdx uint32 `bson:"staking_output_idx"` + StakerBtcPkHex string `bson:"staker_btc_pk_hex"` + FinalityProviderBtcPksHex []string `bson:"finality_provider_btc_pks_hex"` + StartHeight uint32 `bson:"start_height"` + EndHeight uint32 `bson:"end_height"` + State DelegationState `bson:"state"` + ParamsVersion uint32 `bson:"params_version"` + UnbondingTime uint32 `bson:"unbonding_time"` + UnbondingTx string `bson:"unbonding_tx"` + CovenantUnbondingSignatures []CovenantSignature `bson:"covenant_unbonding_signatures"` + BTCDelegationCreatedBlock BTCDelegationCreatedBbnBlock `bson:"btc_delegation_created_bbn_block"` + SlashingTxHex string `bson:"slashing_tx_hex"` // Will be "" if not slashed + UnbondingSlashingTxHex string `bson:"unbonding_slashing_tx_hex"` // Will be "" if not slashed +} diff --git a/internal/db/model/params.go b/internal/db/model/params.go new file mode 100644 index 0000000..52c872c --- /dev/null +++ b/internal/db/model/params.go @@ -0,0 +1,32 @@ +package model + +// StakingParams represents the staking parameters of the BBN chain +// Reference: https://github.com/babylonlabs-io/babylon/blob/main/proto/babylon/btcstaking/v1/params.proto +type StakingParams struct { + CovenantPks []string `bson:"covenant_pks"` + CovenantQuorum uint32 `bson:"covenant_quorum"` + MinStakingValueSat int64 `bson:"min_staking_value_sat"` + MaxStakingValueSat int64 `bson:"max_staking_value_sat"` + MinStakingTimeBlocks uint32 `bson:"min_staking_time_blocks"` + MaxStakingTimeBlocks uint32 `bson:"max_staking_time_blocks"` + SlashingPkScript string `bson:"slashing_pk_script"` + MinSlashingTxFeeSat int64 `bson:"min_slashing_tx_fee_sat"` + SlashingRate string `bson:"slashing_rate"` + UnbondingTimeBlocks uint32 `bson:"unbonding_time_blocks"` + UnbondingFeeSat int64 `bson:"unbonding_fee_sat"` + MinCommissionRate string `bson:"min_commission_rate"` + DelegationCreationBaseGasFee uint64 `bson:"delegation_creation_base_gas_fee"` + AllowListExpirationHeight uint64 `bson:"allow_list_expiration_height"` + BtcActivationHeight uint32 `bson:"btc_activation_height"` +} + +type BaseParamsDocument struct { + Type string `bson:"type"` + Version uint32 `bson:"version"` +} + +// Specific document for staking params +type StakingParamsDocument struct { + BaseParamsDocument `bson:",inline"` + Params *StakingParams `bson:"params"` +} diff --git a/internal/db/model/setup.go b/internal/db/model/setup.go index db27bf9..da98286 100644 --- a/internal/db/model/setup.go +++ b/internal/db/model/setup.go @@ -1,6 +1,7 @@ package model const ( - TimeLockCollection = "timelock_queue" - DelegationsCollection = "delegations" + TimeLockCollection = "timelock_queue" + DelegationsCollection = "delegations" + GlobalParamsCollection = "global_params" ) diff --git a/internal/db/params.go b/internal/db/params.go new file mode 100644 index 0000000..e522a8e --- /dev/null +++ b/internal/db/params.go @@ -0,0 +1,29 @@ +package db + +import ( + "context" + "fmt" + + "github.com/babylonlabs-io/staking-expiry-checker/internal/db/model" + "go.mongodb.org/mongo-driver/bson" +) + +const STAKING_PARAMS_TYPE = "staking_params" + +func (db *Database) GetStakingParams(ctx context.Context, version uint32) (*model.StakingParams, error) { + collection := db.client.Database(db.dbName). + Collection(model.GlobalParamsCollection) + + filter := bson.M{ + "type": STAKING_PARAMS_TYPE, + "version": version, + } + + var params model.StakingParamsDocument + err := collection.FindOne(ctx, filter).Decode(¶ms) + if err != nil { + return nil, fmt.Errorf("failed to get staking params: %w", err) + } + + return params.Params, nil +} diff --git a/internal/services/service.go b/internal/services/service.go index 945c1fa..a91eea9 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -1,18 +1,35 @@ package services import ( + "sync" + "github.com/babylonlabs-io/staking-expiry-checker/internal/btcclient" + "github.com/babylonlabs-io/staking-expiry-checker/internal/config" "github.com/babylonlabs-io/staking-expiry-checker/internal/db" + notifier "github.com/lightningnetwork/lnd/chainntnfs" ) type Service struct { - db db.DbInterface - btc btcclient.BtcInterface + wg sync.WaitGroup + quit chan struct{} + + cfg *config.Config + db db.DbInterface + btcNotifier notifier.ChainNotifier + btc btcclient.BtcInterface } -func NewService(db db.DbInterface, btc btcclient.BtcInterface) *Service { +func NewService( + cfg *config.Config, + db db.DbInterface, + btcNotifier notifier.ChainNotifier, + btc btcclient.BtcInterface, +) *Service { return &Service{ - db: db, - btc: btc, + quit: make(chan struct{}), + cfg: cfg, + db: db, + btcNotifier: btcNotifier, + btc: btc, } } diff --git a/internal/services/watch_btc_events.go b/internal/services/watch_btc_events.go new file mode 100644 index 0000000..3f0fcbc --- /dev/null +++ b/internal/services/watch_btc_events.go @@ -0,0 +1,625 @@ +package services + +import ( + "bytes" + "context" + "encoding/hex" + "errors" + "fmt" + "net/http" + + "github.com/babylonlabs-io/babylon/btcstaking" + bbn "github.com/babylonlabs-io/babylon/types" + "github.com/babylonlabs-io/staking-expiry-checker/internal/db/model" + "github.com/babylonlabs-io/staking-expiry-checker/internal/types" + "github.com/babylonlabs-io/staking-expiry-checker/internal/utils" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + notifier "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/rs/zerolog/log" +) + +func (s *Service) watchForSpendStakingTx( + spendEvent *notifier.SpendEvent, + stakingTxHashHex string, +) { + defer s.wg.Done() + quitCtx, cancel := s.quitContext() + defer cancel() + + // Get spending details + select { + case spendDetail := <-spendEvent.Spend: + log.Debug(). + Str("staking_tx", stakingTxHashHex). + Str("spending_tx", spendDetail.SpendingTx.TxHash().String()). + Msg("staking tx has been spent") + if err := s.handleSpendingStakingTransaction( + quitCtx, + spendDetail.SpendingTx, + spendDetail.SpenderInputIndex, + uint32(spendDetail.SpendingHeight), + stakingTxHashHex, + ); err != nil { + log.Error(). + Err(err). + Str("staking_tx", stakingTxHashHex). + Str("spending_tx", spendDetail.SpendingTx.TxHash().String()). + Msg("failed to handle spending staking transaction") + return + } + + case <-s.quit: + return + case <-quitCtx.Done(): + return + } + +} + +func (s *Service) watchForSpendUnbondingTx( + spendEvent *notifier.SpendEvent, + delegation *model.BTCDelegationDetails, +) { + defer s.wg.Done() + quitCtx, cancel := s.quitContext() + defer cancel() + + // Get spending details + select { + case spendDetail := <-spendEvent.Spend: + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Str("unbonding_tx", spendDetail.SpendingTx.TxHash().String()). + Msg("unbonding tx has been spent") + if err := s.handleSpendingUnbondingTransaction( + quitCtx, + spendDetail.SpendingTx, + uint32(spendDetail.SpendingHeight), + spendDetail.SpenderInputIndex, + delegation, + ); err != nil { + log.Error(). + Err(err). + Str("staking_tx", delegation.StakingTxHashHex). + Str("unbonding_tx", spendDetail.SpendingTx.TxHash().String()). + Msg("failed to handle spending unbonding transaction") + return + } + + case <-s.quit: + return + case <-quitCtx.Done(): + return + } +} + +func (s *Service) handleSpendingStakingTransaction( + ctx context.Context, + spendingTx *wire.MsgTx, + spendingInputIdx uint32, + spendingHeight uint32, + stakingTxHashHex string, +) error { + delegation, err := s.db.GetBTCDelegationByStakingTxHash(ctx, stakingTxHashHex) + if err != nil { + return fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", err) + } + + params, err := s.db.GetStakingParams(ctx, delegation.ParamsVersion) + if err != nil { + return fmt.Errorf("failed to get staking params: %w", err) + } + + // First try to validate as unbonding tx + isUnbonding, err := s.IsValidUnbondingTx(spendingTx, delegation, params) + if err != nil { + return fmt.Errorf("failed to validate unbonding tx: %w", err) + } + if isUnbonding { + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Str("unbonding_tx", spendingTx.TxHash().String()). + Msg("staking tx has been spent through unbonding path") + + // Register unbonding spend notification + return s.registerUnbondingSpendNotification(ctx, delegation) + } + + // Try to validate as withdrawal transaction + withdrawalErr := s.validateWithdrawalTxFromStaking(spendingTx, spendingInputIdx, delegation, params) + if withdrawalErr == nil { + // It's a valid withdrawal, process it + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Str("withdrawal_tx", spendingTx.TxHash().String()). + Msg("staking tx has been spent through withdrawal path") + // TODO here + //return s.handleWithdrawal(ctx, delegation, types.SubStateTimelock) + } + + // If it's not a valid withdrawal, check if it's a valid slashing + if !errors.Is(withdrawalErr, types.ErrInvalidWithdrawalTx) { + return fmt.Errorf("failed to validate withdrawal tx: %w", withdrawalErr) + } + + // Try to validate as slashing transaction + if err := s.validateSlashingTxFromStaking(spendingTx, spendingInputIdx, delegation, params); err != nil { + if errors.Is(err, types.ErrInvalidSlashingTx) { + // Neither withdrawal nor slashing - this is an invalid spend + return fmt.Errorf("transaction is neither valid unbonding, withdrawal, nor slashing: %w", err) + } + return fmt.Errorf("failed to validate slashing tx: %w", err) + } + return nil +} + +func (s *Service) handleSpendingUnbondingTransaction( + ctx context.Context, + spendingTx *wire.MsgTx, + spendingHeight uint32, + spendingInputIdx uint32, + delegation *model.BTCDelegationDetails, +) error { + params, err := s.db.GetStakingParams(ctx, delegation.ParamsVersion) + if err != nil { + return fmt.Errorf("failed to get staking params: %w", err) + } + + // First try to validate as withdrawal transaction + withdrawalErr := s.validateWithdrawalTxFromUnbonding(spendingTx, delegation, spendingInputIdx, params) + if withdrawalErr == nil { + // It's a valid withdrawal, process it + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Str("unbonding_tx", spendingTx.TxHash().String()). + Msg("unbonding tx has been spent through withdrawal path") + + // TODO: here + // return s.handleWithdrawal(ctx, delegation, types.SubStateEarlyUnbonding) + } + + // If it's not a valid withdrawal, check if it's a valid slashing + if !errors.Is(withdrawalErr, types.ErrInvalidWithdrawalTx) { + return fmt.Errorf("failed to validate withdrawal tx: %w", withdrawalErr) + } + + // // Try to validate as slashing transaction + // if err := s.validateSlashingTxFromUnbonding(spendingTx, delegation, spendingInputIdx, params); err != nil { + // if errors.Is(err, types.ErrInvalidSlashingTx) { + // // Neither withdrawal nor slashing - this is an invalid spend + // return fmt.Errorf("transaction is neither valid withdrawal nor slashing: %w", err) + // } + // return fmt.Errorf("failed to validate slashing tx: %w", err) + // } + + // // Save unbonding slashing tx hex + // unbondingSlashingTx, err := bstypes.NewBTCSlashingTxFromMsgTx(spendingTx) + // if err != nil { + // return fmt.Errorf("failed to convert unbonding slashing tx to bytes: %w", err) + // } + // unbondingSlashingTxHex := unbondingSlashingTx.ToHexStr() + // if err := s.db.SaveBTCDelegationUnbondingSlashingTxHex(ctx, delegation.StakingTxHashHex, unbondingSlashingTxHex); err != nil { + // return fmt.Errorf("failed to save unbonding slashing tx hex: %w", err) + // } + + // // It's a valid slashing tx, watch for spending change output + // return s.startWatchingSlashingChange( + // ctx, + // spendingTx, + // spendingHeight, + // delegation, + // types.SubStateEarlyUnbondingSlashing, + // ) + + return nil +} + +// func (s *Service) handleWithdrawal( +// ctx context.Context, +// delegation *model.BTCDelegationDetails, +// subState types.DelegationSubState, +// ) error { +// delegationState, err := s.db.GetBTCDelegationState(ctx, delegation.StakingTxHashHex) +// if err != nil { +// return fmt.Errorf("failed to get delegation state: %w", err) +// } + +// qualifiedStates := types.QualifiedStatesForWithdrawn() +// if qualifiedStates == nil || !utils.Contains(qualifiedStates, *delegationState) { +// log.Error(). +// Str("staking_tx", delegation.StakingTxHashHex). +// Str("current_state", delegationState.String()). +// Msg("current state is not qualified for withdrawal") +// return fmt.Errorf("current state %s is not qualified for withdrawal", *delegationState) +// } + +// // Update to withdrawn state +// log.Debug(). +// Str("staking_tx", delegation.StakingTxHashHex). +// Str("state", types.StateWithdrawn.String()). +// Str("sub_state", subState.String()). +// Msg("updating delegation state to withdrawn") +// return s.db.UpdateBTCDelegationState( +// ctx, +// delegation.StakingTxHashHex, +// types.QualifiedStatesForWithdrawn(), +// types.StateWithdrawn, +// &subState, +// ) +// } + +// IsValidUnbondingTx tries to identify a tx is a valid unbonding tx +// It returns error when (1) it fails to verify the unbonding tx due +// to invalid parameters, and (2) the tx spends the unbonding path +// but is invalid +func (s *Service) IsValidUnbondingTx( + tx *wire.MsgTx, + delegation *model.BTCDelegationDetails, + params *model.StakingParams, +) (bool, error) { + stakingTx, err := utils.DeserializeBtcTransactionFromHex(delegation.StakingTxHex) + if err != nil { + return false, fmt.Errorf("failed to deserialize staking tx: %w", err) + } + stakingTxHash := stakingTx.TxHash() + + // 1. an unbonding tx must be a transfer tx + if err := btcstaking.IsTransferTx(tx); err != nil { + return false, nil + } + + // 2. an unbonding tx must spend the staking output + if !tx.TxIn[0].PreviousOutPoint.Hash.IsEqual(&stakingTxHash) { + return false, nil + } + if tx.TxIn[0].PreviousOutPoint.Index != delegation.StakingOutputIdx { + return false, nil + } + + stakerPk, err := bbn.NewBIP340PubKeyFromHex(delegation.StakerBtcPkHex) + if err != nil { + return false, fmt.Errorf("failed to convert staker btc pkh to a public key: %w", err) + } + + finalityProviderPks := make([]*btcec.PublicKey, len(delegation.FinalityProviderBtcPksHex)) + for i, hex := range delegation.FinalityProviderBtcPksHex { + fpPk, err := bbn.NewBIP340PubKeyFromHex(hex) + if err != nil { + return false, fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) + } + finalityProviderPks[i] = fpPk.MustToBTCPK() + } + + covPks := make([]*btcec.PublicKey, len(params.CovenantPks)) + for i, hex := range params.CovenantPks { + covPk, err := bbn.NewBIP340PubKeyFromHex(hex) + if err != nil { + return false, fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) + } + covPks[i] = covPk.MustToBTCPK() + } + + btcParams := utils.GetBTCParams(s.cfg.Btc.NetParams) + + stakingValue := btcutil.Amount(stakingTx.TxOut[delegation.StakingOutputIdx].Value) + + // 3. re-build the unbonding path script and check whether the script from + // the witness matches + stakingInfo, err := btcstaking.BuildStakingInfo( + stakerPk.MustToBTCPK(), + finalityProviderPks, + covPks, + params.CovenantQuorum, + uint16(delegation.StakingTime), + stakingValue, + btcParams, + ) + if err != nil { + return false, fmt.Errorf("failed to rebuid the staking info: %w", err) + } + unbondingPathInfo, err := stakingInfo.UnbondingPathSpendInfo() + if err != nil { + return false, fmt.Errorf("failed to get the unbonding path spend info: %w", err) + } + + witness := tx.TxIn[0].Witness + if len(witness) < 2 { + panic(fmt.Errorf("spending tx should have at least 2 elements in witness, got %d", len(witness))) + } + + scriptFromWitness := tx.TxIn[0].Witness[len(tx.TxIn[0].Witness)-2] + + if !bytes.Equal(unbondingPathInfo.GetPkScriptPath(), scriptFromWitness) { + // not unbonding tx as it does not unlock the unbonding path + return false, nil + } + + // 4. check whether the unbonding tx enables rbf has time lock + if tx.TxIn[0].Sequence != wire.MaxTxInSequenceNum { + return false, fmt.Errorf("%w: unbonding tx should not enable rbf", types.ErrInvalidUnbondingTx) + } + if tx.LockTime != 0 { + return false, fmt.Errorf("%w: unbonding tx should not set lock time", types.ErrInvalidUnbondingTx) + } + + // 5. check whether the script of an unbonding tx output is expected + // by re-building unbonding output from params + unbondingFee := btcutil.Amount(params.UnbondingFeeSat) + expectedUnbondingOutputValue := stakingValue - unbondingFee + if expectedUnbondingOutputValue <= 0 { + return false, fmt.Errorf("%w: staking output value is too low, got %v, unbonding fee: %v", + types.ErrInvalidUnbondingTx, stakingValue, params.UnbondingFeeSat) + } + unbondingInfo, err := btcstaking.BuildUnbondingInfo( + stakerPk.MustToBTCPK(), + finalityProviderPks, + covPks, + params.CovenantQuorum, + uint16(delegation.UnbondingTime), + expectedUnbondingOutputValue, + btcParams, + ) + if err != nil { + return false, fmt.Errorf("failed to rebuid the unbonding info: %w", err) + } + if !bytes.Equal(tx.TxOut[0].PkScript, unbondingInfo.UnbondingOutput.PkScript) { + return false, fmt.Errorf("%w: the unbonding output is not expected", types.ErrInvalidUnbondingTx) + } + if tx.TxOut[0].Value != unbondingInfo.UnbondingOutput.Value { + return false, fmt.Errorf("%w: the unbonding output value %d is not expected %d", + types.ErrInvalidUnbondingTx, tx.TxOut[0].Value, unbondingInfo.UnbondingOutput.Value) + } + + return true, nil +} + +func (s *Service) validateWithdrawalTxFromStaking( + tx *wire.MsgTx, + spendingInputIdx uint32, + delegation *model.BTCDelegationDetails, + params *model.StakingParams, +) error { + stakerPk, err := bbn.NewBIP340PubKeyFromHex(delegation.StakerBtcPkHex) + if err != nil { + return fmt.Errorf("failed to convert staker btc pkh to a public key: %w", err) + } + + finalityProviderPks := make([]*btcec.PublicKey, len(delegation.FinalityProviderBtcPksHex)) + for i, hex := range delegation.FinalityProviderBtcPksHex { + fpPk, err := bbn.NewBIP340PubKeyFromHex(hex) + if err != nil { + return fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) + } + finalityProviderPks[i] = fpPk.MustToBTCPK() + } + + covPks := make([]*btcec.PublicKey, len(params.CovenantPks)) + for i, hex := range params.CovenantPks { + covPk, err := bbn.NewBIP340PubKeyFromHex(hex) + if err != nil { + return fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) + } + covPks[i] = covPk.MustToBTCPK() + } + + btcParams := utils.GetBTCParams(s.cfg.Btc.NetParams) + + stakingTx, err := utils.DeserializeBtcTransactionFromHex(delegation.StakingTxHex) + if err != nil { + return fmt.Errorf("failed to deserialize staking tx: %w", err) + } + + stakingValue := btcutil.Amount(stakingTx.TxOut[delegation.StakingOutputIdx].Value) + + // 3. re-build the unbonding path script and check whether the script from + // the witness matches + stakingInfo, err := btcstaking.BuildStakingInfo( + stakerPk.MustToBTCPK(), + finalityProviderPks, + covPks, + params.CovenantQuorum, + uint16(delegation.StakingTime), + stakingValue, + btcParams, + ) + if err != nil { + return fmt.Errorf("failed to rebuid the staking info: %w", err) + } + + timelockPathInfo, err := stakingInfo.TimeLockPathSpendInfo() + if err != nil { + return fmt.Errorf("failed to get the unbonding path spend info: %w", err) + } + + witness := tx.TxIn[spendingInputIdx].Witness + if len(witness) < 2 { + panic(fmt.Errorf("spending tx should have at least 2 elements in witness, got %d", len(witness))) + } + + scriptFromWitness := tx.TxIn[spendingInputIdx].Witness[len(tx.TxIn[spendingInputIdx].Witness)-2] + + if !bytes.Equal(timelockPathInfo.GetPkScriptPath(), scriptFromWitness) { + return fmt.Errorf("%w: the tx does not unlock the time-lock path", types.ErrInvalidWithdrawalTx) + } + + return nil +} + +func (s *Service) validateWithdrawalTxFromUnbonding( + tx *wire.MsgTx, + delegation *model.BTCDelegationDetails, + spendingInputIdx uint32, + params *model.StakingParams, +) error { + stakerPk, err := bbn.NewBIP340PubKeyFromHex(delegation.StakerBtcPkHex) + if err != nil { + return fmt.Errorf("failed to convert staker btc pkh to a public key: %w", err) + } + + finalityProviderPks := make([]*btcec.PublicKey, len(delegation.FinalityProviderBtcPksHex)) + for i, hex := range delegation.FinalityProviderBtcPksHex { + fpPk, err := bbn.NewBIP340PubKeyFromHex(hex) + if err != nil { + return fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) + } + finalityProviderPks[i] = fpPk.MustToBTCPK() + } + + covPks := make([]*btcec.PublicKey, len(params.CovenantPks)) + for i, hex := range params.CovenantPks { + covPk, err := bbn.NewBIP340PubKeyFromHex(hex) + if err != nil { + return fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) + } + covPks[i] = covPk.MustToBTCPK() + } + + btcParams := utils.GetBTCParams(s.cfg.Btc.NetParams) + + stakingTx, err := utils.DeserializeBtcTransactionFromHex(delegation.StakingTxHex) + if err != nil { + return fmt.Errorf("failed to deserialize staking tx: %w", err) + } + + // re-build the time-lock path script and check whether the script from + // the witness matches + stakingValue := btcutil.Amount(stakingTx.TxOut[delegation.StakingOutputIdx].Value) + unbondingFee := btcutil.Amount(params.UnbondingFeeSat) + expectedUnbondingOutputValue := stakingValue - unbondingFee + unbondingInfo, err := btcstaking.BuildUnbondingInfo( + stakerPk.MustToBTCPK(), + finalityProviderPks, + covPks, + params.CovenantQuorum, + uint16(delegation.UnbondingTime), + expectedUnbondingOutputValue, + btcParams, + ) + if err != nil { + return fmt.Errorf("failed to rebuid the unbonding info: %w", err) + } + timelockPathInfo, err := unbondingInfo.TimeLockPathSpendInfo() + if err != nil { + return fmt.Errorf("failed to get the unbonding path spend info: %w", err) + } + + witness := tx.TxIn[spendingInputIdx].Witness + if len(witness) < 2 { + panic(fmt.Errorf("spending tx should have at least 2 elements in witness, got %d", len(witness))) + } + + scriptFromWitness := tx.TxIn[spendingInputIdx].Witness[len(tx.TxIn[spendingInputIdx].Witness)-2] + + if !bytes.Equal(timelockPathInfo.GetPkScriptPath(), scriptFromWitness) { + return fmt.Errorf("%w: the tx does not unlock the time-lock path", types.ErrInvalidWithdrawalTx) + } + + return nil +} + +func (s *Service) quitContext() (context.Context, func()) { + ctx, cancel := context.WithCancel(context.Background()) + s.wg.Add(1) + go func() { + defer cancel() + defer s.wg.Done() + + select { + case <-s.quit: + case <-ctx.Done(): + } + }() + + return ctx, cancel +} + +func (s *Service) RegisterStakingSpendNotification( + ctx context.Context, + stakingTxHashHex string, + stakingTxHex string, + stakingOutputIdx uint32, + stakingStartHeight uint32, +) error { + stakingTxHash, err := chainhash.NewHashFromStr(stakingTxHashHex) + if err != nil { + return fmt.Errorf("failed to parse staking tx hash: %w", err) + } + + stakingTx, err := utils.DeserializeBtcTransactionFromHex(stakingTxHex) + if err != nil { + return fmt.Errorf("failed to deserialize staking tx: %w", err) + } + + stakingOutpoint := wire.OutPoint{ + Hash: *stakingTxHash, + Index: stakingOutputIdx, + } + + spendEv, err := s.btcNotifier.RegisterSpendNtfn( + &stakingOutpoint, + stakingTx.TxOut[stakingOutputIdx].PkScript, + stakingStartHeight, + ) + if err != nil { + return fmt.Errorf("failed to register spend ntfn for staking tx %s: %w", stakingTxHashHex, err) + } + + s.wg.Add(1) + go s.watchForSpendStakingTx(spendEv, stakingTxHashHex) + + return nil +} + +func (s *Service) registerUnbondingSpendNotification( + ctx context.Context, + delegation *model.BTCDelegationDetails, +) *types.Error { + unbondingTxBytes, parseErr := hex.DecodeString(delegation.UnbondingTx) + if parseErr != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to decode unbonding tx: %w", parseErr), + ) + } + + unbondingTx, parseErr := bbn.NewBTCTxFromBytes(unbondingTxBytes) + if parseErr != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to parse unbonding tx: %w", parseErr), + ) + } + + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Str("unbonding_tx", unbondingTx.TxHash().String()). + Msg("registering early unbonding spend notification") + + unbondingOutpoint := wire.OutPoint{ + Hash: unbondingTx.TxHash(), + Index: 0, // unbonding tx has only 1 output + } + + spendEv, btcErr := s.btcNotifier.RegisterSpendNtfn( + &unbondingOutpoint, + unbondingTx.TxOut[0].PkScript, + delegation.StartHeight, + ) + if btcErr != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to register spend ntfn for unbonding tx %s: %w", delegation.StakingTxHashHex, btcErr), + ) + } + + s.wg.Add(1) + go s.watchForSpendUnbondingTx(spendEv, delegation) + + return nil +} diff --git a/internal/types/error.go b/internal/types/error.go index 3a21386..42855a1 100644 --- a/internal/types/error.go +++ b/internal/types/error.go @@ -60,3 +60,17 @@ func NewInternalServiceError(err error) *Error { Err: err, } } + +var ( + // ErrInvalidUnbondingTx the transaction spends the unbonding path but is invalid + ErrInvalidUnbondingTx = errors.New("invalid unbonding tx") + + // ErrInvalidStakingTx the stake transaction is invalid as it does not follow the global parameters + ErrInvalidStakingTx = errors.New("invalid staking tx") + + // ErrInvalidWithdrawalTx the withdrawal transaction is invalid as it does not unlock the expected time lock path + ErrInvalidWithdrawalTx = errors.New("invalid withdrawal tx") + + // ErrInvalidSlashingTx the slashing transaction is invalid as it does not unlock the expected slashing path + ErrInvalidSlashingTx = errors.New("invalid slashing tx") +) diff --git a/internal/utils/utils.go b/internal/utils/utils.go index de68560..75e2463 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -1,10 +1,14 @@ package utils import ( + "bytes" + "encoding/hex" + "fmt" "runtime" "strings" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" ) type SupportedBtcNetwork string @@ -77,3 +81,19 @@ func shortFuncName(fullName string) string { } return fullName } + +func DeserializeBtcTransactionFromHex(txHex string) (*wire.MsgTx, error) { + // First decode the hex string into bytes + txBytes, err := hex.DecodeString(txHex) + if err != nil { + return nil, fmt.Errorf("failed to decode hex string: %w", err) + } + + // Then deserialize the bytes into a transaction + reader := bytes.NewReader(txBytes) + tx := wire.NewMsgTx(wire.TxVersion) + if err := tx.Deserialize(reader); err != nil { + return nil, fmt.Errorf("failed to deserialize transaction: %w", err) + } + return tx, nil +} From badc1fc17d2fffc14e39ac1c409faaed2377a935 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Tue, 10 Dec 2024 13:12:02 +0530 Subject: [PATCH 02/35] fix --- internal/services/watch_btc_events.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/services/watch_btc_events.go b/internal/services/watch_btc_events.go index 3f0fcbc..1dd553c 100644 --- a/internal/services/watch_btc_events.go +++ b/internal/services/watch_btc_events.go @@ -145,14 +145,14 @@ func (s *Service) handleSpendingStakingTransaction( return fmt.Errorf("failed to validate withdrawal tx: %w", withdrawalErr) } - // Try to validate as slashing transaction - if err := s.validateSlashingTxFromStaking(spendingTx, spendingInputIdx, delegation, params); err != nil { - if errors.Is(err, types.ErrInvalidSlashingTx) { - // Neither withdrawal nor slashing - this is an invalid spend - return fmt.Errorf("transaction is neither valid unbonding, withdrawal, nor slashing: %w", err) - } - return fmt.Errorf("failed to validate slashing tx: %w", err) - } + // // Try to validate as slashing transaction + // if err := s.validateSlashingTxFromStaking(spendingTx, spendingInputIdx, delegation, params); err != nil { + // if errors.Is(err, types.ErrInvalidSlashingTx) { + // // Neither withdrawal nor slashing - this is an invalid spend + // return fmt.Errorf("transaction is neither valid unbonding, withdrawal, nor slashing: %w", err) + // } + // return fmt.Errorf("failed to validate slashing tx: %w", err) + // } return nil } From e96599bf7f6b237f880b73aacfc051711901f856 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Tue, 10 Dec 2024 13:47:43 +0530 Subject: [PATCH 03/35] fix --- internal/services/watch_btc_events.go | 82 +++++++++++++++++++-------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/internal/services/watch_btc_events.go b/internal/services/watch_btc_events.go index 1dd553c..7ca04fe 100644 --- a/internal/services/watch_btc_events.go +++ b/internal/services/watch_btc_events.go @@ -116,6 +116,21 @@ func (s *Service) handleSpendingStakingTransaction( // First try to validate as unbonding tx isUnbonding, err := s.IsValidUnbondingTx(spendingTx, delegation, params) if err != nil { + if errors.Is(err, types.ErrInvalidUnbondingTx) { + // TODO: here + // invalidTransactionsCounter.WithLabelValues("confirmed_unbonding_transactions").Inc() + // si.logger.Warn("found an invalid unbonding tx", + // zap.String("tx_hash", tx.TxHash().String()), + // zap.Uint64("height", height), + // zap.Bool("is_confirmed", true), + // zap.Error(err), + // ) + + return nil + } + // record metrics + // failedVerifyingUnbondingTxsCounter.Inc() + // return err return fmt.Errorf("failed to validate unbonding tx: %w", err) } if isUnbonding { @@ -130,20 +145,36 @@ func (s *Service) handleSpendingStakingTransaction( // Try to validate as withdrawal transaction withdrawalErr := s.validateWithdrawalTxFromStaking(spendingTx, spendingInputIdx, delegation, params) - if withdrawalErr == nil { - // It's a valid withdrawal, process it - log.Debug(). - Str("staking_tx", delegation.StakingTxHashHex). - Str("withdrawal_tx", spendingTx.TxHash().String()). - Msg("staking tx has been spent through withdrawal path") - // TODO here - //return s.handleWithdrawal(ctx, delegation, types.SubStateTimelock) - } + if withdrawalErr != nil { + if errors.Is(err, types.ErrInvalidWithdrawalTx) { + // invalidTransactionsCounter.WithLabelValues("confirmed_withdraw_staking_transactions").Inc() + // si.logger.Warn("found an invalid withdrawal tx from staking", + // zap.String("tx_hash", tx.TxHash().String()), + // zap.Uint64("height", height), + // zap.Bool("is_confirmed", true), + // zap.Error(err), + // ) + + return nil + } + + //failedProcessingWithdrawTxsFromStakingCounter.Inc() + return err + } + // if withdrawalErr == nil { + // // It's a valid withdrawal, process it + // log.Debug(). + // Str("staking_tx", delegation.StakingTxHashHex). + // Str("withdrawal_tx", spendingTx.TxHash().String()). + // Msg("staking tx has been spent through withdrawal path") + // // TODO here + // //return s.handleWithdrawal(ctx, delegation, types.SubStateTimelock) + // } // If it's not a valid withdrawal, check if it's a valid slashing - if !errors.Is(withdrawalErr, types.ErrInvalidWithdrawalTx) { - return fmt.Errorf("failed to validate withdrawal tx: %w", withdrawalErr) - } + // if !errors.Is(withdrawalErr, types.ErrInvalidWithdrawalTx) { + // return fmt.Errorf("failed to validate withdrawal tx: %w", withdrawalErr) + // } // // Try to validate as slashing transaction // if err := s.validateSlashingTxFromStaking(spendingTx, spendingInputIdx, delegation, params); err != nil { @@ -170,22 +201,23 @@ func (s *Service) handleSpendingUnbondingTransaction( // First try to validate as withdrawal transaction withdrawalErr := s.validateWithdrawalTxFromUnbonding(spendingTx, delegation, spendingInputIdx, params) - if withdrawalErr == nil { - // It's a valid withdrawal, process it - log.Debug(). - Str("staking_tx", delegation.StakingTxHashHex). - Str("unbonding_tx", spendingTx.TxHash().String()). - Msg("unbonding tx has been spent through withdrawal path") - - // TODO: here - // return s.handleWithdrawal(ctx, delegation, types.SubStateEarlyUnbonding) - } - - // If it's not a valid withdrawal, check if it's a valid slashing - if !errors.Is(withdrawalErr, types.ErrInvalidWithdrawalTx) { + if withdrawalErr != nil { + if errors.Is(withdrawalErr, types.ErrInvalidWithdrawalTx) { + // invalidTransactionsCounter.WithLabelValues("confirmed_withdraw_staking_transactions").Inc() + // si.logger.Warn("found an invalid withdrawal tx from staking", + // zap.String("tx_hash", tx.TxHash().String()), + // zap.Uint64("height", height), + // zap.Bool("is_confirmed", true), + // zap.Error(err), + // ) + + return nil + } return fmt.Errorf("failed to validate withdrawal tx: %w", withdrawalErr) } + // todo: handle withdrawal here + // // Try to validate as slashing transaction // if err := s.validateSlashingTxFromUnbonding(spendingTx, delegation, spendingInputIdx, params); err != nil { // if errors.Is(err, types.ErrInvalidSlashingTx) { From aac2260aa08bbe690a5a78014715b41cf7ada6c0 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Tue, 10 Dec 2024 16:54:50 +0530 Subject: [PATCH 04/35] improve config --- cmd/staking-expiry-checker/main.go | 28 ++++++++-- internal/config/config.go | 4 +- internal/config/poller.go | 29 ++++++++-- internal/poller/poller.go | 56 ++++++++++++------- internal/services/btc_subscriber.go | 11 ++++ .../services/{expiry.go => expiry_checker.go} | 0 internal/services/watch_btc_events.go | 2 +- 7 files changed, 95 insertions(+), 35 deletions(-) create mode 100644 internal/services/btc_subscriber.go rename internal/services/{expiry.go => expiry_checker.go} (100%) diff --git a/cmd/staking-expiry-checker/main.go b/cmd/staking-expiry-checker/main.go index 62a6772..4ea4b74 100644 --- a/cmd/staking-expiry-checker/main.go +++ b/cmd/staking-expiry-checker/main.go @@ -60,14 +60,32 @@ func main() { log.Fatal().Err(err).Msg("error while creating btc notifier") } - delegationService := services.NewService(cfg, dbClient, btcNotifier, btcClient) + service := services.NewService(cfg, dbClient, btcNotifier, btcClient) if err != nil { - log.Fatal().Err(err).Msg("error while creating delegation service") + log.Fatal().Err(err).Msg("error while creating service") } - p, err := poller.NewPoller(cfg.Poller, delegationService) + // Create expiry poller + expiryPoller, err := poller.NewPoller( + poller.ExpiryPoller, + cfg.Pollers.ExpiryChecker, + service.ProcessExpiredDelegations, + ) + if err != nil { + log.Fatal().Err(err).Msg("error while creating expiry poller") + } + + // Create BTC subscriber poller + btcSubscriberPoller, err := poller.NewPoller( + poller.BTCSubscriberPoller, + cfg.Pollers.BtcSubscriber, + service.ProcessBTCSubscriber, + ) if err != nil { - log.Fatal().Err(err).Msg("error while creating poller") + log.Fatal().Err(err).Msg("error while creating BTC subscriber poller") } - p.Start(ctx) + + // Start pollers in separate goroutines + go expiryPoller.Start(ctx) + go btcSubscriberPoller.Start(ctx) } diff --git a/internal/config/config.go b/internal/config/config.go index f48bb9a..c5fb72a 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -9,14 +9,14 @@ import ( ) type Config struct { - Poller PollerConfig `mapstructure:"poller"` + Pollers PollersConfig `mapstructure:"pollers"` Db DbConfig `mapstructure:"db"` Btc BtcConfig `mapstructure:"btc"` Metrics MetricsConfig `mapstructure:"metrics"` } func (cfg *Config) Validate() error { - if err := cfg.Poller.Validate(); err != nil { + if err := cfg.Pollers.Validate(); err != nil { return err } diff --git a/internal/config/poller.go b/internal/config/poller.go index eaad77b..3ec3b66 100644 --- a/internal/config/poller.go +++ b/internal/config/poller.go @@ -10,19 +10,36 @@ import ( type PollerConfig struct { Interval time.Duration `mapstructure:"interval"` - LogLevel string `mapstructure:"log-level"` Timeout time.Duration `mapstructure:"timeout"` } -func (cfg *PollerConfig) Validate() error { - if cfg.Interval < 0 { - return errors.New("poll interval cannot be negative") - } +type PollersConfig struct { + LogLevel string `mapstructure:"log-level"` + ExpiryChecker PollerConfig `mapstructure:"expiry-checker"` + BtcSubscriber PollerConfig `mapstructure:"btc-subscriber"` +} +func (cfg *PollersConfig) Validate() error { if err := cfg.ValidateServiceLogLevel(); err != nil { return err } + if err := cfg.ExpiryChecker.Validate(); err != nil { + return err + } + + if err := cfg.BtcSubscriber.Validate(); err != nil { + return err + } + + return nil +} + +func (cfg *PollerConfig) Validate() error { + if cfg.Interval <= 0 { + return errors.New("poll interval cannot be negative") + } + if cfg.Timeout <= 0 { return errors.New("poll timeout must be greater than 0") } @@ -30,7 +47,7 @@ func (cfg *PollerConfig) Validate() error { return nil } -func (cfg *PollerConfig) ValidateServiceLogLevel() error { +func (cfg *PollersConfig) ValidateServiceLogLevel() error { // If log level is not set, we don't need to validate it, a default value will be used in service if cfg.LogLevel == "" { return nil diff --git a/internal/poller/poller.go b/internal/poller/poller.go index fa5a38e..c54f0be 100644 --- a/internal/poller/poller.go +++ b/internal/poller/poller.go @@ -7,22 +7,33 @@ import ( "github.com/rs/zerolog/log" "github.com/babylonlabs-io/staking-expiry-checker/internal/config" - "github.com/babylonlabs-io/staking-expiry-checker/internal/services" + "github.com/babylonlabs-io/staking-expiry-checker/internal/types" ) +type PollerType string + +const ( + ExpiryPoller PollerType = "expiry" + BTCSubscriberPoller PollerType = "btc-subscriber" +) + +type PollerOperation func(ctx context.Context) *types.Error + type Poller struct { - service *services.Service - interval time.Duration - timeout time.Duration - quit chan struct{} + pollerType PollerType + operation PollerOperation + interval time.Duration + timeout time.Duration + quit chan struct{} } -func NewPoller(cfg config.PollerConfig, service *services.Service) (*Poller, error) { +func NewPoller(pollerType PollerType, cfg config.PollerConfig, operation PollerOperation) (*Poller, error) { return &Poller{ - service: service, - interval: cfg.Interval, - timeout: cfg.Timeout, - quit: make(chan struct{}), + pollerType: pollerType, + operation: operation, + interval: cfg.Interval, + timeout: cfg.Timeout, + quit: make(chan struct{}), }, nil } @@ -36,8 +47,11 @@ func (p *Poller) Start(ctx context.Context) { pollingCtx, cancel := context.WithTimeout(ctx, p.timeout) defer cancel() - if err := p.poll(pollingCtx); err != nil { - log.Error().Err(err).Msg("Error polling") + if err := p.operation(pollingCtx); err != nil { + log.Error(). + Err(err). + Str("poller", string(p.pollerType)). + Msg("Error in polling operation") } case <-ctx.Done(): // Handle context cancellation. @@ -54,12 +68,12 @@ func (p *Poller) Stop() { close(p.quit) } -func (p *Poller) poll(ctx context.Context) error { - log.Debug().Msg("Polling started") - if err := p.service.ProcessExpiredDelegations(ctx); err != nil { - log.Error().Err(err).Msg("Error processing expired delegations") - return err - } - log.Debug().Msg("Polling completed") - return nil -} +// func (p *Poller) poll(ctx context.Context) error { +// log.Debug().Msg("Polling started") +// if err := p.service.ProcessExpiredDelegations(ctx); err != nil { +// log.Error().Err(err).Msg("Error processing expired delegations") +// return err +// } +// log.Debug().Msg("Polling completed") +// return nil +// } diff --git a/internal/services/btc_subscriber.go b/internal/services/btc_subscriber.go new file mode 100644 index 0000000..beca25f --- /dev/null +++ b/internal/services/btc_subscriber.go @@ -0,0 +1,11 @@ +package services + +import ( + "context" + + "github.com/babylonlabs-io/staking-expiry-checker/internal/types" +) + +func (s *Service) ProcessBTCSubscriber(ctx context.Context) *types.Error { + return nil +} diff --git a/internal/services/expiry.go b/internal/services/expiry_checker.go similarity index 100% rename from internal/services/expiry.go rename to internal/services/expiry_checker.go diff --git a/internal/services/watch_btc_events.go b/internal/services/watch_btc_events.go index 7ca04fe..a5f93a8 100644 --- a/internal/services/watch_btc_events.go +++ b/internal/services/watch_btc_events.go @@ -568,7 +568,7 @@ func (s *Service) quitContext() (context.Context, func()) { return ctx, cancel } -func (s *Service) RegisterStakingSpendNotification( +func (s *Service) registerStakingSpendNotification( ctx context.Context, stakingTxHashHex string, stakingTxHex string, From 27130c410b547a48f3fb04564ff54327e9962474 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Tue, 10 Dec 2024 17:01:52 +0530 Subject: [PATCH 05/35] fix --- cmd/staking-expiry-checker/main.go | 19 +++++------------- internal/poller/poller.go | 32 ++++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/cmd/staking-expiry-checker/main.go b/cmd/staking-expiry-checker/main.go index 4ea4b74..b9dc149 100644 --- a/cmd/staking-expiry-checker/main.go +++ b/cmd/staking-expiry-checker/main.go @@ -65,25 +65,16 @@ func main() { log.Fatal().Err(err).Msg("error while creating service") } - // Create expiry poller - expiryPoller, err := poller.NewPoller( - poller.ExpiryPoller, + // Even though we pass service, it's viewed only through the specific interface + expiryPoller := poller.NewExpiryPoller( cfg.Pollers.ExpiryChecker, - service.ProcessExpiredDelegations, + service, // service implements ExpiryChecker ) - if err != nil { - log.Fatal().Err(err).Msg("error while creating expiry poller") - } - // Create BTC subscriber poller - btcSubscriberPoller, err := poller.NewPoller( - poller.BTCSubscriberPoller, + btcSubscriberPoller := poller.NewBTCSubscriberPoller( cfg.Pollers.BtcSubscriber, - service.ProcessBTCSubscriber, + service, // service implements BTCSubscriber ) - if err != nil { - log.Fatal().Err(err).Msg("error while creating BTC subscriber poller") - } // Start pollers in separate goroutines go expiryPoller.Start(ctx) diff --git a/internal/poller/poller.go b/internal/poller/poller.go index c54f0be..5d0f9f8 100644 --- a/internal/poller/poller.go +++ b/internal/poller/poller.go @@ -10,6 +10,15 @@ import ( "github.com/babylonlabs-io/staking-expiry-checker/internal/types" ) +// Define minimal interfaces for each poller type +type ExpiryChecker interface { + ProcessExpiredDelegations(ctx context.Context) *types.Error +} + +type BTCSubscriber interface { + ProcessBTCSubscriber(ctx context.Context) *types.Error +} + type PollerType string const ( @@ -21,20 +30,31 @@ type PollerOperation func(ctx context.Context) *types.Error type Poller struct { pollerType PollerType - operation PollerOperation + poll func(ctx context.Context) *types.Error interval time.Duration timeout time.Duration quit chan struct{} } -func NewPoller(pollerType PollerType, cfg config.PollerConfig, operation PollerOperation) (*Poller, error) { +// Constructors now accept interfaces instead of the full service +func NewExpiryPoller(cfg config.PollerConfig, checker ExpiryChecker) *Poller { + return &Poller{ + pollerType: ExpiryPoller, + interval: cfg.Interval, + timeout: cfg.Timeout, + poll: checker.ProcessExpiredDelegations, + quit: make(chan struct{}), + } +} + +func NewBTCSubscriberPoller(cfg config.PollerConfig, subscriber BTCSubscriber) *Poller { return &Poller{ - pollerType: pollerType, - operation: operation, + pollerType: BTCSubscriberPoller, interval: cfg.Interval, timeout: cfg.Timeout, + poll: subscriber.ProcessBTCSubscriber, quit: make(chan struct{}), - }, nil + } } func (p *Poller) Start(ctx context.Context) { @@ -47,7 +67,7 @@ func (p *Poller) Start(ctx context.Context) { pollingCtx, cancel := context.WithTimeout(ctx, p.timeout) defer cancel() - if err := p.operation(pollingCtx); err != nil { + if err := p.poll(pollingCtx); err != nil { log.Error(). Err(err). Str("poller", string(p.pollerType)). From 859b9b52b86f8b3fcde733e261f76e2fda0de3ac Mon Sep 17 00:00:00 2001 From: Gurjot Date: Tue, 10 Dec 2024 17:38:08 +0530 Subject: [PATCH 06/35] fix mocks and processes --- internal/db/delegation.go | 28 +++++++++ internal/db/interface.go | 1 + internal/services/btc_subscriber.go | 39 ++++++++++++ tests/mocks/mock_btc_client.go | 2 +- tests/mocks/mock_db_client.go | 92 ++++++++++++++++++++++++++++- 5 files changed, 160 insertions(+), 2 deletions(-) diff --git a/internal/db/delegation.go b/internal/db/delegation.go index b4522f5..4125ed5 100644 --- a/internal/db/delegation.go +++ b/internal/db/delegation.go @@ -88,3 +88,31 @@ func (db *Database) GetBTCDelegationByStakingTxHash( return &delegationDoc, nil } + +func (db *Database) GetBTCDelegationsByStates( + ctx context.Context, + states []model.DelegationState, +) ([]*model.BTCDelegationDetails, error) { + // Convert states to a slice of strings + stateStrings := make([]string, len(states)) + for i, state := range states { + stateStrings[i] = state.ToString() + } + + filter := bson.M{"state": bson.M{"$in": stateStrings}} + + cursor, err := db.client.Database(db.dbName). + Collection(model.DelegationsCollection). + Find(ctx, filter) + if err != nil { + return nil, err + } + defer cursor.Close(ctx) + + var delegations []*model.BTCDelegationDetails + if err := cursor.All(ctx, &delegations); err != nil { + return nil, err + } + + return delegations, nil +} diff --git a/internal/db/interface.go b/internal/db/interface.go index 95db0d8..739e831 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -29,4 +29,5 @@ type DbInterface interface { ctx context.Context, stakingTxHash string, ) (*model.BTCDelegationDetails, error) GetStakingParams(ctx context.Context, version uint32) (*model.StakingParams, error) + GetBTCDelegationsByStates(ctx context.Context, states []model.DelegationState) ([]*model.BTCDelegationDetails, error) } diff --git a/internal/services/btc_subscriber.go b/internal/services/btc_subscriber.go index beca25f..5f487bd 100644 --- a/internal/services/btc_subscriber.go +++ b/internal/services/btc_subscriber.go @@ -3,9 +3,48 @@ package services import ( "context" + "github.com/babylonlabs-io/staking-expiry-checker/internal/db/model" "github.com/babylonlabs-io/staking-expiry-checker/internal/types" + "github.com/rs/zerolog/log" ) func (s *Service) ProcessBTCSubscriber(ctx context.Context) *types.Error { + // Get delegations that need BTC notifications + delegations, err := s.db.GetBTCDelegationsByStates(ctx, []model.DelegationState{ + model.Unbonded, + model.UnbondingRequested, + }) + if err != nil { + log.Error().Err(err).Msg("Failed to get delegations for BTC subscription") + return types.NewInternalServiceError(err) + } + + if len(delegations) == 0 { + log.Debug().Msg("No delegations found for BTC subscription") + return nil + } + + // Process each delegation + for _, delegation := range delegations { + err := s.registerStakingSpendNotification( + ctx, + delegation.StakingTxHashHex, + delegation.StakingTxHex, + delegation.StakingOutputIdx, + delegation.StartHeight, + ) + if err != nil { + log.Error(). + Err(err). + Str("stakingTxHash", delegation.StakingTxHashHex). + Msg("Failed to register staking spend notification") + return types.NewInternalServiceError(err) + } + + log.Debug(). + Str("stakingTxHash", delegation.StakingTxHashHex). + Msg("Successfully registered BTC notification") + } + return nil } diff --git a/tests/mocks/mock_btc_client.go b/tests/mocks/mock_btc_client.go index 21a8239..37068ed 100644 --- a/tests/mocks/mock_btc_client.go +++ b/tests/mocks/mock_btc_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.41.0. DO NOT EDIT. +// Code generated by mockery v2.44.1. DO NOT EDIT. package mocks diff --git a/tests/mocks/mock_db_client.go b/tests/mocks/mock_db_client.go index e672a49..d16ec84 100644 --- a/tests/mocks/mock_db_client.go +++ b/tests/mocks/mock_db_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.41.0. DO NOT EDIT. +// Code generated by mockery v2.44.1. DO NOT EDIT. package mocks @@ -67,6 +67,96 @@ func (_m *DbInterface) FindExpiredDelegations(ctx context.Context, btcTipHeight return r0, r1 } +// GetBTCDelegationByStakingTxHash provides a mock function with given fields: ctx, stakingTxHash +func (_m *DbInterface) GetBTCDelegationByStakingTxHash(ctx context.Context, stakingTxHash string) (*model.BTCDelegationDetails, error) { + ret := _m.Called(ctx, stakingTxHash) + + if len(ret) == 0 { + panic("no return value specified for GetBTCDelegationByStakingTxHash") + } + + var r0 *model.BTCDelegationDetails + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*model.BTCDelegationDetails, error)); ok { + return rf(ctx, stakingTxHash) + } + if rf, ok := ret.Get(0).(func(context.Context, string) *model.BTCDelegationDetails); ok { + r0 = rf(ctx, stakingTxHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.BTCDelegationDetails) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, stakingTxHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetBTCDelegationsByStates provides a mock function with given fields: ctx, states +func (_m *DbInterface) GetBTCDelegationsByStates(ctx context.Context, states []model.DelegationState) ([]*model.BTCDelegationDetails, error) { + ret := _m.Called(ctx, states) + + if len(ret) == 0 { + panic("no return value specified for GetBTCDelegationsByStates") + } + + var r0 []*model.BTCDelegationDetails + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []model.DelegationState) ([]*model.BTCDelegationDetails, error)); ok { + return rf(ctx, states) + } + if rf, ok := ret.Get(0).(func(context.Context, []model.DelegationState) []*model.BTCDelegationDetails); ok { + r0 = rf(ctx, states) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*model.BTCDelegationDetails) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []model.DelegationState) error); ok { + r1 = rf(ctx, states) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetStakingParams provides a mock function with given fields: ctx, version +func (_m *DbInterface) GetStakingParams(ctx context.Context, version uint32) (*model.StakingParams, error) { + ret := _m.Called(ctx, version) + + if len(ret) == 0 { + panic("no return value specified for GetStakingParams") + } + + var r0 *model.StakingParams + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint32) (*model.StakingParams, error)); ok { + return rf(ctx, version) + } + if rf, ok := ret.Get(0).(func(context.Context, uint32) *model.StakingParams); ok { + r0 = rf(ctx, version) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.StakingParams) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uint32) error); ok { + r1 = rf(ctx, version) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Ping provides a mock function with given fields: ctx func (_m *DbInterface) Ping(ctx context.Context) error { ret := _m.Called(ctx) From 03d3f9b3c31d09b3472dc7684866c19df118fdd9 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Tue, 10 Dec 2024 23:02:25 +0530 Subject: [PATCH 07/35] tracked subs --- internal/services/btc_subscriber.go | 11 +++++ internal/services/service.go | 2 + internal/services/tracked_subscriptions.go | 54 ++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 internal/services/tracked_subscriptions.go diff --git a/internal/services/btc_subscriber.go b/internal/services/btc_subscriber.go index 5f487bd..69632a7 100644 --- a/internal/services/btc_subscriber.go +++ b/internal/services/btc_subscriber.go @@ -26,6 +26,14 @@ func (s *Service) ProcessBTCSubscriber(ctx context.Context) *types.Error { // Process each delegation for _, delegation := range delegations { + // Check against local copy - no locks needed + if s.trackedSubs.IsSubscribed(delegation.StakingTxHashHex) { // Each iteration: RLock/RUnlock + log.Debug(). + Str("stakingTxHash", delegation.StakingTxHashHex). + Msg("Delegation already subscribed, skipping") + continue + } + err := s.registerStakingSpendNotification( ctx, delegation.StakingTxHashHex, @@ -41,6 +49,9 @@ func (s *Service) ProcessBTCSubscriber(ctx context.Context) *types.Error { return types.NewInternalServiceError(err) } + // Add to tracked subscriptions after successful registration + s.trackedSubs.AddSubscription(delegation.StakingTxHashHex) + log.Debug(). Str("stakingTxHash", delegation.StakingTxHashHex). Msg("Successfully registered BTC notification") diff --git a/internal/services/service.go b/internal/services/service.go index a91eea9..12919c4 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -17,6 +17,7 @@ type Service struct { db db.DbInterface btcNotifier notifier.ChainNotifier btc btcclient.BtcInterface + trackedSubs *TrackedSubscriptions } func NewService( @@ -31,5 +32,6 @@ func NewService( db: db, btcNotifier: btcNotifier, btc: btc, + trackedSubs: NewTrackedSubscriptions(), } } diff --git a/internal/services/tracked_subscriptions.go b/internal/services/tracked_subscriptions.go new file mode 100644 index 0000000..69d8a1e --- /dev/null +++ b/internal/services/tracked_subscriptions.go @@ -0,0 +1,54 @@ +package services + +import "sync" + +type TrackedSubscriptions struct { + mu sync.RWMutex + subscriptions map[string]struct{} // Using empty struct as value since we only care about existence +} + +func NewTrackedSubscriptions() *TrackedSubscriptions { + return &TrackedSubscriptions{ + subscriptions: make(map[string]struct{}), + } +} + +func (ts *TrackedSubscriptions) IsSubscribed(stakingTxHash string) bool { + ts.mu.RLock() + defer ts.mu.RUnlock() + _, exists := ts.subscriptions[stakingTxHash] + return exists +} + +func (ts *TrackedSubscriptions) GetSubscribedHashes() map[string]struct{} { + ts.mu.RLock() + defer ts.mu.RUnlock() + + subscribed := make(map[string]struct{}, len(ts.subscriptions)) + for hash := range ts.subscriptions { + subscribed[hash] = struct{}{} // Just copying hashes + } + return subscribed +} + +// AddSubscriptions adds multiple subscriptions at once +func (ts *TrackedSubscriptions) AddSubscriptions(stakingTxHashes []string) { + ts.mu.Lock() + defer ts.mu.Unlock() + + for _, hash := range stakingTxHashes { + ts.subscriptions[hash] = struct{}{} + } +} + +func (ts *TrackedSubscriptions) AddSubscription(stakingTxHash string) { + ts.mu.Lock() + defer ts.mu.Unlock() + ts.subscriptions[stakingTxHash] = struct{}{} // Empty struct uses no memory +} + +func (ts *TrackedSubscriptions) RemoveSubscription(stakingTxHash string) { + ts.mu.Lock() + defer ts.mu.Unlock() + delete(ts.subscriptions, stakingTxHash) +} From 061a80866d69359481aa4e3c5d4748338210c1b2 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Tue, 10 Dec 2024 23:06:13 +0530 Subject: [PATCH 08/35] fix config --- config/config-local.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/config/config-local.yml b/config/config-local.yml index 1c71076..959f3e9 100644 --- a/config/config-local.yml +++ b/config/config-local.yml @@ -1,7 +1,11 @@ -poller: - interval: 5s +pollers: log-level: debug - timeout: 10s + expiry-checker: + interval: 5s + timeout: 10s + btc-subscriber: + interval: 5s + timeout: 10s db: username: root password: example From 82249712222593647cdf5a3fc553b182205bfe5b Mon Sep 17 00:00:00 2001 From: Gurjot Date: Tue, 10 Dec 2024 23:23:22 +0530 Subject: [PATCH 09/35] fix funcs --- internal/services/tracked_subscriptions.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/internal/services/tracked_subscriptions.go b/internal/services/tracked_subscriptions.go index 69d8a1e..184834a 100644 --- a/internal/services/tracked_subscriptions.go +++ b/internal/services/tracked_subscriptions.go @@ -20,27 +20,6 @@ func (ts *TrackedSubscriptions) IsSubscribed(stakingTxHash string) bool { return exists } -func (ts *TrackedSubscriptions) GetSubscribedHashes() map[string]struct{} { - ts.mu.RLock() - defer ts.mu.RUnlock() - - subscribed := make(map[string]struct{}, len(ts.subscriptions)) - for hash := range ts.subscriptions { - subscribed[hash] = struct{}{} // Just copying hashes - } - return subscribed -} - -// AddSubscriptions adds multiple subscriptions at once -func (ts *TrackedSubscriptions) AddSubscriptions(stakingTxHashes []string) { - ts.mu.Lock() - defer ts.mu.Unlock() - - for _, hash := range stakingTxHashes { - ts.subscriptions[hash] = struct{}{} - } -} - func (ts *TrackedSubscriptions) AddSubscription(stakingTxHash string) { ts.mu.Lock() defer ts.mu.Unlock() From d387f35caf878a9115c078102786bc59f931046d Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 00:01:57 +0530 Subject: [PATCH 10/35] push to channels --- internal/services/service.go | 18 ++-- internal/services/watch_btc_events.go | 128 ++++++++------------------ 2 files changed, 52 insertions(+), 94 deletions(-) diff --git a/internal/services/service.go b/internal/services/service.go index 12919c4..c85bc42 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -6,6 +6,7 @@ import ( "github.com/babylonlabs-io/staking-expiry-checker/internal/btcclient" "github.com/babylonlabs-io/staking-expiry-checker/internal/config" "github.com/babylonlabs-io/staking-expiry-checker/internal/db" + "github.com/babylonlabs-io/staking-expiry-checker/internal/db/model" notifier "github.com/lightningnetwork/lnd/chainntnfs" ) @@ -18,6 +19,9 @@ type Service struct { btcNotifier notifier.ChainNotifier btc btcclient.BtcInterface trackedSubs *TrackedSubscriptions + + unbondingDelegationChan chan *model.BTCDelegationDetails + withdrawnDelegationChan chan *model.BTCDelegationDetails } func NewService( @@ -27,11 +31,13 @@ func NewService( btc btcclient.BtcInterface, ) *Service { return &Service{ - quit: make(chan struct{}), - cfg: cfg, - db: db, - btcNotifier: btcNotifier, - btc: btc, - trackedSubs: NewTrackedSubscriptions(), + quit: make(chan struct{}), + cfg: cfg, + db: db, + btcNotifier: btcNotifier, + btc: btc, + trackedSubs: NewTrackedSubscriptions(), + unbondingDelegationChan: make(chan *model.BTCDelegationDetails, 100), // buffered + withdrawnDelegationChan: make(chan *model.BTCDelegationDetails, 100), // buffered } } diff --git a/internal/services/watch_btc_events.go b/internal/services/watch_btc_events.go index a5f93a8..7e0d51b 100644 --- a/internal/services/watch_btc_events.go +++ b/internal/services/watch_btc_events.go @@ -77,7 +77,6 @@ func (s *Service) watchForSpendUnbondingTx( if err := s.handleSpendingUnbondingTransaction( quitCtx, spendDetail.SpendingTx, - uint32(spendDetail.SpendingHeight), spendDetail.SpenderInputIndex, delegation, ); err != nil { @@ -100,7 +99,6 @@ func (s *Service) handleSpendingStakingTransaction( ctx context.Context, spendingTx *wire.MsgTx, spendingInputIdx uint32, - spendingHeight uint32, stakingTxHashHex string, ) error { delegation, err := s.db.GetBTCDelegationByStakingTxHash(ctx, stakingTxHashHex) @@ -139,6 +137,19 @@ func (s *Service) handleSpendingStakingTransaction( Str("unbonding_tx", spendingTx.TxHash().String()). Msg("staking tx has been spent through unbonding path") + // Blocking send - will wait if channel is full + select { + case s.unbondingDelegationChan <- delegation: + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("sent delegation to unbonding handler") + case <-ctx.Done(): + log.Error(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("context cancelled while waiting to send to unbonding channel") + return ctx.Err() + } + // Register unbonding spend notification return s.registerUnbondingSpendNotification(ctx, delegation) } @@ -161,36 +172,26 @@ func (s *Service) handleSpendingStakingTransaction( //failedProcessingWithdrawTxsFromStakingCounter.Inc() return err } - // if withdrawalErr == nil { - // // It's a valid withdrawal, process it - // log.Debug(). - // Str("staking_tx", delegation.StakingTxHashHex). - // Str("withdrawal_tx", spendingTx.TxHash().String()). - // Msg("staking tx has been spent through withdrawal path") - // // TODO here - // //return s.handleWithdrawal(ctx, delegation, types.SubStateTimelock) - // } - - // If it's not a valid withdrawal, check if it's a valid slashing - // if !errors.Is(withdrawalErr, types.ErrInvalidWithdrawalTx) { - // return fmt.Errorf("failed to validate withdrawal tx: %w", withdrawalErr) - // } - - // // Try to validate as slashing transaction - // if err := s.validateSlashingTxFromStaking(spendingTx, spendingInputIdx, delegation, params); err != nil { - // if errors.Is(err, types.ErrInvalidSlashingTx) { - // // Neither withdrawal nor slashing - this is an invalid spend - // return fmt.Errorf("transaction is neither valid unbonding, withdrawal, nor slashing: %w", err) - // } - // return fmt.Errorf("failed to validate slashing tx: %w", err) - // } + + // Blocking send - will wait if channel is full + select { + case s.withdrawnDelegationChan <- delegation: + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("sent delegation to withdrawn handler") + case <-ctx.Done(): + log.Error(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("context cancelled while waiting to send to withdrawn channel") + return ctx.Err() + } + return nil } func (s *Service) handleSpendingUnbondingTransaction( ctx context.Context, spendingTx *wire.MsgTx, - spendingHeight uint32, spendingInputIdx uint32, delegation *model.BTCDelegationDetails, ) error { @@ -216,73 +217,24 @@ func (s *Service) handleSpendingUnbondingTransaction( return fmt.Errorf("failed to validate withdrawal tx: %w", withdrawalErr) } - // todo: handle withdrawal here + // Blocking send - will wait if channel is full + select { + case s.withdrawnDelegationChan <- delegation: + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("sent delegation to withdrawn handler") + case <-ctx.Done(): + log.Error(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("context cancelled while waiting to send to withdrawn channel") + return ctx.Err() + } - // // Try to validate as slashing transaction - // if err := s.validateSlashingTxFromUnbonding(spendingTx, delegation, spendingInputIdx, params); err != nil { - // if errors.Is(err, types.ErrInvalidSlashingTx) { - // // Neither withdrawal nor slashing - this is an invalid spend - // return fmt.Errorf("transaction is neither valid withdrawal nor slashing: %w", err) - // } - // return fmt.Errorf("failed to validate slashing tx: %w", err) - // } - - // // Save unbonding slashing tx hex - // unbondingSlashingTx, err := bstypes.NewBTCSlashingTxFromMsgTx(spendingTx) - // if err != nil { - // return fmt.Errorf("failed to convert unbonding slashing tx to bytes: %w", err) - // } - // unbondingSlashingTxHex := unbondingSlashingTx.ToHexStr() - // if err := s.db.SaveBTCDelegationUnbondingSlashingTxHex(ctx, delegation.StakingTxHashHex, unbondingSlashingTxHex); err != nil { - // return fmt.Errorf("failed to save unbonding slashing tx hex: %w", err) - // } - - // // It's a valid slashing tx, watch for spending change output - // return s.startWatchingSlashingChange( - // ctx, - // spendingTx, - // spendingHeight, - // delegation, - // types.SubStateEarlyUnbondingSlashing, - // ) + // todo: handle withdrawal here return nil } -// func (s *Service) handleWithdrawal( -// ctx context.Context, -// delegation *model.BTCDelegationDetails, -// subState types.DelegationSubState, -// ) error { -// delegationState, err := s.db.GetBTCDelegationState(ctx, delegation.StakingTxHashHex) -// if err != nil { -// return fmt.Errorf("failed to get delegation state: %w", err) -// } - -// qualifiedStates := types.QualifiedStatesForWithdrawn() -// if qualifiedStates == nil || !utils.Contains(qualifiedStates, *delegationState) { -// log.Error(). -// Str("staking_tx", delegation.StakingTxHashHex). -// Str("current_state", delegationState.String()). -// Msg("current state is not qualified for withdrawal") -// return fmt.Errorf("current state %s is not qualified for withdrawal", *delegationState) -// } - -// // Update to withdrawn state -// log.Debug(). -// Str("staking_tx", delegation.StakingTxHashHex). -// Str("state", types.StateWithdrawn.String()). -// Str("sub_state", subState.String()). -// Msg("updating delegation state to withdrawn") -// return s.db.UpdateBTCDelegationState( -// ctx, -// delegation.StakingTxHashHex, -// types.QualifiedStatesForWithdrawn(), -// types.StateWithdrawn, -// &subState, -// ) -// } - // IsValidUnbondingTx tries to identify a tx is a valid unbonding tx // It returns error when (1) it fails to verify the unbonding tx due // to invalid parameters, and (2) the tx spends the unbonding path From 9cc16cba4790a802e93513d0c086fa3bb57dcde4 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 00:45:32 +0530 Subject: [PATCH 11/35] chan handlers --- internal/db/delegation.go | 33 +++++++------ internal/db/interface.go | 10 ++-- internal/db/model/delegation.go | 38 +-------------- internal/services/btc_subscriber.go | 7 ++- internal/services/expiry_checker.go | 6 +-- internal/services/service.go | 69 +++++++++++++++++++++++++++ internal/services/watch_btc_events.go | 1 - internal/types/delegation.go | 37 ++++++++++++++ internal/types/transaction.go | 25 +++++----- internal/utils/state_transition.go | 28 +++++++++++ internal/utils/utils.go | 11 +++++ 11 files changed, 188 insertions(+), 77 deletions(-) create mode 100644 internal/types/delegation.go create mode 100644 internal/utils/state_transition.go diff --git a/internal/db/delegation.go b/internal/db/delegation.go index 4125ed5..c072ad3 100644 --- a/internal/db/delegation.go +++ b/internal/db/delegation.go @@ -6,31 +6,30 @@ import ( "github.com/babylonlabs-io/staking-expiry-checker/internal/db/model" "github.com/babylonlabs-io/staking-expiry-checker/internal/types" + "github.com/babylonlabs-io/staking-expiry-checker/internal/utils" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" ) -func (db *Database) TransitionToUnbonded( +func (db *Database) TransitionToUnbondedState( ctx context.Context, stakingTxHashHex string, - unbondTxType types.TransactionType, + unbondTxType types.StakingTxType, ) error { - eligiblePreviousStates := qualifiedStatesToUnbonded(unbondTxType) return db.transitionState( - ctx, stakingTxHashHex, model.Unbonded, - eligiblePreviousStates, + ctx, stakingTxHashHex, types.Unbonded, + utils.QualifiedStatesToUnbonded(unbondTxType), ) } -func qualifiedStatesToUnbonded(unbondTxType types.TransactionType) []model.DelegationState { - switch unbondTxType { - case types.TransactionTypeActive: - return []model.DelegationState{model.Active} - case types.TransactionTypeUnbonding: - return []model.DelegationState{model.Unbonding} - default: - return nil - } +func (db *Database) TransitionToUnbondingState( + ctx context.Context, + stakingTxHashHex string, +) error { + return db.transitionState( + ctx, stakingTxHashHex, types.Unbonding, + utils.QualifiedStatesToUnbonding(), + ) } // TransitionState updates the state of a staking transaction to a new state @@ -39,8 +38,8 @@ func qualifiedStatesToUnbonded(unbondTxType types.TransactionType) []model.Deleg func (db *Database) transitionState( ctx context.Context, stakingTxHashHex string, - newState model.DelegationState, - eligiblePreviousState []model.DelegationState, + newState types.DelegationState, + eligiblePreviousState []types.DelegationState, ) error { client := db.client.Database( db.dbName, @@ -91,7 +90,7 @@ func (db *Database) GetBTCDelegationByStakingTxHash( func (db *Database) GetBTCDelegationsByStates( ctx context.Context, - states []model.DelegationState, + states []types.DelegationState, ) ([]*model.BTCDelegationDetails, error) { // Convert states to a slice of strings stateStrings := make([]string, len(states)) diff --git a/internal/db/interface.go b/internal/db/interface.go index 739e831..7e4e51c 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -20,14 +20,18 @@ type DbInterface interface { ctx context.Context, stakingTxHashHex string, expireHeight uint64, txType string, ) error - TransitionToUnbonded( + TransitionToUnbondedState( + ctx context.Context, + stakingTxHashHex string, + unbondTxType types.StakingTxType, + ) error + TransitionToUnbondingState( ctx context.Context, stakingTxHashHex string, - unbondTxType types.TransactionType, ) error GetBTCDelegationByStakingTxHash( ctx context.Context, stakingTxHash string, ) (*model.BTCDelegationDetails, error) GetStakingParams(ctx context.Context, version uint32) (*model.StakingParams, error) - GetBTCDelegationsByStates(ctx context.Context, states []model.DelegationState) ([]*model.BTCDelegationDetails, error) + GetBTCDelegationsByStates(ctx context.Context, states []types.DelegationState) ([]*model.BTCDelegationDetails, error) } diff --git a/internal/db/model/delegation.go b/internal/db/model/delegation.go index 44b278a..6f77d7b 100644 --- a/internal/db/model/delegation.go +++ b/internal/db/model/delegation.go @@ -1,40 +1,6 @@ package model -import "fmt" - -type DelegationState string - -const ( - Active DelegationState = "active" - UnbondingRequested DelegationState = "unbonding_requested" - Unbonding DelegationState = "unbonding" - Unbonded DelegationState = "unbonded" - Withdrawn DelegationState = "withdrawn" - Transitioned DelegationState = "transitioned" -) - -func (s DelegationState) ToString() string { - return string(s) -} - -func FromStringToDelegationState(s string) (DelegationState, error) { - switch s { - case "active": - return Active, nil - case "unbonding_requested": - return UnbondingRequested, nil - case "unbonding": - return Unbonding, nil - case "unbonded": - return Unbonded, nil - case "withdrawn": - return Withdrawn, nil - case "transitioned": - return Transitioned, nil - default: - return "", fmt.Errorf("invalid delegation state: %s", s) - } -} +import "github.com/babylonlabs-io/staking-expiry-checker/internal/types" type CovenantSignature struct { CovenantBtcPkHex string `bson:"covenant_btc_pk_hex"` @@ -56,7 +22,7 @@ type BTCDelegationDetails struct { FinalityProviderBtcPksHex []string `bson:"finality_provider_btc_pks_hex"` StartHeight uint32 `bson:"start_height"` EndHeight uint32 `bson:"end_height"` - State DelegationState `bson:"state"` + State types.DelegationState `bson:"state"` ParamsVersion uint32 `bson:"params_version"` UnbondingTime uint32 `bson:"unbonding_time"` UnbondingTx string `bson:"unbonding_tx"` diff --git a/internal/services/btc_subscriber.go b/internal/services/btc_subscriber.go index 69632a7..8cbe5ac 100644 --- a/internal/services/btc_subscriber.go +++ b/internal/services/btc_subscriber.go @@ -3,16 +3,15 @@ package services import ( "context" - "github.com/babylonlabs-io/staking-expiry-checker/internal/db/model" "github.com/babylonlabs-io/staking-expiry-checker/internal/types" "github.com/rs/zerolog/log" ) func (s *Service) ProcessBTCSubscriber(ctx context.Context) *types.Error { // Get delegations that need BTC notifications - delegations, err := s.db.GetBTCDelegationsByStates(ctx, []model.DelegationState{ - model.Unbonded, - model.UnbondingRequested, + delegations, err := s.db.GetBTCDelegationsByStates(ctx, []types.DelegationState{ + types.Unbonded, + types.UnbondingRequested, }) if err != nil { log.Error().Err(err).Msg("Failed to get delegations for BTC subscription") diff --git a/internal/services/expiry_checker.go b/internal/services/expiry_checker.go index 10da72b..0b3a4f5 100644 --- a/internal/services/expiry_checker.go +++ b/internal/services/expiry_checker.go @@ -13,7 +13,7 @@ import ( // This method tolerate duplicated calls on the same stakingTxHashHex. func (s *Service) ProcessExpireCheck( ctx context.Context, stakingTxHashHex string, - startHeight, timelock uint64, txType types.TransactionType, + startHeight, timelock uint64, txType types.StakingTxType, ) *types.Error { expireHeight := startHeight + timelock err := s.db.SaveTimeLockExpireCheck( @@ -68,14 +68,14 @@ func (s *Service) ProcessExpiredDelegation( ctx context.Context, delegation model.TimeLockDocument, ) *types.Error { // Check what type of the timelock is - timelockType, err := types.FromString(delegation.TxType) + timelockType, err := types.StakingTxTypeFromString(delegation.TxType) if err != nil { log.Error().Err(err).Msgf("Invalid timelock type: %s", delegation.TxType) return types.NewInternalServiceError(err) } // Try to transition to unbonded, will skip if not eligible (NotFoundError) - err = s.db.TransitionToUnbonded( + err = s.db.TransitionToUnbondedState( ctx, delegation.StakingTxHashHex, timelockType, ) if err != nil { diff --git a/internal/services/service.go b/internal/services/service.go index c85bc42..5a6fec8 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -1,13 +1,16 @@ package services import ( + "context" "sync" "github.com/babylonlabs-io/staking-expiry-checker/internal/btcclient" "github.com/babylonlabs-io/staking-expiry-checker/internal/config" "github.com/babylonlabs-io/staking-expiry-checker/internal/db" "github.com/babylonlabs-io/staking-expiry-checker/internal/db/model" + "github.com/babylonlabs-io/staking-expiry-checker/internal/utils" notifier "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/rs/zerolog/log" ) type Service struct { @@ -41,3 +44,69 @@ func NewService( withdrawnDelegationChan: make(chan *model.BTCDelegationDetails, 100), // buffered } } + +// HandleUnbondingDelegationChannel processes unbonding delegations +func (s *Service) HandleUnbondingDelegationChannel(ctx context.Context) { + for { + select { + case delegation := <-s.unbondingDelegationChan: + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("processing unbonding delegation") + + if utils.Contains(utils.OutdatedStatesForUnbonding(), delegation.State) { + // Ignore the message as the delegation state already passed the unbonding state. This is an outdated duplication + log.Ctx(ctx).Debug().Str("StakingTxHashHex", delegation.StakingTxHashHex). + Msg("delegation state is outdated for unbonding event") + continue + } + + // Save the unbonding staking delegation. This is the final step in the unbonding staking event processing + // Please refer to the README.md for the details on the unbonding staking event processing workflow + transitionErr := s.db.TransitionToUnbondingState( + ctx, delegation.StakingTxHashHex, + ) + if transitionErr != nil { + log.Error(). + Err(transitionErr). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("failed to transition to unbonding state") + } + + case <-ctx.Done(): + log.Info().Msg("stopping unbonding channel listener: context cancelled") + return + + case <-s.quit: + log.Info().Msg("stopping unbonding channel listener: service shutting down") + return + } + } +} + +// HandleWithdrawnDelegationChannel processes withdrawn delegations +func (s *Service) HandleWithdrawnDelegationChannel(ctx context.Context) { + for { + select { + case delegation := <-s.withdrawnDelegationChan: + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("processing withdrawn delegation") + + // if err := s.processWithdrawnDelegation(ctx, delegation); err != nil { + // log.Error(). + // Err(err). + // Str("staking_tx", delegation.StakingTxHashHex). + // Msg("failed to process withdrawn delegation") + // } + + case <-ctx.Done(): + log.Info().Msg("stopping withdrawn channel listener: context cancelled") + return + + case <-s.quit: + log.Info().Msg("stopping withdrawn channel listener: service shutting down") + return + } + } +} diff --git a/internal/services/watch_btc_events.go b/internal/services/watch_btc_events.go index 7e0d51b..1766e1d 100644 --- a/internal/services/watch_btc_events.go +++ b/internal/services/watch_btc_events.go @@ -40,7 +40,6 @@ func (s *Service) watchForSpendStakingTx( quitCtx, spendDetail.SpendingTx, spendDetail.SpenderInputIndex, - uint32(spendDetail.SpendingHeight), stakingTxHashHex, ); err != nil { log.Error(). diff --git a/internal/types/delegation.go b/internal/types/delegation.go new file mode 100644 index 0000000..5e687ba --- /dev/null +++ b/internal/types/delegation.go @@ -0,0 +1,37 @@ +package types + +import "fmt" + +type DelegationState string + +const ( + Active DelegationState = "active" + UnbondingRequested DelegationState = "unbonding_requested" + Unbonding DelegationState = "unbonding" + Unbonded DelegationState = "unbonded" + Withdrawn DelegationState = "withdrawn" + Transitioned DelegationState = "transitioned" +) + +func (s DelegationState) ToString() string { + return string(s) +} + +func FromStringToDelegationState(s string) (DelegationState, error) { + switch s { + case "active": + return Active, nil + case "unbonding_requested": + return UnbondingRequested, nil + case "unbonding": + return Unbonding, nil + case "unbonded": + return Unbonded, nil + case "withdrawn": + return Withdrawn, nil + case "transitioned": + return Transitioned, nil + default: + return "", fmt.Errorf("invalid delegation state: %s", s) + } +} diff --git a/internal/types/transaction.go b/internal/types/transaction.go index f11550a..6a2a0c4 100644 --- a/internal/types/transaction.go +++ b/internal/types/transaction.go @@ -2,25 +2,24 @@ package types import "fmt" -type TransactionType string +type StakingTxType string const ( - // Refer to natural timelock expired staking transaction - TransactionTypeActive TransactionType = "active" - // Refer to early unbonding of staking transaction - TransactionTypeUnbonding TransactionType = "unbonding" + ActiveTxType StakingTxType = "active" + UnbondingTxType StakingTxType = "unbonding" ) -func (t TransactionType) ToString() string { - return string(t) +func (s StakingTxType) ToString() string { + return string(s) } -func FromString(s string) (TransactionType, error) { +func StakingTxTypeFromString(s string) (StakingTxType, error) { switch s { - case string(TransactionTypeActive): - return TransactionTypeActive, nil - case string(TransactionTypeUnbonding): - return TransactionTypeUnbonding, nil + case ActiveTxType.ToString(): + return ActiveTxType, nil + case UnbondingTxType.ToString(): + return UnbondingTxType, nil + default: + return "", fmt.Errorf("unknown staking tx type: %s", s) } - return "", fmt.Errorf("invalid transaction type: %s", s) } diff --git a/internal/utils/state_transition.go b/internal/utils/state_transition.go new file mode 100644 index 0000000..e4ed46b --- /dev/null +++ b/internal/utils/state_transition.go @@ -0,0 +1,28 @@ +package utils + +import ( + "github.com/babylonlabs-io/staking-expiry-checker/internal/types" +) + +// QualifiedStatesToUnbonding returns the qualified exisitng states to transition to "unbonding" +// The Active state is allowed to directly transition to Unbonding without the need of UnbondingRequested due to bootstrap usecase +func QualifiedStatesToUnbonding() []types.DelegationState { + return []types.DelegationState{types.Active, types.UnbondingRequested} +} + +// List of states to be ignored for unbonding as it means it's already been processed +func OutdatedStatesForUnbonding() []types.DelegationState { + return []types.DelegationState{types.Unbonding, types.Unbonded, types.Withdrawn} +} + +// QualifiedStatesToUnbonded returns the qualified exisitng states to transition to "unbonded" +func QualifiedStatesToUnbonded(unbondTxType types.StakingTxType) []types.DelegationState { + switch unbondTxType { + case types.ActiveTxType: + return []types.DelegationState{types.Active} + case types.UnbondingTxType: + return []types.DelegationState{types.Unbonding} + default: + return nil + } +} diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 75e2463..9810d2a 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -97,3 +97,14 @@ func DeserializeBtcTransactionFromHex(txHex string) (*wire.MsgTx, error) { } return tx, nil } + +// Contains checks if a slice contains a specific element. +// It uses type parameters to work with any slice type. +func Contains[T comparable](slice []T, element T) bool { + for _, item := range slice { + if item == element { + return true + } + } + return false +} From d173d6180fe95faa5de7f50f29328f4f04db7688 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 00:45:52 +0530 Subject: [PATCH 12/35] mocks --- cmd/staking-expiry-checker/main.go | 2 ++ tests/mocks/mock_db_client.go | 34 +++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/cmd/staking-expiry-checker/main.go b/cmd/staking-expiry-checker/main.go index b9dc149..10c9928 100644 --- a/cmd/staking-expiry-checker/main.go +++ b/cmd/staking-expiry-checker/main.go @@ -79,4 +79,6 @@ func main() { // Start pollers in separate goroutines go expiryPoller.Start(ctx) go btcSubscriberPoller.Start(ctx) + + go service.HandleUnbondingDelegationChannel(ctx) } diff --git a/tests/mocks/mock_db_client.go b/tests/mocks/mock_db_client.go index d16ec84..7032261 100644 --- a/tests/mocks/mock_db_client.go +++ b/tests/mocks/mock_db_client.go @@ -98,7 +98,7 @@ func (_m *DbInterface) GetBTCDelegationByStakingTxHash(ctx context.Context, stak } // GetBTCDelegationsByStates provides a mock function with given fields: ctx, states -func (_m *DbInterface) GetBTCDelegationsByStates(ctx context.Context, states []model.DelegationState) ([]*model.BTCDelegationDetails, error) { +func (_m *DbInterface) GetBTCDelegationsByStates(ctx context.Context, states []types.DelegationState) ([]*model.BTCDelegationDetails, error) { ret := _m.Called(ctx, states) if len(ret) == 0 { @@ -107,10 +107,10 @@ func (_m *DbInterface) GetBTCDelegationsByStates(ctx context.Context, states []m var r0 []*model.BTCDelegationDetails var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []model.DelegationState) ([]*model.BTCDelegationDetails, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, []types.DelegationState) ([]*model.BTCDelegationDetails, error)); ok { return rf(ctx, states) } - if rf, ok := ret.Get(0).(func(context.Context, []model.DelegationState) []*model.BTCDelegationDetails); ok { + if rf, ok := ret.Get(0).(func(context.Context, []types.DelegationState) []*model.BTCDelegationDetails); ok { r0 = rf(ctx, states) } else { if ret.Get(0) != nil { @@ -118,7 +118,7 @@ func (_m *DbInterface) GetBTCDelegationsByStates(ctx context.Context, states []m } } - if rf, ok := ret.Get(1).(func(context.Context, []model.DelegationState) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, []types.DelegationState) error); ok { r1 = rf(ctx, states) } else { r1 = ret.Error(1) @@ -193,16 +193,16 @@ func (_m *DbInterface) SaveTimeLockExpireCheck(ctx context.Context, stakingTxHas return r0 } -// TransitionToUnbonded provides a mock function with given fields: ctx, stakingTxHashHex, unbondTxType -func (_m *DbInterface) TransitionToUnbonded(ctx context.Context, stakingTxHashHex string, unbondTxType types.TransactionType) error { +// TransitionToUnbondedState provides a mock function with given fields: ctx, stakingTxHashHex, unbondTxType +func (_m *DbInterface) TransitionToUnbondedState(ctx context.Context, stakingTxHashHex string, unbondTxType types.StakingTxType) error { ret := _m.Called(ctx, stakingTxHashHex, unbondTxType) if len(ret) == 0 { - panic("no return value specified for TransitionToUnbonded") + panic("no return value specified for TransitionToUnbondedState") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, types.TransactionType) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, string, types.StakingTxType) error); ok { r0 = rf(ctx, stakingTxHashHex, unbondTxType) } else { r0 = ret.Error(0) @@ -211,6 +211,24 @@ func (_m *DbInterface) TransitionToUnbonded(ctx context.Context, stakingTxHashHe return r0 } +// TransitionToUnbondingState provides a mock function with given fields: ctx, stakingTxHashHex +func (_m *DbInterface) TransitionToUnbondingState(ctx context.Context, stakingTxHashHex string) error { + ret := _m.Called(ctx, stakingTxHashHex) + + if len(ret) == 0 { + panic("no return value specified for TransitionToUnbondingState") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, stakingTxHashHex) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // NewDbInterface creates a new instance of DbInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewDbInterface(t interface { From 31d3dcd3238199771080df82b1eecc21a1ce7d4c Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 11:39:08 +0530 Subject: [PATCH 13/35] add metrics --- internal/observability/metrics/metrics.go | 71 +++++++++++++++++++++-- internal/services/btc_subscriber.go | 4 +- internal/services/watch_btc_events.go | 53 +++++++---------- 3 files changed, 90 insertions(+), 38 deletions(-) diff --git a/internal/observability/metrics/metrics.go b/internal/observability/metrics/metrics.go index aa94559..d711ba8 100644 --- a/internal/observability/metrics/metrics.go +++ b/internal/observability/metrics/metrics.go @@ -28,10 +28,14 @@ func (O Outcome) String() string { } var ( - once sync.Once - metricsRouter *chi.Mux - pollDurationHistogram *prometheus.HistogramVec - btcClientDurationHistogram *prometheus.HistogramVec + once sync.Once + metricsRouter *chi.Mux + pollDurationHistogram *prometheus.HistogramVec + btcClientDurationHistogram *prometheus.HistogramVec + invalidTransactionsCounter *prometheus.CounterVec + failedVerifyingUnbondingTxsCounter prometheus.Counter + failedVerifyingStakingWithdrawalTxsCounter prometheus.Counter + failedVerifyingUnbondingWithdrawalTxsCounter prometheus.Counter ) // Init initializes the metrics package. @@ -88,9 +92,44 @@ func registerMetrics() { []string{"function", "status"}, ) + invalidTransactionsCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "invalid_txs_counter", + Help: "Total number of invalid transactions", + }, + []string{ + "tx_type", + }, + ) + + failedVerifyingUnbondingTxsCounter = prometheus.NewCounter( + prometheus.CounterOpts{ + Name: "failed_verifying_unbonding_txs_counter", + Help: "Total number of failed verifying unbonding txs", + }, + ) + + failedVerifyingStakingWithdrawalTxsCounter = prometheus.NewCounter( + prometheus.CounterOpts{ + Name: "failed_verifying_staking_withdrawal_txs_counter", + Help: "Total number of failed verifying staking withdrawal txs", + }, + ) + + failedVerifyingUnbondingWithdrawalTxsCounter = prometheus.NewCounter( + prometheus.CounterOpts{ + Name: "failed_verifying_unbonding_withdrawal_txs_counter", + Help: "Total number of failed verifying unbonding withdrawal txs", + }, + ) + prometheus.MustRegister( pollDurationHistogram, btcClientDurationHistogram, + invalidTransactionsCounter, + failedVerifyingUnbondingTxsCounter, + failedVerifyingStakingWithdrawalTxsCounter, + failedVerifyingUnbondingWithdrawalTxsCounter, ) } @@ -116,3 +155,27 @@ func RecordBtcClientMetrics[T any](clientRequest func() (T, error)) (T, error) { return result, err } + +func IncrementInvalidStakingWithdrawalTxCounter() { + invalidTransactionsCounter.WithLabelValues("withdraw_staking_transactions").Inc() +} + +func IncrementInvalidUnbondingWithdrawalTxCounter() { + invalidTransactionsCounter.WithLabelValues("withdraw_unbonding_transactions").Inc() +} + +func IncrementInvalidUnbondingTxCounter() { + invalidTransactionsCounter.WithLabelValues("unbonding_transactions").Inc() +} + +func IncrementFailedVerifyingUnbondingTxCounter() { + failedVerifyingUnbondingTxsCounter.Inc() +} + +func IncrementFailedVerifyingStakingWithdrawalTxCounter() { + failedVerifyingStakingWithdrawalTxsCounter.Inc() +} + +func IncrementFailedVerifyingUnbondingWithdrawalTxCounter() { + failedVerifyingUnbondingWithdrawalTxsCounter.Inc() +} diff --git a/internal/services/btc_subscriber.go b/internal/services/btc_subscriber.go index 8cbe5ac..d3558ed 100644 --- a/internal/services/btc_subscriber.go +++ b/internal/services/btc_subscriber.go @@ -25,8 +25,7 @@ func (s *Service) ProcessBTCSubscriber(ctx context.Context) *types.Error { // Process each delegation for _, delegation := range delegations { - // Check against local copy - no locks needed - if s.trackedSubs.IsSubscribed(delegation.StakingTxHashHex) { // Each iteration: RLock/RUnlock + if s.trackedSubs.IsSubscribed(delegation.StakingTxHashHex) { log.Debug(). Str("stakingTxHash", delegation.StakingTxHashHex). Msg("Delegation already subscribed, skipping") @@ -34,7 +33,6 @@ func (s *Service) ProcessBTCSubscriber(ctx context.Context) *types.Error { } err := s.registerStakingSpendNotification( - ctx, delegation.StakingTxHashHex, delegation.StakingTxHex, delegation.StakingOutputIdx, diff --git a/internal/services/watch_btc_events.go b/internal/services/watch_btc_events.go index 1766e1d..963ac2c 100644 --- a/internal/services/watch_btc_events.go +++ b/internal/services/watch_btc_events.go @@ -11,6 +11,7 @@ import ( "github.com/babylonlabs-io/babylon/btcstaking" bbn "github.com/babylonlabs-io/babylon/types" "github.com/babylonlabs-io/staking-expiry-checker/internal/db/model" + "github.com/babylonlabs-io/staking-expiry-checker/internal/observability/metrics" "github.com/babylonlabs-io/staking-expiry-checker/internal/types" "github.com/babylonlabs-io/staking-expiry-checker/internal/utils" "github.com/btcsuite/btcd/btcec/v2" @@ -114,20 +115,16 @@ func (s *Service) handleSpendingStakingTransaction( isUnbonding, err := s.IsValidUnbondingTx(spendingTx, delegation, params) if err != nil { if errors.Is(err, types.ErrInvalidUnbondingTx) { - // TODO: here - // invalidTransactionsCounter.WithLabelValues("confirmed_unbonding_transactions").Inc() - // si.logger.Warn("found an invalid unbonding tx", - // zap.String("tx_hash", tx.TxHash().String()), - // zap.Uint64("height", height), - // zap.Bool("is_confirmed", true), - // zap.Error(err), - // ) + metrics.IncrementInvalidUnbondingTxCounter() + log.Error(). + Err(err). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("found an invalid unbonding tx") return nil } - // record metrics - // failedVerifyingUnbondingTxsCounter.Inc() - // return err + + metrics.IncrementFailedVerifyingUnbondingTxCounter() return fmt.Errorf("failed to validate unbonding tx: %w", err) } if isUnbonding { @@ -150,25 +147,23 @@ func (s *Service) handleSpendingStakingTransaction( } // Register unbonding spend notification - return s.registerUnbondingSpendNotification(ctx, delegation) + return s.registerUnbondingSpendNotification(delegation) } // Try to validate as withdrawal transaction withdrawalErr := s.validateWithdrawalTxFromStaking(spendingTx, spendingInputIdx, delegation, params) if withdrawalErr != nil { if errors.Is(err, types.ErrInvalidWithdrawalTx) { - // invalidTransactionsCounter.WithLabelValues("confirmed_withdraw_staking_transactions").Inc() - // si.logger.Warn("found an invalid withdrawal tx from staking", - // zap.String("tx_hash", tx.TxHash().String()), - // zap.Uint64("height", height), - // zap.Bool("is_confirmed", true), - // zap.Error(err), - // ) + metrics.IncrementInvalidStakingWithdrawalTxCounter() + log.Error(). + Err(withdrawalErr). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("found an invalid withdrawal tx from staking") return nil } - //failedProcessingWithdrawTxsFromStakingCounter.Inc() + metrics.IncrementFailedVerifyingStakingWithdrawalTxCounter() return err } @@ -203,16 +198,16 @@ func (s *Service) handleSpendingUnbondingTransaction( withdrawalErr := s.validateWithdrawalTxFromUnbonding(spendingTx, delegation, spendingInputIdx, params) if withdrawalErr != nil { if errors.Is(withdrawalErr, types.ErrInvalidWithdrawalTx) { - // invalidTransactionsCounter.WithLabelValues("confirmed_withdraw_staking_transactions").Inc() - // si.logger.Warn("found an invalid withdrawal tx from staking", - // zap.String("tx_hash", tx.TxHash().String()), - // zap.Uint64("height", height), - // zap.Bool("is_confirmed", true), - // zap.Error(err), - // ) + metrics.IncrementInvalidUnbondingWithdrawalTxCounter() + log.Error(). + Err(withdrawalErr). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("found an invalid withdrawal tx from unbonding") return nil } + + metrics.IncrementFailedVerifyingUnbondingWithdrawalTxCounter() return fmt.Errorf("failed to validate withdrawal tx: %w", withdrawalErr) } @@ -229,8 +224,6 @@ func (s *Service) handleSpendingUnbondingTransaction( return ctx.Err() } - // todo: handle withdrawal here - return nil } @@ -520,7 +513,6 @@ func (s *Service) quitContext() (context.Context, func()) { } func (s *Service) registerStakingSpendNotification( - ctx context.Context, stakingTxHashHex string, stakingTxHex string, stakingOutputIdx uint32, @@ -557,7 +549,6 @@ func (s *Service) registerStakingSpendNotification( } func (s *Service) registerUnbondingSpendNotification( - ctx context.Context, delegation *model.BTCDelegationDetails, ) *types.Error { unbondingTxBytes, parseErr := hex.DecodeString(delegation.UnbondingTx) From 61a6bbe6b71f32532eeceb07eb465b3294c1f04a Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 12:20:03 +0530 Subject: [PATCH 14/35] handling withdrawn delegations --- cmd/staking-expiry-checker/main.go | 1 + internal/db/delegation.go | 11 +++ internal/db/interface.go | 4 ++ internal/poller/poller.go | 12 ---- internal/services/delegation_handlers.go | 91 ++++++++++++++++++++++++ internal/services/service.go | 78 ++------------------ internal/utils/state_transition.go | 19 +++-- tests/mocks/mock_db_client.go | 18 +++++ 8 files changed, 146 insertions(+), 88 deletions(-) create mode 100644 internal/services/delegation_handlers.go diff --git a/cmd/staking-expiry-checker/main.go b/cmd/staking-expiry-checker/main.go index 10c9928..12655a5 100644 --- a/cmd/staking-expiry-checker/main.go +++ b/cmd/staking-expiry-checker/main.go @@ -81,4 +81,5 @@ func main() { go btcSubscriberPoller.Start(ctx) go service.HandleUnbondingDelegationChannel(ctx) + go service.HandleWithdrawnDelegationChannel(ctx) } diff --git a/internal/db/delegation.go b/internal/db/delegation.go index c072ad3..6d46c34 100644 --- a/internal/db/delegation.go +++ b/internal/db/delegation.go @@ -32,6 +32,17 @@ func (db *Database) TransitionToUnbondingState( ) } +func (db *Database) TransitionToWithdrawnState(ctx context.Context, stakingTxHashHex string) error { + err := db.transitionState( + ctx, stakingTxHashHex, types.Withdrawn, + utils.QualifiedStatesToWithdraw(), + ) + if err != nil { + return err + } + return nil +} + // TransitionState updates the state of a staking transaction to a new state // It returns an NotFoundError if the staking transaction is not found or not // in the eligible state to transition diff --git a/internal/db/interface.go b/internal/db/interface.go index 7e4e51c..fde290d 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -29,6 +29,10 @@ type DbInterface interface { ctx context.Context, stakingTxHashHex string, ) error + TransitionToWithdrawnState( + ctx context.Context, + stakingTxHashHex string, + ) error GetBTCDelegationByStakingTxHash( ctx context.Context, stakingTxHash string, ) (*model.BTCDelegationDetails, error) diff --git a/internal/poller/poller.go b/internal/poller/poller.go index 5d0f9f8..a7f733b 100644 --- a/internal/poller/poller.go +++ b/internal/poller/poller.go @@ -26,8 +26,6 @@ const ( BTCSubscriberPoller PollerType = "btc-subscriber" ) -type PollerOperation func(ctx context.Context) *types.Error - type Poller struct { pollerType PollerType poll func(ctx context.Context) *types.Error @@ -87,13 +85,3 @@ func (p *Poller) Start(ctx context.Context) { func (p *Poller) Stop() { close(p.quit) } - -// func (p *Poller) poll(ctx context.Context) error { -// log.Debug().Msg("Polling started") -// if err := p.service.ProcessExpiredDelegations(ctx); err != nil { -// log.Error().Err(err).Msg("Error processing expired delegations") -// return err -// } -// log.Debug().Msg("Polling completed") -// return nil -// } diff --git a/internal/services/delegation_handlers.go b/internal/services/delegation_handlers.go new file mode 100644 index 0000000..f302dac --- /dev/null +++ b/internal/services/delegation_handlers.go @@ -0,0 +1,91 @@ +package services + +import ( + "context" + + "github.com/babylonlabs-io/staking-expiry-checker/internal/utils" + "github.com/rs/zerolog/log" +) + +// HandleUnbondingDelegationChannel processes unbonding delegations +func (s *Service) HandleUnbondingDelegationChannel(ctx context.Context) { + defer s.wg.Done() + for { + select { + case delegation := <-s.unbondingDelegationChan: + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("processing unbonding delegation") + + if utils.Contains(utils.OutdatedStatesForUnbonding(), delegation.State) { + // Ignore the message as the delegation state already passed the unbonding state. This is an outdated duplication + log.Ctx(ctx).Debug().Str("StakingTxHashHex", delegation.StakingTxHashHex). + Msg("delegation state is outdated for unbonding event") + continue + } + + if !utils.Contains(utils.QualifiedStatesToWithdraw(), delegation.State) { + errMsg := "delegation is not in the qualified state to transition to withdrawn" + log.Ctx(ctx).Warn().Str("stakingTxHashHex", delegation.StakingTxHashHex). + Str("state", delegation.State.ToString()).Msg(errMsg) + continue + } + + transitionErr := s.db.TransitionToUnbondingState( + ctx, delegation.StakingTxHashHex, + ) + if transitionErr != nil { + log.Error(). + Err(transitionErr). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("failed to transition to unbonding state") + } + + case <-ctx.Done(): + log.Info().Msg("stopping unbonding channel listener: context cancelled") + return + + case <-s.quit: + log.Info().Msg("stopping unbonding channel listener: service shutting down") + return + } + } +} + +// HandleWithdrawnDelegationChannel processes withdrawn delegations +func (s *Service) HandleWithdrawnDelegationChannel(ctx context.Context) { + defer s.wg.Done() + for { + select { + case delegation := <-s.withdrawnDelegationChan: + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("processing withdrawn delegation") + + if utils.Contains(utils.OutdatedStatesForWithdraw(), delegation.State) { + // Ignore the message as the delegation state is withdrawn. Nothing to do anymore + log.Ctx(ctx).Debug().Str("stakingTxHashHex", delegation.StakingTxHashHex). + Msg("delegation state is outdated for withdrawn event") + continue + } + + transitionErr := s.db.TransitionToWithdrawnState( + ctx, delegation.StakingTxHashHex, + ) + if transitionErr != nil { + log.Error(). + Err(transitionErr). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("failed to transition to withdrawn state") + } + + case <-ctx.Done(): + log.Info().Msg("stopping withdrawn channel listener: context cancelled") + return + + case <-s.quit: + log.Info().Msg("stopping withdrawn channel listener: service shutting down") + return + } + } +} diff --git a/internal/services/service.go b/internal/services/service.go index 5a6fec8..7e0221b 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -1,16 +1,13 @@ package services import ( - "context" "sync" "github.com/babylonlabs-io/staking-expiry-checker/internal/btcclient" "github.com/babylonlabs-io/staking-expiry-checker/internal/config" "github.com/babylonlabs-io/staking-expiry-checker/internal/db" "github.com/babylonlabs-io/staking-expiry-checker/internal/db/model" - "github.com/babylonlabs-io/staking-expiry-checker/internal/utils" notifier "github.com/lightningnetwork/lnd/chainntnfs" - "github.com/rs/zerolog/log" ) type Service struct { @@ -18,11 +15,16 @@ type Service struct { quit chan struct{} cfg *config.Config - db db.DbInterface btcNotifier notifier.ChainNotifier - btc btcclient.BtcInterface + + // interfaces + db db.DbInterface + btc btcclient.BtcInterface + + // in memory stores trackedSubs *TrackedSubscriptions + // channels unbondingDelegationChan chan *model.BTCDelegationDetails withdrawnDelegationChan chan *model.BTCDelegationDetails } @@ -44,69 +46,3 @@ func NewService( withdrawnDelegationChan: make(chan *model.BTCDelegationDetails, 100), // buffered } } - -// HandleUnbondingDelegationChannel processes unbonding delegations -func (s *Service) HandleUnbondingDelegationChannel(ctx context.Context) { - for { - select { - case delegation := <-s.unbondingDelegationChan: - log.Debug(). - Str("staking_tx", delegation.StakingTxHashHex). - Msg("processing unbonding delegation") - - if utils.Contains(utils.OutdatedStatesForUnbonding(), delegation.State) { - // Ignore the message as the delegation state already passed the unbonding state. This is an outdated duplication - log.Ctx(ctx).Debug().Str("StakingTxHashHex", delegation.StakingTxHashHex). - Msg("delegation state is outdated for unbonding event") - continue - } - - // Save the unbonding staking delegation. This is the final step in the unbonding staking event processing - // Please refer to the README.md for the details on the unbonding staking event processing workflow - transitionErr := s.db.TransitionToUnbondingState( - ctx, delegation.StakingTxHashHex, - ) - if transitionErr != nil { - log.Error(). - Err(transitionErr). - Str("staking_tx", delegation.StakingTxHashHex). - Msg("failed to transition to unbonding state") - } - - case <-ctx.Done(): - log.Info().Msg("stopping unbonding channel listener: context cancelled") - return - - case <-s.quit: - log.Info().Msg("stopping unbonding channel listener: service shutting down") - return - } - } -} - -// HandleWithdrawnDelegationChannel processes withdrawn delegations -func (s *Service) HandleWithdrawnDelegationChannel(ctx context.Context) { - for { - select { - case delegation := <-s.withdrawnDelegationChan: - log.Debug(). - Str("staking_tx", delegation.StakingTxHashHex). - Msg("processing withdrawn delegation") - - // if err := s.processWithdrawnDelegation(ctx, delegation); err != nil { - // log.Error(). - // Err(err). - // Str("staking_tx", delegation.StakingTxHashHex). - // Msg("failed to process withdrawn delegation") - // } - - case <-ctx.Done(): - log.Info().Msg("stopping withdrawn channel listener: context cancelled") - return - - case <-s.quit: - log.Info().Msg("stopping withdrawn channel listener: service shutting down") - return - } - } -} diff --git a/internal/utils/state_transition.go b/internal/utils/state_transition.go index e4ed46b..4b59035 100644 --- a/internal/utils/state_transition.go +++ b/internal/utils/state_transition.go @@ -4,17 +4,21 @@ import ( "github.com/babylonlabs-io/staking-expiry-checker/internal/types" ) +// List of states to be ignored for unbonding as it means it's already been processed +func OutdatedStatesForUnbonding() []types.DelegationState { + return []types.DelegationState{types.Unbonding, types.Unbonded, types.Withdrawn} +} + +func OutdatedStatesForWithdraw() []types.DelegationState { + return []types.DelegationState{types.Withdrawn} +} + // QualifiedStatesToUnbonding returns the qualified exisitng states to transition to "unbonding" // The Active state is allowed to directly transition to Unbonding without the need of UnbondingRequested due to bootstrap usecase func QualifiedStatesToUnbonding() []types.DelegationState { return []types.DelegationState{types.Active, types.UnbondingRequested} } -// List of states to be ignored for unbonding as it means it's already been processed -func OutdatedStatesForUnbonding() []types.DelegationState { - return []types.DelegationState{types.Unbonding, types.Unbonded, types.Withdrawn} -} - // QualifiedStatesToUnbonded returns the qualified exisitng states to transition to "unbonded" func QualifiedStatesToUnbonded(unbondTxType types.StakingTxType) []types.DelegationState { switch unbondTxType { @@ -26,3 +30,8 @@ func QualifiedStatesToUnbonded(unbondTxType types.StakingTxType) []types.Delegat return nil } } + +// QualifiedStatesToWithdrawn returns the qualified exisitng states to transition to "withdrawn" +func QualifiedStatesToWithdraw() []types.DelegationState { + return []types.DelegationState{types.Unbonded} +} diff --git a/tests/mocks/mock_db_client.go b/tests/mocks/mock_db_client.go index 7032261..91e7dfd 100644 --- a/tests/mocks/mock_db_client.go +++ b/tests/mocks/mock_db_client.go @@ -229,6 +229,24 @@ func (_m *DbInterface) TransitionToUnbondingState(ctx context.Context, stakingTx return r0 } +// TransitionToWithdrawnState provides a mock function with given fields: ctx, stakingTxHashHex +func (_m *DbInterface) TransitionToWithdrawnState(ctx context.Context, stakingTxHashHex string) error { + ret := _m.Called(ctx, stakingTxHashHex) + + if len(ret) == 0 { + panic("no return value specified for TransitionToWithdrawnState") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, stakingTxHashHex) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // NewDbInterface creates a new instance of DbInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewDbInterface(t interface { From 52ef5abdf7e3178eb0a4e67ad3702c1a42e23128 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 12:24:09 +0530 Subject: [PATCH 15/35] fix msgs --- internal/services/delegation_handlers.go | 25 +++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/internal/services/delegation_handlers.go b/internal/services/delegation_handlers.go index f302dac..7c032fd 100644 --- a/internal/services/delegation_handlers.go +++ b/internal/services/delegation_handlers.go @@ -18,16 +18,16 @@ func (s *Service) HandleUnbondingDelegationChannel(ctx context.Context) { Msg("processing unbonding delegation") if utils.Contains(utils.OutdatedStatesForUnbonding(), delegation.State) { - // Ignore the message as the delegation state already passed the unbonding state. This is an outdated duplication - log.Ctx(ctx).Debug().Str("StakingTxHashHex", delegation.StakingTxHashHex). - Msg("delegation state is outdated for unbonding event") + debugMsg := "delegation state is outdated for unbonding event" + log.Ctx(ctx).Debug().Str("stakingTxHashHex", delegation.StakingTxHashHex). + Msg(debugMsg) continue } - if !utils.Contains(utils.QualifiedStatesToWithdraw(), delegation.State) { - errMsg := "delegation is not in the qualified state to transition to withdrawn" - log.Ctx(ctx).Warn().Str("stakingTxHashHex", delegation.StakingTxHashHex). - Str("state", delegation.State.ToString()).Msg(errMsg) + if !utils.Contains(utils.QualifiedStatesToUnbonding(), delegation.State) { + debugMsg := "delegation is not in the qualified state to transition to unbonding" + log.Ctx(ctx).Debug().Str("stakingTxHashHex", delegation.StakingTxHashHex). + Str("state", delegation.State.ToString()).Msg(debugMsg) continue } @@ -63,9 +63,16 @@ func (s *Service) HandleWithdrawnDelegationChannel(ctx context.Context) { Msg("processing withdrawn delegation") if utils.Contains(utils.OutdatedStatesForWithdraw(), delegation.State) { - // Ignore the message as the delegation state is withdrawn. Nothing to do anymore + debugMsg := "delegation state is outdated for withdrawn event" + log.Ctx(ctx).Debug().Str("stakingTxHashHex", delegation.StakingTxHashHex). + Msg(debugMsg) + continue + } + + if !utils.Contains(utils.QualifiedStatesToWithdraw(), delegation.State) { + debugMsg := "delegation is not in the qualified state to transition to withdrawn" log.Ctx(ctx).Debug().Str("stakingTxHashHex", delegation.StakingTxHashHex). - Msg("delegation state is outdated for withdrawn event") + Str("state", delegation.State.ToString()).Msg(debugMsg) continue } From fa8f26986cc888cc5f09c4ca9c331c41e1391528 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 12:59:06 +0530 Subject: [PATCH 16/35] fix methods --- cmd/staking-expiry-checker/main.go | 43 +++++++++-------- internal/db/dbclient.go | 4 ++ internal/poller/poller.go | 2 - internal/services/expiry_checker.go | 75 ++++++++++++++--------------- 4 files changed, 63 insertions(+), 61 deletions(-) diff --git a/cmd/staking-expiry-checker/main.go b/cmd/staking-expiry-checker/main.go index 12655a5..b24930f 100644 --- a/cmd/staking-expiry-checker/main.go +++ b/cmd/staking-expiry-checker/main.go @@ -3,6 +3,9 @@ package main import ( "context" "fmt" + "os" + "os/signal" + "syscall" "github.com/joho/godotenv" "github.com/rs/zerolog/log" @@ -23,25 +26,31 @@ func init() { } func main() { - ctx := context.Background() + // Create a context that is cancelled on SIGINT or SIGTERM + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() - // setup cli commands and flags + // Setup signal handling + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + + // Setup CLI commands and flags if err := cli.Setup(); err != nil { log.Fatal().Err(err).Msg("error while setting up cli") } - // load config + // Load config cfgPath := cli.GetConfigPath() cfg, err := config.New(cfgPath) if err != nil { log.Fatal().Err(err).Msg(fmt.Sprintf("error while loading config file: %s", cfgPath)) } - // initialize metrics with the metrics port from config + // Initialize metrics with the metrics port from config metricsPort := cfg.Metrics.GetMetricsPort() metrics.Init(metricsPort) - // create new db client + // Create new DB client dbClient, err := db.New(ctx, cfg.Db) if err != nil { log.Fatal().Err(err).Msg("error while creating db client") @@ -65,21 +74,17 @@ func main() { log.Fatal().Err(err).Msg("error while creating service") } - // Even though we pass service, it's viewed only through the specific interface - expiryPoller := poller.NewExpiryPoller( - cfg.Pollers.ExpiryChecker, - service, // service implements ExpiryChecker - ) - - btcSubscriberPoller := poller.NewBTCSubscriberPoller( - cfg.Pollers.BtcSubscriber, - service, // service implements BTCSubscriber - ) - - // Start pollers in separate goroutines - go expiryPoller.Start(ctx) - go btcSubscriberPoller.Start(ctx) + // Start pollers + go poller.NewExpiryPoller(cfg.Pollers.ExpiryChecker, service).Start(ctx) + go poller.NewBTCSubscriberPoller(cfg.Pollers.BtcSubscriber, service).Start(ctx) + // Start service handlers go service.HandleUnbondingDelegationChannel(ctx) go service.HandleWithdrawnDelegationChannel(ctx) + + // Wait for a signal to shutdown + <-sigChan + + // Cancel the context to signal all goroutines to stop + cancel() } diff --git a/internal/db/dbclient.go b/internal/db/dbclient.go index da5cb4a..b4b244c 100644 --- a/internal/db/dbclient.go +++ b/internal/db/dbclient.go @@ -38,3 +38,7 @@ func (db *Database) Ping(ctx context.Context) error { } return nil } + +func (db *Database) Shutdown(ctx context.Context) error { + return db.client.Disconnect(ctx) +} diff --git a/internal/poller/poller.go b/internal/poller/poller.go index a7f733b..1a91ae7 100644 --- a/internal/poller/poller.go +++ b/internal/poller/poller.go @@ -10,7 +10,6 @@ import ( "github.com/babylonlabs-io/staking-expiry-checker/internal/types" ) -// Define minimal interfaces for each poller type type ExpiryChecker interface { ProcessExpiredDelegations(ctx context.Context) *types.Error } @@ -34,7 +33,6 @@ type Poller struct { quit chan struct{} } -// Constructors now accept interfaces instead of the full service func NewExpiryPoller(cfg config.PollerConfig, checker ExpiryChecker) *Poller { return &Poller{ pollerType: ExpiryPoller, diff --git a/internal/services/expiry_checker.go b/internal/services/expiry_checker.go index 0b3a4f5..db5fc14 100644 --- a/internal/services/expiry_checker.go +++ b/internal/services/expiry_checker.go @@ -9,22 +9,22 @@ import ( "github.com/rs/zerolog/log" ) -// ProcessExpireCheck checks if the staking delegation has expired and updates the database. -// This method tolerate duplicated calls on the same stakingTxHashHex. -func (s *Service) ProcessExpireCheck( - ctx context.Context, stakingTxHashHex string, - startHeight, timelock uint64, txType types.StakingTxType, -) *types.Error { - expireHeight := startHeight + timelock - err := s.db.SaveTimeLockExpireCheck( - ctx, stakingTxHashHex, expireHeight, txType.ToString(), - ) - if err != nil { - log.Ctx(ctx).Err(err).Msg("Failed to save expire check") - return types.NewInternalServiceError(err) - } - return nil -} +// // ProcessExpireCheck checks if the staking delegation has expired and updates the database. +// // This method tolerate duplicated calls on the same stakingTxHashHex. +// func (s *Service) ProcessExpireCheck( +// ctx context.Context, stakingTxHashHex string, +// startHeight, timelock uint64, txType types.StakingTxType, +// ) *types.Error { +// expireHeight := startHeight + timelock +// err := s.db.SaveTimeLockExpireCheck( +// ctx, stakingTxHashHex, expireHeight, txType.ToString(), +// ) +// if err != nil { +// log.Ctx(ctx).Err(err).Msg("Failed to save expire check") +// return types.NewInternalServiceError(err) +// } +// return nil +// } func (s *Service) ProcessExpiredDelegations(ctx context.Context) *types.Error { btcTip, err := s.btc.GetBlockCount() @@ -33,38 +33,33 @@ func (s *Service) ProcessExpiredDelegations(ctx context.Context) *types.Error { return types.NewInternalServiceError(err) } - for { - expiredDelegations, err := s.db.FindExpiredDelegations(ctx, uint64(btcTip)) - if err != nil { - log.Error().Err(err).Msg("Error finding expired delegations") - return types.NewInternalServiceError(err) - } - if len(expiredDelegations) == 0 { - break - } + // Single batch of expired delegations + expiredDelegations, err := s.db.FindExpiredDelegations(ctx, uint64(btcTip)) + if err != nil { + log.Error().Err(err).Msg("Error finding expired delegations") + return types.NewInternalServiceError(err) + } - for _, delegation := range expiredDelegations { - err := s.ProcessExpiredDelegation(ctx, delegation) - if err != nil { - log.Error().Err(err).Msgf("Error processing expired delegation: %v", delegation.ID) - return err - } + // Process each delegation in the batch + for _, delegation := range expiredDelegations { + if err := s.transitionToUnbondedIfEligible(ctx, delegation); err != nil { + log.Error().Err(err). + Msgf("Error transitioning delegation to unbonded: %v", delegation.ID) + return err + } - // After successfully sending the event, delete the entry from the database. - if err := s.db.DeleteExpiredDelegation(ctx, delegation.ID); err != nil { - log.Error().Err(err).Msg("Error deleting expired delegation") - return types.NewInternalServiceError(err) - } + if err := s.db.DeleteExpiredDelegation(ctx, delegation.ID); err != nil { + log.Error().Err(err).Msg("Error deleting expired delegation") + return types.NewInternalServiceError(err) } } return nil } -// ProcessExpiredDelegation processes an expired delegation by -// transitioning it to unbonded. -// Do nothing if the delegation is not in an eligible state to transition. -func (s *Service) ProcessExpiredDelegation( +// transitionToUnbondedIfEligible attempts to transition a delegation to unbonded state +// if it's in an eligible state. +func (s *Service) transitionToUnbondedIfEligible( ctx context.Context, delegation model.TimeLockDocument, ) *types.Error { // Check what type of the timelock is From 5f7a1a9c408a05605e91d1a0fd5bde768c245c27 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 13:38:00 +0530 Subject: [PATCH 17/35] fix channel handlers --- internal/db/delegation.go | 10 +++++ internal/db/interface.go | 1 + internal/services/delegation_handlers.go | 37 +++++++++++++++++-- internal/services/expiry_checker.go | 32 ++++++++-------- internal/services/service.go | 10 ++--- internal/services/watch_btc_events.go | 47 ++++++------------------ internal/types/delegation_events.go | 23 ++++++++++++ internal/utils/utils.go | 8 ++++ tests/mocks/mock_db_client.go | 30 +++++++++++++++ 9 files changed, 137 insertions(+), 61 deletions(-) create mode 100644 internal/types/delegation_events.go diff --git a/internal/db/delegation.go b/internal/db/delegation.go index 6d46c34..a031858 100644 --- a/internal/db/delegation.go +++ b/internal/db/delegation.go @@ -126,3 +126,13 @@ func (db *Database) GetBTCDelegationsByStates( return delegations, nil } + +func (db *Database) GetBTCDelegationState( + ctx context.Context, stakingTxHash string, +) (*types.DelegationState, error) { + delegation, err := db.GetBTCDelegationByStakingTxHash(ctx, stakingTxHash) + if err != nil { + return nil, err + } + return &delegation.State, nil +} diff --git a/internal/db/interface.go b/internal/db/interface.go index fde290d..4b4901d 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -38,4 +38,5 @@ type DbInterface interface { ) (*model.BTCDelegationDetails, error) GetStakingParams(ctx context.Context, version uint32) (*model.StakingParams, error) GetBTCDelegationsByStates(ctx context.Context, states []types.DelegationState) ([]*model.BTCDelegationDetails, error) + GetBTCDelegationState(ctx context.Context, stakingTxHash string) (*types.DelegationState, error) } diff --git a/internal/services/delegation_handlers.go b/internal/services/delegation_handlers.go index 7c032fd..de756d6 100644 --- a/internal/services/delegation_handlers.go +++ b/internal/services/delegation_handlers.go @@ -3,6 +3,7 @@ package services import ( "context" + "github.com/babylonlabs-io/staking-expiry-checker/internal/types" "github.com/babylonlabs-io/staking-expiry-checker/internal/utils" "github.com/rs/zerolog/log" ) @@ -12,11 +13,20 @@ func (s *Service) HandleUnbondingDelegationChannel(ctx context.Context) { defer s.wg.Done() for { select { - case delegation := <-s.unbondingDelegationChan: + case event := <-s.unbondingDelegationChan: log.Debug(). - Str("staking_tx", delegation.StakingTxHashHex). + Str("staking_tx", event.StakingTxHashHex). Msg("processing unbonding delegation") + delegation, err := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHashHex) + if err != nil { + log.Error(). + Err(err). + Str("staking_tx", event.StakingTxHashHex). + Msg("failed to get delegation") + continue + } + if utils.Contains(utils.OutdatedStatesForUnbonding(), delegation.State) { debugMsg := "delegation state is outdated for unbonding event" log.Ctx(ctx).Debug().Str("stakingTxHashHex", delegation.StakingTxHashHex). @@ -31,6 +41,16 @@ func (s *Service) HandleUnbondingDelegationChannel(ctx context.Context) { continue } + unbondingStartHeight := uint64(event.UnbondingStartHeight) + + expireCheckErr := s.SaveNewTimeLockExpire(ctx, delegation.StakingTxHashHex, unbondingStartHeight, uint64(delegation.UnbondingTime), types.UnbondingTxType) + if expireCheckErr != nil { + log.Error().Err(expireCheckErr). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("failed to process expire check") + continue + } + transitionErr := s.db.TransitionToUnbondingState( ctx, delegation.StakingTxHashHex, ) @@ -57,11 +77,20 @@ func (s *Service) HandleWithdrawnDelegationChannel(ctx context.Context) { defer s.wg.Done() for { select { - case delegation := <-s.withdrawnDelegationChan: + case event := <-s.withdrawnDelegationChan: log.Debug(). - Str("staking_tx", delegation.StakingTxHashHex). + Str("staking_tx", event.StakingTxHashHex). Msg("processing withdrawn delegation") + delegation, err := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHashHex) + if err != nil { + log.Error(). + Err(err). + Str("staking_tx", event.StakingTxHashHex). + Msg("failed to get delegation") + continue + } + if utils.Contains(utils.OutdatedStatesForWithdraw(), delegation.State) { debugMsg := "delegation state is outdated for withdrawn event" log.Ctx(ctx).Debug().Str("stakingTxHashHex", delegation.StakingTxHashHex). diff --git a/internal/services/expiry_checker.go b/internal/services/expiry_checker.go index db5fc14..b64a362 100644 --- a/internal/services/expiry_checker.go +++ b/internal/services/expiry_checker.go @@ -9,22 +9,22 @@ import ( "github.com/rs/zerolog/log" ) -// // ProcessExpireCheck checks if the staking delegation has expired and updates the database. -// // This method tolerate duplicated calls on the same stakingTxHashHex. -// func (s *Service) ProcessExpireCheck( -// ctx context.Context, stakingTxHashHex string, -// startHeight, timelock uint64, txType types.StakingTxType, -// ) *types.Error { -// expireHeight := startHeight + timelock -// err := s.db.SaveTimeLockExpireCheck( -// ctx, stakingTxHashHex, expireHeight, txType.ToString(), -// ) -// if err != nil { -// log.Ctx(ctx).Err(err).Msg("Failed to save expire check") -// return types.NewInternalServiceError(err) -// } -// return nil -// } +// SaveNewTimeLockExpire checks if the staking delegation has expired and updates the database. +// This method tolerate duplicated calls on the same stakingTxHashHex. +func (s *Service) SaveNewTimeLockExpire( + ctx context.Context, stakingTxHashHex string, + startHeight, timelock uint64, txType types.StakingTxType, +) *types.Error { + expireHeight := startHeight + timelock + err := s.db.SaveTimeLockExpireCheck( + ctx, stakingTxHashHex, expireHeight, txType.ToString(), + ) + if err != nil { + log.Ctx(ctx).Err(err).Msg("Failed to save expire check") + return types.NewInternalServiceError(err) + } + return nil +} func (s *Service) ProcessExpiredDelegations(ctx context.Context) *types.Error { btcTip, err := s.btc.GetBlockCount() diff --git a/internal/services/service.go b/internal/services/service.go index 7e0221b..1cc9162 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -6,7 +6,7 @@ import ( "github.com/babylonlabs-io/staking-expiry-checker/internal/btcclient" "github.com/babylonlabs-io/staking-expiry-checker/internal/config" "github.com/babylonlabs-io/staking-expiry-checker/internal/db" - "github.com/babylonlabs-io/staking-expiry-checker/internal/db/model" + "github.com/babylonlabs-io/staking-expiry-checker/internal/types" notifier "github.com/lightningnetwork/lnd/chainntnfs" ) @@ -25,8 +25,8 @@ type Service struct { trackedSubs *TrackedSubscriptions // channels - unbondingDelegationChan chan *model.BTCDelegationDetails - withdrawnDelegationChan chan *model.BTCDelegationDetails + unbondingDelegationChan chan *types.UnbondingDelegationEvent + withdrawnDelegationChan chan *types.WithdrawnDelegationEvent } func NewService( @@ -42,7 +42,7 @@ func NewService( btcNotifier: btcNotifier, btc: btc, trackedSubs: NewTrackedSubscriptions(), - unbondingDelegationChan: make(chan *model.BTCDelegationDetails, 100), // buffered - withdrawnDelegationChan: make(chan *model.BTCDelegationDetails, 100), // buffered + unbondingDelegationChan: make(chan *types.UnbondingDelegationEvent, 100), // buffered + withdrawnDelegationChan: make(chan *types.WithdrawnDelegationEvent, 100), // buffered } } diff --git a/internal/services/watch_btc_events.go b/internal/services/watch_btc_events.go index 963ac2c..4bee4be 100644 --- a/internal/services/watch_btc_events.go +++ b/internal/services/watch_btc_events.go @@ -40,6 +40,7 @@ func (s *Service) watchForSpendStakingTx( if err := s.handleSpendingStakingTransaction( quitCtx, spendDetail.SpendingTx, + uint32(spendDetail.SpendingHeight), spendDetail.SpenderInputIndex, stakingTxHashHex, ); err != nil { @@ -98,6 +99,7 @@ func (s *Service) watchForSpendUnbondingTx( func (s *Service) handleSpendingStakingTransaction( ctx context.Context, spendingTx *wire.MsgTx, + spendingHeight, spendingInputIdx uint32, stakingTxHashHex string, ) error { @@ -133,18 +135,11 @@ func (s *Service) handleSpendingStakingTransaction( Str("unbonding_tx", spendingTx.TxHash().String()). Msg("staking tx has been spent through unbonding path") - // Blocking send - will wait if channel is full - select { - case s.unbondingDelegationChan <- delegation: - log.Debug(). - Str("staking_tx", delegation.StakingTxHashHex). - Msg("sent delegation to unbonding handler") - case <-ctx.Done(): - log.Error(). - Str("staking_tx", delegation.StakingTxHashHex). - Msg("context cancelled while waiting to send to unbonding channel") - return ctx.Err() - } + unbondingEvent := types.NewUnbondingDelegationEvent( + delegation.StakingTxHashHex, + spendingHeight, + ) + utils.PushOrQuit(s.unbondingDelegationChan, unbondingEvent, s.quit) // Register unbonding spend notification return s.registerUnbondingSpendNotification(delegation) @@ -167,18 +162,8 @@ func (s *Service) handleSpendingStakingTransaction( return err } - // Blocking send - will wait if channel is full - select { - case s.withdrawnDelegationChan <- delegation: - log.Debug(). - Str("staking_tx", delegation.StakingTxHashHex). - Msg("sent delegation to withdrawn handler") - case <-ctx.Done(): - log.Error(). - Str("staking_tx", delegation.StakingTxHashHex). - Msg("context cancelled while waiting to send to withdrawn channel") - return ctx.Err() - } + withdrawnEvent := types.NewWithdrawnDelegationEvent(delegation.StakingTxHashHex) + utils.PushOrQuit(s.withdrawnDelegationChan, withdrawnEvent, s.quit) return nil } @@ -211,18 +196,8 @@ func (s *Service) handleSpendingUnbondingTransaction( return fmt.Errorf("failed to validate withdrawal tx: %w", withdrawalErr) } - // Blocking send - will wait if channel is full - select { - case s.withdrawnDelegationChan <- delegation: - log.Debug(). - Str("staking_tx", delegation.StakingTxHashHex). - Msg("sent delegation to withdrawn handler") - case <-ctx.Done(): - log.Error(). - Str("staking_tx", delegation.StakingTxHashHex). - Msg("context cancelled while waiting to send to withdrawn channel") - return ctx.Err() - } + withdrawnEvent := types.NewWithdrawnDelegationEvent(delegation.StakingTxHashHex) + utils.PushOrQuit(s.withdrawnDelegationChan, withdrawnEvent, s.quit) return nil } diff --git a/internal/types/delegation_events.go b/internal/types/delegation_events.go new file mode 100644 index 0000000..c0e9480 --- /dev/null +++ b/internal/types/delegation_events.go @@ -0,0 +1,23 @@ +package types + +type UnbondingDelegationEvent struct { + StakingTxHashHex string + UnbondingStartHeight uint32 +} + +type WithdrawnDelegationEvent struct { + StakingTxHashHex string +} + +func NewUnbondingDelegationEvent(stakingTxHashHex string, unbondingStartHeight uint32) *UnbondingDelegationEvent { + return &UnbondingDelegationEvent{ + StakingTxHashHex: stakingTxHashHex, + UnbondingStartHeight: unbondingStartHeight, + } +} + +func NewWithdrawnDelegationEvent(stakingTxHashHex string) *WithdrawnDelegationEvent { + return &WithdrawnDelegationEvent{ + StakingTxHashHex: stakingTxHashHex, + } +} diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 9810d2a..bf7733d 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -108,3 +108,11 @@ func Contains[T comparable](slice []T, element T) bool { } return false } + +// push msg to channel c, or quit if quit channel is closed +func PushOrQuit[T any](c chan<- T, msg T, quit <-chan struct{}) { + select { + case c <- msg: + case <-quit: + } +} diff --git a/tests/mocks/mock_db_client.go b/tests/mocks/mock_db_client.go index 91e7dfd..da0898c 100644 --- a/tests/mocks/mock_db_client.go +++ b/tests/mocks/mock_db_client.go @@ -97,6 +97,36 @@ func (_m *DbInterface) GetBTCDelegationByStakingTxHash(ctx context.Context, stak return r0, r1 } +// GetBTCDelegationState provides a mock function with given fields: ctx, stakingTxHash +func (_m *DbInterface) GetBTCDelegationState(ctx context.Context, stakingTxHash string) (*types.DelegationState, error) { + ret := _m.Called(ctx, stakingTxHash) + + if len(ret) == 0 { + panic("no return value specified for GetBTCDelegationState") + } + + var r0 *types.DelegationState + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*types.DelegationState, error)); ok { + return rf(ctx, stakingTxHash) + } + if rf, ok := ret.Get(0).(func(context.Context, string) *types.DelegationState); ok { + r0 = rf(ctx, stakingTxHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.DelegationState) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, stakingTxHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetBTCDelegationsByStates provides a mock function with given fields: ctx, states func (_m *DbInterface) GetBTCDelegationsByStates(ctx context.Context, states []types.DelegationState) ([]*model.BTCDelegationDetails, error) { ret := _m.Called(ctx, states) From 6cf69f11644390e41adc3213688c34e6ad166d13 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 15:01:55 +0530 Subject: [PATCH 18/35] bump workflows --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdc2b15..cd76d09 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: jobs: lint_test: - uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.6.0 + uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.7.0 with: run-unit-tests: true run-integration-tests: false @@ -17,7 +17,7 @@ jobs: gosec-args: "-no-fail ./..." docker_pipeline: - uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.6.0 + uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.7.0 secrets: inherit with: publish: false From 8eb710edd395f346d66f0d6503a02c1569d0b098 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 15:51:27 +0530 Subject: [PATCH 19/35] fix gh wf --- .github/workflows/ci.yml | 16 +++++++++------- .github/workflows/publish.yml | 2 ++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd76d09..97d6d99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,13 +9,15 @@ jobs: lint_test: uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.7.0 with: - run-unit-tests: true - run-integration-tests: false - run-lint: true - run-build: true - run-gosec: true - gosec-args: "-no-fail ./..." - + go-version: '1.23' + go-lint-version: 'v1.60.2' + run-unit-tests: true + run-integration-tests: false + run-lint: true + run-build: true + run-gosec: true + gosec-args: "-no-fail ./..." + docker_pipeline: uses: babylonlabs-io/.github/.github/workflows/reusable_docker_pipeline.yml@v0.7.0 secrets: inherit diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 52e732b..1d59766 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,6 +11,8 @@ jobs: lint_test: uses: babylonlabs-io/.github/.github/workflows/reusable_go_lint_test.yml@v0.6.0 with: + go-version: '1.23' + go-lint-version: 'v1.60.2' run-unit-tests: true run-integration-tests: false run-lint: true From 3859f30dccbedd5e30da39c2998a292e7f865c00 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 23:32:55 +0530 Subject: [PATCH 20/35] global params --- internal/db/delegation.go | 8 +-- internal/db/interface.go | 4 +- internal/db/model/delegation.go | 41 +++++------- internal/services/btc_subscriber.go | 6 +- internal/services/delegation_handlers.go | 2 +- internal/services/params.go | 20 ++++++ internal/services/service.go | 3 + internal/services/watch_btc_events.go | 83 ++++++++++++------------ internal/types/global_params.go | 51 +++++++++++++++ 9 files changed, 141 insertions(+), 77 deletions(-) create mode 100644 internal/services/params.go create mode 100644 internal/types/global_params.go diff --git a/internal/db/delegation.go b/internal/db/delegation.go index a031858..47e61ab 100644 --- a/internal/db/delegation.go +++ b/internal/db/delegation.go @@ -77,14 +77,14 @@ func (db *Database) transitionState( func (db *Database) GetBTCDelegationByStakingTxHash( ctx context.Context, stakingTxHash string, -) (*model.BTCDelegationDetails, error) { +) (*model.DelegationDocument, error) { filter := bson.M{"_id": stakingTxHash} res := db.client.Database(db.dbName). Collection(model.DelegationsCollection). FindOne(ctx, filter) - var delegationDoc model.BTCDelegationDetails + var delegationDoc model.DelegationDocument err := res.Decode(&delegationDoc) if err != nil { if errors.Is(err, mongo.ErrNoDocuments) { @@ -102,7 +102,7 @@ func (db *Database) GetBTCDelegationByStakingTxHash( func (db *Database) GetBTCDelegationsByStates( ctx context.Context, states []types.DelegationState, -) ([]*model.BTCDelegationDetails, error) { +) ([]*model.DelegationDocument, error) { // Convert states to a slice of strings stateStrings := make([]string, len(states)) for i, state := range states { @@ -119,7 +119,7 @@ func (db *Database) GetBTCDelegationsByStates( } defer cursor.Close(ctx) - var delegations []*model.BTCDelegationDetails + var delegations []*model.DelegationDocument if err := cursor.All(ctx, &delegations); err != nil { return nil, err } diff --git a/internal/db/interface.go b/internal/db/interface.go index 4b4901d..1ba0a3d 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -35,8 +35,8 @@ type DbInterface interface { ) error GetBTCDelegationByStakingTxHash( ctx context.Context, stakingTxHash string, - ) (*model.BTCDelegationDetails, error) + ) (*model.DelegationDocument, error) GetStakingParams(ctx context.Context, version uint32) (*model.StakingParams, error) - GetBTCDelegationsByStates(ctx context.Context, states []types.DelegationState) ([]*model.BTCDelegationDetails, error) + GetBTCDelegationsByStates(ctx context.Context, states []types.DelegationState) ([]*model.DelegationDocument, error) GetBTCDelegationState(ctx context.Context, stakingTxHash string) (*types.DelegationState, error) } diff --git a/internal/db/model/delegation.go b/internal/db/model/delegation.go index 6f77d7b..5e91f11 100644 --- a/internal/db/model/delegation.go +++ b/internal/db/model/delegation.go @@ -2,32 +2,21 @@ package model import "github.com/babylonlabs-io/staking-expiry-checker/internal/types" -type CovenantSignature struct { - CovenantBtcPkHex string `bson:"covenant_btc_pk_hex"` - SignatureHex string `bson:"signature_hex"` +type TimelockTransaction struct { + TxHex string `bson:"tx_hex"` + OutputIndex uint64 `bson:"output_index"` + StartTimestamp int64 `bson:"start_timestamp"` + StartHeight uint64 `bson:"start_height"` + TimeLock uint64 `bson:"timelock"` } -type BTCDelegationCreatedBbnBlock struct { - Height int64 `bson:"height"` - Timestamp int64 `bson:"timestamp"` // epoch time in seconds -} - -type BTCDelegationDetails struct { - StakingTxHashHex string `bson:"_id"` // Primary key - StakingTxHex string `bson:"staking_tx_hex"` - StakingTime uint32 `bson:"staking_time"` - StakingAmount uint64 `bson:"staking_amount"` - StakingOutputIdx uint32 `bson:"staking_output_idx"` - StakerBtcPkHex string `bson:"staker_btc_pk_hex"` - FinalityProviderBtcPksHex []string `bson:"finality_provider_btc_pks_hex"` - StartHeight uint32 `bson:"start_height"` - EndHeight uint32 `bson:"end_height"` - State types.DelegationState `bson:"state"` - ParamsVersion uint32 `bson:"params_version"` - UnbondingTime uint32 `bson:"unbonding_time"` - UnbondingTx string `bson:"unbonding_tx"` - CovenantUnbondingSignatures []CovenantSignature `bson:"covenant_unbonding_signatures"` - BTCDelegationCreatedBlock BTCDelegationCreatedBbnBlock `bson:"btc_delegation_created_bbn_block"` - SlashingTxHex string `bson:"slashing_tx_hex"` // Will be "" if not slashed - UnbondingSlashingTxHex string `bson:"unbonding_slashing_tx_hex"` // Will be "" if not slashed +type DelegationDocument struct { + StakingTxHashHex string `bson:"_id"` // Primary key + StakerPkHex string `bson:"staker_pk_hex"` + FinalityProviderPkHex string `bson:"finality_provider_pk_hex"` + StakingValue uint64 `bson:"staking_value"` + State types.DelegationState `bson:"state"` + StakingTx *TimelockTransaction `bson:"staking_tx"` // Always exist + UnbondingTx *TimelockTransaction `bson:"unbonding_tx,omitempty"` + IsOverflow bool `bson:"is_overflow"` } diff --git a/internal/services/btc_subscriber.go b/internal/services/btc_subscriber.go index d3558ed..79ccbbb 100644 --- a/internal/services/btc_subscriber.go +++ b/internal/services/btc_subscriber.go @@ -34,9 +34,9 @@ func (s *Service) ProcessBTCSubscriber(ctx context.Context) *types.Error { err := s.registerStakingSpendNotification( delegation.StakingTxHashHex, - delegation.StakingTxHex, - delegation.StakingOutputIdx, - delegation.StartHeight, + delegation.StakingTx.TxHex, + uint32(delegation.StakingTx.OutputIndex), + uint32(delegation.StakingTx.StartHeight), ) if err != nil { log.Error(). diff --git a/internal/services/delegation_handlers.go b/internal/services/delegation_handlers.go index de756d6..0996dd7 100644 --- a/internal/services/delegation_handlers.go +++ b/internal/services/delegation_handlers.go @@ -43,7 +43,7 @@ func (s *Service) HandleUnbondingDelegationChannel(ctx context.Context) { unbondingStartHeight := uint64(event.UnbondingStartHeight) - expireCheckErr := s.SaveNewTimeLockExpire(ctx, delegation.StakingTxHashHex, unbondingStartHeight, uint64(delegation.UnbondingTime), types.UnbondingTxType) + expireCheckErr := s.SaveNewTimeLockExpire(ctx, delegation.StakingTxHashHex, unbondingStartHeight, uint64(delegation.UnbondingTx.TimeLock), types.UnbondingTxType) if expireCheckErr != nil { log.Error().Err(expireCheckErr). Str("staking_tx", delegation.StakingTxHashHex). diff --git a/internal/services/params.go b/internal/services/params.go new file mode 100644 index 0000000..4ff8f0c --- /dev/null +++ b/internal/services/params.go @@ -0,0 +1,20 @@ +package services + +import ( + "github.com/babylonlabs-io/staking-expiry-checker/internal/types" +) + +// GetVersionedGlobalParamsByHeight returns the versioned global params +// for a particular bitcoin height +func (s *Service) GetVersionedGlobalParamsByHeight(height uint64) *types.VersionedGlobalParams { + // Iterate the list in reverse (i.e. decreasing ActivationHeight) + // and identify the first element that has an activation height below + // the specified BTC height. + for i := len(s.params.Versions) - 1; i >= 0; i-- { + paramsVersion := s.params.Versions[i] + if paramsVersion.ActivationHeight <= height { + return paramsVersion + } + } + return nil +} diff --git a/internal/services/service.go b/internal/services/service.go index 1cc9162..75d6756 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -16,6 +16,7 @@ type Service struct { cfg *config.Config btcNotifier notifier.ChainNotifier + params *types.GlobalParams // interfaces db db.DbInterface @@ -31,6 +32,7 @@ type Service struct { func NewService( cfg *config.Config, + params *types.GlobalParams, db db.DbInterface, btcNotifier notifier.ChainNotifier, btc btcclient.BtcInterface, @@ -40,6 +42,7 @@ func NewService( cfg: cfg, db: db, btcNotifier: btcNotifier, + params: params, btc: btc, trackedSubs: NewTrackedSubscriptions(), unbondingDelegationChan: make(chan *types.UnbondingDelegationEvent, 100), // buffered diff --git a/internal/services/watch_btc_events.go b/internal/services/watch_btc_events.go index 4bee4be..80ceeaf 100644 --- a/internal/services/watch_btc_events.go +++ b/internal/services/watch_btc_events.go @@ -62,7 +62,7 @@ func (s *Service) watchForSpendStakingTx( func (s *Service) watchForSpendUnbondingTx( spendEvent *notifier.SpendEvent, - delegation *model.BTCDelegationDetails, + delegation *model.DelegationDocument, ) { defer s.wg.Done() quitCtx, cancel := s.quitContext() @@ -108,13 +108,17 @@ func (s *Service) handleSpendingStakingTransaction( return fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", err) } - params, err := s.db.GetStakingParams(ctx, delegation.ParamsVersion) - if err != nil { - return fmt.Errorf("failed to get staking params: %w", err) + paramsVersion := s.GetVersionedGlobalParamsByHeight(delegation.StakingTx.StartHeight) + if paramsVersion == nil { + log.Ctx(ctx).Error().Msg("failed to get global params") + return types.NewErrorWithMsg( + http.StatusInternalServerError, types.InternalServiceError, + "failed to get global params based on the staking tx height", + ) } // First try to validate as unbonding tx - isUnbonding, err := s.IsValidUnbondingTx(spendingTx, delegation, params) + isUnbonding, err := s.IsValidUnbondingTx(spendingTx, delegation, paramsVersion) if err != nil { if errors.Is(err, types.ErrInvalidUnbondingTx) { metrics.IncrementInvalidUnbondingTxCounter() @@ -172,7 +176,7 @@ func (s *Service) handleSpendingUnbondingTransaction( ctx context.Context, spendingTx *wire.MsgTx, spendingInputIdx uint32, - delegation *model.BTCDelegationDetails, + delegation *model.DelegationDocument, ) error { params, err := s.db.GetStakingParams(ctx, delegation.ParamsVersion) if err != nil { @@ -208,10 +212,10 @@ func (s *Service) handleSpendingUnbondingTransaction( // but is invalid func (s *Service) IsValidUnbondingTx( tx *wire.MsgTx, - delegation *model.BTCDelegationDetails, - params *model.StakingParams, + delegation *model.DelegationDocument, + params *types.VersionedGlobalParams, ) (bool, error) { - stakingTx, err := utils.DeserializeBtcTransactionFromHex(delegation.StakingTxHex) + stakingTx, err := utils.DeserializeBtcTransactionFromHex(delegation.StakingTx.TxHex) if err != nil { return false, fmt.Errorf("failed to deserialize staking tx: %w", err) } @@ -226,23 +230,20 @@ func (s *Service) IsValidUnbondingTx( if !tx.TxIn[0].PreviousOutPoint.Hash.IsEqual(&stakingTxHash) { return false, nil } - if tx.TxIn[0].PreviousOutPoint.Index != delegation.StakingOutputIdx { + if tx.TxIn[0].PreviousOutPoint.Index != uint32(delegation.StakingTx.OutputIndex) { return false, nil } - stakerPk, err := bbn.NewBIP340PubKeyFromHex(delegation.StakerBtcPkHex) + stakerPk, err := bbn.NewBIP340PubKeyFromHex(delegation.StakerPkHex) if err != nil { return false, fmt.Errorf("failed to convert staker btc pkh to a public key: %w", err) } - finalityProviderPks := make([]*btcec.PublicKey, len(delegation.FinalityProviderBtcPksHex)) - for i, hex := range delegation.FinalityProviderBtcPksHex { - fpPk, err := bbn.NewBIP340PubKeyFromHex(hex) - if err != nil { - return false, fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) - } - finalityProviderPks[i] = fpPk.MustToBTCPK() + fpPKBIP340, err := bbn.NewBIP340PubKeyFromHex(delegation.FinalityProviderPkHex) + if err != nil { + return false, fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) } + fpPK := fpPKBIP340.MustToBTCPK() covPks := make([]*btcec.PublicKey, len(params.CovenantPks)) for i, hex := range params.CovenantPks { @@ -255,16 +256,16 @@ func (s *Service) IsValidUnbondingTx( btcParams := utils.GetBTCParams(s.cfg.Btc.NetParams) - stakingValue := btcutil.Amount(stakingTx.TxOut[delegation.StakingOutputIdx].Value) + stakingValue := btcutil.Amount(stakingTx.TxOut[delegation.StakingTx.OutputIndex].Value) // 3. re-build the unbonding path script and check whether the script from // the witness matches stakingInfo, err := btcstaking.BuildStakingInfo( stakerPk.MustToBTCPK(), - finalityProviderPks, + []*btcec.PublicKey{fpPK}, covPks, - params.CovenantQuorum, - uint16(delegation.StakingTime), + uint32(params.CovenantQuorum), + uint16(delegation.StakingTx.TimeLock), stakingValue, btcParams, ) @@ -308,8 +309,8 @@ func (s *Service) IsValidUnbondingTx( stakerPk.MustToBTCPK(), finalityProviderPks, covPks, - params.CovenantQuorum, - uint16(delegation.UnbondingTime), + uint32(params.CovenantQuorum), + uint16(delegation.UnbondingTx.TimeLock), expectedUnbondingOutputValue, btcParams, ) @@ -330,16 +331,16 @@ func (s *Service) IsValidUnbondingTx( func (s *Service) validateWithdrawalTxFromStaking( tx *wire.MsgTx, spendingInputIdx uint32, - delegation *model.BTCDelegationDetails, + delegation *model.DelegationDocument, params *model.StakingParams, ) error { - stakerPk, err := bbn.NewBIP340PubKeyFromHex(delegation.StakerBtcPkHex) + stakerPk, err := bbn.NewBIP340PubKeyFromHex(delegation.StakerPkHex) if err != nil { return fmt.Errorf("failed to convert staker btc pkh to a public key: %w", err) } - finalityProviderPks := make([]*btcec.PublicKey, len(delegation.FinalityProviderBtcPksHex)) - for i, hex := range delegation.FinalityProviderBtcPksHex { + finalityProviderPks := make([]*btcec.PublicKey, len(delegation.FinalityProviderPkHex)) + for i, hex := range delegation.FinalityProviderPkHex { fpPk, err := bbn.NewBIP340PubKeyFromHex(hex) if err != nil { return fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) @@ -358,12 +359,12 @@ func (s *Service) validateWithdrawalTxFromStaking( btcParams := utils.GetBTCParams(s.cfg.Btc.NetParams) - stakingTx, err := utils.DeserializeBtcTransactionFromHex(delegation.StakingTxHex) + stakingTx, err := utils.DeserializeBtcTransactionFromHex(delegation.StakingTx.TxHex) if err != nil { return fmt.Errorf("failed to deserialize staking tx: %w", err) } - stakingValue := btcutil.Amount(stakingTx.TxOut[delegation.StakingOutputIdx].Value) + stakingValue := btcutil.Amount(stakingTx.TxOut[delegation.StakingTx.OutputIndex].Value) // 3. re-build the unbonding path script and check whether the script from // the witness matches @@ -372,7 +373,7 @@ func (s *Service) validateWithdrawalTxFromStaking( finalityProviderPks, covPks, params.CovenantQuorum, - uint16(delegation.StakingTime), + uint16(delegation.StakingTx.TimeLock), stakingValue, btcParams, ) @@ -401,17 +402,17 @@ func (s *Service) validateWithdrawalTxFromStaking( func (s *Service) validateWithdrawalTxFromUnbonding( tx *wire.MsgTx, - delegation *model.BTCDelegationDetails, + delegation *model.DelegationDocument, spendingInputIdx uint32, params *model.StakingParams, ) error { - stakerPk, err := bbn.NewBIP340PubKeyFromHex(delegation.StakerBtcPkHex) + stakerPk, err := bbn.NewBIP340PubKeyFromHex(delegation.StakerPkHex) if err != nil { return fmt.Errorf("failed to convert staker btc pkh to a public key: %w", err) } - finalityProviderPks := make([]*btcec.PublicKey, len(delegation.FinalityProviderBtcPksHex)) - for i, hex := range delegation.FinalityProviderBtcPksHex { + finalityProviderPks := make([]*btcec.PublicKey, len(delegation.FinalityProviderPkHex)) + for i, hex := range delegation.FinalityProviderPkHex { fpPk, err := bbn.NewBIP340PubKeyFromHex(hex) if err != nil { return fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) @@ -430,14 +431,14 @@ func (s *Service) validateWithdrawalTxFromUnbonding( btcParams := utils.GetBTCParams(s.cfg.Btc.NetParams) - stakingTx, err := utils.DeserializeBtcTransactionFromHex(delegation.StakingTxHex) + stakingTx, err := utils.DeserializeBtcTransactionFromHex(delegation.StakingTx.TxHex) if err != nil { return fmt.Errorf("failed to deserialize staking tx: %w", err) } // re-build the time-lock path script and check whether the script from // the witness matches - stakingValue := btcutil.Amount(stakingTx.TxOut[delegation.StakingOutputIdx].Value) + stakingValue := btcutil.Amount(stakingTx.TxOut[delegation.StakingTx.OutputIndex].Value) unbondingFee := btcutil.Amount(params.UnbondingFeeSat) expectedUnbondingOutputValue := stakingValue - unbondingFee unbondingInfo, err := btcstaking.BuildUnbondingInfo( @@ -445,7 +446,7 @@ func (s *Service) validateWithdrawalTxFromUnbonding( finalityProviderPks, covPks, params.CovenantQuorum, - uint16(delegation.UnbondingTime), + uint16(delegation.UnbondingTx.TimeLock), expectedUnbondingOutputValue, btcParams, ) @@ -524,9 +525,9 @@ func (s *Service) registerStakingSpendNotification( } func (s *Service) registerUnbondingSpendNotification( - delegation *model.BTCDelegationDetails, + delegation *model.DelegationDocument, ) *types.Error { - unbondingTxBytes, parseErr := hex.DecodeString(delegation.UnbondingTx) + unbondingTxBytes, parseErr := hex.DecodeString(delegation.UnbondingTx.TxHex) if parseErr != nil { return types.NewError( http.StatusInternalServerError, @@ -557,7 +558,7 @@ func (s *Service) registerUnbondingSpendNotification( spendEv, btcErr := s.btcNotifier.RegisterSpendNtfn( &unbondingOutpoint, unbondingTx.TxOut[0].PkScript, - delegation.StartHeight, + uint32(delegation.UnbondingTx.StartHeight), ) if btcErr != nil { return types.NewError( diff --git a/internal/types/global_params.go b/internal/types/global_params.go new file mode 100644 index 0000000..0e5bffa --- /dev/null +++ b/internal/types/global_params.go @@ -0,0 +1,51 @@ +package types + +import ( + "encoding/hex" + "encoding/json" + "os" + "path/filepath" + + "github.com/babylonlabs-io/networks/parameters/parser" + "github.com/btcsuite/btcd/btcec/v2" +) + +type VersionedGlobalParams = parser.VersionedGlobalParams + +type GlobalParams = parser.GlobalParams + +func NewGlobalParams(path string) (*GlobalParams, error) { + data, err := os.ReadFile(filepath.Clean(path)) + if err != nil { + return nil, err + } + + var globalParams GlobalParams + err = json.Unmarshal(data, &globalParams) + if err != nil { + return nil, err + } + + _, err = parser.ParseGlobalParams(&globalParams) + if err != nil { + return nil, err + } + + return &globalParams, nil +} + +// parseCovenantPubKeyFromHex parses public key string to btc public key +// the input should be 33 bytes +func parseCovenantPubKeyFromHex(pkStr string) (*btcec.PublicKey, error) { + pkBytes, err := hex.DecodeString(pkStr) + if err != nil { + return nil, err + } + + pk, err := btcec.ParsePubKey(pkBytes) + if err != nil { + return nil, err + } + + return pk, nil +} From 8c1b5ce570c98745fa8948bec507ab3983e7ade3 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 23:33:27 +0530 Subject: [PATCH 21/35] fix global params --- cmd/staking-expiry-checker/cli/root.go | 15 ++++++++++++--- cmd/staking-expiry-checker/main.go | 9 ++++++++- tests/mocks/mock_db_client.go | 20 ++++++++++---------- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/cmd/staking-expiry-checker/cli/root.go b/cmd/staking-expiry-checker/cli/root.go index 20faa8c..6df658d 100644 --- a/cmd/staking-expiry-checker/cli/root.go +++ b/cmd/staking-expiry-checker/cli/root.go @@ -9,12 +9,14 @@ import ( ) const ( - defaultConfigFileName = "config.yml" + defaultConfigFileName = "config.yml" + defaultGlobalParamsFileName = "global_params.json" ) var ( - cfgPath string - rootCmd = &cobra.Command{ + cfgPath string + globalParamsPath string + rootCmd = &cobra.Command{ Use: "start-server", } ) @@ -26,8 +28,11 @@ func Setup() error { } defaultConfigPath := getDefaultConfigFile(homePath, defaultConfigFileName) + defaultGlobalParamsPath := getDefaultConfigFile(homePath, defaultGlobalParamsFileName) rootCmd.PersistentFlags().StringVar(&cfgPath, "config", defaultConfigPath, fmt.Sprintf("config file (default %s)", defaultConfigPath)) + rootCmd.PersistentFlags().StringVar(&globalParamsPath, "params", defaultGlobalParamsPath, fmt.Sprintf("global params file (default %s)", defaultGlobalParamsPath)) + if err := rootCmd.Execute(); err != nil { return err } @@ -42,3 +47,7 @@ func getDefaultConfigFile(homePath, filename string) string { func GetConfigPath() string { return cfgPath } + +func GetGlobalParamsPath() string { + return globalParamsPath +} diff --git a/cmd/staking-expiry-checker/main.go b/cmd/staking-expiry-checker/main.go index b24930f..211b5bf 100644 --- a/cmd/staking-expiry-checker/main.go +++ b/cmd/staking-expiry-checker/main.go @@ -17,6 +17,7 @@ import ( "github.com/babylonlabs-io/staking-expiry-checker/internal/observability/metrics" "github.com/babylonlabs-io/staking-expiry-checker/internal/poller" "github.com/babylonlabs-io/staking-expiry-checker/internal/services" + "github.com/babylonlabs-io/staking-expiry-checker/internal/types" ) func init() { @@ -46,6 +47,12 @@ func main() { log.Fatal().Err(err).Msg(fmt.Sprintf("error while loading config file: %s", cfgPath)) } + paramsPath := cli.GetGlobalParamsPath() + params, err := types.NewGlobalParams(paramsPath) + if err != nil { + log.Fatal().Err(err).Msg(fmt.Sprintf("error while loading global params file: %s", paramsPath)) + } + // Initialize metrics with the metrics port from config metricsPort := cfg.Metrics.GetMetricsPort() metrics.Init(metricsPort) @@ -69,7 +76,7 @@ func main() { log.Fatal().Err(err).Msg("error while creating btc notifier") } - service := services.NewService(cfg, dbClient, btcNotifier, btcClient) + service := services.NewService(cfg, params, dbClient, btcNotifier, btcClient) if err != nil { log.Fatal().Err(err).Msg("error while creating service") } diff --git a/tests/mocks/mock_db_client.go b/tests/mocks/mock_db_client.go index da0898c..911da4b 100644 --- a/tests/mocks/mock_db_client.go +++ b/tests/mocks/mock_db_client.go @@ -68,23 +68,23 @@ func (_m *DbInterface) FindExpiredDelegations(ctx context.Context, btcTipHeight } // GetBTCDelegationByStakingTxHash provides a mock function with given fields: ctx, stakingTxHash -func (_m *DbInterface) GetBTCDelegationByStakingTxHash(ctx context.Context, stakingTxHash string) (*model.BTCDelegationDetails, error) { +func (_m *DbInterface) GetBTCDelegationByStakingTxHash(ctx context.Context, stakingTxHash string) (*model.DelegationDocument, error) { ret := _m.Called(ctx, stakingTxHash) if len(ret) == 0 { panic("no return value specified for GetBTCDelegationByStakingTxHash") } - var r0 *model.BTCDelegationDetails + var r0 *model.DelegationDocument var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*model.BTCDelegationDetails, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, string) (*model.DelegationDocument, error)); ok { return rf(ctx, stakingTxHash) } - if rf, ok := ret.Get(0).(func(context.Context, string) *model.BTCDelegationDetails); ok { + if rf, ok := ret.Get(0).(func(context.Context, string) *model.DelegationDocument); ok { r0 = rf(ctx, stakingTxHash) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.BTCDelegationDetails) + r0 = ret.Get(0).(*model.DelegationDocument) } } @@ -128,23 +128,23 @@ func (_m *DbInterface) GetBTCDelegationState(ctx context.Context, stakingTxHash } // GetBTCDelegationsByStates provides a mock function with given fields: ctx, states -func (_m *DbInterface) GetBTCDelegationsByStates(ctx context.Context, states []types.DelegationState) ([]*model.BTCDelegationDetails, error) { +func (_m *DbInterface) GetBTCDelegationsByStates(ctx context.Context, states []types.DelegationState) ([]*model.DelegationDocument, error) { ret := _m.Called(ctx, states) if len(ret) == 0 { panic("no return value specified for GetBTCDelegationsByStates") } - var r0 []*model.BTCDelegationDetails + var r0 []*model.DelegationDocument var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []types.DelegationState) ([]*model.BTCDelegationDetails, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, []types.DelegationState) ([]*model.DelegationDocument, error)); ok { return rf(ctx, states) } - if rf, ok := ret.Get(0).(func(context.Context, []types.DelegationState) []*model.BTCDelegationDetails); ok { + if rf, ok := ret.Get(0).(func(context.Context, []types.DelegationState) []*model.DelegationDocument); ok { r0 = rf(ctx, states) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]*model.BTCDelegationDetails) + r0 = ret.Get(0).([]*model.DelegationDocument) } } From 1c840599ab477d1361b659db9c9c0eda7123abb0 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 23:33:46 +0530 Subject: [PATCH 22/35] deps --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index b17e754..c66d5c5 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/aead/siphash v1.0.1 // indirect + github.com/babylonlabs-io/networks/parameters v0.2.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/btcsuite/btcd/btcutil/psbt v1.1.8 // indirect diff --git a/go.sum b/go.sum index 788932e..81fa90a 100644 --- a/go.sum +++ b/go.sum @@ -98,6 +98,8 @@ github.com/aws/aws-sdk-go v1.49.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3Tju github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/babylonlabs-io/babylon v0.18.1 h1:ID8BDDHc+snOnW2nqjP7OMf3a//5mvjbEmiAoJAtZlc= github.com/babylonlabs-io/babylon v0.18.1/go.mod h1:sT+KG2U+M0tDMNZZ2L5CwlXX0OpagGEs56BiWXqaZFw= +github.com/babylonlabs-io/networks/parameters v0.2.3 h1:T1nigYrU61GWSpJZko3Gylt3T3eHHoxXLWkhw7s3uz0= +github.com/babylonlabs-io/networks/parameters v0.2.3/go.mod h1:iEJVOzaLsE33vpP7J4u+CRGfkSIfErUAwRmgCFCBpyI= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= From 797b13430105a7f54f05bc508d34725400a6ae29 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Wed, 11 Dec 2024 23:58:17 +0530 Subject: [PATCH 23/35] fix --- internal/db/interface.go | 1 - internal/db/model/params.go | 32 --------------- internal/db/params.go | 29 -------------- internal/services/watch_btc_events.go | 58 +++++++++++++-------------- tests/mocks/mock_db_client.go | 30 -------------- 5 files changed, 28 insertions(+), 122 deletions(-) delete mode 100644 internal/db/model/params.go delete mode 100644 internal/db/params.go diff --git a/internal/db/interface.go b/internal/db/interface.go index 1ba0a3d..3972116 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -36,7 +36,6 @@ type DbInterface interface { GetBTCDelegationByStakingTxHash( ctx context.Context, stakingTxHash string, ) (*model.DelegationDocument, error) - GetStakingParams(ctx context.Context, version uint32) (*model.StakingParams, error) GetBTCDelegationsByStates(ctx context.Context, states []types.DelegationState) ([]*model.DelegationDocument, error) GetBTCDelegationState(ctx context.Context, stakingTxHash string) (*types.DelegationState, error) } diff --git a/internal/db/model/params.go b/internal/db/model/params.go deleted file mode 100644 index 52c872c..0000000 --- a/internal/db/model/params.go +++ /dev/null @@ -1,32 +0,0 @@ -package model - -// StakingParams represents the staking parameters of the BBN chain -// Reference: https://github.com/babylonlabs-io/babylon/blob/main/proto/babylon/btcstaking/v1/params.proto -type StakingParams struct { - CovenantPks []string `bson:"covenant_pks"` - CovenantQuorum uint32 `bson:"covenant_quorum"` - MinStakingValueSat int64 `bson:"min_staking_value_sat"` - MaxStakingValueSat int64 `bson:"max_staking_value_sat"` - MinStakingTimeBlocks uint32 `bson:"min_staking_time_blocks"` - MaxStakingTimeBlocks uint32 `bson:"max_staking_time_blocks"` - SlashingPkScript string `bson:"slashing_pk_script"` - MinSlashingTxFeeSat int64 `bson:"min_slashing_tx_fee_sat"` - SlashingRate string `bson:"slashing_rate"` - UnbondingTimeBlocks uint32 `bson:"unbonding_time_blocks"` - UnbondingFeeSat int64 `bson:"unbonding_fee_sat"` - MinCommissionRate string `bson:"min_commission_rate"` - DelegationCreationBaseGasFee uint64 `bson:"delegation_creation_base_gas_fee"` - AllowListExpirationHeight uint64 `bson:"allow_list_expiration_height"` - BtcActivationHeight uint32 `bson:"btc_activation_height"` -} - -type BaseParamsDocument struct { - Type string `bson:"type"` - Version uint32 `bson:"version"` -} - -// Specific document for staking params -type StakingParamsDocument struct { - BaseParamsDocument `bson:",inline"` - Params *StakingParams `bson:"params"` -} diff --git a/internal/db/params.go b/internal/db/params.go deleted file mode 100644 index e522a8e..0000000 --- a/internal/db/params.go +++ /dev/null @@ -1,29 +0,0 @@ -package db - -import ( - "context" - "fmt" - - "github.com/babylonlabs-io/staking-expiry-checker/internal/db/model" - "go.mongodb.org/mongo-driver/bson" -) - -const STAKING_PARAMS_TYPE = "staking_params" - -func (db *Database) GetStakingParams(ctx context.Context, version uint32) (*model.StakingParams, error) { - collection := db.client.Database(db.dbName). - Collection(model.GlobalParamsCollection) - - filter := bson.M{ - "type": STAKING_PARAMS_TYPE, - "version": version, - } - - var params model.StakingParamsDocument - err := collection.FindOne(ctx, filter).Decode(¶ms) - if err != nil { - return nil, fmt.Errorf("failed to get staking params: %w", err) - } - - return params.Params, nil -} diff --git a/internal/services/watch_btc_events.go b/internal/services/watch_btc_events.go index 80ceeaf..00a4afe 100644 --- a/internal/services/watch_btc_events.go +++ b/internal/services/watch_btc_events.go @@ -150,7 +150,7 @@ func (s *Service) handleSpendingStakingTransaction( } // Try to validate as withdrawal transaction - withdrawalErr := s.validateWithdrawalTxFromStaking(spendingTx, spendingInputIdx, delegation, params) + withdrawalErr := s.validateWithdrawalTxFromStaking(spendingTx, spendingInputIdx, delegation, paramsVersion) if withdrawalErr != nil { if errors.Is(err, types.ErrInvalidWithdrawalTx) { metrics.IncrementInvalidStakingWithdrawalTxCounter() @@ -178,13 +178,17 @@ func (s *Service) handleSpendingUnbondingTransaction( spendingInputIdx uint32, delegation *model.DelegationDocument, ) error { - params, err := s.db.GetStakingParams(ctx, delegation.ParamsVersion) - if err != nil { - return fmt.Errorf("failed to get staking params: %w", err) + paramsVersion := s.GetVersionedGlobalParamsByHeight(delegation.StakingTx.StartHeight) + if paramsVersion == nil { + log.Ctx(ctx).Error().Msg("failed to get global params") + return types.NewErrorWithMsg( + http.StatusInternalServerError, types.InternalServiceError, + "failed to get global params based on the staking tx height", + ) } // First try to validate as withdrawal transaction - withdrawalErr := s.validateWithdrawalTxFromUnbonding(spendingTx, delegation, spendingInputIdx, params) + withdrawalErr := s.validateWithdrawalTxFromUnbonding(spendingTx, delegation, spendingInputIdx, paramsVersion) if withdrawalErr != nil { if errors.Is(withdrawalErr, types.ErrInvalidWithdrawalTx) { metrics.IncrementInvalidUnbondingWithdrawalTxCounter() @@ -299,15 +303,15 @@ func (s *Service) IsValidUnbondingTx( // 5. check whether the script of an unbonding tx output is expected // by re-building unbonding output from params - unbondingFee := btcutil.Amount(params.UnbondingFeeSat) + unbondingFee := btcutil.Amount(params.UnbondingFee) expectedUnbondingOutputValue := stakingValue - unbondingFee if expectedUnbondingOutputValue <= 0 { return false, fmt.Errorf("%w: staking output value is too low, got %v, unbonding fee: %v", - types.ErrInvalidUnbondingTx, stakingValue, params.UnbondingFeeSat) + types.ErrInvalidUnbondingTx, stakingValue, params.UnbondingFee) } unbondingInfo, err := btcstaking.BuildUnbondingInfo( stakerPk.MustToBTCPK(), - finalityProviderPks, + []*btcec.PublicKey{fpPK}, covPks, uint32(params.CovenantQuorum), uint16(delegation.UnbondingTx.TimeLock), @@ -332,21 +336,18 @@ func (s *Service) validateWithdrawalTxFromStaking( tx *wire.MsgTx, spendingInputIdx uint32, delegation *model.DelegationDocument, - params *model.StakingParams, + params *types.VersionedGlobalParams, ) error { stakerPk, err := bbn.NewBIP340PubKeyFromHex(delegation.StakerPkHex) if err != nil { return fmt.Errorf("failed to convert staker btc pkh to a public key: %w", err) } - finalityProviderPks := make([]*btcec.PublicKey, len(delegation.FinalityProviderPkHex)) - for i, hex := range delegation.FinalityProviderPkHex { - fpPk, err := bbn.NewBIP340PubKeyFromHex(hex) - if err != nil { - return fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) - } - finalityProviderPks[i] = fpPk.MustToBTCPK() + fpPKBIP340, err := bbn.NewBIP340PubKeyFromHex(delegation.FinalityProviderPkHex) + if err != nil { + return fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) } + fpPK := fpPKBIP340.MustToBTCPK() covPks := make([]*btcec.PublicKey, len(params.CovenantPks)) for i, hex := range params.CovenantPks { @@ -370,9 +371,9 @@ func (s *Service) validateWithdrawalTxFromStaking( // the witness matches stakingInfo, err := btcstaking.BuildStakingInfo( stakerPk.MustToBTCPK(), - finalityProviderPks, + []*btcec.PublicKey{fpPK}, covPks, - params.CovenantQuorum, + uint32(params.CovenantQuorum), uint16(delegation.StakingTx.TimeLock), stakingValue, btcParams, @@ -404,21 +405,18 @@ func (s *Service) validateWithdrawalTxFromUnbonding( tx *wire.MsgTx, delegation *model.DelegationDocument, spendingInputIdx uint32, - params *model.StakingParams, + params *types.VersionedGlobalParams, ) error { stakerPk, err := bbn.NewBIP340PubKeyFromHex(delegation.StakerPkHex) if err != nil { return fmt.Errorf("failed to convert staker btc pkh to a public key: %w", err) } - finalityProviderPks := make([]*btcec.PublicKey, len(delegation.FinalityProviderPkHex)) - for i, hex := range delegation.FinalityProviderPkHex { - fpPk, err := bbn.NewBIP340PubKeyFromHex(hex) - if err != nil { - return fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) - } - finalityProviderPks[i] = fpPk.MustToBTCPK() + fpPKBIP340, err := bbn.NewBIP340PubKeyFromHex(delegation.FinalityProviderPkHex) + if err != nil { + return fmt.Errorf("failed to convert finality provider pk hex to a public key: %w", err) } + fpPK := fpPKBIP340.MustToBTCPK() covPks := make([]*btcec.PublicKey, len(params.CovenantPks)) for i, hex := range params.CovenantPks { @@ -439,14 +437,14 @@ func (s *Service) validateWithdrawalTxFromUnbonding( // re-build the time-lock path script and check whether the script from // the witness matches stakingValue := btcutil.Amount(stakingTx.TxOut[delegation.StakingTx.OutputIndex].Value) - unbondingFee := btcutil.Amount(params.UnbondingFeeSat) + unbondingFee := btcutil.Amount(params.UnbondingFee) expectedUnbondingOutputValue := stakingValue - unbondingFee unbondingInfo, err := btcstaking.BuildUnbondingInfo( stakerPk.MustToBTCPK(), - finalityProviderPks, + []*btcec.PublicKey{fpPK}, covPks, - params.CovenantQuorum, - uint16(delegation.UnbondingTx.TimeLock), + uint32(params.CovenantQuorum), + uint16(params.UnbondingTime), expectedUnbondingOutputValue, btcParams, ) diff --git a/tests/mocks/mock_db_client.go b/tests/mocks/mock_db_client.go index 911da4b..4b0499d 100644 --- a/tests/mocks/mock_db_client.go +++ b/tests/mocks/mock_db_client.go @@ -157,36 +157,6 @@ func (_m *DbInterface) GetBTCDelegationsByStates(ctx context.Context, states []t return r0, r1 } -// GetStakingParams provides a mock function with given fields: ctx, version -func (_m *DbInterface) GetStakingParams(ctx context.Context, version uint32) (*model.StakingParams, error) { - ret := _m.Called(ctx, version) - - if len(ret) == 0 { - panic("no return value specified for GetStakingParams") - } - - var r0 *model.StakingParams - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint32) (*model.StakingParams, error)); ok { - return rf(ctx, version) - } - if rf, ok := ret.Get(0).(func(context.Context, uint32) *model.StakingParams); ok { - r0 = rf(ctx, version) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*model.StakingParams) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint32) error); ok { - r1 = rf(ctx, version) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // Ping provides a mock function with given fields: ctx func (_m *DbInterface) Ping(ctx context.Context) error { ret := _m.Called(ctx) From 6e064faae3c2bfbcc068d6e41ff03cdbbb549014 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 12 Dec 2024 00:56:37 +0530 Subject: [PATCH 24/35] fix --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5353616..0d82011 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,9 @@ stop-service: run-local: ./bin/local-startup.sh; - go run cmd/staking-expiry-checker/main.go --config config/config-local.yml + go run cmd/staking-expiry-checker/main.go \ + --config config/config-local.yml \ + --params config/global-params.json generate-mock-interface: cd internal/db && mockery --name=DbInterface --output=../../tests/mocks --outpkg=mocks --filename=mock_db_client.go From 01bb56d4017261048e42fff60c54eecf20a6e10d Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 12 Dec 2024 00:56:56 +0530 Subject: [PATCH 25/35] fix notifier start --- cmd/staking-expiry-checker/main.go | 4 ++++ internal/db/model/setup.go | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/staking-expiry-checker/main.go b/cmd/staking-expiry-checker/main.go index 211b5bf..1dc8492 100644 --- a/cmd/staking-expiry-checker/main.go +++ b/cmd/staking-expiry-checker/main.go @@ -76,6 +76,10 @@ func main() { log.Fatal().Err(err).Msg("error while creating btc notifier") } + if err := btcNotifier.Start(); err != nil { + log.Fatal().Err(err).Msg("failed to start btc chain notifier") + } + service := services.NewService(cfg, params, dbClient, btcNotifier, btcClient) if err != nil { log.Fatal().Err(err).Msg("error while creating service") diff --git a/internal/db/model/setup.go b/internal/db/model/setup.go index da98286..db27bf9 100644 --- a/internal/db/model/setup.go +++ b/internal/db/model/setup.go @@ -1,7 +1,6 @@ package model const ( - TimeLockCollection = "timelock_queue" - DelegationsCollection = "delegations" - GlobalParamsCollection = "global_params" + TimeLockCollection = "timelock_queue" + DelegationsCollection = "delegations" ) From c3ff26092e0d2dfa53aa1eb670875c163a310440 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 12 Dec 2024 10:41:58 +0530 Subject: [PATCH 26/35] fix docker --- config/global-params.json | 25 +++++++++++++++++++ .../staking-expiry-checker/entrypoint.sh | 8 +++++- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 config/global-params.json diff --git a/config/global-params.json b/config/global-params.json new file mode 100644 index 0000000..3af5bbf --- /dev/null +++ b/config/global-params.json @@ -0,0 +1,25 @@ +{ + "versions": [ + { + "version": 0, + "activation_height": 192840, + "staking_cap": 50000000000, + "tag": "01020304", + "covenant_pks": [ + "0381b70c01535f5153a8039c21150c53f3e49a083555b57930103db8a7272ff336", + "02159f46467124f6bbba77060520571ddb07c7e95ff54d8b9958ec0b0d59d86c03", + "039705be04f3a3eb5c3d0dd61e648e06ea8170975744594fe702e8088bcceff375", + "02ce138027bfdfb4dd631e9cecf097082c8a505ab16de36f5e3eb816d105ba7575", + "03e15dba250612e79e22abf28a1828ba5e6bdfaaa6ed2d87462b046994c33fa46f" + ], + "covenant_quorum": 3, + "unbonding_time": 1000, + "unbonding_fee": 20000, + "max_staking_amount": 1000000000, + "min_staking_amount": 1000000, + "max_staking_time": 65000, + "min_staking_time": 64000, + "confirmation_depth": 10 + } + ] +} \ No newline at end of file diff --git a/contrib/images/staking-expiry-checker/entrypoint.sh b/contrib/images/staking-expiry-checker/entrypoint.sh index cb0f5ad..f72bd6e 100755 --- a/contrib/images/staking-expiry-checker/entrypoint.sh +++ b/contrib/images/staking-expiry-checker/entrypoint.sh @@ -4,6 +4,7 @@ set -x BINARY=${BINARY:-/bin/staking-expiry-checker} CONFIG=${CONFIG:-/home/staking-expiry-checker/config.yml} +PARAMS=${PARAMS:-/home/staking-expiry-checker/global-params.json} if ! [ -f "${BINARY}" ]; then echo "The binary $(basename "${BINARY}") cannot be found." @@ -15,4 +16,9 @@ if ! [ -f "${CONFIG}" ]; then exit 1 fi -$BINARY --config "$CONFIG" 2>&1 \ No newline at end of file +if ! [ -f "${PARAMS}" ]; then + echo "The global parameters file $(basename "${PARAMS}") cannot be found. Please add the global parameters file to the shared folder. Use the PARAMS environment variable if the name of the global parameters file is not 'global-params.json'" + exit 1 +fi + +$BINARY --config "$CONFIG" --params "$PARAMS" 2>&1 \ No newline at end of file From 5443ca7679982fc4dccb9a7bd11cf267af51d4c2 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 12 Dec 2024 11:15:05 +0530 Subject: [PATCH 27/35] start clean --- cmd/staking-expiry-checker/main.go | 35 +++++------------------------- internal/services/service.go | 33 +++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/cmd/staking-expiry-checker/main.go b/cmd/staking-expiry-checker/main.go index 1dc8492..90e2d56 100644 --- a/cmd/staking-expiry-checker/main.go +++ b/cmd/staking-expiry-checker/main.go @@ -3,7 +3,6 @@ package main import ( "context" "fmt" - "os" "os/signal" "syscall" @@ -14,8 +13,6 @@ import ( "github.com/babylonlabs-io/staking-expiry-checker/internal/btcclient" "github.com/babylonlabs-io/staking-expiry-checker/internal/config" "github.com/babylonlabs-io/staking-expiry-checker/internal/db" - "github.com/babylonlabs-io/staking-expiry-checker/internal/observability/metrics" - "github.com/babylonlabs-io/staking-expiry-checker/internal/poller" "github.com/babylonlabs-io/staking-expiry-checker/internal/services" "github.com/babylonlabs-io/staking-expiry-checker/internal/types" ) @@ -27,14 +24,6 @@ func init() { } func main() { - // Create a context that is cancelled on SIGINT or SIGTERM - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // Setup signal handling - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) - // Setup CLI commands and flags if err := cli.Setup(); err != nil { log.Fatal().Err(err).Msg("error while setting up cli") @@ -53,9 +42,9 @@ func main() { log.Fatal().Err(err).Msg(fmt.Sprintf("error while loading global params file: %s", paramsPath)) } - // Initialize metrics with the metrics port from config - metricsPort := cfg.Metrics.GetMetricsPort() - metrics.Init(metricsPort) + // Create context with signal handling + ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer cancel() // Create new DB client dbClient, err := db.New(ctx, cfg.Db) @@ -81,21 +70,7 @@ func main() { } service := services.NewService(cfg, params, dbClient, btcNotifier, btcClient) - if err != nil { - log.Fatal().Err(err).Msg("error while creating service") + if err := service.RunUntilShutdown(ctx); err != nil { + log.Fatal().Err(err).Msg("failed to start service") } - - // Start pollers - go poller.NewExpiryPoller(cfg.Pollers.ExpiryChecker, service).Start(ctx) - go poller.NewBTCSubscriberPoller(cfg.Pollers.BtcSubscriber, service).Start(ctx) - - // Start service handlers - go service.HandleUnbondingDelegationChannel(ctx) - go service.HandleWithdrawnDelegationChannel(ctx) - - // Wait for a signal to shutdown - <-sigChan - - // Cancel the context to signal all goroutines to stop - cancel() } diff --git a/internal/services/service.go b/internal/services/service.go index 75d6756..e176a98 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -1,13 +1,18 @@ package services import ( + "context" + "fmt" "sync" "github.com/babylonlabs-io/staking-expiry-checker/internal/btcclient" "github.com/babylonlabs-io/staking-expiry-checker/internal/config" "github.com/babylonlabs-io/staking-expiry-checker/internal/db" + "github.com/babylonlabs-io/staking-expiry-checker/internal/observability/metrics" + "github.com/babylonlabs-io/staking-expiry-checker/internal/poller" "github.com/babylonlabs-io/staking-expiry-checker/internal/types" notifier "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/rs/zerolog/log" ) type Service struct { @@ -40,12 +45,38 @@ func NewService( return &Service{ quit: make(chan struct{}), cfg: cfg, - db: db, btcNotifier: btcNotifier, params: params, + db: db, btc: btc, trackedSubs: NewTrackedSubscriptions(), unbondingDelegationChan: make(chan *types.UnbondingDelegationEvent, 100), // buffered withdrawnDelegationChan: make(chan *types.WithdrawnDelegationEvent, 100), // buffered } } + +func (s *Service) RunUntilShutdown(ctx context.Context) error { + // Initialize metrics + metricsPort := s.cfg.Metrics.GetMetricsPort() + metrics.Init(metricsPort) + + // Start BTCNotifier + if err := s.btcNotifier.Start(); err != nil { + return fmt.Errorf("failed to start btc chain notifier: %w", err) + } + defer s.btcNotifier.Stop() + + // Start pollers + go poller.NewExpiryPoller(s.cfg.Pollers.ExpiryChecker, s).Start(ctx) + go poller.NewBTCSubscriberPoller(s.cfg.Pollers.BtcSubscriber, s).Start(ctx) + + // Start service handlers + go s.HandleUnbondingDelegationChannel(ctx) + go s.HandleWithdrawnDelegationChannel(ctx) + + // Wait for context cancellation + <-ctx.Done() + log.Info().Msg("Shutdown signal received, stopping service...") + + return nil +} From 25df15d192997105c31d911b110d2443cfd80615 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 12 Dec 2024 12:01:20 +0530 Subject: [PATCH 28/35] imrpove pollers --- internal/poller/poller.go | 85 ------------------- internal/services/btc_subscriber.go | 58 ------------- internal/services/delegation_handlers.go | 27 +++++- .../{expiry_checker.go => pollers.go} | 57 ++++++++++--- internal/services/service.go | 64 ++++++++++++-- 5 files changed, 127 insertions(+), 164 deletions(-) delete mode 100644 internal/poller/poller.go delete mode 100644 internal/services/btc_subscriber.go rename internal/services/{expiry_checker.go => pollers.go} (61%) diff --git a/internal/poller/poller.go b/internal/poller/poller.go deleted file mode 100644 index 1a91ae7..0000000 --- a/internal/poller/poller.go +++ /dev/null @@ -1,85 +0,0 @@ -package poller - -import ( - "context" - "time" - - "github.com/rs/zerolog/log" - - "github.com/babylonlabs-io/staking-expiry-checker/internal/config" - "github.com/babylonlabs-io/staking-expiry-checker/internal/types" -) - -type ExpiryChecker interface { - ProcessExpiredDelegations(ctx context.Context) *types.Error -} - -type BTCSubscriber interface { - ProcessBTCSubscriber(ctx context.Context) *types.Error -} - -type PollerType string - -const ( - ExpiryPoller PollerType = "expiry" - BTCSubscriberPoller PollerType = "btc-subscriber" -) - -type Poller struct { - pollerType PollerType - poll func(ctx context.Context) *types.Error - interval time.Duration - timeout time.Duration - quit chan struct{} -} - -func NewExpiryPoller(cfg config.PollerConfig, checker ExpiryChecker) *Poller { - return &Poller{ - pollerType: ExpiryPoller, - interval: cfg.Interval, - timeout: cfg.Timeout, - poll: checker.ProcessExpiredDelegations, - quit: make(chan struct{}), - } -} - -func NewBTCSubscriberPoller(cfg config.PollerConfig, subscriber BTCSubscriber) *Poller { - return &Poller{ - pollerType: BTCSubscriberPoller, - interval: cfg.Interval, - timeout: cfg.Timeout, - poll: subscriber.ProcessBTCSubscriber, - quit: make(chan struct{}), - } -} - -func (p *Poller) Start(ctx context.Context) { - ticker := time.NewTicker(p.interval) - - for { - select { - case <-ticker.C: - // Start a new context for each poll - pollingCtx, cancel := context.WithTimeout(ctx, p.timeout) - defer cancel() - - if err := p.poll(pollingCtx); err != nil { - log.Error(). - Err(err). - Str("poller", string(p.pollerType)). - Msg("Error in polling operation") - } - case <-ctx.Done(): - // Handle context cancellation. - log.Info().Msg("Poller stopped due to context cancellation") - return - case <-p.quit: - ticker.Stop() // Stop the ticker - return - } - } -} - -func (p *Poller) Stop() { - close(p.quit) -} diff --git a/internal/services/btc_subscriber.go b/internal/services/btc_subscriber.go deleted file mode 100644 index 79ccbbb..0000000 --- a/internal/services/btc_subscriber.go +++ /dev/null @@ -1,58 +0,0 @@ -package services - -import ( - "context" - - "github.com/babylonlabs-io/staking-expiry-checker/internal/types" - "github.com/rs/zerolog/log" -) - -func (s *Service) ProcessBTCSubscriber(ctx context.Context) *types.Error { - // Get delegations that need BTC notifications - delegations, err := s.db.GetBTCDelegationsByStates(ctx, []types.DelegationState{ - types.Unbonded, - types.UnbondingRequested, - }) - if err != nil { - log.Error().Err(err).Msg("Failed to get delegations for BTC subscription") - return types.NewInternalServiceError(err) - } - - if len(delegations) == 0 { - log.Debug().Msg("No delegations found for BTC subscription") - return nil - } - - // Process each delegation - for _, delegation := range delegations { - if s.trackedSubs.IsSubscribed(delegation.StakingTxHashHex) { - log.Debug(). - Str("stakingTxHash", delegation.StakingTxHashHex). - Msg("Delegation already subscribed, skipping") - continue - } - - err := s.registerStakingSpendNotification( - delegation.StakingTxHashHex, - delegation.StakingTx.TxHex, - uint32(delegation.StakingTx.OutputIndex), - uint32(delegation.StakingTx.StartHeight), - ) - if err != nil { - log.Error(). - Err(err). - Str("stakingTxHash", delegation.StakingTxHashHex). - Msg("Failed to register staking spend notification") - return types.NewInternalServiceError(err) - } - - // Add to tracked subscriptions after successful registration - s.trackedSubs.AddSubscription(delegation.StakingTxHashHex) - - log.Debug(). - Str("stakingTxHash", delegation.StakingTxHashHex). - Msg("Successfully registered BTC notification") - } - - return nil -} diff --git a/internal/services/delegation_handlers.go b/internal/services/delegation_handlers.go index 0996dd7..080052d 100644 --- a/internal/services/delegation_handlers.go +++ b/internal/services/delegation_handlers.go @@ -8,8 +8,9 @@ import ( "github.com/rs/zerolog/log" ) -// HandleUnbondingDelegationChannel processes unbonding delegations -func (s *Service) HandleUnbondingDelegationChannel(ctx context.Context) { +// handleUnbondingDelegation processes unbonding delegations +func (s *Service) handleUnbondingDelegation(ctx context.Context) { + s.wg.Add(1) defer s.wg.Done() for { select { @@ -72,8 +73,9 @@ func (s *Service) HandleUnbondingDelegationChannel(ctx context.Context) { } } -// HandleWithdrawnDelegationChannel processes withdrawn delegations -func (s *Service) HandleWithdrawnDelegationChannel(ctx context.Context) { +// handleWithdrawnDelegation processes withdrawn delegations +func (s *Service) handleWithdrawnDelegation(ctx context.Context) { + s.wg.Add(1) defer s.wg.Done() for { select { @@ -125,3 +127,20 @@ func (s *Service) HandleWithdrawnDelegationChannel(ctx context.Context) { } } } + +// SaveNewTimeLockExpire checks if the staking delegation has expired and updates the database. +// This method tolerate duplicated calls on the same stakingTxHashHex. +func (s *Service) SaveNewTimeLockExpire( + ctx context.Context, stakingTxHashHex string, + startHeight, timelock uint64, txType types.StakingTxType, +) *types.Error { + expireHeight := startHeight + timelock + err := s.db.SaveTimeLockExpireCheck( + ctx, stakingTxHashHex, expireHeight, txType.ToString(), + ) + if err != nil { + log.Ctx(ctx).Err(err).Msg("Failed to save expire check") + return types.NewInternalServiceError(err) + } + return nil +} diff --git a/internal/services/expiry_checker.go b/internal/services/pollers.go similarity index 61% rename from internal/services/expiry_checker.go rename to internal/services/pollers.go index b64a362..a2a3e32 100644 --- a/internal/services/expiry_checker.go +++ b/internal/services/pollers.go @@ -9,24 +9,57 @@ import ( "github.com/rs/zerolog/log" ) -// SaveNewTimeLockExpire checks if the staking delegation has expired and updates the database. -// This method tolerate duplicated calls on the same stakingTxHashHex. -func (s *Service) SaveNewTimeLockExpire( - ctx context.Context, stakingTxHashHex string, - startHeight, timelock uint64, txType types.StakingTxType, -) *types.Error { - expireHeight := startHeight + timelock - err := s.db.SaveTimeLockExpireCheck( - ctx, stakingTxHashHex, expireHeight, txType.ToString(), - ) +func (s *Service) processBTCSubscriber(ctx context.Context) *types.Error { + // Get delegations that need BTC notifications + delegations, err := s.db.GetBTCDelegationsByStates(ctx, []types.DelegationState{ + types.Unbonded, + types.UnbondingRequested, + }) if err != nil { - log.Ctx(ctx).Err(err).Msg("Failed to save expire check") + log.Error().Err(err).Msg("Failed to get delegations for BTC subscription") return types.NewInternalServiceError(err) } + + if len(delegations) == 0 { + log.Debug().Msg("No delegations found for BTC subscription") + return nil + } + + // Process each delegation + for _, delegation := range delegations { + if s.trackedSubs.IsSubscribed(delegation.StakingTxHashHex) { + log.Debug(). + Str("stakingTxHash", delegation.StakingTxHashHex). + Msg("Delegation already subscribed, skipping") + continue + } + + err := s.registerStakingSpendNotification( + delegation.StakingTxHashHex, + delegation.StakingTx.TxHex, + uint32(delegation.StakingTx.OutputIndex), + uint32(delegation.StakingTx.StartHeight), + ) + if err != nil { + log.Error(). + Err(err). + Str("stakingTxHash", delegation.StakingTxHashHex). + Msg("Failed to register staking spend notification") + return types.NewInternalServiceError(err) + } + + // Add to tracked subscriptions after successful registration + s.trackedSubs.AddSubscription(delegation.StakingTxHashHex) + + log.Debug(). + Str("stakingTxHash", delegation.StakingTxHashHex). + Msg("Successfully registered BTC notification") + } + return nil } -func (s *Service) ProcessExpiredDelegations(ctx context.Context) *types.Error { +func (s *Service) processExpiredDelegations(ctx context.Context) *types.Error { btcTip, err := s.btc.GetBlockCount() if err != nil { log.Error().Err(err).Msg("Error getting BTC tip height") diff --git a/internal/services/service.go b/internal/services/service.go index e176a98..3e8ff82 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -4,12 +4,12 @@ import ( "context" "fmt" "sync" + "time" "github.com/babylonlabs-io/staking-expiry-checker/internal/btcclient" "github.com/babylonlabs-io/staking-expiry-checker/internal/config" "github.com/babylonlabs-io/staking-expiry-checker/internal/db" "github.com/babylonlabs-io/staking-expiry-checker/internal/observability/metrics" - "github.com/babylonlabs-io/staking-expiry-checker/internal/poller" "github.com/babylonlabs-io/staking-expiry-checker/internal/types" notifier "github.com/lightningnetwork/lnd/chainntnfs" "github.com/rs/zerolog/log" @@ -67,16 +67,70 @@ func (s *Service) RunUntilShutdown(ctx context.Context) error { defer s.btcNotifier.Stop() // Start pollers - go poller.NewExpiryPoller(s.cfg.Pollers.ExpiryChecker, s).Start(ctx) - go poller.NewBTCSubscriberPoller(s.cfg.Pollers.BtcSubscriber, s).Start(ctx) + go s.startExpiryPoller(ctx) + go s.startBTCSubscriberPoller(ctx) // Start service handlers - go s.HandleUnbondingDelegationChannel(ctx) - go s.HandleWithdrawnDelegationChannel(ctx) + go s.handleUnbondingDelegation(ctx) + go s.handleWithdrawnDelegation(ctx) // Wait for context cancellation <-ctx.Done() log.Info().Msg("Shutdown signal received, stopping service...") + // Signal all components to stop + close(s.quit) + + // Wait for all goroutines to finish + s.wg.Wait() + return nil } + +func (s *Service) startExpiryPoller(ctx context.Context) { + s.wg.Add(1) + defer s.wg.Done() + + ticker := time.NewTicker(s.cfg.Pollers.ExpiryChecker.Interval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + pollingCtx, cancel := context.WithTimeout(ctx, s.cfg.Pollers.ExpiryChecker.Timeout) + if err := s.processExpiredDelegations(pollingCtx); err != nil { + log.Error().Err(err).Msg("Error processing expired delegations") + } + cancel() + case <-ctx.Done(): + log.Info().Msg("Expiry poller stopped due to context cancellation") + return + case <-s.quit: + return + } + } +} + +func (s *Service) startBTCSubscriberPoller(ctx context.Context) { + s.wg.Add(1) + defer s.wg.Done() + + ticker := time.NewTicker(s.cfg.Pollers.BtcSubscriber.Interval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + pollingCtx, cancel := context.WithTimeout(ctx, s.cfg.Pollers.BtcSubscriber.Timeout) + if err := s.processBTCSubscriber(pollingCtx); err != nil { + log.Error().Err(err).Msg("Error processing BTC subscriptions") + } + cancel() + case <-ctx.Done(): + log.Info().Msg("BTC subscriber poller stopped due to context cancellation") + return + case <-s.quit: + return + } + } +} From 8a664db77b8d4843e2ce921f419cee62f1e58afc Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 12 Dec 2024 12:02:50 +0530 Subject: [PATCH 29/35] fix cfg --- config/config-docker.yml | 10 +++++++--- config/config-local.yml | 14 +++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/config/config-docker.yml b/config/config-docker.yml index 1c71076..959f3e9 100644 --- a/config/config-docker.yml +++ b/config/config-docker.yml @@ -1,7 +1,11 @@ -poller: - interval: 5s +pollers: log-level: debug - timeout: 10s + expiry-checker: + interval: 5s + timeout: 10s + btc-subscriber: + interval: 5s + timeout: 10s db: username: root password: example diff --git a/config/config-local.yml b/config/config-local.yml index 959f3e9..c3cff66 100644 --- a/config/config-local.yml +++ b/config/config-local.yml @@ -2,21 +2,21 @@ pollers: log-level: debug expiry-checker: interval: 5s - timeout: 10s + timeout: 1000s btc-subscriber: interval: 5s - timeout: 10s + timeout: 1000s db: username: root password: example address: "mongodb://localhost:27017" db-name: staking-api-service btc: - endpoint: localhost:18332 - disable-tls: false - net-params: testnet - rpc-user: rpcuser - rpc-pass: rpcpass + endpoint: 127.0.0.1:38332 # signet endpoint + disable-tls: true # should be disabled for local testing + net-params: signet # signet network params + rpc-user: rpcuser # rpc user + rpc-pass: rpcpass # rpc pass metrics: host: 0.0.0.0 port: 2112 \ No newline at end of file From 750ca47ed3508737ed9a3d728299e414f9698e8c Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 12 Dec 2024 12:15:33 +0530 Subject: [PATCH 30/35] fix lint --- internal/services/service.go | 6 +++++- internal/types/global_params.go | 18 ------------------ 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/internal/services/service.go b/internal/services/service.go index 3e8ff82..67ebcda 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -64,7 +64,11 @@ func (s *Service) RunUntilShutdown(ctx context.Context) error { if err := s.btcNotifier.Start(); err != nil { return fmt.Errorf("failed to start btc chain notifier: %w", err) } - defer s.btcNotifier.Stop() + defer func() { + if err := s.btcNotifier.Stop(); err != nil { + log.Error().Err(err).Msg("failed to stop btc chain notifier") + } + }() // Start pollers go s.startExpiryPoller(ctx) diff --git a/internal/types/global_params.go b/internal/types/global_params.go index 0e5bffa..cddacb7 100644 --- a/internal/types/global_params.go +++ b/internal/types/global_params.go @@ -1,13 +1,11 @@ package types import ( - "encoding/hex" "encoding/json" "os" "path/filepath" "github.com/babylonlabs-io/networks/parameters/parser" - "github.com/btcsuite/btcd/btcec/v2" ) type VersionedGlobalParams = parser.VersionedGlobalParams @@ -33,19 +31,3 @@ func NewGlobalParams(path string) (*GlobalParams, error) { return &globalParams, nil } - -// parseCovenantPubKeyFromHex parses public key string to btc public key -// the input should be 33 bytes -func parseCovenantPubKeyFromHex(pkStr string) (*btcec.PublicKey, error) { - pkBytes, err := hex.DecodeString(pkStr) - if err != nil { - return nil, err - } - - pk, err := btcec.ParsePubKey(pkBytes) - if err != nil { - return nil, err - } - - return pk, nil -} From c91514c9e4bd7b09007a461505399cd886ccfb56 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 12 Dec 2024 13:13:32 +0530 Subject: [PATCH 31/35] fix cmnts --- cmd/staking-expiry-checker/main.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/staking-expiry-checker/main.go b/cmd/staking-expiry-checker/main.go index 90e2d56..bd7171c 100644 --- a/cmd/staking-expiry-checker/main.go +++ b/cmd/staking-expiry-checker/main.go @@ -46,17 +46,19 @@ func main() { ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer cancel() - // Create new DB client + // Create DB client dbClient, err := db.New(ctx, cfg.Db) if err != nil { log.Fatal().Err(err).Msg("error while creating db client") } + // Create BTC client btcClient, err := btcclient.NewBtcClient(&cfg.Btc) if err != nil { log.Fatal().Err(err).Msg("error while creating btc client") } + // Create BTC notifier btcNotifier, err := btcclient.NewBTCNotifier( &cfg.Btc, &btcclient.EmptyHintCache{}, @@ -65,10 +67,7 @@ func main() { log.Fatal().Err(err).Msg("error while creating btc notifier") } - if err := btcNotifier.Start(); err != nil { - log.Fatal().Err(err).Msg("failed to start btc chain notifier") - } - + // Create service service := services.NewService(cfg, params, dbClient, btcNotifier, btcClient) if err := service.RunUntilShutdown(ctx); err != nil { log.Fatal().Err(err).Msg("failed to start service") From f84a9fef7f804fa1d573d2cbac9335c6a041e9de Mon Sep 17 00:00:00 2001 From: Gurjot Date: Fri, 13 Dec 2024 12:35:11 +0530 Subject: [PATCH 32/35] pr comment: rm unneeded fields in delegation document --- internal/db/model/delegation.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/db/model/delegation.go b/internal/db/model/delegation.go index 5e91f11..c8b2e26 100644 --- a/internal/db/model/delegation.go +++ b/internal/db/model/delegation.go @@ -14,9 +14,7 @@ type DelegationDocument struct { StakingTxHashHex string `bson:"_id"` // Primary key StakerPkHex string `bson:"staker_pk_hex"` FinalityProviderPkHex string `bson:"finality_provider_pk_hex"` - StakingValue uint64 `bson:"staking_value"` State types.DelegationState `bson:"state"` StakingTx *TimelockTransaction `bson:"staking_tx"` // Always exist UnbondingTx *TimelockTransaction `bson:"unbonding_tx,omitempty"` - IsOverflow bool `bson:"is_overflow"` } From d881e4f40826ca5a9933f0be12acc755896a00cc Mon Sep 17 00:00:00 2001 From: Gurjot Date: Fri, 13 Dec 2024 12:58:42 +0530 Subject: [PATCH 33/35] pr comment: set fetch limit in mongo to avoid fetching too many results --- internal/db/delegation.go | 4 +++- internal/db/expiry.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/db/delegation.go b/internal/db/delegation.go index 47e61ab..4928661 100644 --- a/internal/db/delegation.go +++ b/internal/db/delegation.go @@ -9,6 +9,7 @@ import ( "github.com/babylonlabs-io/staking-expiry-checker/internal/utils" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" ) func (db *Database) TransitionToUnbondedState( @@ -110,10 +111,11 @@ func (db *Database) GetBTCDelegationsByStates( } filter := bson.M{"state": bson.M{"$in": stateStrings}} + opts := options.Find().SetLimit(200) // to prevent large result sets cursor, err := db.client.Database(db.dbName). Collection(model.DelegationsCollection). - Find(ctx, filter) + Find(ctx, filter, opts) if err != nil { return nil, err } diff --git a/internal/db/expiry.go b/internal/db/expiry.go index de71191..cd7e80f 100644 --- a/internal/db/expiry.go +++ b/internal/db/expiry.go @@ -14,7 +14,7 @@ func (db *Database) FindExpiredDelegations(ctx context.Context, btcTipHeight uin client := db.client.Database(db.dbName).Collection(model.TimeLockCollection) filter := bson.M{"expire_height": bson.M{"$lte": btcTipHeight}} - opts := options.Find().SetLimit(100) + opts := options.Find().SetLimit(200) // to prevent large result sets cursor, err := client.Find(ctx, filter, opts) if err != nil { return nil, err From a89b2983e6b85a387aa6bd684ddc15404c6fa5b5 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Fri, 13 Dec 2024 14:13:06 +0530 Subject: [PATCH 34/35] pr comment: update delegation document to include unbonding tx data --- internal/btcclient/btcclient.go | 16 ++++++ internal/btcclient/interface.go | 1 + internal/db/delegation.go | 64 +++++++++++++----------- internal/db/interface.go | 5 +- internal/services/delegation_handlers.go | 33 ++++++++++-- internal/services/pollers.go | 39 +++++++-------- internal/services/watch_btc_events.go | 18 ++++++- internal/types/delegation_events.go | 28 +++++++++-- internal/utils/utils.go | 8 +++ tests/mocks/mock_btc_client.go | 28 +++++++++++ tests/mocks/mock_db_client.go | 30 +++++------ 11 files changed, 193 insertions(+), 77 deletions(-) diff --git a/internal/btcclient/btcclient.go b/internal/btcclient/btcclient.go index 9dce989..361fc0c 100644 --- a/internal/btcclient/btcclient.go +++ b/internal/btcclient/btcclient.go @@ -43,3 +43,19 @@ func NewBtcClient(cfg *config.BtcConfig) (*BtcClient, error) { func (b *BtcClient) GetBlockCount() (int64, error) { return metrics.RecordBtcClientMetrics[int64](b.client.GetBlockCount) } + +func (b *BtcClient) GetBlockTimestamp(height uint64) (int64, error) { + return metrics.RecordBtcClientMetrics[int64](func() (int64, error) { + hash, err := b.client.GetBlockHash(int64(height)) + if err != nil { + return 0, err + } + + header, err := b.client.GetBlockHeader(hash) + if err != nil { + return 0, err + } + + return header.Timestamp.Unix(), nil + }) +} diff --git a/internal/btcclient/interface.go b/internal/btcclient/interface.go index 99c8bd6..aa0932a 100644 --- a/internal/btcclient/interface.go +++ b/internal/btcclient/interface.go @@ -2,4 +2,5 @@ package btcclient type BtcInterface interface { GetBlockCount() (int64, error) + GetBlockTimestamp(height uint64) (int64, error) } diff --git a/internal/db/delegation.go b/internal/db/delegation.go index 4928661..a02365e 100644 --- a/internal/db/delegation.go +++ b/internal/db/delegation.go @@ -15,28 +15,30 @@ import ( func (db *Database) TransitionToUnbondedState( ctx context.Context, stakingTxHashHex string, - unbondTxType types.StakingTxType, + eligiblePreviousStates []types.DelegationState, ) error { - return db.transitionState( - ctx, stakingTxHashHex, types.Unbonded, - utils.QualifiedStatesToUnbonded(unbondTxType), - ) + return db.transitionState(ctx, stakingTxHashHex, types.Unbonded.ToString(), eligiblePreviousStates, nil) } +// Change the state to `unbonding` and save the unbondingTx data +// Return not found error if the stakingTxHashHex is not found or the existing state is not eligible for unbonding func (db *Database) TransitionToUnbondingState( - ctx context.Context, - stakingTxHashHex string, + ctx context.Context, stakingTxHashHex string, + unbondingStartHeight, unbondingTimelock, unbondingOutputIndex uint64, + unbondingTxHex string, unbondingStartTimestamp int64, ) error { - return db.transitionState( - ctx, stakingTxHashHex, types.Unbonding, - utils.QualifiedStatesToUnbonding(), - ) -} + unbondingTxMap := make(map[string]interface{}) + unbondingTxMap["unbonding_tx"] = model.TimelockTransaction{ + TxHex: unbondingTxHex, + OutputIndex: unbondingOutputIndex, + StartTimestamp: unbondingStartTimestamp, + StartHeight: unbondingStartHeight, + TimeLock: unbondingTimelock, + } -func (db *Database) TransitionToWithdrawnState(ctx context.Context, stakingTxHashHex string) error { err := db.transitionState( - ctx, stakingTxHashHex, types.Withdrawn, - utils.QualifiedStatesToWithdraw(), + ctx, stakingTxHashHex, types.Unbonding.ToString(), + utils.QualifiedStatesToUnbonding(), unbondingTxMap, ) if err != nil { return err @@ -44,25 +46,27 @@ func (db *Database) TransitionToWithdrawnState(ctx context.Context, stakingTxHas return nil } -// TransitionState updates the state of a staking transaction to a new state -// It returns an NotFoundError if the staking transaction is not found or not -// in the eligible state to transition -func (db *Database) transitionState( +func (db *Database) TransitionToWithdrawnState( ctx context.Context, stakingTxHashHex string, - newState types.DelegationState, - eligiblePreviousState []types.DelegationState, + eligiblePreviousStates []types.DelegationState, +) error { + return db.transitionState(ctx, stakingTxHashHex, types.Withdrawn.ToString(), eligiblePreviousStates, nil) +} + +// TransitionState updates the state of a staking transaction to a new state +// It returns an NotFoundError if the staking transaction is not found or not in the eligible state to transition +func (db *Database) transitionState( + ctx context.Context, stakingTxHashHex, newState string, + eligiblePreviousState []types.DelegationState, additionalUpdates map[string]interface{}, ) error { - client := db.client.Database( - db.dbName, - ).Collection(model.DelegationsCollection) - filter := bson.M{ - "_id": stakingTxHashHex, - "state": bson.M{ - "$in": eligiblePreviousState, - }, + client := db.client.Database(db.dbName).Collection(model.DelegationsCollection) + filter := bson.M{"_id": stakingTxHashHex, "state": bson.M{"$in": eligiblePreviousState}} + update := bson.M{"$set": bson.M{"state": newState}} + for field, value := range additionalUpdates { + // Add additional fields to the $set operation + update["$set"].(bson.M)[field] = value } - update := bson.M{"$set": bson.M{"state": newState.ToString()}} _, err := client.UpdateOne(ctx, filter, update) if err != nil { if errors.Is(err, mongo.ErrNoDocuments) { diff --git a/internal/db/interface.go b/internal/db/interface.go index 3972116..02b78ab 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -23,15 +23,18 @@ type DbInterface interface { TransitionToUnbondedState( ctx context.Context, stakingTxHashHex string, - unbondTxType types.StakingTxType, + eligiblePreviousStates []types.DelegationState, ) error TransitionToUnbondingState( ctx context.Context, stakingTxHashHex string, + unbondingStartHeight, unbondingTimelock, unbondingOutputIndex uint64, + unbondingTxHex string, unbondingStartTimestamp int64, ) error TransitionToWithdrawnState( ctx context.Context, stakingTxHashHex string, + eligiblePreviousStates []types.DelegationState, ) error GetBTCDelegationByStakingTxHash( ctx context.Context, stakingTxHash string, diff --git a/internal/services/delegation_handlers.go b/internal/services/delegation_handlers.go index 080052d..49f285e 100644 --- a/internal/services/delegation_handlers.go +++ b/internal/services/delegation_handlers.go @@ -2,7 +2,9 @@ package services import ( "context" + "net/http" + "github.com/babylonlabs-io/staking-expiry-checker/internal/db" "github.com/babylonlabs-io/staking-expiry-checker/internal/types" "github.com/babylonlabs-io/staking-expiry-checker/internal/utils" "github.com/rs/zerolog/log" @@ -42,9 +44,12 @@ func (s *Service) handleUnbondingDelegation(ctx context.Context) { continue } - unbondingStartHeight := uint64(event.UnbondingStartHeight) - - expireCheckErr := s.SaveNewTimeLockExpire(ctx, delegation.StakingTxHashHex, unbondingStartHeight, uint64(delegation.UnbondingTx.TimeLock), types.UnbondingTxType) + expireCheckErr := s.SaveNewTimeLockExpire(ctx, + delegation.StakingTxHashHex, + event.UnbondingStartHeight, + event.UnbondingTimeLock, + types.UnbondingTxType, + ) if expireCheckErr != nil { log.Error().Err(expireCheckErr). Str("staking_tx", delegation.StakingTxHashHex). @@ -54,6 +59,8 @@ func (s *Service) handleUnbondingDelegation(ctx context.Context) { transitionErr := s.db.TransitionToUnbondingState( ctx, delegation.StakingTxHashHex, + event.UnbondingStartHeight, event.UnbondingTimeLock, event.UnbondingOutputIndex, + event.UnbondingTxHex, event.UnbondingStartTimestamp, ) if transitionErr != nil { log.Error(). @@ -73,6 +80,25 @@ func (s *Service) handleUnbondingDelegation(ctx context.Context) { } } +// TransitionToUnbondingState process the actual confirmed unbonding tx by updating the delegation state to `unbonding` +// It returns true if the delegation is found and successfully transitioned to unbonding state. +func (s *Service) TransitionToUnbondingState( + ctx context.Context, stakingTxHashHex string, + unbondingStartHeight, unbondingTimelock, unbondingOutputIndex uint64, + unbondingTxHex string, unbondingStartTimestamp int64, +) *types.Error { + err := s.db.TransitionToUnbondingState(ctx, stakingTxHashHex, unbondingStartHeight, unbondingTimelock, unbondingOutputIndex, unbondingTxHex, unbondingStartTimestamp) + if err != nil { + if ok := db.IsNotFoundError(err); ok { + log.Ctx(ctx).Warn().Str("stakingTxHashHex", stakingTxHashHex).Err(err).Msg("delegation not found or no longer eligible for unbonding") + return nil + } + log.Ctx(ctx).Error().Str("stakingTxHashHex", stakingTxHashHex).Err(err).Msg("failed to transition to unbonding state") + return types.NewError(http.StatusInternalServerError, types.InternalServiceError, err) + } + return nil +} + // handleWithdrawnDelegation processes withdrawn delegations func (s *Service) handleWithdrawnDelegation(ctx context.Context) { s.wg.Add(1) @@ -109,6 +135,7 @@ func (s *Service) handleWithdrawnDelegation(ctx context.Context) { transitionErr := s.db.TransitionToWithdrawnState( ctx, delegation.StakingTxHashHex, + utils.QualifiedStatesToWithdraw(), ) if transitionErr != nil { log.Error(). diff --git a/internal/services/pollers.go b/internal/services/pollers.go index a2a3e32..56c08fe 100644 --- a/internal/services/pollers.go +++ b/internal/services/pollers.go @@ -4,8 +4,8 @@ import ( "context" "github.com/babylonlabs-io/staking-expiry-checker/internal/db" - "github.com/babylonlabs-io/staking-expiry-checker/internal/db/model" "github.com/babylonlabs-io/staking-expiry-checker/internal/types" + "github.com/babylonlabs-io/staking-expiry-checker/internal/utils" "github.com/rs/zerolog/log" ) @@ -75,7 +75,13 @@ func (s *Service) processExpiredDelegations(ctx context.Context) *types.Error { // Process each delegation in the batch for _, delegation := range expiredDelegations { - if err := s.transitionToUnbondedIfEligible(ctx, delegation); err != nil { + txType, err := types.StakingTxTypeFromString(delegation.TxType) + if err != nil { + log.Error().Err(err).Msgf("Invalid timelock type: %s", delegation.TxType) + return types.NewInternalServiceError(err) + } + + if err := s.TransitionToUnbondedState(ctx, txType, delegation.StakingTxHashHex); err != nil { log.Error().Err(err). Msgf("Error transitioning delegation to unbonded: %v", delegation.ID) return err @@ -90,33 +96,22 @@ func (s *Service) processExpiredDelegations(ctx context.Context) *types.Error { return nil } -// transitionToUnbondedIfEligible attempts to transition a delegation to unbonded state -// if it's in an eligible state. -func (s *Service) transitionToUnbondedIfEligible( - ctx context.Context, delegation model.TimeLockDocument, +// TransitionToUnbondedState transitions the staking delegation to unbonded state. +// It returns true if the delegation is found and successfully transitioned to unbonded state. +func (s *Service) TransitionToUnbondedState( + ctx context.Context, stakingTxType types.StakingTxType, stakingTxHashHex string, ) *types.Error { - // Check what type of the timelock is - timelockType, err := types.StakingTxTypeFromString(delegation.TxType) - if err != nil { - log.Error().Err(err).Msgf("Invalid timelock type: %s", delegation.TxType) - return types.NewInternalServiceError(err) - } - // Try to transition to unbonded, will skip if not eligible (NotFoundError) - err = s.db.TransitionToUnbondedState( - ctx, delegation.StakingTxHashHex, timelockType, - ) + err := s.db.TransitionToUnbondedState(ctx, stakingTxHashHex, utils.QualifiedStatesToUnbonded(stakingTxType)) if err != nil { + // If the delegation is not found, we can ignore the error, it just means the delegation is not in a state that we can transition to unbonded if db.IsNotFoundError(err) { - // Silently skip if not eligible - log.Debug().Msgf( - "Delegation not found or not in eligible state to transition: %v", delegation.ID, - ) + errMsg := "delegation not found or no longer eligible to be unbonded after timelock expired" + log.Ctx(ctx).Warn().Str("stakingTxHashHex", stakingTxHashHex).Err(err).Msg(errMsg) return nil } - log.Error().Err(err).Msgf("Error transitioning to unbonded: %v", delegation.ID) + log.Ctx(ctx).Err(err).Str("stakingTxHash", stakingTxHashHex).Msg("Failed to transition to unbonded state") return types.NewInternalServiceError(err) } - return nil } diff --git a/internal/services/watch_btc_events.go b/internal/services/watch_btc_events.go index 00a4afe..ae8c700 100644 --- a/internal/services/watch_btc_events.go +++ b/internal/services/watch_btc_events.go @@ -139,9 +139,25 @@ func (s *Service) handleSpendingStakingTransaction( Str("unbonding_tx", spendingTx.TxHash().String()). Msg("staking tx has been spent through unbonding path") + unbondingTxHex, err := utils.SerializeBtcTransaction(spendingTx) + if err != nil { + return fmt.Errorf("failed to serialize unbonding tx: %w", err) + } + + unbondingTxTimestamp, err := s.btc.GetBlockTimestamp(uint64(spendingHeight)) + if err != nil { + return fmt.Errorf("failed to get block timestamp: %w", err) + } + unbondingEvent := types.NewUnbondingDelegationEvent( delegation.StakingTxHashHex, - spendingHeight, + uint64(spendingHeight), + unbondingTxTimestamp, + paramsVersion.UnbondingTime, + // valid unbonding tx always has one output + uint64(0), + unbondingTxHex, + spendingTx.TxHash().String(), ) utils.PushOrQuit(s.unbondingDelegationChan, unbondingEvent, s.quit) diff --git a/internal/types/delegation_events.go b/internal/types/delegation_events.go index c0e9480..114b917 100644 --- a/internal/types/delegation_events.go +++ b/internal/types/delegation_events.go @@ -1,18 +1,36 @@ package types type UnbondingDelegationEvent struct { - StakingTxHashHex string - UnbondingStartHeight uint32 + StakingTxHashHex string + UnbondingStartHeight uint64 + UnbondingStartTimestamp int64 + UnbondingTimeLock uint64 + UnbondingOutputIndex uint64 + UnbondingTxHex string + UnbondingTxHashHex string } type WithdrawnDelegationEvent struct { StakingTxHashHex string } -func NewUnbondingDelegationEvent(stakingTxHashHex string, unbondingStartHeight uint32) *UnbondingDelegationEvent { +func NewUnbondingDelegationEvent( + stakingTxHashHex string, + unbondingStartHeight uint64, + unbondingStartTimestamp int64, + unbondingTimeLock uint64, + unbondingOutputIndex uint64, + unbondingTxHex string, + unbondingTxHashHex string, +) *UnbondingDelegationEvent { return &UnbondingDelegationEvent{ - StakingTxHashHex: stakingTxHashHex, - UnbondingStartHeight: unbondingStartHeight, + StakingTxHashHex: stakingTxHashHex, + UnbondingStartHeight: unbondingStartHeight, + UnbondingStartTimestamp: unbondingStartTimestamp, + UnbondingTimeLock: unbondingTimeLock, + UnbondingOutputIndex: unbondingOutputIndex, + UnbondingTxHex: unbondingTxHex, + UnbondingTxHashHex: unbondingTxHashHex, } } diff --git a/internal/utils/utils.go b/internal/utils/utils.go index bf7733d..bcd8363 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -116,3 +116,11 @@ func PushOrQuit[T any](c chan<- T, msg T, quit <-chan struct{}) { case <-quit: } } + +func SerializeBtcTransaction(tx *wire.MsgTx) (string, error) { + var txBuf bytes.Buffer + if err := tx.Serialize(&txBuf); err != nil { + return "", err + } + return hex.EncodeToString(txBuf.Bytes()), nil +} diff --git a/tests/mocks/mock_btc_client.go b/tests/mocks/mock_btc_client.go index 37068ed..4212d75 100644 --- a/tests/mocks/mock_btc_client.go +++ b/tests/mocks/mock_btc_client.go @@ -37,6 +37,34 @@ func (_m *BtcInterface) GetBlockCount() (int64, error) { return r0, r1 } +// GetBlockTimestamp provides a mock function with given fields: height +func (_m *BtcInterface) GetBlockTimestamp(height uint64) (int64, error) { + ret := _m.Called(height) + + if len(ret) == 0 { + panic("no return value specified for GetBlockTimestamp") + } + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(uint64) (int64, error)); ok { + return rf(height) + } + if rf, ok := ret.Get(0).(func(uint64) int64); ok { + r0 = rf(height) + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func(uint64) error); ok { + r1 = rf(height) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // NewBtcInterface creates a new instance of BtcInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewBtcInterface(t interface { diff --git a/tests/mocks/mock_db_client.go b/tests/mocks/mock_db_client.go index 4b0499d..4455396 100644 --- a/tests/mocks/mock_db_client.go +++ b/tests/mocks/mock_db_client.go @@ -193,17 +193,17 @@ func (_m *DbInterface) SaveTimeLockExpireCheck(ctx context.Context, stakingTxHas return r0 } -// TransitionToUnbondedState provides a mock function with given fields: ctx, stakingTxHashHex, unbondTxType -func (_m *DbInterface) TransitionToUnbondedState(ctx context.Context, stakingTxHashHex string, unbondTxType types.StakingTxType) error { - ret := _m.Called(ctx, stakingTxHashHex, unbondTxType) +// TransitionToUnbondedState provides a mock function with given fields: ctx, stakingTxHashHex, eligiblePreviousStates +func (_m *DbInterface) TransitionToUnbondedState(ctx context.Context, stakingTxHashHex string, eligiblePreviousStates []types.DelegationState) error { + ret := _m.Called(ctx, stakingTxHashHex, eligiblePreviousStates) if len(ret) == 0 { panic("no return value specified for TransitionToUnbondedState") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, types.StakingTxType) error); ok { - r0 = rf(ctx, stakingTxHashHex, unbondTxType) + if rf, ok := ret.Get(0).(func(context.Context, string, []types.DelegationState) error); ok { + r0 = rf(ctx, stakingTxHashHex, eligiblePreviousStates) } else { r0 = ret.Error(0) } @@ -211,17 +211,17 @@ func (_m *DbInterface) TransitionToUnbondedState(ctx context.Context, stakingTxH return r0 } -// TransitionToUnbondingState provides a mock function with given fields: ctx, stakingTxHashHex -func (_m *DbInterface) TransitionToUnbondingState(ctx context.Context, stakingTxHashHex string) error { - ret := _m.Called(ctx, stakingTxHashHex) +// TransitionToUnbondingState provides a mock function with given fields: ctx, stakingTxHashHex, unbondingStartHeight, unbondingTimelock, unbondingOutputIndex, unbondingTxHex, unbondingStartTimestamp +func (_m *DbInterface) TransitionToUnbondingState(ctx context.Context, stakingTxHashHex string, unbondingStartHeight uint64, unbondingTimelock uint64, unbondingOutputIndex uint64, unbondingTxHex string, unbondingStartTimestamp int64) error { + ret := _m.Called(ctx, stakingTxHashHex, unbondingStartHeight, unbondingTimelock, unbondingOutputIndex, unbondingTxHex, unbondingStartTimestamp) if len(ret) == 0 { panic("no return value specified for TransitionToUnbondingState") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, stakingTxHashHex) + if rf, ok := ret.Get(0).(func(context.Context, string, uint64, uint64, uint64, string, int64) error); ok { + r0 = rf(ctx, stakingTxHashHex, unbondingStartHeight, unbondingTimelock, unbondingOutputIndex, unbondingTxHex, unbondingStartTimestamp) } else { r0 = ret.Error(0) } @@ -229,17 +229,17 @@ func (_m *DbInterface) TransitionToUnbondingState(ctx context.Context, stakingTx return r0 } -// TransitionToWithdrawnState provides a mock function with given fields: ctx, stakingTxHashHex -func (_m *DbInterface) TransitionToWithdrawnState(ctx context.Context, stakingTxHashHex string) error { - ret := _m.Called(ctx, stakingTxHashHex) +// TransitionToWithdrawnState provides a mock function with given fields: ctx, stakingTxHashHex, eligiblePreviousStates +func (_m *DbInterface) TransitionToWithdrawnState(ctx context.Context, stakingTxHashHex string, eligiblePreviousStates []types.DelegationState) error { + ret := _m.Called(ctx, stakingTxHashHex, eligiblePreviousStates) if len(ret) == 0 { panic("no return value specified for TransitionToWithdrawnState") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, stakingTxHashHex) + if rf, ok := ret.Get(0).(func(context.Context, string, []types.DelegationState) error); ok { + r0 = rf(ctx, stakingTxHashHex, eligiblePreviousStates) } else { r0 = ret.Error(0) } From 6b60e28c7389f2c12adc3089ef93172724ed9a26 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Fri, 13 Dec 2024 14:53:32 +0530 Subject: [PATCH 35/35] fix docker config --- config/config-docker.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config/config-docker.yml b/config/config-docker.yml index 959f3e9..bc58971 100644 --- a/config/config-docker.yml +++ b/config/config-docker.yml @@ -12,11 +12,11 @@ db: address: "mongodb://localhost:27017" db-name: staking-api-service btc: - endpoint: localhost:18332 - disable-tls: false - net-params: testnet - rpc-user: rpcuser - rpc-pass: rpcpass + endpoint: localhost:38332 # signet endpoint + disable-tls: true # should be disabled for local testing + net-params: signet # signet network params + rpc-user: rpcuser # rpc user + rpc-pass: rpcpass # rpc pass metrics: host: 0.0.0.0 port: 2112 \ No newline at end of file