diff --git a/apps/evm/cmd/run.go b/apps/evm/cmd/run.go index a88e548d21..7829c2e1b0 100644 --- a/apps/evm/cmd/run.go +++ b/apps/evm/cmd/run.go @@ -131,7 +131,10 @@ var RunCmd = &cobra.Command{ }() } - return rollcmd.StartNode(logger, cmd, executor, sequencer, nodeKey, datastore, nodeConfig, genesis, node.NodeOptions{}) + // nil fiberClient: the EVM app doesn't wire Fibre DA. See + // tools/celestia-node-fiber for the adapter; testapp/cmd/run.go + // has the same TODO note for matching context. + return rollcmd.StartNode(logger, cmd, executor, sequencer, nodeKey, datastore, nodeConfig, genesis, node.NodeOptions{}, nil) }, } diff --git a/apps/grpc/cmd/run.go b/apps/grpc/cmd/run.go index 22ca71f587..586a89eddc 100644 --- a/apps/grpc/cmd/run.go +++ b/apps/grpc/cmd/run.go @@ -86,8 +86,10 @@ The execution client must implement the Evolve execution gRPC interface.`, return err } - // Start the node - return rollcmd.StartNode(logger, cmd, executor, sequencer, nodeKey, datastore, nodeConfig, genesis, node.NodeOptions{}) + // Start the node. nil fiberClient: the gRPC app doesn't wire + // Fibre DA. See tools/celestia-node-fiber for the adapter; + // testapp/cmd/run.go has the same TODO note for context. + return rollcmd.StartNode(logger, cmd, executor, sequencer, nodeKey, datastore, nodeConfig, genesis, node.NodeOptions{}, nil) }, } diff --git a/apps/testapp/cmd/run.go b/apps/testapp/cmd/run.go index 75e5a49019..05698f1e60 100644 --- a/apps/testapp/cmd/run.go +++ b/apps/testapp/cmd/run.go @@ -97,7 +97,12 @@ var RunCmd = &cobra.Command{ return err } - return cmd.StartNode(logger, command, executor, sequencer, nodeKey, datastore, nodeConfig, genesis, node.NodeOptions{}) + // nil fiberClient: testapp doesn't yet wire Fibre DA. To enable + // fiber support here, build a *cnfiber.Adapter from + // nodeConfig.DA.Fiber and pass it as the last argument. The + // adapter wiring lives in tools/celestia-node-fiber; see the + // fiber-bench tool's run.go for a working caller. + return cmd.StartNode(logger, command, executor, sequencer, nodeKey, datastore, nodeConfig, genesis, node.NodeOptions{}, nil) }, } diff --git a/block/internal/cache/manager.go b/block/internal/cache/manager.go index e907002600..9e293c775a 100644 --- a/block/internal/cache/manager.go +++ b/block/internal/cache/manager.go @@ -25,8 +25,24 @@ const ( // DataDAIncludedPrefix is the store key prefix for data DA inclusion tracking. DataDAIncludedPrefix = "cache/data-da-included/" - // DefaultTxCacheRetention is the default time to keep transaction hashes in cache. - DefaultTxCacheRetention = 24 * time.Hour + // DefaultTxCacheRetention is how long tx hashes stay in the + // seen-tx cache before CleanupOldTxs evicts them. + // + // HACK(fiber-throughput): dropped from 24h to 30s while we chase + // throughput, but the previous default was itself wrong: 24h is + // retention × tps in memory, so any rollup with meaningful TPS + // would OOM (we hit ~16 GB in under a minute at ~1.5M tx/s). + // What this should be properly: + // - Bounded by entry count, not wall time. The dedup window + // should be "the last N txs we saw", LRU-evicted, so cache + // memory is fixed regardless of throughput. + // - Or expressed in DA blocks: "drop hashes once their txs + // would have been retried out of the mempool", which is a + // property of mempool TTL × DA block time, not 24 hours. + // - 30s is a fine measurement default and a reasonable upper + // bound for pretty much any rollup; pick the right number + // when the cache structure itself is reworked. + DefaultTxCacheRetention = 30 * time.Second ) // CacheManager provides thread-safe cache operations for tracking seen blocks diff --git a/block/internal/common/consts.go b/block/internal/common/consts.go index 840b2faa97..8e1e679fc3 100644 --- a/block/internal/common/consts.go +++ b/block/internal/common/consts.go @@ -2,19 +2,54 @@ package common import "strconv" -// defaultMaxBlobSizeStr holds the string representation of the default blob -// size limit. Override at link time via: +// defaultMaxBlobSizeStr holds the string representation of the default +// blob size limit. Anchored to Fibre's actual cap: protocol MaxBlobSize +// (1 << 27 = 128 MiB) minus the 5-byte Fibre blob header (1 byte +// version + 4 byte data size). See celestia-app/v9/fibre/blob.go +// (blobHeaderLen) and fibre/protocol_params.go (MaxBlobSize). // -// go build -ldflags "-X github.com/evstack/ev-node/block/internal/common.defaultMaxBlobSizeStr=125829120" -var defaultMaxBlobSizeStr = "5242880" // 5 MB +// HACK(fiber-throughput): this default is correct for fiber-enabled +// deployments but WRONG for the legacy JSON-RPC blob client path — +// the bridge / chain rejects blobs above its own (much smaller) cap, +// so a non-fiber node started against this default would fail to +// submit. The right shape is per-backend: fiber's cap is one number, +// blob-RPC's cap is another, and DefaultMaxBlobSize shouldn't be a +// single global. Restructure into config when the throughput-cleanup +// TODO lands; until then, non-fiber callers should override via +// ldflag or local config. +// +// MUST be a string literal: Go's `-ldflags "-X ..."` only takes effect +// on variables initialized to a string constant, NOT a function call. +// A previous version used strconv.FormatUint here, which compiled but +// silently ignored ldflag overrides. +// +// Override at link time via: +// +// go build -ldflags "-X github.com/evstack/ev-node/block/internal/common.defaultMaxBlobSizeStr=33554432" +var defaultMaxBlobSizeStr = "134217723" // 1 << 27 - 5 = 128 MiB - 5 B // DefaultMaxBlobSize is the max blob size limit used for blob submission. +// +// TODO(throughput-cleanup): this single value is currently plugged in +// at two semantically different limits and the conflation has caused +// real bugs (a packed block marshals larger than its raw-tx total, so +// using MaxBlobSize as both input cap and output cap let blocks blow +// past the DA cap). Split into two: +// +// MaxBlobSize — chain-side ceiling on a marshaled DA blob +// MaxBlockTxBytes() — derived raw-tx budget = MaxBlobSize - per-block +// marshal overhead. Used by RetrieveBatch / +// FilterTxs. +// +// Once that derivation exists, drop the ad-hoc 2% reservation in +// executing/executor.go::RetrieveBatch and the duplicate cap in +// submitting/da_submitter.go::defaultRetryPolicy. var DefaultMaxBlobSize uint64 func init() { v, err := strconv.ParseUint(defaultMaxBlobSizeStr, 10, 64) if err != nil || v == 0 { - DefaultMaxBlobSize = 5 * 1024 * 1024 // 5 MB fallback + DefaultMaxBlobSize = 134217723 return } DefaultMaxBlobSize = v diff --git a/block/internal/da/fiber/types.go b/block/internal/da/fiber/types.go new file mode 100644 index 0000000000..7c5aacbeae --- /dev/null +++ b/block/internal/da/fiber/types.go @@ -0,0 +1,69 @@ +// Package fiber defines the Fiber DA backend interface and shared types. +// +// # Design Assumptions +// +// - The sequencer trusts the encoder to eventually confirm blob inclusion. +// Upload returns after the blob is uploaded and the PFF transaction is +// broadcast, NOT after on-chain confirmation. This keeps the sequencer's +// write path fast (~2s per 128 MB blob). +// +// - Callers are expected to batch/buffer their data into blobs sized for the +// protocol maximum (128 MiB - 5 byte header = 134,217,723 bytes). +// The interface accepts arbitrary sizes but the implementation may batch +// or reject oversized blobs. +// +// - Confirmation/finality is intentionally omitted from the initial API. +// The sequencer does not need it; the read path (Listen + Download) is +// sufficient for full nodes. A Status or Confirm RPC can be added later +// if needed without breaking existing callers. +// +// - Blob ordering is encoded in the blob data itself by the caller. +// The interface does not impose or guarantee ordering. +// +// - The interface is the same whether the encoder runs in-process or as an +// external gRPC service. For in-process use, call the mock or real +// implementation directly; for external use, connect via gRPC. +package fiber + +import ( + "context" + "time" +) + +// BlobID uniquely identifies an uploaded blob (version byte + 32-byte commitment). +type BlobID []byte + +// UploadResult is returned by Upload after the blob is accepted. +type UploadResult struct { + BlobID BlobID + ExpiresAt time.Time +} + +// BlobEvent is delivered via Listen when a blob is confirmed on-chain. +type BlobEvent struct { + BlobID BlobID + Height uint64 + DataSize uint64 +} + +// DA is the interface for interacting with the Fiber data availability layer. +type DA interface { + // Upload submits a blob under the given namespace to the DA network. + // Returns after the blob is uploaded and the payment transaction is broadcast. + // Does NOT wait for on-chain confirmation (see package doc for rationale). + Upload(ctx context.Context, namespace []byte, data []byte) (UploadResult, error) + + // Download retrieves and reconstructs a blob by its ID. + // Returns the original data that was passed to Upload. + Download(ctx context.Context, blobID BlobID) ([]byte, error) + + // Listen streams confirmed blob events for the given namespace, + // starting at fromHeight. + // + // fromHeight == 0 starts the stream from the current chain head; any + // positive value replays events from that block forward so a + // subscriber can resume after a restart without missing blobs (the + // DA backend is expected to block, not error, on future heights). + // The returned channel is closed when ctx is cancelled. + Listen(ctx context.Context, namespace []byte, fromHeight uint64) (<-chan BlobEvent, error) +} diff --git a/block/internal/da/fiber_client.go b/block/internal/da/fiber_client.go new file mode 100644 index 0000000000..db92f06792 --- /dev/null +++ b/block/internal/da/fiber_client.go @@ -0,0 +1,435 @@ +package da + +import ( + "context" + "encoding/binary" + "errors" + "fmt" + "sync" + "time" + + "github.com/rs/zerolog" + + "github.com/evstack/ev-node/block/internal/common" + "github.com/evstack/ev-node/block/internal/da/fiber" + datypes "github.com/evstack/ev-node/pkg/da/types" +) + +type ( + FiberClient = fiber.DA + BlobID = fiber.BlobID + UploadResult = fiber.UploadResult + BlobEvent = fiber.BlobEvent +) + +type FiberConfig struct { + Client FiberClient + Logger zerolog.Logger + DefaultTimeout time.Duration + Namespace string + DataNamespace string + LastKnownDAHeight uint64 +} + +type fiberDAClient struct { + fiber FiberClient + logger zerolog.Logger + defaultTimeout time.Duration + namespaceBz []byte + dataNamespaceBz []byte + lastKnownDAHeight uint64 +} + +var _ FullClient = (*fiberDAClient)(nil) + +func NewFiberClient(cfg FiberConfig) (FullClient, error) { + if cfg.Client == nil { + return nil, fmt.Errorf("fiber client in config is nil") + } + + if cfg.DefaultTimeout == 0 { + cfg.DefaultTimeout = 60 * time.Second + } + + return &fiberDAClient{ + fiber: cfg.Client, + logger: cfg.Logger.With().Str("component", "fiber_da_client").Logger(), + defaultTimeout: cfg.DefaultTimeout, + lastKnownDAHeight: cfg.LastKnownDAHeight, + namespaceBz: datypes.NamespaceFromString(cfg.Namespace).Bytes(), + dataNamespaceBz: datypes.NamespaceFromString(cfg.DataNamespace).Bytes(), + }, nil +} + +func (c *fiberDAClient) Submit(ctx context.Context, data [][]byte, _ float64, namespace []byte, _ []byte) datypes.ResultSubmit { + if len(data) == 0 { + return datypes.ResultSubmit{ + BaseResult: datypes.BaseResult{ + Code: datypes.StatusSuccess, + SubmittedCount: 0, + Timestamp: time.Now(), + }, + } + } + + var blobSize uint64 + for _, b := range data { + blobSize += uint64(len(b)) + } + + for i, raw := range data { + if uint64(len(raw)) > common.DefaultMaxBlobSize { + return datypes.ResultSubmit{ + BaseResult: datypes.BaseResult{ + Code: datypes.StatusTooBig, + Message: fmt.Sprintf("blob %d exceeds max size (%d > %d)", i, len(raw), common.DefaultMaxBlobSize), + }, + } + } + } + + // Per-item concurrent Upload. Fibre's per-Upload latency is + // dominated by validator signature aggregation (~1.5 s on a + // healthy network) and does not scale up linearly under multiple + // in-flight Uploads, so settlement throughput scales with the + // number of concurrent items submitted in a single batch. Each + // item gets its own goroutine, its own Upload call, and its own + // BlobID in the result; the previous flatten step was both + // memory-wasteful (a MaxBlobSize-sized memcpy on every Submit) + // and inherently serial (one Upload per Submit). + // + // TODO: wire-format compat — old splitBlobs assumed all items in + // a Submit were written as a single prefixed blob. With per-item + // Uploads, retrievers must treat each BlobID separately. The + // retrieve path in this file still uses splitBlobs and will need + // a follow-up to read the new per-item blobs as raw payloads. + nsID := namespace[len(namespace)-10:] + type uploadResult struct { + idx int + id []byte + err error + } + results := make([]uploadResult, len(data)) + var wg sync.WaitGroup + for i := range data { + wg.Add(1) + go func(i int) { + defer wg.Done() + res, err := c.fiber.Upload(ctx, nsID, data[i]) + if err != nil { + results[i] = uploadResult{idx: i, err: err} + return + } + id := make([]byte, len(res.BlobID)) + copy(id, res.BlobID) + results[i] = uploadResult{idx: i, id: id} + }(i) + } + wg.Wait() + + // Walk results in submission order. submitToDA's retry logic + // expects "prefix of successes": SubmittedCount=N means items + // [0..N) succeeded and the caller will re-submit items [N..end) + // on the next attempt. Reporting interleaved successes would + // double-submit blobs and waste escrow; matching prefix + // semantics keeps the contract intact even when individual + // Uploads fail out-of-order. + ids := make([][]byte, 0, len(data)) + var firstErr error + for _, r := range results { + if r.err != nil { + firstErr = r.err + break + } + ids = append(ids, r.id) + } + + if len(ids) == 0 && firstErr != nil { + code := datypes.StatusError + switch { + case errors.Is(firstErr, context.Canceled): + code = datypes.StatusContextCanceled + case errors.Is(firstErr, context.DeadlineExceeded): + code = datypes.StatusContextDeadline + } + c.logger.Error().Err(firstErr).Msg("fiber upload failed") + return datypes.ResultSubmit{ + BaseResult: datypes.BaseResult{ + Code: code, + Message: fmt.Sprintf("fiber upload failed for blob: %v", firstErr), + SubmittedCount: 0, + BlobSize: blobSize, + Timestamp: time.Now(), + }, + } + } + + if firstErr != nil { + c.logger.Warn().Err(firstErr). + Int("submitted", len(ids)). + Int("total", len(data)). + Msg("fiber upload partial success — caller will retry the remainder") + } + + c.logger.Debug().Int("num_ids", len(ids)).Uint64("height", 0 /* TODO */).Msg("fiber DA submission successful") + + return datypes.ResultSubmit{ + BaseResult: datypes.BaseResult{ + Code: datypes.StatusSuccess, + IDs: ids, + SubmittedCount: uint64(len(ids)), + Height: 0, /* TODO */ + BlobSize: blobSize, + Timestamp: time.Now(), + }, + } +} + +func (c *fiberDAClient) Retrieve(ctx context.Context, height uint64, namespace []byte) datypes.ResultRetrieve { + return c.retrieve(ctx, height, namespace, true) +} + +func (c *fiberDAClient) RetrieveBlobs(ctx context.Context, height uint64, namespace []byte) datypes.ResultRetrieve { + return c.retrieve(ctx, height, namespace, false) +} + +func (c *fiberDAClient) retrieve(ctx context.Context, height uint64, namespace []byte, _ bool) datypes.ResultRetrieve { + listenCtx, listenCancel := context.WithTimeout(ctx, c.defaultTimeout) + defer listenCancel() + + blobCh, err := c.fiber.Listen(listenCtx, namespace[len(namespace)-10:], height) + if err != nil { + return datypes.ResultRetrieve{ + BaseResult: datypes.BaseResult{ + Code: datypes.StatusError, + Message: fmt.Sprintf("fiber listen failed: %v", err), + Height: height, + Timestamp: time.Now(), + }, + } + } + + var blobIDs []BlobID +loop: + for { + select { + case <-listenCtx.Done(): + break loop + case event, ok := <-blobCh: + if !ok { + break loop + } + if event.Height > height { + break loop + } + blobIDs = append(blobIDs, event.BlobID) + } + } + + if len(blobIDs) == 0 { + return datypes.ResultRetrieve{ + BaseResult: datypes.BaseResult{ + Code: datypes.StatusNotFound, + Message: "no blobs found at height for given namespace", + Height: height, + Timestamp: time.Now(), + }, + } + } + + ids := make([]datypes.ID, 0, len(blobIDs)) + data := make([][]byte, 0, len(blobIDs)) + for _, blobID := range blobIDs { + dlCtx, dlCancel := context.WithTimeout(ctx, c.defaultTimeout) + blobData, dlErr := c.fiber.Download(dlCtx, blobID) + dlCancel() + if dlErr != nil { + return datypes.ResultRetrieve{ + BaseResult: datypes.BaseResult{ + Code: datypes.StatusError, + Message: fmt.Sprintf("fiber download failed for blob %x: %v", blobID, dlErr), + Height: height, + Timestamp: time.Now(), + }, + } + } + split, splitErr := splitBlobs(blobData) + if splitErr != nil { + return datypes.ResultRetrieve{ + BaseResult: datypes.BaseResult{ + Code: datypes.StatusError, + Message: fmt.Sprintf("fiber decode failed for blob %x: %v", blobID, splitErr), + Height: height, + Timestamp: time.Now(), + }, + } + } + for _, b := range split { + ids = append(ids, blobID) + data = append(data, b) + } + } + + return datypes.ResultRetrieve{ + BaseResult: datypes.BaseResult{ + Code: datypes.StatusSuccess, + Height: height, + IDs: ids, + Timestamp: time.Now(), + }, + Data: data, + } +} + +func (c *fiberDAClient) Get(ctx context.Context, ids []datypes.ID, _ []byte) ([]datypes.Blob, error) { + if len(ids) == 0 { + return nil, nil + } + + res := make([]datypes.Blob, 0, len(ids)) + for _, id := range ids { + downloadCtx, cancel := context.WithTimeout(ctx, c.defaultTimeout) + data, err := c.fiber.Download(downloadCtx, id) + cancel() + if err != nil { + return nil, fmt.Errorf("fiber download failed for blob %x: %w", id, err) + } + split, splitErr := splitBlobs(data) + if splitErr != nil { + return nil, fmt.Errorf("fiber decode failed for blob %x: %w", id, splitErr) + } + res = append(res, split...) + } + + return res, nil +} + +const fiberSubscribeChanSize = 42 + +func (c *fiberDAClient) Subscribe(ctx context.Context, namespace []byte, _ bool) (<-chan datypes.SubscriptionEvent, error) { + out := make(chan datypes.SubscriptionEvent, fiberSubscribeChanSize) + + go func() { + defer close(out) + + // The outer DA Subscribe entry point does not expose a starting + // height, so start from the live tip (fromHeight=0). A future + // refactor that plumbs resume-from-height through datypes.DA can + // thread the value here. + blobCh, err := c.fiber.Listen(ctx, namespace[len(namespace)-10:], c.lastKnownDAHeight) + if err != nil { + c.logger.Error().Err(err).Msg("fiber listen failed") + return + } + + for { + select { + case <-ctx.Done(): + return + case event, ok := <-blobCh: + if !ok { + return + } + + blobData, err := c.fiber.Download(ctx, event.BlobID) + if err != nil { + c.logger.Error().Err(err).Bytes("blob_id", event.BlobID).Msg("failed to retrieve blob") + continue + } + + split, splitErr := splitBlobs(blobData) + if splitErr != nil { + c.logger.Error().Err(splitErr).Bytes("blob_id", event.BlobID).Msg("failed to decode blob") + continue + } + + select { + case out <- datypes.SubscriptionEvent{ + Height: event.Height, + Timestamp: time.Now(), + Blobs: split, + }: + case <-ctx.Done(): + return + } + } + } + }() + + return out, nil +} + +func (c *fiberDAClient) GetLatestDAHeight(context.Context) (uint64, error) { + panic(fmt.Errorf("p2p should not be enabled")) +} + +func (c *fiberDAClient) GetProofs(_ context.Context, ids []datypes.ID, _ []byte) ([]datypes.Proof, error) { + return []datypes.Proof{}, fmt.Errorf("not implemented") +} + +func (c *fiberDAClient) Validate(_ context.Context, ids []datypes.ID, proofs []datypes.Proof, _ []byte) ([]bool, error) { + if len(ids) != len(proofs) { + return nil, errors.New("number of IDs and proofs must match") + } + if len(ids) == 0 { + return []bool{}, nil + } + + results := make([]bool, len(ids)) + + // not implemented. + for i := range results { + results[i] = true + } + + return results, nil +} + +func (c *fiberDAClient) GetHeaderNamespace() []byte { return c.namespaceBz } +func (c *fiberDAClient) GetDataNamespace() []byte { return c.dataNamespaceBz } + +// splitBlobs decodes the legacy "count + per-item length" framing that +// the previous Submit path used to pack multiple blobs into a single +// Upload. The per-item-concurrent Submit path no longer writes that +// framing — each item is uploaded raw — so any blob written by this +// branch's Submit will fail to decode here. +// +// Callers (Retrieve / RetrieveBlobs / Get / Subscribe) therefore only +// work for blobs written by the OLD code path, OR for the multi-item +// header batches that still use it. Pair the format change with a +// matching update to the read path before any node on this branch +// tries to sync from another node on this branch. +// +// Tracked alongside the wire-format TODO on Submit (above). +func splitBlobs(data []byte) ([][]byte, error) { + if len(data) == 0 { + return nil, nil + } + if len(data) < 4 { + return nil, fmt.Errorf("invalid blob encoding: header too short") + } + + count := int(binary.BigEndian.Uint32(data)) + off := 4 + blobs := make([][]byte, 0, count) + for i := range count { + if off+4 > len(data) { + return nil, fmt.Errorf("invalid blob encoding: truncated length at index %d", i) + } + size := int(binary.BigEndian.Uint32(data[off:])) + off += 4 + end := off + size + if end < off || end > len(data) { + return nil, fmt.Errorf("invalid blob encoding: truncated data at index %d", i) + } + blob := make([]byte, size) + copy(blob, data[off:end]) + off = end + blobs = append(blobs, blob) + } + return blobs, nil +} + +// Force Inclusion is disabled for Fiber PoC. +func (c *fiberDAClient) HasForcedInclusionNamespace() bool { return false } +func (c *fiberDAClient) GetForcedInclusionNamespace() []byte { return nil } diff --git a/block/internal/da/fiber_client_test.go b/block/internal/da/fiber_client_test.go new file mode 100644 index 0000000000..fed6f978f7 --- /dev/null +++ b/block/internal/da/fiber_client_test.go @@ -0,0 +1,539 @@ +package da + +import ( + "context" + "errors" + "sync/atomic" + "testing" + "time" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + + "github.com/evstack/ev-node/block/internal/da/fiber" + "github.com/evstack/ev-node/block/internal/da/fibremock" + datypes "github.com/evstack/ev-node/pkg/da/types" +) + +func makeTestFiberClient(t *testing.T) (*fibremock.MockDA, FullClient) { + t.Helper() + mock := fibremock.NewMockDA(fibremock.DefaultMockDAConfig()) + cl, err := NewFiberClient(FiberConfig{ + Client: mock, + Logger: zerolog.Nop(), + DefaultTimeout: 5 * time.Second, + Namespace: "test-ns", + DataNamespace: "test-ns", + }) + require.NotNil(t, cl) + require.NoError(t, err) + return mock, cl +} + +func TestFiberClient_NewClient_Nil(t *testing.T) { + _, err := NewFiberClient(FiberConfig{Client: nil}) + require.Error(t, err) +} + +func TestFiberClient_Submit_Success(t *testing.T) { + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + res := cl.Submit(context.Background(), [][]byte{[]byte("hello"), []byte("world")}, 0, ns, nil) + + require.Equal(t, datypes.StatusSuccess, res.Code) + require.Len(t, res.IDs, 1) + require.Equal(t, uint64(2), res.SubmittedCount) + require.Equal(t, uint64(0), res.Height) + require.Equal(t, uint64(10), res.BlobSize) +} + +func TestFiberClient_Submit_SingleBlob(t *testing.T) { + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + res := cl.Submit(context.Background(), [][]byte{[]byte("single")}, 0, ns, nil) + + require.Equal(t, datypes.StatusSuccess, res.Code) + require.Len(t, res.IDs, 1) + require.Equal(t, uint64(6), res.BlobSize) +} + +func TestFiberClient_Submit_EmptyData(t *testing.T) { + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + res := cl.Submit(context.Background(), [][]byte{}, 0, ns, nil) + + require.Equal(t, datypes.StatusSuccess, res.Code) + require.Empty(t, res.IDs) + require.Equal(t, uint64(0), res.SubmittedCount) +} + +func TestFiberClient_Submit_UploadError(t *testing.T) { + mock := fibremock.NewMockDA(fibremock.DefaultMockDAConfig()) + cl, err := NewFiberClient(FiberConfig{ + Client: &faultInjector{FiberClient: mock, err: errors.New("upload failed")}, + Logger: zerolog.Nop(), + DefaultTimeout: 5 * time.Second, + Namespace: "test-ns", + DataNamespace: "test-ns", + }) + require.NoError(t, err) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + res := cl.Submit(context.Background(), [][]byte{[]byte("data")}, 0, ns, nil) + + require.Equal(t, datypes.StatusError, res.Code) + require.Contains(t, res.Message, "fiber upload failed") +} + +func TestFiberClient_Submit_CanceledContext(t *testing.T) { + mock := fibremock.NewMockDA(fibremock.DefaultMockDAConfig()) + cl, err := NewFiberClient(FiberConfig{ + Client: &faultInjector{FiberClient: mock, err: context.Canceled}, + Logger: zerolog.Nop(), + DefaultTimeout: 5 * time.Second, + Namespace: "test-ns", + DataNamespace: "test-ns", + }) + require.NoError(t, err) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + res := cl.Submit(context.Background(), [][]byte{[]byte("data")}, 0, ns, nil) + + require.Equal(t, datypes.StatusContextCanceled, res.Code) +} + +func TestFiberClient_Submit_DeadlineExceeded(t *testing.T) { + mock := fibremock.NewMockDA(fibremock.DefaultMockDAConfig()) + cl, err := NewFiberClient(FiberConfig{ + Client: &faultInjector{FiberClient: mock, err: context.DeadlineExceeded}, + Logger: zerolog.Nop(), + DefaultTimeout: 5 * time.Second, + Namespace: "test-ns", + DataNamespace: "test-ns", + }) + require.NoError(t, err) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + res := cl.Submit(context.Background(), [][]byte{[]byte("data")}, 0, ns, nil) + + require.Equal(t, datypes.StatusContextDeadline, res.Code) +} + +func TestFiberClient_Submit_BlobTooLarge(t *testing.T) { + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + largeBlob := make([]byte, 6*1024*1024) + res := cl.Submit(context.Background(), [][]byte{largeBlob}, 0, ns, nil) + + require.Equal(t, datypes.StatusTooBig, res.Code) +} + +func TestFiberClient_Retrieve_Success(t *testing.T) { + t.Skip("pending Height tracking from fiber DA") + + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + submitRes := cl.Submit(context.Background(), [][]byte{[]byte("hello")}, 0, ns, nil) + require.Equal(t, datypes.StatusSuccess, submitRes.Code) + + retrieveRes := cl.Retrieve(context.Background(), submitRes.Height, ns) + require.Equal(t, datypes.StatusSuccess, retrieveRes.Code) + require.Len(t, retrieveRes.Data, 1) + require.Equal(t, []byte("hello"), retrieveRes.Data[0]) + require.Equal(t, submitRes.IDs, retrieveRes.IDs) +} + +func TestFiberClient_RetrieveBlobs_Success(t *testing.T) { + t.Skip("pending Height tracking from fiber DA") + + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + submitRes := cl.Submit(context.Background(), [][]byte{[]byte("blob1"), []byte("blob2")}, 0, ns, nil) + require.Equal(t, datypes.StatusSuccess, submitRes.Code) + + retrieveRes := cl.RetrieveBlobs(context.Background(), submitRes.Height, ns) + require.Equal(t, datypes.StatusSuccess, retrieveRes.Code) + require.Len(t, retrieveRes.Data, 2) + require.Equal(t, []byte("blob1"), retrieveRes.Data[0]) + require.Equal(t, []byte("blob2"), retrieveRes.Data[1]) +} + +func TestFiberClient_Retrieve_NotFound(t *testing.T) { + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + retrieveRes := cl.Retrieve(context.Background(), 9999, ns) + require.Equal(t, datypes.StatusNotFound, retrieveRes.Code) +} + +func TestFiberClient_Retrieve_NamespaceFiltering(t *testing.T) { + t.Skip("pending Height tracking from fiber DA") + + _, cl := makeTestFiberClient(t) + + ns1 := datypes.NamespaceFromString("ns-a").Bytes() + ns2 := datypes.NamespaceFromString("ns-b").Bytes() + + res1 := cl.Submit(context.Background(), [][]byte{[]byte("alpha")}, 0, ns1, nil) + require.Equal(t, datypes.StatusSuccess, res1.Code) + + res2 := cl.Submit(context.Background(), [][]byte{[]byte("beta")}, 0, ns2, nil) + require.Equal(t, datypes.StatusSuccess, res2.Code) + + rr1 := cl.Retrieve(context.Background(), res1.Height, ns1) + require.Equal(t, datypes.StatusSuccess, rr1.Code) + require.Equal(t, []byte("alpha"), rr1.Data[0]) + + rr2 := cl.Retrieve(context.Background(), res1.Height, ns2) + require.Equal(t, datypes.StatusNotFound, rr2.Code) + + rr3 := cl.Retrieve(context.Background(), res2.Height, ns2) + require.Equal(t, datypes.StatusSuccess, rr3.Code) + require.Equal(t, []byte("beta"), rr3.Data[0]) +} + +func TestFiberClient_Get_Success(t *testing.T) { + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + submitRes := cl.Submit(context.Background(), [][]byte{[]byte("getme")}, 0, ns, nil) + require.Equal(t, datypes.StatusSuccess, submitRes.Code) + require.Len(t, submitRes.IDs, 1) + + blobs, err := cl.Get(context.Background(), submitRes.IDs, ns) + require.NoError(t, err) + require.Len(t, blobs, 1) + require.Equal(t, []byte("getme"), blobs[0]) +} + +func TestFiberClient_Get_EmptyIDs(t *testing.T) { + _, cl := makeTestFiberClient(t) + + blobs, err := cl.Get(context.Background(), nil, nil) + require.NoError(t, err) + require.Nil(t, blobs) +} + +func TestFiberClient_Get_DownloadError(t *testing.T) { + _, cl := makeTestFiberClient(t) + + fakeBlobID := make([]byte, 33) + _, err := cl.Get(context.Background(), []datypes.ID{fakeBlobID}, nil) + require.Error(t, err) + require.Contains(t, err.Error(), "fiber download failed") +} + +func TestFiberClient_GetProofs_Success(t *testing.T) { + t.Skip() // not implemented + + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + submitRes := cl.Submit(context.Background(), [][]byte{[]byte("prooftest")}, 0, ns, nil) + require.Equal(t, datypes.StatusSuccess, submitRes.Code) + + proofs, err := cl.GetProofs(context.Background(), submitRes.IDs, ns) + require.NoError(t, err) + require.Len(t, proofs, 1) + require.NotEmpty(t, proofs[0]) +} + +func TestFiberClient_GetProofs_Empty(t *testing.T) { + t.Skip() // not implemented + + _, cl := makeTestFiberClient(t) + + proofs, err := cl.GetProofs(context.Background(), nil, nil) + require.NoError(t, err) + require.Empty(t, proofs) +} + +func TestFiberClient_Validate_Success(t *testing.T) { + t.Skip() // not implemented + + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + submitRes := cl.Submit(context.Background(), [][]byte{[]byte("validateme")}, 0, ns, nil) + require.Equal(t, datypes.StatusSuccess, submitRes.Code) + + proofs, err := cl.GetProofs(context.Background(), submitRes.IDs, ns) + require.NoError(t, err) + + results, err := cl.Validate(context.Background(), submitRes.IDs, proofs, ns) + require.NoError(t, err) + require.Len(t, results, 1) + require.True(t, results[0]) +} + +func TestFiberClient_Validate_MismatchedLengths(t *testing.T) { + _, cl := makeTestFiberClient(t) + + _, err := cl.Validate(context.Background(), make([]datypes.ID, 3), make([]datypes.Proof, 2), nil) + require.Error(t, err) + require.Contains(t, err.Error(), "must match") +} + +func TestFiberClient_Validate_Empty(t *testing.T) { + _, cl := makeTestFiberClient(t) + + results, err := cl.Validate(context.Background(), nil, nil, nil) + require.NoError(t, err) + require.Empty(t, results) +} + +func TestFiberClient_Validate_WrongProof(t *testing.T) { + t.Skip() // not implemented + + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + submitRes := cl.Submit(context.Background(), [][]byte{[]byte("validatewrong")}, 0, ns, nil) + require.Equal(t, datypes.StatusSuccess, submitRes.Code) + + fakeProofs := []datypes.Proof{[]byte("wrong-proof")} + results, err := cl.Validate(context.Background(), submitRes.IDs, fakeProofs, ns) + require.NoError(t, err) + require.Len(t, results, 1) + require.False(t, results[0]) +} + +func TestFiberClient_Validate_EmptyProof(t *testing.T) { + t.Skip() // not implemented + + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + submitRes := cl.Submit(context.Background(), [][]byte{[]byte("data")}, 0, ns, nil) + require.Equal(t, datypes.StatusSuccess, submitRes.Code) + + emptyProofs := []datypes.Proof{[]byte{}} + results, err := cl.Validate(context.Background(), submitRes.IDs, emptyProofs, ns) + require.NoError(t, err) + require.False(t, results[0]) +} + +func TestFiberClient_Namespaces(t *testing.T) { + mock := fibremock.NewMockDA(fibremock.DefaultMockDAConfig()) + cl, err := NewFiberClient(FiberConfig{ + Client: mock, + Logger: zerolog.Nop(), + Namespace: "header-ns", + DataNamespace: "data-ns", + }) + require.NotNil(t, cl) + require.NoError(t, err) + + require.Equal(t, datypes.NamespaceFromString("header-ns").Bytes(), cl.GetHeaderNamespace()) + require.Equal(t, datypes.NamespaceFromString("data-ns").Bytes(), cl.GetDataNamespace()) + require.False(t, cl.HasForcedInclusionNamespace()) +} + +func TestFiberClient_NoForcedNamespace(t *testing.T) { + mock := fibremock.NewMockDA(fibremock.DefaultMockDAConfig()) + cl, err := NewFiberClient(FiberConfig{ + Client: mock, + Logger: zerolog.Nop(), + Namespace: "header-ns", + DataNamespace: "data-ns", + }) + require.NotNil(t, cl) + require.NoError(t, err) + + require.Nil(t, cl.GetForcedInclusionNamespace()) + require.False(t, cl.HasForcedInclusionNamespace()) +} + +func TestFiberClient_Subscribe(t *testing.T) { + t.Skip("pending Height tracking from fiber DA") + _, cl := makeTestFiberClient(t) + + ctx := t.Context() + + ch, err := cl.Subscribe(ctx, datypes.NamespaceFromString("test-ns").Bytes(), false) + require.NoError(t, err) + require.NotNil(t, ch) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + submitRes := cl.Submit(context.Background(), [][]byte{[]byte("sub-data")}, 0, ns, nil) + require.Equal(t, datypes.StatusSuccess, submitRes.Code) + + select { + case ev := <-ch: + require.Equal(t, submitRes.Height, ev.Height) + require.Len(t, ev.Blobs, 1) + require.Equal(t, []byte("sub-data"), ev.Blobs[0]) + case <-time.After(5 * time.Second): + t.Fatal("subscribe did not emit event within timeout") + } +} + +func TestFiberClient_Submit_MultipleBlobs(t *testing.T) { + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + data := [][]byte{[]byte("first"), []byte("second"), []byte("third")} + res := cl.Submit(context.Background(), data, 0, ns, nil) + + require.Equal(t, datypes.StatusSuccess, res.Code) + require.Len(t, res.IDs, 1) + require.Equal(t, uint64(3), res.SubmittedCount) + + blobs, err := cl.Get(context.Background(), res.IDs, ns) + require.NoError(t, err) + require.Len(t, blobs, 3) + require.Equal(t, []byte("first"), blobs[0]) + require.Equal(t, []byte("second"), blobs[1]) + require.Equal(t, []byte("third"), blobs[2]) +} + +func TestFiberClient_SubmitAndDownload(t *testing.T) { + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + data := []byte("download-test") + submitRes := cl.Submit(context.Background(), [][]byte{data}, 0, ns, nil) + require.Equal(t, datypes.StatusSuccess, submitRes.Code) + + blobs, err := cl.Get(context.Background(), submitRes.IDs, ns) + require.NoError(t, err) + require.Len(t, blobs, 1) + require.Equal(t, data, blobs[0]) +} + +func TestFiberClient_DefaultTimeout(t *testing.T) { + mock := fibremock.NewMockDA(fibremock.DefaultMockDAConfig()) + cl, err := NewFiberClient(FiberConfig{ + Client: mock, + Logger: zerolog.Nop(), + Namespace: "ns", + DataNamespace: "ns", + }) + require.NotNil(t, cl) + require.NoError(t, err) + + fc := cl.(*fiberDAClient) + require.Equal(t, 60*time.Second, fc.defaultTimeout) +} + +func TestFiberClient_FullSubmitRetrieveCycle(t *testing.T) { + t.Skip() // not implemented + + _, cl := makeTestFiberClient(t) + + ns := datypes.NamespaceFromString("test-ns").Bytes() + + submitRes := cl.Submit(context.Background(), [][]byte{[]byte("cycle-data")}, 0, ns, nil) + require.Equal(t, datypes.StatusSuccess, submitRes.Code) + require.Len(t, submitRes.IDs, 1) + submittedHeight := submitRes.Height + + retrieveRes := cl.Retrieve(context.Background(), submittedHeight, ns) + require.Equal(t, datypes.StatusSuccess, retrieveRes.Code) + require.Equal(t, []byte("cycle-data"), retrieveRes.Data[0]) + + blobs, err := cl.Get(context.Background(), submitRes.IDs, ns) + require.NoError(t, err) + require.Equal(t, []byte("cycle-data"), blobs[0]) + + proofs, err := cl.GetProofs(context.Background(), submitRes.IDs, ns) + require.NoError(t, err) + require.NotEmpty(t, proofs[0]) + + valid, err := cl.Validate(context.Background(), submitRes.IDs, proofs, ns) + require.NoError(t, err) + require.True(t, valid[0]) +} + +type faultInjector struct { + FiberClient + err error +} + +func (f *faultInjector) SetError(err error) { f.err = err } + +func (f *faultInjector) Upload(ctx context.Context, namespace, data []byte) (fiber.UploadResult, error) { + if f.err != nil { + return fiber.UploadResult{}, f.err + } + return f.FiberClient.Upload(ctx, namespace, data) +} + +type failOnNthUpload struct { + FiberClient + failAt uint64 + err error + callCount atomic.Uint64 +} + +func (f *failOnNthUpload) Upload(ctx context.Context, namespace, data []byte) (fiber.UploadResult, error) { + n := f.callCount.Add(1) + if n == f.failAt { + return fiber.UploadResult{}, f.err + } + return f.FiberClient.Upload(ctx, namespace, data) +} + +func TestFlattenSplitBlobs_Roundtrip(t *testing.T) { + cases := []struct { + name string + blobs [][]byte + }{ + {"single", [][]byte{[]byte("hello")}}, + {"multiple", [][]byte{[]byte("first"), []byte("second"), []byte("third")}}, + {"empty_blob", [][]byte{[]byte{}, []byte("data"), []byte{}}}, + {"nil_blob", [][]byte{nil, []byte("data")}}, + {"large", [][]byte{make([]byte, 1024), make([]byte, 4096)}}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + flat := flattenBlobs(tc.blobs) + got, err := splitBlobs(flat) + require.NoError(t, err) + require.Equal(t, len(tc.blobs), len(got)) + for i, b := range got { + expected := tc.blobs[i] + if expected == nil { + expected = []byte{} + } + require.Equal(t, expected, b) + } + }) + } +} + +func TestFlattenBlobs_Empty(t *testing.T) { + require.Nil(t, flattenBlobs(nil)) + require.Nil(t, flattenBlobs([][]byte{})) +} + +func TestSplitBlobs_Empty(t *testing.T) { + got, err := splitBlobs(nil) + require.NoError(t, err) + require.Nil(t, got) + + got, err = splitBlobs([]byte{}) + require.NoError(t, err) + require.Nil(t, got) +} + +func TestSplitBlobs_Truncated(t *testing.T) { + _, err := splitBlobs([]byte{0x01}) + require.Error(t, err) + + _, err = splitBlobs([]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00}) + require.Error(t, err) +} + +func TestSplitBlobs_CountMismatch(t *testing.T) { + data := []byte{0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x41} + _, err := splitBlobs(data) + require.Error(t, err) +} diff --git a/block/internal/da/fibremock/mock.go b/block/internal/da/fibremock/mock.go new file mode 100644 index 0000000000..906e76fe74 --- /dev/null +++ b/block/internal/da/fibremock/mock.go @@ -0,0 +1,294 @@ +package fibremock + +import ( + "context" + "crypto/sha256" + "errors" + "fmt" + "sync" + "time" + + "github.com/evstack/ev-node/block/internal/da/fiber" +) + +var ( + // ErrBlobNotFound is returned when a blob ID is not in the store. + ErrBlobNotFound = errors.New("blob not found") + // ErrDataEmpty is returned when Upload is called with empty data. + ErrDataEmpty = errors.New("data cannot be empty") +) + +// MockDAConfig configures the mock DA implementation. +type MockDAConfig struct { + // MaxBlobs is the maximum number of blobs stored in memory. + // When exceeded, the oldest blob is evicted regardless of retention. + // 0 means no limit (use with caution — large blobs will OOM). + MaxBlobs int + // Retention is how long blobs are kept before automatic pruning. + // 0 means blobs are kept until evicted by MaxBlobs. + Retention time.Duration +} + +// DefaultMockDAConfig returns a config suitable for testing: +// 100 blobs max, 10 minute retention. +func DefaultMockDAConfig() MockDAConfig { + return MockDAConfig{ + MaxBlobs: 100, + Retention: 10 * time.Minute, + } +} + +// storedBlob holds a blob and its metadata in the mock store. +type storedBlob struct { + namespace []byte + data []byte + height uint64 + expiresAt time.Time + createdAt time.Time +} + +// subscriber tracks a Listen subscription. +type subscriber struct { + namespace []byte + ch chan fiber.BlobEvent +} + +// MockDA is an in-memory mock implementation of the DA interface. +// It stores blobs in memory with configurable retention and max blob count. +// Safe for concurrent use. +type MockDA struct { + cfg MockDAConfig + + mu sync.RWMutex + blobs map[string]*storedBlob // keyed by hex(blobID) + order []string // insertion order for LRU eviction + height uint64 + subscribers []subscriber +} + +// NewMockDA creates a new mock DA with the given config. +func NewMockDA(cfg MockDAConfig) *MockDA { + return &MockDA{ + cfg: cfg, + blobs: make(map[string]*storedBlob), + } +} + +// Upload stores the blob in memory and notifies listeners. +func (m *MockDA) Upload(ctx context.Context, namespace []byte, data []byte) (fiber.UploadResult, error) { + if len(data) == 0 { + return fiber.UploadResult{}, ErrDataEmpty + } + + blobID := mockBlobID(data) + key := fmt.Sprintf("%x", blobID) + now := time.Now() + + var expiresAt time.Time + if m.cfg.Retention > 0 { + expiresAt = now.Add(m.cfg.Retention) + } + + m.mu.Lock() + + // Evict oldest if at capacity + if m.cfg.MaxBlobs > 0 && len(m.blobs) >= m.cfg.MaxBlobs { + m.evictOldestLocked() + } + + // Prune expired blobs opportunistically + if m.cfg.Retention > 0 { + m.pruneExpiredLocked(now) + } + + m.height++ + height := m.height + + m.blobs[key] = &storedBlob{ + namespace: namespace, + data: data, + height: height, + expiresAt: expiresAt, + createdAt: now, + } + m.order = append(m.order, key) + + // Notify subscribers (non-blocking) + event := fiber.BlobEvent{ + BlobID: blobID, + Height: height, + DataSize: uint64(len(data)), + } + for i := range m.subscribers { + if namespaceMatch(m.subscribers[i].namespace, namespace) { + select { + case m.subscribers[i].ch <- event: + default: + // Channel full, drop event. Subscriber is too slow. + } + } + } + + m.mu.Unlock() + + return fiber.UploadResult{ + BlobID: blobID, + ExpiresAt: expiresAt, + }, nil +} + +// Download retrieves a blob by ID. +func (m *MockDA) Download(ctx context.Context, blobID fiber.BlobID) ([]byte, error) { + key := fmt.Sprintf("%x", blobID) + + m.mu.RLock() + blob, ok := m.blobs[key] + m.mu.RUnlock() + + if !ok { + return nil, ErrBlobNotFound + } + + if !blob.expiresAt.IsZero() && time.Now().After(blob.expiresAt) { + return nil, ErrBlobNotFound + } + + return blob.data, nil +} + +// Listen returns a channel that receives events when blobs matching the +// namespace are uploaded, starting at fromHeight. +// +// fromHeight == 0 subscribes to future uploads only. fromHeight > 0 first +// replays every matching blob still in the store with height >= fromHeight, +// then attaches a live subscriber for subsequent uploads. The replay may +// interleave with live events emitted between the Listen call and the +// replay goroutine's drain; consumers should dedupe by BlobID. +// +// The channel is closed when ctx is cancelled. +func (m *MockDA) Listen(ctx context.Context, namespace []byte, fromHeight uint64) (<-chan fiber.BlobEvent, error) { + ch := make(chan fiber.BlobEvent, 64) + + m.mu.Lock() + // Snapshot matching historicals under the lock to avoid racing with + // concurrent Upload calls; the replay goroutine emits them after. + var replay []fiber.BlobEvent + if fromHeight > 0 { + for _, key := range m.order { + b, ok := m.blobs[key] + if !ok { + continue + } + if !namespaceMatch(namespace, b.namespace) { + continue + } + if b.height < fromHeight { + continue + } + replay = append(replay, fiber.BlobEvent{ + BlobID: mockBlobID(b.data), + Height: b.height, + DataSize: uint64(len(b.data)), + }) + } + } + idx := len(m.subscribers) + m.subscribers = append(m.subscribers, subscriber{ + namespace: namespace, + ch: ch, + }) + m.mu.Unlock() + + // Replay historical events in a goroutine so the caller isn't + // blocked if the buffer fills. Live events may interleave. + var replayDone sync.WaitGroup + if len(replay) > 0 { + replayDone.Go(func() { + for _, ev := range replay { + select { + case ch <- ev: + case <-ctx.Done(): + return + } + } + }) + } + + // Clean up when context is done. + go func() { + <-ctx.Done() + m.mu.Lock() + // Remove subscriber by swapping with last + last := len(m.subscribers) - 1 + if idx <= last { + m.subscribers[idx] = m.subscribers[last] + } + m.subscribers = m.subscribers[:last] + m.mu.Unlock() + replayDone.Wait() + close(ch) + }() + + return ch, nil +} + +// BlobCount returns the number of blobs currently stored. +func (m *MockDA) BlobCount() int { + m.mu.RLock() + defer m.mu.RUnlock() + return len(m.blobs) +} + +// evictOldestLocked removes the oldest blob. Caller must hold m.mu. +func (m *MockDA) evictOldestLocked() { + if len(m.order) == 0 { + return + } + key := m.order[0] + m.order = m.order[1:] + delete(m.blobs, key) +} + +// pruneExpiredLocked removes blobs past their retention. Caller must hold m.mu. +func (m *MockDA) pruneExpiredLocked(now time.Time) { + surviving := m.order[:0] + for _, key := range m.order { + blob, ok := m.blobs[key] + if !ok { + continue + } + if !blob.expiresAt.IsZero() && now.After(blob.expiresAt) { + delete(m.blobs, key) + } else { + surviving = append(surviving, key) + } + } + m.order = surviving +} + +// namespaceMatch returns true if the subscription namespace matches the blob namespace. +// An empty subscription namespace matches all namespaces (wildcard). +func namespaceMatch(subNS, blobNS []byte) bool { + if len(subNS) == 0 { + return true + } + if len(subNS) != len(blobNS) { + return false + } + for i := range subNS { + if subNS[i] != blobNS[i] { + return false + } + } + return true +} + +// mockBlobID produces a deterministic blob ID from the data. +// Format: 1 byte version (0) + 32 bytes SHA256 hash. +func mockBlobID(data []byte) fiber.BlobID { + hash := sha256.Sum256(data) + id := make([]byte, 33) + id[0] = 0 // version byte + copy(id[1:], hash[:]) + return id +} diff --git a/block/internal/da/fibremock/mock_test.go b/block/internal/da/fibremock/mock_test.go new file mode 100644 index 0000000000..5bfe38af7a --- /dev/null +++ b/block/internal/da/fibremock/mock_test.go @@ -0,0 +1,196 @@ +package fibremock + +import ( + "bytes" + "context" + "testing" + "time" + + "github.com/evstack/ev-node/block/internal/da/fiber" +) + +func TestMockDA_UploadDownload(t *testing.T) { + m := NewMockDA(DefaultMockDAConfig()) + ctx := context.Background() + + ns := []byte("test-ns") + data := []byte("hello fibre") + + result, err := m.Upload(ctx, ns, data) + if err != nil { + t.Fatal(err) + } + if len(result.BlobID) != 33 { + t.Fatalf("expected 33-byte blob ID, got %d", len(result.BlobID)) + } + + got, err := m.Download(ctx, result.BlobID) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(got, data) { + t.Fatalf("data mismatch: got %q, want %q", got, data) + } +} + +func TestMockDA_UploadEmpty(t *testing.T) { + m := NewMockDA(DefaultMockDAConfig()) + _, err := m.Upload(context.Background(), []byte("ns"), nil) + if err != ErrDataEmpty { + t.Fatalf("expected ErrDataEmpty, got %v", err) + } +} + +func TestMockDA_DownloadNotFound(t *testing.T) { + m := NewMockDA(DefaultMockDAConfig()) + _, err := m.Download(context.Background(), fiber.BlobID{0, 1, 2}) + if err != ErrBlobNotFound { + t.Fatalf("expected ErrBlobNotFound, got %v", err) + } +} + +func TestMockDA_Listen(t *testing.T) { + m := NewMockDA(DefaultMockDAConfig()) + ctx := t.Context() + + ns := []byte("test-ns") + ch, err := m.Listen(ctx, ns, 0) + if err != nil { + t.Fatal(err) + } + + // Upload a blob — should trigger the listener + data := []byte("listened blob") + result, err := m.Upload(ctx, ns, data) + if err != nil { + t.Fatal(err) + } + + select { + case event := <-ch: + if !bytes.Equal(event.BlobID, result.BlobID) { + t.Fatal("blob ID mismatch in event") + } + if event.Height == 0 { + t.Fatal("expected non-zero height") + } + if event.DataSize != uint64(len(data)) { + t.Fatalf("expected data size %d, got %d", len(data), event.DataSize) + } + case <-time.After(time.Second): + t.Fatal("timeout waiting for event") + } +} + +func TestMockDA_ListenNamespaceFilter(t *testing.T) { + m := NewMockDA(DefaultMockDAConfig()) + ctx := t.Context() + + ch, err := m.Listen(ctx, []byte("ns-A"), 0) + if err != nil { + t.Fatal(err) + } + + // Upload to different namespace — should NOT trigger + m.Upload(ctx, []byte("ns-B"), []byte("wrong namespace")) + + select { + case <-ch: + t.Fatal("should not receive event for different namespace") + case <-time.After(50 * time.Millisecond): + // good + } +} + +func TestMockDA_ListenWildcard(t *testing.T) { + m := NewMockDA(DefaultMockDAConfig()) + ctx := t.Context() + + // Empty namespace = wildcard + ch, err := m.Listen(ctx, nil, 0) + if err != nil { + t.Fatal(err) + } + + m.Upload(ctx, []byte("any-ns"), []byte("wildcard test")) + + select { + case event := <-ch: + if event.Height == 0 { + t.Fatal("expected event") + } + case <-time.After(time.Second): + t.Fatal("timeout waiting for wildcard event") + } +} + +func TestMockDA_MaxBlobsEviction(t *testing.T) { + m := NewMockDA(MockDAConfig{MaxBlobs: 3}) + ctx := context.Background() + + var ids []fiber.BlobID + for i := range 5 { + r, err := m.Upload(ctx, nil, []byte{byte(i), 1, 2, 3}) + if err != nil { + t.Fatal(err) + } + ids = append(ids, r.BlobID) + } + + // First two should be evicted + if _, err := m.Download(ctx, ids[0]); err != ErrBlobNotFound { + t.Fatal("expected first blob to be evicted") + } + if _, err := m.Download(ctx, ids[1]); err != ErrBlobNotFound { + t.Fatal("expected second blob to be evicted") + } + + // Last three should still be there + for i := 2; i < 5; i++ { + if _, err := m.Download(ctx, ids[i]); err != nil { + t.Fatalf("blob %d should exist: %v", i, err) + } + } + + if m.BlobCount() != 3 { + t.Fatalf("expected 3 blobs, got %d", m.BlobCount()) + } +} + +func TestMockDA_Retention(t *testing.T) { + m := NewMockDA(MockDAConfig{Retention: 50 * time.Millisecond}) + ctx := context.Background() + + r, err := m.Upload(ctx, nil, []byte("ephemeral")) + if err != nil { + t.Fatal(err) + } + + // Should exist immediately + if _, err := m.Download(ctx, r.BlobID); err != nil { + t.Fatal("blob should exist immediately") + } + + // Wait for expiry + time.Sleep(100 * time.Millisecond) + + if _, err := m.Download(ctx, r.BlobID); err != ErrBlobNotFound { + t.Fatal("blob should have expired") + } +} + +func TestMockDA_DeterministicBlobID(t *testing.T) { + m := NewMockDA(DefaultMockDAConfig()) + ctx := context.Background() + + data := []byte("deterministic") + r1, _ := m.Upload(ctx, nil, data) + r2, _ := m.Upload(ctx, nil, data) + + if !bytes.Equal(r1.BlobID, r2.BlobID) { + t.Fatal("same data should produce same blob ID") + } +} + +// Verify MockDA satisfies the DA interface at compile time. +var _ fiber.DA = (*MockDA)(nil) diff --git a/block/internal/executing/executor.go b/block/internal/executing/executor.go index f5be5e1b40..69746841ad 100644 --- a/block/internal/executing/executor.go +++ b/block/internal/executing/executor.go @@ -627,19 +627,22 @@ func (e *Executor) ProduceBlock(ctx context.Context) error { signature: signature, }) - // Broadcast header and data to P2P network sequentially. - // IMPORTANT: Header MUST be broadcast before data — the P2P layer validates - // incoming data against the current and previous header, so out-of-order - // delivery would cause validation failures on peers. - if err := e.headerBroadcaster.WriteToStoreAndBroadcast(ctx, &types.P2PSignedHeader{ - SignedHeader: header, - }); err != nil { - e.logger.Error().Err(err).Msg("failed to broadcast header") - } - if err := e.dataBroadcaster.WriteToStoreAndBroadcast(ctx, &types.P2PData{ - Data: data, - }); err != nil { - e.logger.Error().Err(err).Msg("failed to broadcast data") + // No broadcast to P2P when fiber is enabled. + if !e.config.DA.IsFiberEnabled() { + // Broadcast header and data to P2P network sequentially. + // IMPORTANT: Header MUST be broadcast before data — the P2P layer validates + // incoming data against the current and previous header, so out-of-order + // delivery would cause validation failures on peers. + if err := e.headerBroadcaster.WriteToStoreAndBroadcast(ctx, &types.P2PSignedHeader{ + SignedHeader: header, + }); err != nil { + e.logger.Error().Err(err).Msg("failed to broadcast header") + } + if err := e.dataBroadcaster.WriteToStoreAndBroadcast(ctx, &types.P2PData{ + Data: data, + }); err != nil { + e.logger.Error().Err(err).Msg("failed to broadcast data") + } } e.recordBlockMetrics(newState, data) @@ -660,11 +663,35 @@ func (e *Executor) ProduceBlock(ctx context.Context) error { return nil } +// blockMarshalOverhead reserves a fraction of MaxBlobSize for the proto +// framing + Metadata overhead added when types.Data is marshaled into a +// DA blob. Empirically the per-tx proto length-prefix runs ~3 bytes, +// which is roughly 1.5% at 200 B txs and stays in that range across +// realistic tx sizes; 2% gives margin for fixed Metadata fields without +// leaving meaningful capacity unused. Reserving here (vs. inside +// FilterTxs) keeps the executor’s view of MaxBytes equal to the raw-tx +// budget and prevents a fully packed batch from blowing past the +// submitter’s MaxBlobSize check. +// +// TODO(throughput-cleanup): this is the workaround half of a deeper +// issue — common.DefaultMaxBlobSize is used as both the raw-tx +// budget AND the marshaled-blob ceiling. The right fix is to derive +// a MaxBlockTxBytes() value once (= MaxBlobSize - overhead) and have +// RetrieveBatch / FilterTxs / da_submitter.limitBatchBySize all +// reference the appropriate value rather than each enforcing the +// same number with their own ad-hoc adjustments. See +// common/consts.go for the umbrella TODO. +const blockMarshalOverheadPct = 2 + // RetrieveBatch gets the next batch of transactions from the sequencer. func (e *Executor) RetrieveBatch(ctx context.Context) (*BatchData, error) { + maxTxBytes := common.DefaultMaxBlobSize + if reserve := maxTxBytes * blockMarshalOverheadPct / 100; reserve < maxTxBytes { + maxTxBytes -= reserve + } req := coresequencer.GetNextBatchRequest{ Id: []byte(e.genesis.ChainID), - MaxBytes: common.DefaultMaxBlobSize, + MaxBytes: maxTxBytes, LastBatchData: [][]byte{}, // Can be populated if needed for sequencer context } diff --git a/block/internal/reaping/reaper.go b/block/internal/reaping/reaper.go index d35dbfff3e..7cd9e27450 100644 --- a/block/internal/reaping/reaper.go +++ b/block/internal/reaping/reaper.go @@ -21,7 +21,21 @@ import ( const ( // MaxBackoffInterval is the maximum backoff interval for retries MaxBackoffInterval = 30 * time.Second - CleanupInterval = 1 * time.Hour + + // CleanupInterval is how often the reaper sweeps expired hashes + // out of the seen-tx cache. + // + // HACK(fiber-throughput): dropped from 1h to 5s. The original + // 1h was effectively coupled to the previous 24h retention — + // sweeping every hour against a 24h window means a cache entry + // can outlive its retention by 1h, which is fine when retention + // is a day but completely breaks at 30s retention (entries + // would survive 12× past expiry). Whatever the right retention + // turns out to be (see DefaultTxCacheRetention's note in + // cache/manager.go), this value should be a small fraction of + // it — not a fixed time. Better to derive: e.g. + // retention/10 with a sane min/max. + CleanupInterval = 5 * time.Second ) // Reaper is responsible for periodically retrieving transactions from the executor, diff --git a/block/internal/submitting/da_submitter.go b/block/internal/submitting/da_submitter.go index 83f56d9cb5..bb30eb6461 100644 --- a/block/internal/submitting/da_submitter.go +++ b/block/internal/submitting/da_submitter.go @@ -43,14 +43,48 @@ type retryPolicy struct { MinBackoff time.Duration MaxBackoff time.Duration MaxBlobBytes uint64 + // MaxItems caps the number of items packed into a single Submit + // call. DA clients that fan out per-item Uploads (fiber) benefit + // linearly from larger batches — settlement throughput scales + // with concurrency until per-Upload latency dominates. Default + // 1 preserves legacy single-item-per-Submit semantics for + // backends that flatten a batch into one blob (JSON-RPC blob + // client). The fiber path overrides this from config. + MaxItems int } +// defaultBatchItems is the conservative default for non-fiber backends +// that historically expected one item per Submit call. The fiber path +// raises this via config because it can fan out per-item Uploads. +const defaultBatchItems = 1 + +// fiberDefaultBatchItems is the upper bound on items packed into a +// single fiber Submit. Each item gets its own concurrent Upload, so +// this caps the per-batch goroutine fan-out. +// +// HACK(fiber-throughput): hardcoded at 16. The right value depends on +// memory budget × per-item Upload size × Fibre validator-side +// throughput, none of which the submitter can know at compile time. +// Should be a config knob (FiberDAConfig.UploadConcurrency was +// scaffolded for exactly this earlier — wire it through here when the +// concurrent-uploads change graduates from prototype). 16 is a +// pragmatic measurement default that gives meaningful concurrency +// without overwhelming celestia-node's per-FSP rate. +const fiberDefaultBatchItems = 16 + func defaultRetryPolicy(maxAttempts int, maxDuration time.Duration) retryPolicy { return retryPolicy{ - MaxAttempts: maxAttempts, - MinBackoff: initialBackoff, - MaxBackoff: maxDuration, + MaxAttempts: maxAttempts, + MinBackoff: initialBackoff, + MaxBackoff: maxDuration, + // TODO(throughput-cleanup): same value is used by + // executing/executor.go::RetrieveBatch as the raw-tx budget + // (with a 2% reservation) and again here as the marshaled + // blob ceiling. They are semantically different limits; + // the duplication is what made packed-block-larger-than-cap + // failures non-obvious. See common/consts.go. MaxBlobBytes: common.DefaultMaxBlobSize, + MaxItems: defaultBatchItems, } } @@ -572,12 +606,19 @@ func submitToDA[T any]( } pol := defaultRetryPolicy(s.config.DA.MaxSubmitAttempts, s.config.DA.BlockTime.Duration) + // Fiber's DA client fans out per-item Uploads concurrently, so + // packing more items per Submit lifts settlement throughput. For + // non-fiber backends the default of 1 preserves the legacy + // flatten-one-blob behavior. + if s.config.DA.IsFiberEnabled() { + pol.MaxItems = fiberDefaultBatchItems + } rs := retryState{Attempt: 0, Backoff: 0} // Limit this submission to a single size-capped batch if len(marshaled) > 0 { - batchItems, batchMarshaled, err := limitBatchBySize(items, marshaled, pol.MaxBlobBytes) + batchItems, batchMarshaled, err := limitBatchBySize(items, marshaled, pol.MaxBlobBytes, pol.MaxItems) if err != nil { s.logger.Error(). Str("itemType", itemType). @@ -688,27 +729,42 @@ func submitToDA[T any]( return fmt.Errorf("failed to submit all %s(s) to DA layer after %d attempts", itemType, rs.Attempt) } -// limitBatchBySize returns a prefix of items whose total marshaled size does not exceed maxBytes. -// If the first item exceeds maxBytes, it returns ErrOversizedItem which is unrecoverable. -func limitBatchBySize[T any](items []T, marshaled [][]byte, maxBytes uint64) ([]T, [][]byte, error) { - total := uint64(0) +// limitBatchBySize returns a prefix of items whose per-item marshaled size +// fits within maxItemBytes. The total batch size is bounded by item count +// (maxItems), not by total bytes — DA clients that can fan out per-item +// Uploads (e.g. the fiber DA client) settle each item in its own +// concurrent Upload call, so packing more items per batch lifts the +// effective settlement throughput. DA clients that flatten a batch into +// a single blob still get one item per call when maxItems == 1. +// +// If the first item exceeds maxItemBytes, returns ErrOversizedItem +// (unrecoverable). If no items fit at all (empty inputs), returns a +// distinct error so the caller can distinguish "nothing to send". +// +// TODO(throughput-cleanup): see common/consts.go — maxItemBytes is the +// per-item chain ceiling, separate from the raw-tx budget driving +// FilterTxs. Once that split lands, the duplicate-cap-everywhere +// problem these fixes work around goes away. +func limitBatchBySize[T any](items []T, marshaled [][]byte, maxItemBytes uint64, maxItems int) ([]T, [][]byte, error) { + if maxItems <= 0 { + maxItems = 1 + } count := 0 for i := range items { + if count >= maxItems { + break + } sz := uint64(len(marshaled[i])) - if sz > maxBytes { + if sz > maxItemBytes { if i == 0 { - return nil, nil, fmt.Errorf("%w: item size %d exceeds max %d", common.ErrOversizedItem, sz, maxBytes) + return nil, nil, fmt.Errorf("%w: item size %d exceeds max %d", common.ErrOversizedItem, sz, maxItemBytes) } break } - if total+sz > maxBytes { - break - } - total += sz count++ } if count == 0 { - return nil, nil, fmt.Errorf("no items fit within %d bytes", maxBytes) + return nil, nil, fmt.Errorf("no items fit within %d bytes", maxItemBytes) } return items[:count], marshaled[:count], nil } diff --git a/block/public.go b/block/public.go index cc7691c299..e871bd0b9d 100644 --- a/block/public.go +++ b/block/public.go @@ -42,6 +42,17 @@ type DAVerifier = da.Verifier // This is the complete interface implemented by the concrete DA client. type FullDAClient = da.FullClient +// FiberClient is the interface for Fiber DA backends. Implementations +// handle upload, download and listen operations against a Fiber network. +type FiberClient = da.FiberClient + +// Fiber types exposed for external adapters (e.g. tools/local-fiber). +type ( + FiberBlobID = da.BlobID + FiberUploadResult = da.UploadResult + FiberBlobEvent = da.BlobEvent +) + // NewDAClient creates a new DA client backed by the blob JSON-RPC API. // The returned client implements both DAClient and DAVerifier interfaces. func NewDAClient( @@ -63,6 +74,34 @@ func NewDAClient( return base } +// NewFiberDAClient creates a new DA client backed by the Fiber protocol. +// The fiberClient parameter must implement the da.FiberClient interface. +// The returned client implements both DAClient and DAVerifier interfaces. +func NewFiberDAClient( + fiberClient da.FiberClient, + config config.Config, + logger zerolog.Logger, + lastKnownDaHeight uint64, +) FullDAClient { + base, err := da.NewFiberClient(da.FiberConfig{ + Client: fiberClient, + Logger: logger, + DefaultTimeout: config.DA.RequestTimeout.Duration, + Namespace: config.DA.GetNamespace(), + DataNamespace: config.DA.GetDataNamespace(), + LastKnownDAHeight: lastKnownDaHeight, + }) + if err != nil { + panic(err) + } + + if config.Instrumentation.IsTracingEnabled() { + return da.WithTracingClient(base) + } + + return base +} + // Exported errors used by the sequencers var ( // ErrForceInclusionNotConfigured is returned when force inclusion is not configured. diff --git a/pkg/cmd/run_node.go b/pkg/cmd/run_node.go index 8b241c98db..39da4cae05 100644 --- a/pkg/cmd/run_node.go +++ b/pkg/cmd/run_node.go @@ -15,7 +15,6 @@ import ( "github.com/rs/zerolog" "github.com/spf13/cobra" - "github.com/evstack/ev-node/block" coreexecutor "github.com/evstack/ev-node/core/execution" coresequencer "github.com/evstack/ev-node/core/sequencer" "github.com/evstack/ev-node/node" @@ -25,7 +24,10 @@ import ( "github.com/evstack/ev-node/pkg/p2p" "github.com/evstack/ev-node/pkg/p2p/key" pkgsigner "github.com/evstack/ev-node/pkg/signer" + "github.com/evstack/ev-node/pkg/store" "github.com/evstack/ev-node/pkg/telemetry" + + "github.com/evstack/ev-node/block" ) // ParseConfig is an helpers that loads the node configuration and validates it. @@ -86,6 +88,7 @@ func StartNode( nodeConfig rollconf.Config, genesis genesispkg.Genesis, nodeOptions node.NodeOptions, + fiberClient block.FiberClient, ) error { ctx, cancel := context.WithCancel(cmd.Context()) defer cancel() @@ -149,12 +152,32 @@ func StartNode( } } - blobClient, err := blobrpc.NewWSClient(ctx, logger, nodeConfig.DA.Address, nodeConfig.DA.AuthToken, "") - if err != nil { - return fmt.Errorf("failed to create blob client: %w", err) + var daClient block.FullDAClient + if nodeConfig.DA.IsFiberEnabled() { + if fiberClient == nil { + return fmt.Errorf("fiber DA is enabled but no fiber client was provided") + } + + mainKV := store.NewEvNodeKVStore(datastore) + baseStore := store.New(mainKV) + + var latestDAHeight uint64 + latestState, err := baseStore.GetState(cmd.Context()) + if err != nil { + latestDAHeight = genesis.DAStartHeight + } else { + latestDAHeight = latestState.DAHeight + } + + daClient = block.NewFiberDAClient(fiberClient, nodeConfig, logger, latestDAHeight) + } else { + blobClient, err := blobrpc.NewWSClient(ctx, logger, nodeConfig.DA.Address, nodeConfig.DA.AuthToken, "") + if err != nil { + return fmt.Errorf("failed to create blob client: %w", err) + } + defer blobClient.Close() + daClient = block.NewDAClient(blobClient, nodeConfig, logger) } - defer blobClient.Close() - daClient := block.NewDAClient(blobClient, nodeConfig, logger) // sanity check for based sequencer if nodeConfig.Node.BasedSequencer && genesis.DAStartHeight == 0 { diff --git a/pkg/cmd/run_node_test.go b/pkg/cmd/run_node_test.go index 1ed1a7e189..c41d7a2a3e 100644 --- a/pkg/cmd/run_node_test.go +++ b/pkg/cmd/run_node_test.go @@ -651,7 +651,7 @@ func TestStartNodeErrors(t *testing.T) { runFunc := func(ctx context.Context) { currentTestLogger := zerolog.Nop() cmd.SetContext(ctx) - err := StartNode(currentTestLogger, cmd, executor, sequencer, nodeKey, ds, nodeConfig, testGenesis, node.NodeOptions{}) //nolint:contextcheck // test invokes command entrypoint directly + err := StartNode(currentTestLogger, cmd, executor, sequencer, nodeKey, ds, nodeConfig, testGenesis, node.NodeOptions{}, nil) //nolint:contextcheck // test invokes command entrypoint directly if tc.expectedError != "" { assert.ErrorContains(t, err, tc.expectedError) } else { @@ -668,7 +668,7 @@ func TestStartNodeErrors(t *testing.T) { } else { assert.NotPanics(t, func() { runFunc(baseCtx) }) checkLogger := zerolog.Nop() - err := StartNode(checkLogger, cmd, executor, sequencer, nodeKey, ds, nodeConfig, testGenesis, node.NodeOptions{}) //nolint:contextcheck // test invokes command entrypoint directly + err := StartNode(checkLogger, cmd, executor, sequencer, nodeKey, ds, nodeConfig, testGenesis, node.NodeOptions{}, nil) //nolint:contextcheck // test invokes command entrypoint directly if tc.expectedError != "" { assert.ErrorContains(t, err, tc.expectedError) } @@ -702,7 +702,7 @@ func newRunNodeCmd( Aliases: []string{"node", "run"}, Short: "Run the rollkit node", RunE: func(cmd *cobra.Command, args []string) error { //nolint:contextcheck // cobra RunE signature is fixed - return StartNode(zerolog.Nop(), cmd, executor, sequencer, nodeKey, datastore, nodeConfig, testGenesis, node.NodeOptions{}) + return StartNode(zerolog.Nop(), cmd, executor, sequencer, nodeKey, datastore, nodeConfig, testGenesis, node.NodeOptions{}, nil) }, } diff --git a/pkg/config/config.go b/pkg/config/config.go index 87e12f5597..6a8bd48d05 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,6 +3,7 @@ package config import ( "errors" "fmt" + "net/url" "os" "path" "path/filepath" @@ -92,6 +93,19 @@ const ( // FlagDAStartHeight is a flag for forcing the DA retrieval height to start from a specific height FlagDAStartHeight = FlagPrefixEvnode + "da.start_height" + // Fiber DA configuration flags + + // FlagDAFiberEnabled enables the Fiber DA client instead of the default JSON-RPC blob client + FlagDAFiberEnabled = FlagPrefixEvnode + "da.fiber.enabled" + // FlagDAFiberConsensusAddress is the gRPC address of the celestia-app node for Fiber state queries + FlagDAFiberConsensusAddress = FlagPrefixEvnode + "da.fiber.consensus_address" + // FlagConsensusChainID is the Chain ID of the celestia app node + FlagDAFiberConsensusChainID = FlagPrefixEvnode + "da.fiber.consensus_chain_id" + // FlagDAFiberBridgeAddress is the gRPC address of the bridge node for Fiber state queries + FlagDAFiberBridgeAddress = FlagPrefixEvnode + "da.fiber.bridge_address" + // FlagDAFiberKeyName is the key name in the keyring to use for signing payment promises + FlagDAFiberKeyName = FlagPrefixEvnode + "da.fiber.key_name" + // P2P configuration flags // FlagP2PListenAddress is a flag for specifying the P2P listen address @@ -266,6 +280,67 @@ type DAConfig struct { BatchSizeThreshold float64 `mapstructure:"batch_size_threshold" yaml:"batch_size_threshold" comment:"Minimum blob size threshold (as fraction of max blob size, 0.0-1.0) before submitting. Only applies to 'size' and 'adaptive' strategies. Example: 0.8 means wait until batch is 80% full. Default: 0.8."` BatchMaxDelay DurationWrapper `mapstructure:"batch_max_delay" yaml:"batch_max_delay" comment:"Maximum time to wait before submitting a batch regardless of size. Applies to 'time' and 'adaptive' strategies. Lower values reduce latency but may increase costs. Examples: \"6s\", \"12s\", \"30s\". Default: DA BlockTime."` BatchMinItems uint64 `mapstructure:"batch_min_items" yaml:"batch_min_items" comment:"Minimum number of items (headers or data) to accumulate before considering submission. Helps avoid submitting single items when more are expected soon. Default: 1."` + + // Fiber DA client configuration + Fiber FiberDAConfig `mapstructure:"fiber" yaml:"fiber"` +} + +// FiberDAConfig contains configuration for the Fiber DA client. +// When Enabled is true, the Fiber client is used instead of the default +// JSON-RPC blob client for DA operations. +type FiberDAConfig struct { + // Enabled switches the DA backend from the default JSON-RPC blob client + // to the Fiber protocol client. + Enabled bool `mapstructure:"enabled" yaml:"enabled" comment:"Enable the Fiber DA client for direct validator communication instead of the default JSON-RPC blob client"` + // ConsensusChainID is the Chain ID of the chain to which data is posted. + ConsensusChainID string `mapstructure:"consensus_chain_id" yaml:"consensus_chain_id" comment:"Chain ID of the chain to which data is posted"` + // ConsensusAddress is the gRPC address of the celestia-app node used for state queries (validator set, chain ID, promise verification). + ConsensusAddress string `mapstructure:"consensus_address" yaml:"consensus_address" comment:"gRPC address of the celestia-app node for Fiber state queries (host:port)"` + // BridgeAddress is the address of the bridge node. + BridgeAddress string `mapstructure:"bridge_address" yaml:"bridge_address" comment:"Bridge Node Address for Fiber"` + // KeyringPath is the directory path containing the keyring for signing + // Fiber payment promises. + KeyringPath string `mapstructure:"keyring_path" yaml:"keyring_path" comment:"Path to the keyring directory for Fiber payment promise signing"` + // KeyName is the name of the key in the keyring to use for signing. + KeyName string `mapstructure:"key_name" yaml:"key_name" comment:"Name of the key in the keyring to use for signing Fiber payment promises"` + // UploadConcurrency limits the number of concurrent upload connections +} + +// Validate checks that a FiberDAConfig is usable. Only called when enabled; +// a disabled block is always valid because the Fibre client is not built. +func (c *FiberDAConfig) Validate() error { + if !c.Enabled { + return nil + } + if c.ConsensusAddress == "" { + return fmt.Errorf("%s is required when fiber DA is enabled", FlagDAFiberConsensusAddress) + } + + if c.BridgeAddress == "" { + return fmt.Errorf("%s is required when fiber DA is enabled", FlagDAFiberBridgeAddress) + } + + if c.ConsensusChainID == "" { + return fmt.Errorf("%s is required when fiber DA is enabled", FlagDAFiberConsensusChainID) + } + + u, err := url.Parse(c.BridgeAddress) + if err != nil { + return fmt.Errorf("%s: %w", FlagDAFiberBridgeAddress, err) + } + if u.Scheme != "ws" && u.Scheme != "wss" { + return fmt.Errorf( + "%s must use ws:// or wss:// (got %q) — blob.Subscribe requires WebSocket, HTTP JSON-RPC cannot stream channels", + FlagDAFiberBridgeAddress, u.Scheme, + ) + } + + return nil +} + +// IsFiberEnabled returns true if the Fiber DA client is configured and enabled. +func (d *DAConfig) IsFiberEnabled() bool { + return d.Fiber.Enabled } // GetNamespace returns the namespace for header submissions. @@ -620,6 +695,13 @@ func AddFlags(cmd *cobra.Command) { cmd.Flags().Uint64(FlagDAStartHeight, def.DA.StartHeight, "force DA retrieval to start from a specific height (0 for disabled)") cmd.Flags().MarkHidden(FlagDAStartHeight) + // Fiber DA configuration flags + cmd.Flags().Bool(FlagDAFiberEnabled, def.DA.Fiber.Enabled, "enable the Fiber DA client for direct validator communication") + cmd.Flags().String(FlagDAFiberConsensusAddress, def.DA.Fiber.ConsensusAddress, "gRPC address of the celestia-app node for Fiber state queries (host:port)") + cmd.Flags().String(FlagDAFiberConsensusChainID, def.DA.Fiber.ConsensusChainID, "Chain ID of the celestia app") + cmd.Flags().String(FlagDAFiberBridgeAddress, def.DA.Fiber.BridgeAddress, "JSON RPC of the DA node") + cmd.Flags().String(FlagDAFiberKeyName, def.DA.Fiber.KeyName, "name of the key in the keyring for signing Fiber payment promises") + // P2P configuration flags cmd.Flags().String(FlagP2PListenAddress, def.P2P.ListenAddress, "P2P listen address (host:port)") cmd.Flags().String(FlagP2PPeers, def.P2P.Peers, "Comma separated list of seed nodes to connect to") diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 02040ebba9..e3a69b9be0 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -78,6 +78,16 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagDAMempoolTTL, DefaultConfig().DA.MempoolTTL) assertFlagValue(t, flags, FlagDAMaxSubmitAttempts, DefaultConfig().DA.MaxSubmitAttempts) assertFlagValue(t, flags, FlagDARequestTimeout, DefaultConfig().DA.RequestTimeout.Duration) + assertFlagValue(t, flags, FlagDABatchingStrategy, DefaultConfig().DA.BatchingStrategy) + assertFlagValue(t, flags, FlagDABatchSizeThreshold, DefaultConfig().DA.BatchSizeThreshold) + assertFlagValue(t, flags, FlagDABatchMaxDelay, DefaultConfig().DA.BatchMaxDelay.Duration) + assertFlagValue(t, flags, FlagDABatchMinItems, DefaultConfig().DA.BatchMinItems) + + // DA Fiber flags + assertFlagValue(t, flags, FlagDAFiberEnabled, DefaultConfig().DA.Fiber.Enabled) + assertFlagValue(t, flags, FlagDAFiberConsensusAddress, DefaultConfig().DA.Fiber.ConsensusAddress) + assertFlagValue(t, flags, FlagDAFiberConsensusChainID, DefaultConfig().DA.Fiber.ConsensusChainID) + assertFlagValue(t, flags, FlagDAFiberKeyName, DefaultConfig().DA.Fiber.KeyName) // P2P flags assertFlagValue(t, flags, FlagP2PListenAddress, DefaultConfig().P2P.ListenAddress) @@ -144,7 +154,7 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagPruningInterval, DefaultConfig().Pruning.Interval.Duration) // Count the number of flags we're explicitly checking - expectedFlagCount := 82 // Update this number if you add more flag checks above + expectedFlagCount := 84 // Update this number if you add more flag checks above // Get the actual number of flags (both regular and persistent) actualFlagCount := 0 diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index 233585e0c5..43c40496b2 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -84,6 +84,13 @@ func DefaultConfig() Config { BatchSizeThreshold: 0.8, BatchMaxDelay: DurationWrapper{0}, // 0 means use DA BlockTime BatchMinItems: 1, + Fiber: FiberDAConfig{ + Enabled: false, + ConsensusAddress: "127.0.0.1:9090", + ConsensusChainID: "mocha-4", + BridgeAddress: "ws://127.0.0.1:1234", + KeyName: "default-fibre", + }, }, Instrumentation: DefaultInstrumentationConfig(), Log: LogConfig{ diff --git a/pkg/sequencers/solo/sequencer.go b/pkg/sequencers/solo/sequencer.go index 0fcae9f31c..86fa08d45d 100644 --- a/pkg/sequencers/solo/sequencer.go +++ b/pkg/sequencers/solo/sequencer.go @@ -16,6 +16,12 @@ import ( var ErrInvalidID = errors.New("invalid chain id") +var ( + emptyBatch = &coresequencer.Batch{} + submitBatchResp = &coresequencer.SubmitBatchTxsResponse{} + verifyBatchOKResp = &coresequencer.VerifyBatchResponse{Status: true} +) + var _ coresequencer.Sequencer = (*SoloSequencer)(nil) // SoloSequencer is a single-leader sequencer without forced inclusion @@ -55,14 +61,14 @@ func (s *SoloSequencer) SubmitBatchTxs(ctx context.Context, req coresequencer.Su } if req.Batch == nil || len(req.Batch.Transactions) == 0 { - return &coresequencer.SubmitBatchTxsResponse{}, nil + return submitBatchResp, nil } s.mu.Lock() defer s.mu.Unlock() s.queue = append(s.queue, req.Batch.Transactions...) - return &coresequencer.SubmitBatchTxsResponse{}, nil + return submitBatchResp, nil } func (s *SoloSequencer) GetNextBatch(ctx context.Context, req coresequencer.GetNextBatchRequest) (*coresequencer.GetNextBatchResponse, error) { @@ -77,7 +83,7 @@ func (s *SoloSequencer) GetNextBatch(ctx context.Context, req coresequencer.GetN if len(txs) == 0 { return &coresequencer.GetNextBatchResponse{ - Batch: &coresequencer.Batch{}, + Batch: emptyBatch, Timestamp: time.Now().UTC(), BatchData: req.LastBatchData, }, nil @@ -94,21 +100,22 @@ func (s *SoloSequencer) GetNextBatch(ctx context.Context, req coresequencer.GetN filterStatuses, err := s.executor.FilterTxs(ctx, txs, req.MaxBytes, maxGas, false) if err != nil { s.logger.Warn().Err(err).Msg("failed to filter transactions, proceeding with unfiltered") - filterStatuses = make([]execution.FilterStatus, len(txs)) - for i := range filterStatuses { - filterStatuses[i] = execution.FilterOK - } + return &coresequencer.GetNextBatchResponse{ + Batch: &coresequencer.Batch{Transactions: txs}, + Timestamp: time.Now().UTC(), + BatchData: req.LastBatchData, + }, nil } - var validTxs [][]byte + write := 0 var postponedTxs [][]byte for i, status := range filterStatuses { switch status { case execution.FilterOK: - validTxs = append(validTxs, txs[i]) + txs[write] = txs[i] + write++ case execution.FilterPostpone: postponedTxs = append(postponedTxs, txs[i]) - case execution.FilterRemove: } } @@ -119,7 +126,7 @@ func (s *SoloSequencer) GetNextBatch(ctx context.Context, req coresequencer.GetN } return &coresequencer.GetNextBatchResponse{ - Batch: &coresequencer.Batch{Transactions: validTxs}, + Batch: &coresequencer.Batch{Transactions: txs[:write]}, Timestamp: time.Now().UTC(), BatchData: req.LastBatchData, }, nil @@ -130,7 +137,7 @@ func (s *SoloSequencer) VerifyBatch(ctx context.Context, req coresequencer.Verif return nil, ErrInvalidID } - return &coresequencer.VerifyBatchResponse{Status: true}, nil + return verifyBatchOKResp, nil } func (s *SoloSequencer) SetDAHeight(height uint64) { diff --git a/pkg/store/kv.go b/pkg/store/kv.go index 3ee23bc2c1..5cea24ba12 100644 --- a/pkg/store/kv.go +++ b/pkg/store/kv.go @@ -8,6 +8,7 @@ import ( ds "github.com/ipfs/go-datastore" ktds "github.com/ipfs/go-datastore/keytransform" dsq "github.com/ipfs/go-datastore/query" + dssync "github.com/ipfs/go-datastore/sync" badger4 "github.com/ipfs/go-ds-badger4" ) @@ -15,7 +16,40 @@ import ( const EvPrefix = "0" // NewDefaultKVStore creates instance of default key-value store. -func NewDefaultKVStore(rootDir, dbPath, dbName string) (ds.Batching, error) { +// +// HACK(fiber-throughput): swapped to a pure in-memory map for the +// Fibre throughput investigation. The real issue this surfaces is +// architectural, not a Badger bug: block.executing.Executor.ProduceBlock +// calls store.batch.Commit() synchronously inside the producer, so +// the storage engine's write rate is a hard ceiling on block +// production. With 128 MiB blocks × ~1 b/s the on-disk path drives +// ~150 MB/s of value-log writes plus heavy compaction; the producer +// blocks on Badger long before the DA submitter is the bottleneck. +// +// Don't revert this in place — fix the underlying design instead. +// Options worth weighing: +// - Move the block save off the producer hot path (async commit +// with a bounded queue). Block durability is not required to +// advance state, only to recover after restart. +// - For Fibre-only rollups specifically: skip local persistence +// entirely. Fibre IS the storage; a node can re-sync from the +// chain on restart. This removes the question. +// - If we keep persisting, pick a write-optimised backend that +// handles 100s of MB/s of large-value writes without compaction +// stalls. Badger v4 with these tunings still hit a .vlog +// rotation race under sustained load. +// +// NewDefaultKVStoreOnDisk preserved below as the literal Badger +// constructor for any caller that explicitly wants disk-backed +// state today; the production wiring should switch to one of the +// three options above before this directory is dropped. +func NewDefaultKVStore(_, _, _ string) (ds.Batching, error) { + return dssync.MutexWrap(ds.NewMapDatastore()), nil +} + +// NewDefaultKVStoreOnDisk is the original Badger-backed constructor, +// preserved for the duration of the throughput-cleanup window. +func NewDefaultKVStoreOnDisk(rootDir, dbPath, dbName string) (ds.Batching, error) { path := filepath.Join(rootify(rootDir, dbPath), dbName) return badger4.NewDatastore(path, BadgerOptions()) } diff --git a/tools/celestia-node-fiber/adapter.go b/tools/celestia-node-fiber/adapter.go new file mode 100644 index 0000000000..ab57829b60 --- /dev/null +++ b/tools/celestia-node-fiber/adapter.go @@ -0,0 +1,150 @@ +package celestianodefiber + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/cosmos/cosmos-sdk/crypto/keyring" + + appfibre "github.com/celestiaorg/celestia-app/v9/fibre" + libshare "github.com/celestiaorg/go-square/v4/share" + + "github.com/celestiaorg/celestia-node/api/client" + blobapi "github.com/celestiaorg/celestia-node/nodebuilder/blob" + fibreapi "github.com/celestiaorg/celestia-node/nodebuilder/fibre" + + "github.com/evstack/ev-node/block" +) + +// blobExpiration is the advisory ExpiresAt returned from Upload. celestia-node +// does not yet expose per-blob retention via UploadResult; 24h mirrors the +// placeholder used by tools/local-fiber so ev-node callers do not treat +// freshly-uploaded blobs as already-expired. +// +// TODO: surface actual retention from the x/fibre protocol params once +// celestia-node exposes it. +const blobExpiration = 24 * time.Hour + +// defaultListenChannelSize matches the buffer used by celestia-node's +// blob.Subscribe so the forwarder does not become a tighter bottleneck than +// the upstream stream. +const defaultListenChannelSize = 16 + +// Adapter implements the ev-node fiber.DA interface on top of a +// celestia-node api/client.Client. Upload and Download run locally against +// consensus gRPC + FSPs; Listen forwards share-version-2 blobs from the +// bridge node's subscription stream. +type Adapter struct { + fibre fibreapi.Module + blob blobapi.Module + listenChannelSz int + + // closer, if non-nil, is invoked by Close. Set only when the Adapter + // owns the underlying api/client.Client (via New). + closer func() error +} + +// Compile-time assertion that Adapter satisfies the ev-node Fiber DA contract. +var _ block.FiberClient = (*Adapter)(nil) + +// New constructs a celestia-node api/client.Client from cfg and wraps it as +// an Adapter. The returned Adapter owns the client and must be closed via +// Close to release its gRPC connections. +func New(ctx context.Context, cfg Config, kr keyring.Keyring) (*Adapter, error) { + c, err := client.New(ctx, cfg.Client, kr) + if err != nil { + return nil, fmt.Errorf("constructing celestia-node client: %w", err) + } + return &Adapter{ + fibre: c.Fibre, + blob: c.Blob, + listenChannelSz: resolveListenChannelSize(cfg.ListenChannelSize), + closer: c.Close, + }, nil +} + +// FromModules wraps existing Fibre and Blob module implementations. It is +// intended for tests and for callers that already own a *client.Client and +// want to pass its Fibre + Blob fields directly. The caller retains +// responsibility for the underlying client's lifecycle; Close is a no-op. +func FromModules(fibre fibreapi.Module, blob blobapi.Module, listenChannelSize int) *Adapter { + return &Adapter{ + fibre: fibre, + blob: blob, + listenChannelSz: resolveListenChannelSize(listenChannelSize), + } +} + +// Close tears down the underlying client, if the Adapter owns one. +func (a *Adapter) Close() error { + if a.closer == nil { + return nil + } + return a.closer() +} + +// Upload implements fiber.DA.Upload. client.Fibre.Upload does off-chain row +// upload plus validator-sig aggregation and spawns a background +// MsgPayForFibre broadcast; this call returns as soon as the off-chain +// stages finish. +func (a *Adapter) Upload( + ctx context.Context, + namespace []byte, + data []byte, +) (block.FiberUploadResult, error) { + ns, err := toV0Namespace(namespace) + if err != nil { + return block.FiberUploadResult{}, fmt.Errorf("namespace: %w", err) + } + up, err := a.fibre.Upload(ctx, ns, data, nil) + if err != nil { + return block.FiberUploadResult{}, fmt.Errorf("fibre upload: %w", err) + } + if up == nil { + return block.FiberUploadResult{}, errors.New("fibre upload returned nil result") + } + // Copy the returned BlobID to decouple the caller from any internal + // reuse of the upstream slice. + id := make(block.FiberBlobID, len(up.BlobID)) + copy(id, up.BlobID) + return block.FiberUploadResult{ + BlobID: id, + ExpiresAt: time.Now().Add(blobExpiration), + }, nil +} + +// Download implements fiber.DA.Download. Reads go directly to FSPs via the +// appfibre client embedded in client.Fibre — no bridge hop. +func (a *Adapter) Download(ctx context.Context, blobID block.FiberBlobID) ([]byte, error) { + res, err := a.fibre.Download(ctx, appfibre.BlobID(blobID)) + if err != nil { + return nil, fmt.Errorf("fibre download: %w", err) + } + if res == nil { + return nil, errors.New("fibre download returned nil result") + } + return res.Data, nil +} + +// toV0Namespace converts an ev-node raw namespace ([]byte) into a libshare +// Namespace. The adapter contract is that callers pass the 10-byte v0 +// namespace ID (matching ev-node's datypes.NamespaceFromString output); we +// surface a clear error on length mismatch rather than silently padding. +func toV0Namespace(id []byte) (libshare.Namespace, error) { + if len(id) != libshare.NamespaceVersionZeroIDSize { + return libshare.Namespace{}, fmt.Errorf( + "expected %d bytes, got %d", + libshare.NamespaceVersionZeroIDSize, len(id), + ) + } + return libshare.NewV0Namespace(id) +} + +func resolveListenChannelSize(size int) int { + if size <= 0 { + return defaultListenChannelSize + } + return size +} diff --git a/tools/celestia-node-fiber/adapter_test.go b/tools/celestia-node-fiber/adapter_test.go new file mode 100644 index 0000000000..b86d2a0386 --- /dev/null +++ b/tools/celestia-node-fiber/adapter_test.go @@ -0,0 +1,278 @@ +package celestianodefiber_test + +import ( + "bytes" + "context" + "errors" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + appfibre "github.com/celestiaorg/celestia-app/v9/fibre" + libshare "github.com/celestiaorg/go-square/v4/share" + + nodeblob "github.com/celestiaorg/celestia-node/blob" + blobapi "github.com/celestiaorg/celestia-node/nodebuilder/blob" + celfibre "github.com/celestiaorg/celestia-node/fibre" + fibreapi "github.com/celestiaorg/celestia-node/nodebuilder/fibre" + "github.com/celestiaorg/celestia-node/state/txclient" + + "github.com/evstack/ev-node/block" + cnfiber "github.com/evstack/ev-node/tools/celestia-node-fiber" +) + +// namespaceBytes returns a 10-byte v0 namespace ID for test fixtures. +func namespaceBytes() []byte { return bytes.Repeat([]byte{0xab}, libshare.NamespaceVersionZeroIDSize) } + +func namespace(t *testing.T) libshare.Namespace { + t.Helper() + ns, err := libshare.NewV0Namespace(namespaceBytes()) + require.NoError(t, err) + return ns +} + +// fakeFibre is a minimal stand-in for fibreapi.Module. We hand-roll it +// because celestia-node's generated mocks use two different gomock forks +// across the fibre and blob packages, which can't share a Controller. +type fakeFibre struct { + uploadFn func(context.Context, libshare.Namespace, []byte, *txclient.TxConfig) (*fibreapi.UploadResult, error) + downloadFn func(context.Context, appfibre.BlobID) (*fibreapi.GetBlobResult, error) +} + +var _ fibreapi.Module = (*fakeFibre)(nil) + +func (f *fakeFibre) Submit(context.Context, libshare.Namespace, []byte, *txclient.TxConfig) (*fibreapi.SubmitResult, error) { + return nil, errors.New("fakeFibre.Submit not implemented") +} + +func (f *fakeFibre) Upload(ctx context.Context, ns libshare.Namespace, data []byte, cfg *txclient.TxConfig) (*fibreapi.UploadResult, error) { + return f.uploadFn(ctx, ns, data, cfg) +} + +func (f *fakeFibre) Download(ctx context.Context, id appfibre.BlobID) (*fibreapi.GetBlobResult, error) { + return f.downloadFn(ctx, id) +} + +func (f *fakeFibre) QueryEscrowAccount(context.Context, string) (*celfibre.EscrowAccount, error) { + return nil, errors.New("fakeFibre.QueryEscrowAccount not implemented") +} + +func (f *fakeFibre) Deposit(context.Context, types.Coin, *txclient.TxConfig) error { + return errors.New("fakeFibre.Deposit not implemented") +} + +func (f *fakeFibre) Withdraw(context.Context, types.Coin, *txclient.TxConfig) error { + return errors.New("fakeFibre.Withdraw not implemented") +} + +func (f *fakeFibre) PendingWithdrawals(context.Context, string) ([]celfibre.PendingWithdrawal, error) { + return nil, errors.New("fakeFibre.PendingWithdrawals not implemented") +} + +// fakeBlob is a minimal stand-in for blobapi.Module. Only Subscribe is +// exercised by the adapter, so the rest return errors if called. +type fakeBlob struct { + subscribeFn func(context.Context, libshare.Namespace, uint64) (<-chan *nodeblob.SubscriptionResponse, error) +} + +var _ blobapi.Module = (*fakeBlob)(nil) + +func (b *fakeBlob) Submit(context.Context, []*nodeblob.Blob, *nodeblob.SubmitOptions) (uint64, error) { + return 0, errors.New("fakeBlob.Submit not implemented") +} + +func (b *fakeBlob) Get(context.Context, uint64, libshare.Namespace, nodeblob.Commitment) (*nodeblob.Blob, error) { + return nil, errors.New("fakeBlob.Get not implemented") +} + +func (b *fakeBlob) GetAll(context.Context, uint64, []libshare.Namespace) ([]*nodeblob.Blob, error) { + return nil, errors.New("fakeBlob.GetAll not implemented") +} + +func (b *fakeBlob) GetProof(context.Context, uint64, libshare.Namespace, nodeblob.Commitment) (*nodeblob.Proof, error) { + return nil, errors.New("fakeBlob.GetProof not implemented") +} + +func (b *fakeBlob) Included(context.Context, uint64, libshare.Namespace, *nodeblob.Proof, nodeblob.Commitment) (bool, error) { + return false, errors.New("fakeBlob.Included not implemented") +} + +func (b *fakeBlob) GetCommitmentProof(context.Context, uint64, libshare.Namespace, []byte) (*nodeblob.CommitmentProof, error) { + return nil, errors.New("fakeBlob.GetCommitmentProof not implemented") +} + +func (b *fakeBlob) Subscribe(ctx context.Context, ns libshare.Namespace, fromHeight uint64) (<-chan *nodeblob.SubscriptionResponse, error) { + return b.subscribeFn(ctx, ns, fromHeight) +} + +// TestAdapterSatisfiesDA is a compile-time assertion that the adapter +// implements the ev-node Fiber DA contract. +func TestAdapterSatisfiesDA(t *testing.T) { + var _ block.FiberClient = (*cnfiber.Adapter)(nil) +} + +func TestUpload_ForwardsNamespaceDataAndBlobID(t *testing.T) { + var commit appfibre.Commitment + copy(commit[:], bytes.Repeat([]byte{0x11}, appfibre.CommitmentSize)) + expectedBlobID := appfibre.NewBlobID(0, commit) + + var seenNs libshare.Namespace + var seenData []byte + var seenCfg *txclient.TxConfig + fibre := &fakeFibre{ + uploadFn: func(_ context.Context, ns libshare.Namespace, data []byte, cfg *txclient.TxConfig) (*fibreapi.UploadResult, error) { + seenNs = ns + seenData = data + seenCfg = cfg + return &fibreapi.UploadResult{BlobID: expectedBlobID}, nil + }, + } + a := cnfiber.FromModules(fibre, &fakeBlob{}, 0) + + data := []byte("hello fibre") + before := time.Now() + got, err := a.Upload(context.Background(), namespaceBytes(), data) + require.NoError(t, err) + require.Equal(t, block.FiberBlobID(expectedBlobID), got.BlobID) + require.Equal(t, namespace(t), seenNs) + require.Equal(t, data, seenData) + require.Nil(t, seenCfg, "adapter passes nil TxConfig to honour client defaults") + require.True(t, got.ExpiresAt.After(before.Add(time.Hour))) +} + +func TestUpload_RejectsWrongNamespaceLength(t *testing.T) { + a := cnfiber.FromModules(&fakeFibre{}, &fakeBlob{}, 0) + _, err := a.Upload(context.Background(), []byte{0x01, 0x02}, []byte("x")) + require.Error(t, err) +} + +func TestUpload_PropagatesFibreError(t *testing.T) { + fibreErr := errors.New("boom") + fibre := &fakeFibre{ + uploadFn: func(context.Context, libshare.Namespace, []byte, *txclient.TxConfig) (*fibreapi.UploadResult, error) { + return nil, fibreErr + }, + } + a := cnfiber.FromModules(fibre, &fakeBlob{}, 0) + _, err := a.Upload(context.Background(), namespaceBytes(), []byte("x")) + require.ErrorIs(t, err, fibreErr) +} + +func TestDownload_ReturnsResultData(t *testing.T) { + var commit appfibre.Commitment + copy(commit[:], bytes.Repeat([]byte{0x22}, appfibre.CommitmentSize)) + id := appfibre.NewBlobID(0, commit) + payload := []byte("payload") + + var seenID appfibre.BlobID + fibre := &fakeFibre{ + downloadFn: func(_ context.Context, arg appfibre.BlobID) (*fibreapi.GetBlobResult, error) { + seenID = arg + return &fibreapi.GetBlobResult{Data: payload}, nil + }, + } + + a := cnfiber.FromModules(fibre, &fakeBlob{}, 0) + got, err := a.Download(context.Background(), block.FiberBlobID(id)) + require.NoError(t, err) + require.Equal(t, payload, got) + require.Equal(t, id, seenID) +} + +func TestListen_FiltersFibreOnlyAndEmitsEvent(t *testing.T) { + ns := namespace(t) + + // v0 (non-Fibre) — must be filtered out. + v0Lib, err := libshare.NewV0Blob(ns, []byte("pfb blob")) + require.NoError(t, err) + v0 := &nodeblob.Blob{Blob: v0Lib} + + // v2 (Fibre) — must be forwarded. libshare requires a 20-byte signer + // for v2 blobs; the content of the signer is irrelevant to the filter. + fibreCommit := bytes.Repeat([]byte{0x33}, appfibre.CommitmentSize) + signer := bytes.Repeat([]byte{0x01}, libshare.SignerSize) + v2Lib, err := libshare.NewV2Blob(ns, 0, fibreCommit, signer) + require.NoError(t, err) + v2 := &nodeblob.Blob{Blob: v2Lib} + + ch := make(chan *nodeblob.SubscriptionResponse, 1) + ch <- &nodeblob.SubscriptionResponse{ + Blobs: []*nodeblob.Blob{v0, v2}, + Height: 42, + } + close(ch) + + var seenNs libshare.Namespace + var seenFromHeight uint64 + blob := &fakeBlob{ + subscribeFn: func(_ context.Context, sub libshare.Namespace, fromHeight uint64) (<-chan *nodeblob.SubscriptionResponse, error) { + seenNs = sub + seenFromHeight = fromHeight + return ch, nil + }, + } + + // Listen now issues a Download per v2 blob to recover the original + // payload size for BlobEvent.DataSize (see Listen's doc comment). + // Feed a deterministic payload back; the test asserts DataSize + // matches its length. + originalPayload := []byte("this is the original blob payload") + fibre := &fakeFibre{ + downloadFn: func(_ context.Context, _ appfibre.BlobID) (*fibreapi.GetBlobResult, error) { + return &fibreapi.GetBlobResult{Data: originalPayload}, nil + }, + } + a := cnfiber.FromModules(fibre, blob, 0) + events, err := a.Listen(context.Background(), namespaceBytes(), 0) + require.NoError(t, err) + require.Equal(t, ns, seenNs) + require.Equal(t, uint64(0), seenFromHeight, "fromHeight=0 must be forwarded to blob.Subscribe") + + select { + case ev, ok := <-events: + require.True(t, ok, "expected one event before channel closes") + var expectedCommit appfibre.Commitment + copy(expectedCommit[:], fibreCommit) + expectedID := appfibre.NewBlobID(0, expectedCommit) + require.Equal(t, block.FiberBlobID(expectedID), ev.BlobID) + require.Equal(t, uint64(42), ev.Height) + require.Equal(t, uint64(len(originalPayload)), ev.DataSize, + "DataSize must match the original payload length resolved via Download") + case <-time.After(time.Second): + t.Fatal("timed out waiting for blob event") + } + + select { + case _, ok := <-events: + require.False(t, ok, "expected adapter channel to close after upstream close") + case <-time.After(time.Second): + t.Fatal("timed out waiting for output channel to close") + } +} + +func TestListen_CancelledContextClosesOutput(t *testing.T) { + ns := namespace(t) + upstream := make(chan *nodeblob.SubscriptionResponse) + blob := &fakeBlob{ + subscribeFn: func(_ context.Context, arg libshare.Namespace, _ uint64) (<-chan *nodeblob.SubscriptionResponse, error) { + require.Equal(t, ns, arg) + return upstream, nil + }, + } + + ctx, cancel := context.WithCancel(context.Background()) + a := cnfiber.FromModules(&fakeFibre{}, blob, 0) + events, err := a.Listen(ctx, namespaceBytes(), 0) + require.NoError(t, err) + + cancel() + + select { + case _, ok := <-events: + require.False(t, ok, "expected adapter channel to close on ctx cancel") + case <-time.After(time.Second): + t.Fatal("timed out waiting for ctx-triggered close") + } +} diff --git a/tools/celestia-node-fiber/cmd/fiber-bench/README.md b/tools/celestia-node-fiber/cmd/fiber-bench/README.md new file mode 100644 index 0000000000..d99419f6e8 --- /dev/null +++ b/tools/celestia-node-fiber/cmd/fiber-bench/README.md @@ -0,0 +1,187 @@ +# fiber-bench + +Single-sequencer ev-node throughput bench against a remote Fibre network. + +## What it is + +A self-contained binary that spins up an ev-node aggregator wired to a +remote Fibre network (no bridge node, no P2P, no syncer, no +state-machine cost) and pumps transactions into its mempool as fast as +the configured backpressure allows. + +The intent is a fail-fast baseline so we can isolate ev-node's +batching + DA-submit pipeline from everything else when chasing the +1k tps regression. + +### What's stripped out (and why) + +| Stripped | Why | +|----------------|------------------------------------------------------------| +| Bridge node | Upload only needs consensus gRPC + FSPs. | +| Syncer | Aggregator-only single-node setup. | +| P2P outbound | ev-node already disables it when `da.fiber.enabled=true`. | +| Forced incl. | Solo sequencer. | +| Real state machine | Constant state root — measure ev-node, not state cost. | +| HTTP tx ingress | Direct `InjectTx`. Removes HTTP from the hot path. | + +## Layout + +``` +tools/celestia-node-fiber/cmd/fiber-bench/ + main.go cobra root + keys.go cosmos keyring management (test backend) + escrow.go Fibre escrow deposit/query + run.go the bench + executor.go in-mem core.Executor with constant state root + loader.go internal tx pump + stats.go periodic stats line + final baseline summary + fibre.go bridge-bypass cnfiber.Adapter constructor + run-bench.sh convenience wrapper +``` + +## Quick start + +```sh +cd tools/celestia-node-fiber + +# 1. Build — the `fibre` build tag is REQUIRED so celestia-app's +# x/fibre messages (MsgPayForFibre, MsgDepositToEscrow) are registered +# in the codec. Without it the async PFF settlement fails with +# "unable to resolve type URL /celestia.fibre.v1.MsgPayForFibre". +go build -tags fibre -o bin/fiber-bench ./cmd/fiber-bench/ + +# 2. Create the bench key (cosmos keyring, test backend = unencrypted on disk) +./bin/fiber-bench keys add bench +# prints: address: celestia1... +# mnemonic: ... + +# 3. Top up the printed address with utia on the chain (out of band). + +# 4. Deposit into the Fibre escrow +./bin/fiber-bench escrow deposit \ + --consensus-grpc 139.59.229.101:9091 \ + --key-name bench \ + --amount 50000000 # 50 TIA + +# 5. Sanity check +./bin/fiber-bench escrow query \ + --consensus-grpc 139.59.229.101:9091 \ + --key-name bench + +# 6. Run the bench +./bin/fiber-bench run \ + --evnode.da.fiber.consensus_address 139.59.229.101:9091 \ + --evnode.da.fiber.consensus_chain_id \ + --evnode.da.fiber.key_name bench \ + --duration 2m \ + --workers 32 \ + --tx-size 200 \ + --evnode.node.block_time 1s \ + --evnode.da.batching_strategy immediate +``` + +The bench reuses canonical ev-node flags (`--evnode.*`) registered by +`pkg/config.AddFlags` rather than defining bench-specific aliases. See +`fiber-bench run --help` for the full list — anything you'd configure on +testapp/evm/grpc apps works here too. + +Or use the convenience wrapper: + +```sh +CONSENSUS_GRPC=139.59.229.101:9091 \ +CHAIN_ID=talis-slab-diag \ + ./cmd/fiber-bench/run-bench.sh 2m 32 +``` + +## What the run prints + +A header, then one line per `--stats-interval` (default 1s): + +``` +elapsed injected inj/s exec/s blocks/s committed_h txs/blk blob_bytes pending drops +------------------------------------------------------------------------------------------------------ +1s 1452609 1452212 0 0.00 0 0.0 0 0 293116 +2s 1544094 91444 0 0.00 0 0.0 0 0 1007270 +``` + +Columns: + +- `injected` — total txs the load generator has called `InjectTx` for +- `inj/s` — injection rate over the last interval +- `exec/s` — txs included in produced blocks (rate) +- `blocks/s` — block production rate +- `committed_h` — last block height confirmed by DA (0 until first + Upload settles) +- `txs/blk` — running average over all blocks +- `blob_bytes` — last block's data size in bytes +- `pending` — `evnode_da_submitter_pending_blobs` gauge +- `drops` — txs the load generator could not enqueue because the + in-mem mempool channel was full (this is the backpressure signal) + +At the end: + +``` +============================================================ + BASELINE SUMMARY +============================================================ +Duration: 2m0s +Injected: XXX (avg N tx/s, peak N tx/s) +Dropped (mempool full): XXX +Mempool high-water: XXX +Blocks produced: XXX (committed_h=YYY) +Txs executed: XXX (avg N tx/s, peak N tx/s, T tx/blk) +============================================================ +``` + +## Knobs worth flipping while debugging + +Bench-local flags: + +| Flag | Default | Why | +|-------------------------|--------------|---------------------------------------------------| +| `--workers` | `32` | Tx-injection concurrency | +| `--tx-size` | `200` | Bytes per tx (matches user-reported regression) | +| `--mempool-size` | `1_000_000` | Bench's bounded backpressure boundary | +| `--keep-home` | `false` | Resume from prior state (defaults to wipe) | +| `--duration` | `1m` | How long to run (0 = until SIGINT) | +| `--stats-interval` | `1s` | Stats line cadence | +| `--keyring-dir` | `~/.fiber-bench/keyring` | Cosmos keyring (Fibre payment promises) | +| `--signer-passphrase` | `fiber-bench-passphrase` | ev-node block-signing key passphrase | + +Canonical ev-node flags worth flipping (full list: `run --help`): + +| Flag | Bench default | Why | +|-------------------------------------|---------------|--------------------------------------| +| `--evnode.node.block_time` | `1s` | Drop to `100ms` to expose per-block overhead | +| `--evnode.da.batching_strategy` | `immediate` | Try `time` / `size` / `adaptive` | +| `--evnode.node.scrape_interval` | `100ms` | How often the mempool drain runs | +| `--evnode.node.max_pending_headers_and_data` | `0` | Cap pending DA blobs to test backpressure | +| `--evnode.log.level` | `info` | `debug` to see ev-node block production logs | + +## ev-node Prometheus + +When `--prometheus=true` (default), ev-node exposes metrics at +`http://127.0.0.1:26660/metrics`. The bench scrapes a handful of them +for its stats line, but you can hit the endpoint directly for the full +picture: `evnode_block_production_duration_seconds`, +`evnode_da_submitter_failures_total`, etc. + +## Operational notes + +- **Test-backend keyring**: keys live unencrypted on disk under + `~/.fiber-bench/keyring`. Fine for a bench account funded with a + small amount of utia. Don't use for anything else. +- **The bench wipes its ev-node home (`~/.fiber-bench/node`) on every + run** unless `--keep-home` is passed. Block-signing key, store, and + any in-flight pending blocks all reset. The cosmos keyring is + separate and is preserved. +- **Bridge bypass**: the bench builds the `cnfiber.Adapter` via + `cnfiber.FromModules` with a stub Blob module that errors on every + call. The aggregator-only setup never invokes Listen/Subscribe, so + this is safe; if the assumption breaks, you'll see a clear + `fiber-bench: blob module not supported` error rather than a nil + panic. +- **Chain ID** is what the consensus node reports; the bench logs it + on startup. Pass the same value via `--chain-id` for config + validation; mismatch is logged but tx submission proceeds against + the chain's actual ID. diff --git a/tools/celestia-node-fiber/cmd/fiber-bench/escrow.go b/tools/celestia-node-fiber/cmd/fiber-bench/escrow.go new file mode 100644 index 0000000000..89e55e25c9 --- /dev/null +++ b/tools/celestia-node-fiber/cmd/fiber-bench/escrow.go @@ -0,0 +1,165 @@ +package main + +import ( + "context" + "fmt" + "time" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/spf13/cobra" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + "github.com/celestiaorg/celestia-app/v9/app" + "github.com/celestiaorg/celestia-app/v9/app/encoding" + "github.com/celestiaorg/celestia-app/v9/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v9/pkg/user" + fibretypes "github.com/celestiaorg/celestia-app/v9/x/fibre/types" +) + +// escrowCmd groups Fibre-escrow operations. Uploads consume utia from +// the signer's escrow account; without a funded escrow, every Upload on +// the bench will fail at the chain. +func escrowCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "escrow", + Short: "Manage Fibre escrow for the bench account", + } + cmd.AddCommand(escrowDepositCmd(), escrowQueryCmd()) + return cmd +} + +func escrowDepositCmd() *cobra.Command { + var ( + consensusGRPC string + keyringDir string + keyName string + amountUtia int64 + gasLimit uint64 + feeUtia uint64 + ) + cmd := &cobra.Command{ + Use: "deposit", + Short: "Deposit utia into the bench account's Fibre escrow", + RunE: func(cmd *cobra.Command, args []string) error { + ctx, cancel := context.WithTimeout(cmd.Context(), 60*time.Second) + defer cancel() + + kr, err := openKeyring(keyringDir) + if err != nil { + return fmt.Errorf("open keyring: %w", err) + } + rec, err := kr.Key(keyName) + if err != nil { + return fmt.Errorf("key %q not found in %s: %w", keyName, keyringDir, err) + } + addr, err := rec.GetAddress() + if err != nil { + return fmt.Errorf("get address: %w", err) + } + + conn, err := grpc.NewClient(consensusGRPC, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return fmt.Errorf("dial grpc: %w", err) + } + defer conn.Close() + + ecfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) + tc, err := user.SetupTxClient(ctx, kr, conn, ecfg, user.WithDefaultAccount(keyName)) + if err != nil { + return fmt.Errorf("setup tx client: %w", err) + } + + amount := sdk.NewCoin(appconsts.BondDenom, sdkmath.NewInt(amountUtia)) + msg := &fibretypes.MsgDepositToEscrow{ + Signer: addr.String(), + Amount: amount, + } + fmt.Printf("submitting MsgDepositToEscrow: signer=%s amount=%s\n", addr.String(), amount.String()) + resp, err := tc.SubmitTx(ctx, []sdk.Msg{msg}, user.SetGasLimit(gasLimit), user.SetFee(feeUtia)) + if err != nil { + return fmt.Errorf("submit tx: %w", err) + } + if resp.Code != 0 { + return fmt.Errorf("deposit tx failed: code=%d codespace=%s", resp.Code, resp.Codespace) + } + fmt.Printf("deposit included: height=%d txhash=%s\n", resp.Height, resp.TxHash) + + // Sanity: read the escrow back so the operator sees the + // new balance immediately. + qc := fibretypes.NewQueryClient(conn) + res, err := qc.EscrowAccount(ctx, &fibretypes.QueryEscrowAccountRequest{Signer: addr.String()}) + if err != nil { + fmt.Printf("(could not query escrow back: %v)\n", err) + return nil + } + if !res.Found { + fmt.Println("(escrow not found after deposit — chain may need another block)") + return nil + } + fmt.Printf("escrow balance: %s\n", res.EscrowAccount.Balance.String()) + return nil + }, + } + cmd.Flags().StringVar(&consensusGRPC, "consensus-grpc", "", "celestia-app gRPC address (host:port). Required.") + cmd.Flags().StringVar(&keyringDir, "keyring-dir", defaultKeyringDir(), "directory holding the bench keyring") + cmd.Flags().StringVar(&keyName, "key-name", "default", "key in the keyring to deposit from") + cmd.Flags().Int64Var(&amountUtia, "amount", 50_000_000, "amount in utia to deposit (default 50 TIA)") + cmd.Flags().Uint64Var(&gasLimit, "gas-limit", 200_000, "tx gas limit") + cmd.Flags().Uint64Var(&feeUtia, "fee", 5_000, "fee in utia") + _ = cobra.MarkFlagRequired(cmd.Flags(), "consensus-grpc") + return cmd +} + +func escrowQueryCmd() *cobra.Command { + var ( + consensusGRPC string + keyringDir string + keyName string + ) + cmd := &cobra.Command{ + Use: "query", + Short: "Print the current Fibre escrow balance for the bench account", + RunE: func(cmd *cobra.Command, args []string) error { + ctx, cancel := context.WithTimeout(cmd.Context(), 30*time.Second) + defer cancel() + + kr, err := openKeyring(keyringDir) + if err != nil { + return err + } + rec, err := kr.Key(keyName) + if err != nil { + return fmt.Errorf("key %q not found: %w", keyName, err) + } + addr, err := rec.GetAddress() + if err != nil { + return err + } + + conn, err := grpc.NewClient(consensusGRPC, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return err + } + defer conn.Close() + + qc := fibretypes.NewQueryClient(conn) + res, err := qc.EscrowAccount(ctx, &fibretypes.QueryEscrowAccountRequest{Signer: addr.String()}) + if err != nil { + return err + } + if !res.Found { + fmt.Printf("address: %s\nescrow: not found (deposit first)\n", addr.String()) + return nil + } + fmt.Printf("address: %s\nescrow: %s\n", addr.String(), res.EscrowAccount.Balance.String()) + return nil + }, + } + cmd.Flags().StringVar(&consensusGRPC, "consensus-grpc", "", "celestia-app gRPC address. Required.") + cmd.Flags().StringVar(&keyringDir, "keyring-dir", defaultKeyringDir(), "keyring directory") + cmd.Flags().StringVar(&keyName, "key-name", "default", "key name") + _ = cobra.MarkFlagRequired(cmd.Flags(), "consensus-grpc") + return cmd +} diff --git a/tools/celestia-node-fiber/cmd/fiber-bench/executor.go b/tools/celestia-node-fiber/cmd/fiber-bench/executor.go new file mode 100644 index 0000000000..088d21ffef --- /dev/null +++ b/tools/celestia-node-fiber/cmd/fiber-bench/executor.go @@ -0,0 +1,156 @@ +package main + +import ( + "context" + "sync/atomic" + "time" + + coreexecution "github.com/evstack/ev-node/core/execution" +) + +// inMemExecutor is a minimal core.Executor that: +// - accepts injected txs via a buffered channel (the "mempool") +// - drains them in GetTxs (non-blocking) +// - "executes" by counting (no state machine) +// - returns a constant state root, so we don't pay O(N) state-root cost on +// every block (which would dominate the measurement and tell us nothing +// about ev-node's batching/submitting performance). +// +// Use FilterTxs's size cap to enforce the configured per-block byte budget. +type inMemExecutor struct { + txCh chan []byte + + injected atomic.Uint64 + dropped atomic.Uint64 + blocksProduced atomic.Uint64 + totalExecutedTxs atomic.Uint64 + + // mempoolHigh tracks the maximum mempool depth observed (snapshot). + mempoolHigh atomic.Int64 + + // constStateRoot is what every block reports as its post-state. The + // measurement target is ev-node, not state computation. + constStateRoot []byte +} + +func newInMemExecutor(mempoolSize int) *inMemExecutor { + return &inMemExecutor{ + txCh: make(chan []byte, mempoolSize), + constStateRoot: []byte("fiber-bench-const-state-root"), + } +} + +// InjectTx is the bench's "mempool entry". Backpressures via channel +// capacity: full → drop and increment counter so the operator sees it. +func (e *inMemExecutor) InjectTx(tx []byte) bool { + select { + case e.txCh <- tx: + e.injected.Add(1) + // Loose mempool-depth high-water; not a hot-path concern. + if d := int64(len(e.txCh)); d > e.mempoolHigh.Load() { + e.mempoolHigh.Store(d) + } + return true + default: + e.dropped.Add(1) + return false + } +} + +func (e *inMemExecutor) MempoolDepth() int { return len(e.txCh) } + +func (e *inMemExecutor) Stats() (injected, dropped, blocks, txs uint64, mempoolHigh int64) { + return e.injected.Load(), + e.dropped.Load(), + e.blocksProduced.Load(), + e.totalExecutedTxs.Load(), + e.mempoolHigh.Load() +} + +// InitChain is called once at genesis. +func (e *inMemExecutor) InitChain(_ context.Context, _ time.Time, _ uint64, _ string) ([]byte, error) { + return e.constStateRoot, nil +} + +// GetTxs drains the mempool channel. Non-blocking — returns whatever is +// currently buffered. ev-node's reaper polls this on its own cadence. +func (e *inMemExecutor) GetTxs(ctx context.Context) ([][]byte, error) { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + n := len(e.txCh) + if n == 0 { + return nil, nil + } + txs := make([][]byte, 0, n) + for i := 0; i < n; i++ { + select { + case tx := <-e.txCh: + txs = append(txs, tx) + default: + return txs, nil + } + } + return txs, nil +} + +// ExecuteTxs is intentionally a no-op state transition: count txs, return +// a constant root. The whole point of this executor is to take state +// computation out of the measurement. +func (e *inMemExecutor) ExecuteTxs(_ context.Context, txs [][]byte, _ uint64, _ time.Time, _ []byte) ([]byte, error) { + e.blocksProduced.Add(1) + e.totalExecutedTxs.Add(uint64(len(txs))) + return e.constStateRoot, nil +} + +func (e *inMemExecutor) SetFinal(_ context.Context, _ uint64) error { return nil } +func (e *inMemExecutor) Rollback(_ context.Context, _ uint64) error { return nil } + +func (e *inMemExecutor) GetExecutionInfo(_ context.Context) (coreexecution.ExecutionInfo, error) { + // MaxGas=0 means "no gas-based filter"; the size cap (FilterTxs) is what + // bounds per-block bytes. + return coreexecution.ExecutionInfo{MaxGas: 0}, nil +} + +// FilterTxs enforces the configured per-block byte budget. Mirrors the +// existing testapp KV executor's behavior: oversized txs are dropped, the +// rest fill until the budget is hit and overflow is postponed for the +// next block. We don't validate tx content — txs from the load generator +// are well-formed by construction. +// +// We honor maxBytes as-is. Per-block proto/Metadata overhead is the +// responsibility of the block-size cap (now anchored to Fibre's actual +// MaxPayload in block/internal/common/consts.go), not the executor. +func (e *inMemExecutor) FilterTxs(_ context.Context, txs [][]byte, maxBytes, _ uint64, _ bool) ([]coreexecution.FilterStatus, error) { + out := make([]coreexecution.FilterStatus, len(txs)) + var used uint64 + limitReached := false + for i, tx := range txs { + size := uint64(len(tx)) + if size == 0 { + out[i] = coreexecution.FilterRemove + continue + } + if maxBytes > 0 && size > maxBytes { + out[i] = coreexecution.FilterRemove + continue + } + if limitReached { + out[i] = coreexecution.FilterPostpone + continue + } + if maxBytes > 0 && used+size > maxBytes { + limitReached = true + out[i] = coreexecution.FilterPostpone + continue + } + used += size + out[i] = coreexecution.FilterOK + } + return out, nil +} + +var _ coreexecution.Executor = (*inMemExecutor)(nil) diff --git a/tools/celestia-node-fiber/cmd/fiber-bench/fibre.go b/tools/celestia-node-fiber/cmd/fiber-bench/fibre.go new file mode 100644 index 0000000000..3a366d0824 --- /dev/null +++ b/tools/celestia-node-fiber/cmd/fiber-bench/fibre.go @@ -0,0 +1,139 @@ +package main + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + appfibre "github.com/celestiaorg/celestia-app/v9/fibre" + libshare "github.com/celestiaorg/go-square/v4/share" + + "github.com/celestiaorg/celestia-node/blob" + "github.com/celestiaorg/celestia-node/fibre" + blobapi "github.com/celestiaorg/celestia-node/nodebuilder/blob" + nodebuilderfibre "github.com/celestiaorg/celestia-node/nodebuilder/fibre" + "github.com/celestiaorg/celestia-node/state/txclient" + + "github.com/evstack/ev-node/block" + cnfiber "github.com/evstack/ev-node/tools/celestia-node-fiber" +) + +// buildFibreAdapter constructs a celestia-node-fiber Adapter that talks +// directly to consensus gRPC + FSPs — no bridge node hop. We do this by +// rebuilding only the submit-side wiring of celestia-node's api/client +// (which is otherwise eager about dialing BridgeDAAddr in NewReadClient). +// +// The returned adapter only supports Upload (and Download via FSPs). +// Listen would invoke a stub blob.Subscribe that returns an error; +// ev-node's aggregator-only setup never calls it (no syncer, no based +// sequencer), so this is fine. +// +// The returned closer releases the gRPC connection and stops the +// underlying app-level fibre client. +func buildFibreAdapter( + ctx context.Context, + consensusGRPC string, + keyName string, + kr keyring.Keyring, +) (block.FiberClient, func() error, error) { + if consensusGRPC == "" { + return nil, nil, errors.New("consensus gRPC address is required") + } + if keyName == "" { + return nil, nil, errors.New("key name is required") + } + + conn, err := grpc.NewClient( + consensusGRPC, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + return nil, nil, fmt.Errorf("dial consensus grpc %q: %w", consensusGRPC, err) + } + + tc, err := txclient.NewTxClient(kr, keyName, conn) + if err != nil { + _ = conn.Close() + return nil, nil, fmt.Errorf("new tx client: %w", err) + } + if err := tc.Start(ctx); err != nil { + _ = conn.Close() + return nil, nil, fmt.Errorf("start tx client: %w", err) + } + + appCfg := appfibre.DefaultClientConfig() + appCfg.DefaultKeyName = keyName + appCfg.StateAddress = conn.Target() + appClient, err := appfibre.NewClient(kr, appCfg) + if err != nil { + _ = tc.Stop(ctx) + _ = conn.Close() + return nil, nil, fmt.Errorf("new app fibre client: %w", err) + } + if err := appClient.Start(ctx); err != nil { + _ = tc.Stop(ctx) + _ = conn.Close() + return nil, nil, fmt.Errorf("start app fibre client: %w", err) + } + + accClient := fibre.NewAccountClient(tc, conn) + svc := fibre.NewService(appClient, tc, accClient) + module := nodebuilderfibre.NewModule(svc) + + adapter := cnfiber.FromModules(module, noBridgeBlob{}, 0) + + closer := func() error { + stopCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + var errs error + if err := appClient.Stop(stopCtx); err != nil { + errs = errors.Join(errs, err) + } + if err := tc.Stop(stopCtx); err != nil { + errs = errors.Join(errs, err) + } + if err := conn.Close(); err != nil { + errs = errors.Join(errs, err) + } + return errs + } + + return adapter, closer, nil +} + +// noBridgeBlob errors on every call. The only path that would invoke it +// is Listen→Subscribe, which our aggregator-only single-sequencer node +// never reaches. A clear error here surfaces an assumption break instead +// of a nil panic. +type noBridgeBlob struct{} + +var _ blobapi.Module = noBridgeBlob{} + +var errNoBridge = errors.New("fiber-bench: blob module not supported (running without a bridge node)") + +func (noBridgeBlob) Submit(context.Context, []*blob.Blob, *blob.SubmitOptions) (uint64, error) { + return 0, errNoBridge +} +func (noBridgeBlob) Get(context.Context, uint64, libshare.Namespace, blob.Commitment) (*blob.Blob, error) { + return nil, errNoBridge +} +func (noBridgeBlob) GetAll(context.Context, uint64, []libshare.Namespace) ([]*blob.Blob, error) { + return nil, errNoBridge +} +func (noBridgeBlob) GetProof(context.Context, uint64, libshare.Namespace, blob.Commitment) (*blob.Proof, error) { + return nil, errNoBridge +} +func (noBridgeBlob) Included(context.Context, uint64, libshare.Namespace, *blob.Proof, blob.Commitment) (bool, error) { + return false, errNoBridge +} +func (noBridgeBlob) GetCommitmentProof(context.Context, uint64, libshare.Namespace, []byte) (*blob.CommitmentProof, error) { + return nil, errNoBridge +} +func (noBridgeBlob) Subscribe(context.Context, libshare.Namespace, uint64) (<-chan *blob.SubscriptionResponse, error) { + return nil, errNoBridge +} diff --git a/tools/celestia-node-fiber/cmd/fiber-bench/instrumented.go b/tools/celestia-node-fiber/cmd/fiber-bench/instrumented.go new file mode 100644 index 0000000000..3acdfea794 --- /dev/null +++ b/tools/celestia-node-fiber/cmd/fiber-bench/instrumented.go @@ -0,0 +1,138 @@ +package main + +import ( + "context" + "sort" + "sync" + "sync/atomic" + "time" + + "github.com/evstack/ev-node/block" +) + +// instrumentedAdapter wraps a block.FiberClient and records latency +// per Upload (and per Download) call. The bench's stats printer +// reads percentiles from here so we can answer "is the bottleneck +// ev-node's submitter serialization, or actual Fibre Upload time?". +// +// We keep the last N samples in a ring buffer rather than an +// unbounded slice so a long run does not grow memory; N is sized for +// a 30-minute run at peak block rate. +type instrumentedAdapter struct { + inner block.FiberClient + + uploadCount atomic.Uint64 + uploadFailures atomic.Uint64 + uploadBytesSent atomic.Uint64 + + mu sync.Mutex + samples []time.Duration // ring buffer of recent durations + idx int // next slot to write + full bool // ring buffer has wrapped at least once +} + +const uploadSampleCapacity = 4096 + +func newInstrumentedAdapter(inner block.FiberClient) *instrumentedAdapter { + return &instrumentedAdapter{ + inner: inner, + samples: make([]time.Duration, uploadSampleCapacity), + } +} + +func (a *instrumentedAdapter) Upload(ctx context.Context, namespace []byte, data []byte) (block.FiberUploadResult, error) { + start := time.Now() + res, err := a.inner.Upload(ctx, namespace, data) + elapsed := time.Since(start) + + a.uploadCount.Add(1) + if err != nil { + a.uploadFailures.Add(1) + } else { + a.uploadBytesSent.Add(uint64(len(data))) + } + + a.mu.Lock() + a.samples[a.idx] = elapsed + a.idx = (a.idx + 1) % len(a.samples) + if a.idx == 0 { + a.full = true + } + a.mu.Unlock() + + return res, err +} + +func (a *instrumentedAdapter) Download(ctx context.Context, blobID block.FiberBlobID) ([]byte, error) { + return a.inner.Download(ctx, blobID) +} + +func (a *instrumentedAdapter) Listen(ctx context.Context, namespace []byte, fromHeight uint64) (<-chan block.FiberBlobEvent, error) { + return a.inner.Listen(ctx, namespace, fromHeight) +} + +// uploadStats returns snapshot p50, p99, mean of recent Upload +// durations plus cumulative counters. Returns zero durations when +// no samples have been recorded yet. +type uploadStats struct { + Count uint64 + Failures uint64 + BytesOK uint64 + P50 time.Duration + P99 time.Duration + Mean time.Duration + Max time.Duration +} + +func (a *instrumentedAdapter) uploadStats() uploadStats { + a.mu.Lock() + var n int + if a.full { + n = len(a.samples) + } else { + n = a.idx + } + if n == 0 { + a.mu.Unlock() + return uploadStats{ + Count: a.uploadCount.Load(), + Failures: a.uploadFailures.Load(), + BytesOK: a.uploadBytesSent.Load(), + } + } + // Copy under lock so we can sort outside it. + cp := make([]time.Duration, n) + copy(cp, a.samples[:n]) + a.mu.Unlock() + + sort.Slice(cp, func(i, j int) bool { return cp[i] < cp[j] }) + + var sum time.Duration + for _, d := range cp { + sum += d + } + + pct := func(p float64) time.Duration { + idx := int(float64(n-1) * p) + if idx < 0 { + idx = 0 + } + if idx >= n { + idx = n - 1 + } + return cp[idx] + } + + return uploadStats{ + Count: a.uploadCount.Load(), + Failures: a.uploadFailures.Load(), + BytesOK: a.uploadBytesSent.Load(), + P50: pct(0.50), + P99: pct(0.99), + Mean: sum / time.Duration(n), + Max: cp[n-1], + } +} + +// Compile-time guard: must satisfy the same interface ev-node consumes. +var _ block.FiberClient = (*instrumentedAdapter)(nil) diff --git a/tools/celestia-node-fiber/cmd/fiber-bench/keys.go b/tools/celestia-node-fiber/cmd/fiber-bench/keys.go new file mode 100644 index 0000000000..7b1e83ce93 --- /dev/null +++ b/tools/celestia-node-fiber/cmd/fiber-bench/keys.go @@ -0,0 +1,153 @@ +package main + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/spf13/cobra" +) + +// openKeyring opens (or creates if missing) a test-backend keyring at +// keyringDir. The "test" backend is unencrypted on disk — fine for a +// bench account, not fine for anything mainnet. +func openKeyring(keyringDir string) (keyring.Keyring, error) { + interfaceRegistry := types.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(interfaceRegistry) + cdc := codec.NewProtoCodec(interfaceRegistry) + return keyring.New( + "fiber-bench", + keyring.BackendTest, + keyringDir, + nil, + cdc, + ) +} + +func keysCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "keys", + Short: "Manage the cosmos keyring used to sign Fibre payment promises", + } + cmd.AddCommand(keysAddCmd(), keysShowCmd(), keysListCmd()) + return cmd +} + +func keysAddCmd() *cobra.Command { + var keyringDir string + cmd := &cobra.Command{ + Use: "add ", + Short: "Create a new key in the bench keyring (test backend, unencrypted)", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + name := args[0] + kr, err := openKeyring(keyringDir) + if err != nil { + return fmt.Errorf("open keyring: %w", err) + } + + if rec, _ := kr.Key(name); rec != nil { + return fmt.Errorf("key %q already exists in keyring %s", name, keyringDir) + } + + rec, mnemonic, err := kr.NewMnemonic( + name, + keyring.English, + sdk.FullFundraiserPath, + keyring.DefaultBIP39Passphrase, + hd.Secp256k1, + ) + if err != nil { + return fmt.Errorf("create key: %w", err) + } + + addr, err := rec.GetAddress() + if err != nil { + return fmt.Errorf("get address: %w", err) + } + + fmt.Printf("name: %s\n", name) + fmt.Printf("address: %s\n", addr.String()) + fmt.Printf("keyring: %s (backend=test)\n", keyringDir) + fmt.Printf("\nmnemonic (back this up — printed once, never stored elsewhere):\n%s\n", mnemonic) + fmt.Printf("\nNext steps:\n") + fmt.Printf(" 1. Top up the address above with utia on the chain.\n") + fmt.Printf(" 2. Deposit into the Fibre escrow with celestia-appd or your tooling, e.g.\n") + fmt.Printf(" celestia-appd tx fibre deposit-escrow --from %s --keyring-backend test --keyring-dir %s --chain-id --node tcp://\n", name, keyringDir) + return nil + }, + } + cmd.Flags().StringVar(&keyringDir, "keyring-dir", defaultKeyringDir(), "directory to store keyring files (test backend)") + return cmd +} + +func keysShowCmd() *cobra.Command { + var keyringDir string + cmd := &cobra.Command{ + Use: "show ", + Short: "Print the address of an existing key", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + kr, err := openKeyring(keyringDir) + if err != nil { + return fmt.Errorf("open keyring: %w", err) + } + rec, err := kr.Key(args[0]) + if err != nil { + return fmt.Errorf("get key: %w", err) + } + addr, err := rec.GetAddress() + if err != nil { + return fmt.Errorf("get address: %w", err) + } + fmt.Printf("name: %s\n", args[0]) + fmt.Printf("address: %s\n", addr.String()) + fmt.Printf("keyring: %s (backend=test)\n", keyringDir) + return nil + }, + } + cmd.Flags().StringVar(&keyringDir, "keyring-dir", defaultKeyringDir(), "directory holding keyring files (test backend)") + return cmd +} + +func keysListCmd() *cobra.Command { + var keyringDir string + cmd := &cobra.Command{ + Use: "list", + Short: "List all keys in the bench keyring", + RunE: func(cmd *cobra.Command, args []string) error { + kr, err := openKeyring(keyringDir) + if err != nil { + return fmt.Errorf("open keyring: %w", err) + } + records, err := kr.List() + if err != nil { + return fmt.Errorf("list keys: %w", err) + } + if len(records) == 0 { + fmt.Printf("(empty — keyring at %s)\n", keyringDir) + return nil + } + for _, rec := range records { + addr, err := rec.GetAddress() + if err != nil { + return err + } + fmt.Printf("%-20s %s\n", rec.Name, addr.String()) + } + return nil + }, + } + cmd.Flags().StringVar(&keyringDir, "keyring-dir", defaultKeyringDir(), "directory holding keyring files (test backend)") + return cmd +} + +// silenceUnusedClient keeps the SDK client package referenced even if a +// future refactor stops using it directly — convenient when wiring a +// proper send/escrow command. +var _ = client.Context{} diff --git a/tools/celestia-node-fiber/cmd/fiber-bench/loader.go b/tools/celestia-node-fiber/cmd/fiber-bench/loader.go new file mode 100644 index 0000000000..4f527e8dc8 --- /dev/null +++ b/tools/celestia-node-fiber/cmd/fiber-bench/loader.go @@ -0,0 +1,89 @@ +package main + +import ( + "context" + "encoding/binary" + "sync" + "sync/atomic" + "time" +) + +// loaderBackoff is what each worker waits when InjectTx returns false +// because the mempool channel is full. A real sleep (rather than +// runtime.Gosched) caps the per-worker drop rate so allocation +// pressure scales with actual drain throughput; without it, full- +// mempool workers spin a tight allocate-then-drop loop at ~200k +// iter/s/worker — millions of short-lived slices per second across the +// pool, which drove the OOM kills we hit early in the investigation. +// 100 µs caps each worker at ~10k drops/s when the mempool is +// permanently full. +const loaderBackoff = 100 * time.Microsecond + +// loader pumps fixed-size payloads into the in-mem executor as fast as it +// can. Backpressure comes from the executor's bounded mempool channel: +// when full, InjectTx returns false and we count it as dropped. +// +// Each payload is `txSize` bytes: a tx-id (uint64) prefix + zero filler. +// Non-deterministic content isn't important — ev-node hashes them for +// the seen-tx cache, so any unique-per-tx prefix is enough to avoid +// dedup hits. +type loader struct { + exec *inMemExecutor + workers int + txSize int + + // counter monotonically increments per generated tx so the + // SHA-256-based seen cache never falsely dedups. + counter atomic.Uint64 +} + +func newLoader(exec *inMemExecutor, workers, txSize int) *loader { + if workers < 1 { + workers = 1 + } + if txSize < 8 { + txSize = 8 + } + return &loader{ + exec: exec, + workers: workers, + txSize: txSize, + } +} + +// run blocks until ctx is done. Each worker spins on InjectTx — when +// full, it briefly yields. We don't sleep-back-off because the entire +// point of the bench is to keep the executor's mempool pressed against +// its bound. +func (l *loader) run(ctx context.Context) { + var wg sync.WaitGroup + for i := 0; i < l.workers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + buf := make([]byte, l.txSize) + for { + if ctx.Err() != nil { + return + } + id := l.counter.Add(1) + binary.BigEndian.PutUint64(buf, id) + // Copy on each Inject — the executor's mempool is a + // channel of []byte, and the consumer keeps a + // reference. Reusing the same buffer would corrupt + // in-flight items. + tx := make([]byte, l.txSize) + copy(tx, buf) + if !l.exec.InjectTx(tx) { + // Mempool full — back off briefly and retry. + select { + case <-ctx.Done(): + return + case <-time.After(loaderBackoff): + } + } + } + }() + } + wg.Wait() +} diff --git a/tools/celestia-node-fiber/cmd/fiber-bench/main.go b/tools/celestia-node-fiber/cmd/fiber-bench/main.go new file mode 100644 index 0000000000..1671e4a784 --- /dev/null +++ b/tools/celestia-node-fiber/cmd/fiber-bench/main.go @@ -0,0 +1,60 @@ +// Package main is the fiber-bench tool: a single-sequencer ev-node wired +// to a remote Fibre network for throughput measurement. +// +// It deliberately runs in the simplest possible configuration: +// +// - Solo sequencer (no based / no forced inclusion) +// - Aggregator-only (no syncer, no P2P) +// - In-memory executor with constant state root (no state computation +// cost in the measurement) +// - Bridge-bypass Fibre adapter (Upload directly via consensus gRPC + FSPs) +// +// The intent is a fail-fast baseline so we can isolate ev-node's batching +// + DA-submit pipeline from everything else. +package main + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + // Pull celestia-app params for its init() that sets the global SDK + // bech32 prefix to "celestia" — must run before any keyring operation + // that prints addresses. + _ "github.com/celestiaorg/celestia-app/v9/app/params" + + rollconf "github.com/evstack/ev-node/pkg/config" +) + +// AppName names the binary. The home dir intentionally lives one level +// deeper at ~/.fiber-bench/node so the bench's --keep-home=false default +// (which os.RemoveAll's cfg.RootDir) cannot wipe the cosmos keyring at +// ~/.fiber-bench/keyring. +const ( + AppName = "fiber-bench" + defaultHomeAppName = AppName + "/node" +) + +func main() { + root := &cobra.Command{ + Use: AppName, + Short: "Single-sequencer ev-node throughput bench against a remote Fibre network", + } + + // Register --home, --evnode.log.level, --evnode.log.format, + // --evnode.log.trace on the root so every subcommand inherits them + // (matches apps/testapp). + rollconf.AddGlobalFlags(root, defaultHomeAppName) + + root.AddCommand( + keysCmd(), + escrowCmd(), + runCmd(), + ) + + if err := root.Execute(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/tools/celestia-node-fiber/cmd/fiber-bench/run-bench.sh b/tools/celestia-node-fiber/cmd/fiber-bench/run-bench.sh new file mode 100755 index 0000000000..43ea31d0be --- /dev/null +++ b/tools/celestia-node-fiber/cmd/fiber-bench/run-bench.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# run-bench.sh — convenience wrapper around `fiber-bench` for the +# common case: build the binary if missing, ensure a key exists, +# print the address, then start a run. +# +# Usage: +# CONSENSUS_GRPC=139.59.229.101:9091 \ +# CHAIN_ID=talis-evnode \ +# ./run-bench.sh [duration] [workers] +# +# All optional flags pass through via FIBER_BENCH_ARGS. +set -euo pipefail + +cd "$(dirname "$0")/../../.." + +CONSENSUS_GRPC="${CONSENSUS_GRPC:-}" +CHAIN_ID="${CHAIN_ID:-}" +KEYRING_DIR="${KEYRING_DIR:-$HOME/.fiber-bench/keyring}" +KEY_NAME="${KEY_NAME:-bench}" +DURATION="${1:-${DURATION:-2m}}" +WORKERS="${2:-${WORKERS:-32}}" +TX_SIZE="${TX_SIZE:-200}" +BLOCK_TIME="${BLOCK_TIME:-1s}" +BATCHING="${BATCHING:-immediate}" +HOME_DIR="${HOME_DIR:-$HOME/.fiber-bench/node}" + +if [[ -z "$CONSENSUS_GRPC" || -z "$CHAIN_ID" ]]; then + echo "ERROR: CONSENSUS_GRPC and CHAIN_ID must be set" >&2 + echo " example: CONSENSUS_GRPC=host:9091 CHAIN_ID=talis-evnode $0" >&2 + exit 1 +fi + +BIN="$(pwd)/bin/fiber-bench" +mkdir -p "$(dirname "$BIN")" + +if [[ ! -x "$BIN" || -n "${REBUILD:-}" ]]; then + echo "==> building fiber-bench (-tags fibre)" + go build -tags fibre -o "$BIN" ./cmd/fiber-bench/ +fi + +# Create the bench key if missing — idempotent: `keys add` errors if the +# key exists, so we only run it on a fresh keyring. +if ! "$BIN" keys show "$KEY_NAME" --keyring-dir "$KEYRING_DIR" >/dev/null 2>&1; then + echo "==> creating bench key '$KEY_NAME' at $KEYRING_DIR" + "$BIN" keys add "$KEY_NAME" --keyring-dir "$KEYRING_DIR" + echo + echo "Top up the address above and run:" + echo " $BIN escrow deposit --consensus-grpc $CONSENSUS_GRPC \\" + echo " --keyring-dir $KEYRING_DIR --key-name $KEY_NAME --amount 50000000" + echo + echo "Then re-run this script." + exit 0 +fi + +echo "==> bench account:" +"$BIN" keys show "$KEY_NAME" --keyring-dir "$KEYRING_DIR" + +echo "==> escrow:" +"$BIN" escrow query --consensus-grpc "$CONSENSUS_GRPC" \ + --keyring-dir "$KEYRING_DIR" --key-name "$KEY_NAME" || true + +echo "==> starting bench: duration=$DURATION workers=$WORKERS tx_size=$TX_SIZE block_time=$BLOCK_TIME batching=$BATCHING" +exec "$BIN" run \ + --evnode.da.fiber.consensus_address "$CONSENSUS_GRPC" \ + --evnode.da.fiber.consensus_chain_id "$CHAIN_ID" \ + --evnode.da.fiber.key_name "$KEY_NAME" \ + --keyring-dir "$KEYRING_DIR" \ + --home "$HOME_DIR" \ + --duration "$DURATION" \ + --workers "$WORKERS" \ + --tx-size "$TX_SIZE" \ + --evnode.node.block_time "$BLOCK_TIME" \ + --evnode.da.batching_strategy "$BATCHING" \ + ${FIBER_BENCH_ARGS:-} diff --git a/tools/celestia-node-fiber/cmd/fiber-bench/run.go b/tools/celestia-node-fiber/cmd/fiber-bench/run.go new file mode 100644 index 0000000000..0666790c26 --- /dev/null +++ b/tools/celestia-node-fiber/cmd/fiber-bench/run.go @@ -0,0 +1,306 @@ +package main + +import ( + "context" + "errors" + "fmt" + "os" + "os/signal" + "path/filepath" + "sync" + "syscall" + "time" + + "github.com/spf13/cobra" + + "github.com/evstack/ev-node/node" + rollcmd "github.com/evstack/ev-node/pkg/cmd" + rollconf "github.com/evstack/ev-node/pkg/config" + "github.com/evstack/ev-node/pkg/genesis" + "github.com/evstack/ev-node/pkg/p2p/key" + "github.com/evstack/ev-node/pkg/sequencers/solo" + "github.com/evstack/ev-node/pkg/signer/file" + "github.com/evstack/ev-node/pkg/store" +) + +// Bench-local flag names. The rest come from rollconf.AddFlags +// (--evnode.da.fiber.consensus_address, --evnode.da.batching_strategy, …) +// and rollconf.AddGlobalFlags (--home, --log.level, …). +const ( + flagKeyringDir = "keyring-dir" + flagKeepHome = "keep-home" + flagDuration = "duration" + flagWorkers = "workers" + flagTxSize = "tx-size" + flagMempoolSize = "mempool-size" + flagStatsInterval = "stats-interval" + flagSignerPassphrase = "signer-passphrase" +) + +func runCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "run", + Short: "Run the bench: start a single-sequencer ev-node against a Fibre network and pump load", + RunE: runBench, + } + + // Canonical ev-node flags: --evnode.node.*, --evnode.da.*, + // --evnode.da.fiber.*, --evnode.signer.*, --evnode.instrumentation.*, + // --evnode.p2p.*, --evnode.signer.passphrase_file, etc. The bench + // applies opinionated defaults post-parse for the ones a thoughtful + // operator would otherwise have to flip every run (see runBench). + rollconf.AddFlags(cmd) + + flags := cmd.Flags() + flags.String(flagKeyringDir, defaultKeyringDir(), "directory holding the bench cosmos keyring (test backend) used to sign Fibre payment promises") + flags.Bool(flagKeepHome, false, "do not wipe the ev-node home before starting (resumes prior state)") + flags.Duration(flagDuration, 60*time.Second, "how long to run the bench before stopping (0 = until SIGINT)") + flags.Int(flagWorkers, 32, "number of concurrent tx-injection goroutines") + flags.Int(flagTxSize, 200, "size of each generated tx in bytes") + flags.Int(flagMempoolSize, 1_000_000, "size of the in-mem executor's mempool channel (backpressure boundary)") + flags.Duration(flagStatsInterval, time.Second, "how often to print a stats line") + flags.String(flagSignerPassphrase, "fiber-bench-passphrase", "passphrase for the ev-node file signer (block-signing key, NOT the cosmos one). Written to a temp file consumed by --evnode.signer.passphrase_file.") + + // Fibre consensus address/chain ID don't have empty defaults + // (DefaultConfig points at 127.0.0.1:9090 / mocha-4), but those + // values are sentinels — running the bench against them is never + // what the operator wants. Force them through. + _ = cobra.MarkFlagRequired(flags, rollconf.FlagDAFiberConsensusAddress) + _ = cobra.MarkFlagRequired(flags, rollconf.FlagDAFiberConsensusChainID) + + return cmd +} + +func runBench(cobraCmd *cobra.Command, _ []string) error { + cfg, err := rollcmd.ParseConfig(cobraCmd) + if err != nil { + return err + } + applyBenchDefaults(cobraCmd, &cfg) + + // Re-validate after the bench's overrides — ParseConfig already ran + // once on parse, but we mutated Aggregator/Fiber/etc. afterwards. + if err := cfg.Validate(); err != nil { + return fmt.Errorf("config invalid after bench overrides: %w", err) + } + + logger := rollcmd.SetupLogger(cfg.Log) + + keyringDir, _ := cobraCmd.Flags().GetString(flagKeyringDir) + keepHome, _ := cobraCmd.Flags().GetBool(flagKeepHome) + duration, _ := cobraCmd.Flags().GetDuration(flagDuration) + workers, _ := cobraCmd.Flags().GetInt(flagWorkers) + txSize, _ := cobraCmd.Flags().GetInt(flagTxSize) + mempoolSize, _ := cobraCmd.Flags().GetInt(flagMempoolSize) + statsInterval, _ := cobraCmd.Flags().GetDuration(flagStatsInterval) + signerPassphrase, _ := cobraCmd.Flags().GetString(flagSignerPassphrase) + + if !keepHome { + _ = os.RemoveAll(cfg.RootDir) + } + if err := os.MkdirAll(cfg.RootDir, 0o755); err != nil { + return fmt.Errorf("create home %s: %w", cfg.RootDir, err) + } + + // 1) Cosmos keyring + bridge-bypass Fibre adapter — the two genuinely + // fiber-bench-specific pieces. Neither lives in the production wiring + // path. + kr, err := openKeyring(keyringDir) + if err != nil { + return fmt.Errorf("open keyring at %s: %w", keyringDir, err) + } + rec, err := kr.Key(cfg.DA.Fiber.KeyName) + if err != nil { + return fmt.Errorf("key %q not found in keyring %s — run `fiber-bench keys add %s` first: %w", + cfg.DA.Fiber.KeyName, keyringDir, cfg.DA.Fiber.KeyName, err) + } + addr, err := rec.GetAddress() + if err != nil { + return fmt.Errorf("derive key address: %w", err) + } + logger.Info().Str("address", addr.String()).Str("key", cfg.DA.Fiber.KeyName).Msg("loaded fibre signing key") + + logger.Info().Str("grpc", cfg.DA.Fiber.ConsensusAddress).Msg("dialing consensus gRPC") + innerFiberClient, fiberClose, err := buildFibreAdapter(cobraCmd.Context(), cfg.DA.Fiber.ConsensusAddress, cfg.DA.Fiber.KeyName, kr) + if err != nil { + return fmt.Errorf("build fibre adapter: %w", err) + } + defer func() { + if err := fiberClose(); err != nil { + logger.Warn().Err(err).Msg("fibre adapter close") + } + }() + // Wrap in a latency-recording proxy so the stats printer can show + // per-Upload p50/p99. + fiberClient := newInstrumentedAdapter(innerFiberClient) + + // 2) ev-node block-signing key. Created in cfg.Signer.SignerPath if + // missing. cmd.StartNode reads the passphrase from the path stored + // in --evnode.signer.passphrase_file; we write a temp file from + // --signer-passphrase and inject the flag value so the canonical + // signer-loading path works without us asking the operator to manage + // a passphrase file by hand. + signerDir := cfg.Signer.SignerPath + if signerDir == "" { + signerDir = filepath.Join(cfg.RootDir, "config") + } + if !filepath.IsAbs(signerDir) { + signerDir = filepath.Join(cfg.RootDir, signerDir) + } + cfg.Signer.SignerPath = signerDir + if err := os.MkdirAll(signerDir, 0o750); err != nil { + return fmt.Errorf("create signer dir: %w", err) + } + signerFile := filepath.Join(signerDir, "signer.json") + if _, statErr := os.Stat(signerFile); os.IsNotExist(statErr) { + s, err := file.CreateFileSystemSigner(signerDir, []byte(signerPassphrase)) + if err != nil { + return fmt.Errorf("create file signer: %w", err) + } + if _, err := s.GetAddress(); err != nil { + return fmt.Errorf("signer address: %w", err) + } + } + passphraseFile := filepath.Join(cfg.RootDir, "passphrase.txt") + if err := os.WriteFile(passphraseFile, []byte(signerPassphrase), 0o600); err != nil { + return fmt.Errorf("write passphrase file: %w", err) + } + if err := cobraCmd.Flags().Set(rollconf.FlagSignerPassphraseFile, passphraseFile); err != nil { + return fmt.Errorf("set passphrase flag: %w", err) + } + + // Reload the signer to derive the genesis proposer address. + loaded, err := file.LoadFileSystemSigner(signerDir, []byte(signerPassphrase)) + if err != nil { + return fmt.Errorf("load file signer: %w", err) + } + signerAddr, err := loaded.GetAddress() + if err != nil { + return fmt.Errorf("signer address: %w", err) + } + + // 3) Genesis. Single proposer = our signer. + gen := genesis.NewGenesis(cfg.DA.Fiber.ConsensusChainID, 1, time.Now().UTC(), signerAddr) + if err := gen.Validate(); err != nil { + return fmt.Errorf("invalid genesis: %w", err) + } + + // 4) Datastore + node-key + executor + sequencer. The first three + // look identical to what testapp/cmd/run.go does; the executor + // is the bench-specific in-memory variant (constant state root, + // see executor.go for rationale) and the sequencer is solo (no + // based-sequencer / no forced inclusion machinery). + ds, err := store.NewDefaultKVStore(cfg.RootDir, cfg.DBPath, "fiber-bench") + if err != nil { + return fmt.Errorf("open datastore: %w", err) + } + // Match canonical layout: node_key.json under /config/, the + // same dir testapp/cmd/run.go reads it from. + nodeKey, err := key.LoadOrGenNodeKey(filepath.Dir(cfg.ConfigPath())) + if err != nil { + return fmt.Errorf("node key: %w", err) + } + exec := newInMemExecutor(mempoolSize) + seq := solo.NewSoloSequencer(logger, []byte(gen.ChainID), exec) + + // 5) Spawn loader + stats printer BEFORE cmd.StartNode (which + // blocks). They run for the lifetime of the bench. cmd.StartNode + // owns its own signal-handling goroutine; we send SIGINT to + // ourselves when the duration timer expires so it can exit + // through its normal shutdown path. + bgCtx, bgCancel := signal.NotifyContext(cobraCmd.Context(), os.Interrupt, syscall.SIGTERM) + defer bgCancel() + + var loaderWg sync.WaitGroup + loaderWg.Add(1) + go func() { + defer loaderWg.Done() + newLoader(exec, workers, txSize).run(bgCtx) + }() + + printer := newStatsPrinter(exec, cfg.Instrumentation.PrometheusListenAddr, txSize, fiberClient) + printer.start(bgCtx, statsInterval) + + logger.Info(). + Dur("duration", duration). + Int("workers", workers). + Int("tx_size", txSize). + Int("mempool", mempoolSize). + Dur("block_time", cfg.Node.BlockTime.Duration). + Str("batching", cfg.DA.BatchingStrategy). + Msg("bench started") + + if duration > 0 { + go func() { + select { + case <-time.After(duration): + logger.Info().Msg("duration elapsed, sending SIGINT to trigger shutdown") + _ = syscall.Kill(syscall.Getpid(), syscall.SIGINT) + case <-bgCtx.Done(): + } + }() + } + + // 6) The actual node — let cmd.StartNode do all the wiring (signer + // load, DA client, p2p, node.NewNode, run loop with shutdown). Same + // call testapp/evm/grpc apps make. + startErr := rollcmd.StartNode( + logger, cobraCmd, exec, seq, nodeKey, ds, cfg, gen, + node.NodeOptions{}, fiberClient, + ) + + bgCancel() + loaderWg.Wait() + printer.printFinalSummary() + + if startErr != nil && !errors.Is(startErr, context.Canceled) { + return startErr + } + return nil +} + +// applyBenchDefaults overrides config fields that the bench needs forced +// (Aggregator, Fiber.Enabled) and the canonical defaults that are wrong +// for a throughput bench (DA block time, batching strategy, scrape +// interval, namespaces). Anything the operator passed on the command line +// is left untouched — we only override where the flag value still equals +// its canonical default. +func applyBenchDefaults(cmd *cobra.Command, cfg *rollconf.Config) { + // Forced for the bench: aggregator-only, Fibre DA, no P2P. + cfg.Node.Aggregator = true + cfg.Node.BasedSequencer = false + cfg.DA.Fiber.Enabled = true + if cfg.DA.Fiber.BridgeAddress == "" { + // FiberDAConfig.Validate requires a ws:// or wss:// address. + // Bench never dials it (see fibre.go: noBridgeBlob). + cfg.DA.Fiber.BridgeAddress = "ws://127.0.0.1:0" + } + cfg.P2P.ListenAddress = "/ip4/127.0.0.1/tcp/0" + cfg.P2P.DisableConnectionGater = true + cfg.RPC.Address = "127.0.0.1:0" + cfg.Signer.SignerType = "file" + cfg.Instrumentation.Pprof = false + // The stats printer scrapes /metrics every tick — keep Prometheus on + // even if the operator didn't pass --evnode.instrumentation.prometheus. + cfg.Instrumentation.Prometheus = true + + // Operator-overridable bench defaults — applied only if the canonical + // flag wasn't passed on the command line. + overrideIfUnchanged := func(name string, set func()) { + if !cmd.Flags().Changed(name) { + set() + } + } + overrideIfUnchanged(rollconf.FlagDABlockTime, func() { + cfg.DA.BlockTime = rollconf.DurationWrapper{Duration: time.Second} + }) + overrideIfUnchanged(rollconf.FlagDABatchingStrategy, func() { cfg.DA.BatchingStrategy = "immediate" }) + overrideIfUnchanged(rollconf.FlagScrapeInterval, func() { + cfg.Node.ScrapeInterval = rollconf.DurationWrapper{Duration: 100 * time.Millisecond} + }) + overrideIfUnchanged(rollconf.FlagDARequestTimeout, func() { + cfg.DA.RequestTimeout = rollconf.DurationWrapper{Duration: 60 * time.Second} + }) + overrideIfUnchanged(rollconf.FlagDANamespace, func() { cfg.DA.Namespace = "fb-bench-h" }) + overrideIfUnchanged(rollconf.FlagDADataNamespace, func() { cfg.DA.DataNamespace = "fb-bench-d" }) +} diff --git a/tools/celestia-node-fiber/cmd/fiber-bench/stats.go b/tools/celestia-node-fiber/cmd/fiber-bench/stats.go new file mode 100644 index 0000000000..f45fb91c06 --- /dev/null +++ b/tools/celestia-node-fiber/cmd/fiber-bench/stats.go @@ -0,0 +1,380 @@ +package main + +import ( + "bufio" + "context" + "fmt" + "io" + "net/http" + "strconv" + "strings" + "sync" + "time" +) + +// statsPrinter periodically prints a one-line summary combining counters +// from the in-mem executor and selected Prometheus metrics scraped from +// ev-node's instrumentation endpoint. +// +// Why scrape Prometheus instead of reaching into ev-node? Because the +// metrics ev-node already exports give us the answers we want +// (committed height, txs-per-block, pending blobs, block-production +// duration histogram) and scraping is zero source diff. It also makes +// the same numbers available to a real Prometheus once we move past the +// fail-fast baseline. +type statsPrinter struct { + exec *inMemExecutor + promURL string + httpClient *http.Client + txSize int + adapter *instrumentedAdapter + + mu sync.Mutex + startedAt time.Time + lastTick time.Time + lastInject uint64 + lastTxs float64 + lastBlocks float64 + lastDaInc float64 + peakInjRPS float64 + peakTxRPS float64 + peakDaRPS float64 + + // lastSnapshot caches the last successful Prometheus scrape so + // the final summary still has values after the node has shut + // down (its /metrics endpoint goes away with it). + lastSnapshot map[string]float64 +} + +func newStatsPrinter(exec *inMemExecutor, promListenAddr string, txSize int, adapter *instrumentedAdapter) *statsPrinter { + url := "" + if promListenAddr != "" { + // PrometheusListenAddr can be ":26660" or "127.0.0.1:26660"; + // normalize to a fetchable URL. + host := promListenAddr + if strings.HasPrefix(host, ":") { + host = "127.0.0.1" + host + } + url = "http://" + host + "/metrics" + } + return &statsPrinter{ + exec: exec, + promURL: url, + httpClient: &http.Client{Timeout: 500 * time.Millisecond}, + txSize: txSize, + adapter: adapter, + } +} + +// start prints a header then ticks every interval until ctx is done. +func (p *statsPrinter) start(ctx context.Context, interval time.Duration) { + if interval <= 0 { + interval = time.Second + } + now := time.Now() + p.mu.Lock() + p.startedAt = now + p.lastTick = now + p.mu.Unlock() + + fmt.Println() + // Each rate column shows " / " so tps and bandwidth + // land side by side without doubling the column count. The blob + // size at the latest block stays as an absolute (blob_KB) since + // it's a level, not a rate. + fmt.Printf("%-9s %-15s %-15s %-15s %-7s %-9s %-7s %-8s %-7s %-10s %s\n", + "elapsed", "inj tps/MBs", "exec tps/MBs", "da tps/MBs", + "prod_h", "da_inc_h", "txs/blk", "blob_KB", "pending", "drops", "upload latency") + fmt.Println(strings.Repeat("-", 140)) + + go func() { + ticker := time.NewTicker(interval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + p.tick() + } + } + }() +} + +func (p *statsPrinter) tick() { + now := time.Now() + + injected, dropped, blocks, txs, _ := p.exec.Stats() + mempool := p.exec.MempoolDepth() + + prom := p.scrapePrometheus() + if len(prom) > 0 { + p.mu.Lock() + p.lastSnapshot = prom + p.mu.Unlock() + } + // ev-node prefixes its metrics with the namespace from the metrics + // provider — for the aggregator path this is "evnode_sequencer". + producedHeight := prom["evnode_sequencer_height"] + daInclusionHeight := prom["evnode_sequencer_da_inclusion_height"] + totalTxs := prom["evnode_sequencer_total_txs"] + if totalTxs == 0 { + totalTxs = float64(txs) + } + blockBytes := prom["evnode_sequencer_block_size_bytes"] + pending := prom["evnode_sequencer_da_submitter_pending_blobs"] + blocksGauge := float64(blocks) + if producedHeight > blocksGauge { + blocksGauge = producedHeight + } + txsPerBlock := txsPerBlockMetric(blocksGauge, totalTxs) + + p.mu.Lock() + dt := now.Sub(p.lastTick).Seconds() + if dt < 0.001 { + p.mu.Unlock() + return + } + injRPS := float64(injected-p.lastInject) / dt + txRPS := (totalTxs - p.lastTxs) / dt + daSettledRPS := (daInclusionHeight - p.lastDaInc) * txsPerBlock / dt + if injRPS > p.peakInjRPS { + p.peakInjRPS = injRPS + } + if txRPS > p.peakTxRPS { + p.peakTxRPS = txRPS + } + if daSettledRPS > p.peakDaRPS { + p.peakDaRPS = daSettledRPS + } + elapsed := now.Sub(p.startedAt).Truncate(time.Millisecond) + p.lastTick = now + p.lastInject = injected + p.lastTxs = totalTxs + p.lastBlocks = blocksGauge + p.lastDaInc = daInclusionHeight + p.mu.Unlock() + + txSizeBytes := float64(p.txSize) + + upStats := p.adapter.uploadStats() + + fmt.Printf("%-9s %-15s %-15s %-15s %-7.0f %-9.0f %-7.0f %-8.0f %-7.0f %-10d %s\n", + elapsed.String(), + formatRate(injRPS, txSizeBytes), + formatRate(txRPS, txSizeBytes), + formatRate(daSettledRPS, txSizeBytes), + producedHeight, daInclusionHeight, txsPerBlock, blockBytes/1024, pending, dropped, + formatUploadLatency(upStats), + ) + + _ = mempool // currently we report drops, not depth — the mempool is large enough that depth isn't the meaningful signal +} + +// formatUploadLatency renders Upload latency stats as a compact suffix +// for the live table. Returns "-" if no samples yet. +func formatUploadLatency(s uploadStats) string { + if s.Count == 0 { + return "upload[-]" + } + failPart := "" + if s.Failures > 0 { + failPart = fmt.Sprintf(",fails=%d", s.Failures) + } + return fmt.Sprintf("upload[n=%d p50=%v p99=%v%s]", + s.Count, s.P50.Truncate(time.Millisecond), s.P99.Truncate(time.Millisecond), failPart) +} + +// formatRate renders " / " compactly, rounding to whole MB/s +// since sub-MB/s precision isn't useful at our throughput levels and a +// short string keeps the table aligned. +func formatRate(rps, txSizeBytes float64) string { + mbps := rps * txSizeBytes / (1024 * 1024) + switch { + case rps >= 1_000_000: + return fmt.Sprintf("%.1fM/%.0fMB", rps/1_000_000, mbps) + case rps >= 1_000: + return fmt.Sprintf("%.0fk/%.0fMB", rps/1_000, mbps) + default: + return fmt.Sprintf("%.0f/%.1fMB", rps, mbps) + } +} + +// txsPerBlockMetric computes the running mean tx/blk over all produced +// blocks. Only meaningful once at least one block has been produced; +// returns 0 otherwise. +func txsPerBlockMetric(blocks, totalTxs float64) float64 { + if blocks <= 0 { + return 0 + } + return totalTxs / blocks +} + +// scrapePrometheus pulls the ev-node /metrics endpoint and parses just +// the gauges/counters we care about. Best effort: returns empty map on +// any error so the bench keeps running even if metrics aren't ready yet. +func (p *statsPrinter) scrapePrometheus() map[string]float64 { + out := map[string]float64{} + if p.promURL == "" { + return out + } + resp, err := p.httpClient.Get(p.promURL) + if err != nil { + return out + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + _, _ = io.Copy(io.Discard, resp.Body) + return out + } + + wanted := map[string]struct{}{ + "evnode_sequencer_height": {}, + "evnode_sequencer_latest_block_height": {}, + "evnode_sequencer_da_inclusion_height": {}, + "evnode_sequencer_total_txs": {}, + "evnode_sequencer_num_txs": {}, + "evnode_sequencer_block_size_bytes": {}, + "evnode_sequencer_da_submitter_pending_blobs": {}, + } + + scanner := bufio.NewScanner(resp.Body) + for scanner.Scan() { + line := scanner.Text() + if line == "" || strings.HasPrefix(line, "#") { + continue + } + // "metric_name{labels...} value [timestamp]" — strip labels and + // trailing timestamp; we don't use them. + nameEnd := strings.IndexAny(line, "{ ") + if nameEnd < 0 { + continue + } + name := line[:nameEnd] + if _, ok := wanted[name]; !ok { + continue + } + // Skip past labels if present. + rest := line[nameEnd:] + if rest[0] == '{' { + closeIdx := strings.Index(rest, "}") + if closeIdx < 0 { + continue + } + rest = rest[closeIdx+1:] + } + rest = strings.TrimSpace(rest) + valEnd := strings.IndexByte(rest, ' ') + valStr := rest + if valEnd >= 0 { + valStr = rest[:valEnd] + } + v, err := strconv.ParseFloat(valStr, 64) + if err != nil { + continue + } + out[name] = v + } + return out +} + +func (p *statsPrinter) printFinalSummary() { + injected, dropped, blocks, txs, mempoolHigh := p.exec.Stats() + // Prefer a fresh scrape, but fall back to the last live snapshot: + // the node's /metrics endpoint goes away as it shuts down, so a + // post-stop scrape returns an empty map and the summary would + // otherwise print zeros. + prom := p.scrapePrometheus() + p.mu.Lock() + if len(prom) == 0 && p.lastSnapshot != nil { + prom = p.lastSnapshot + } + p.mu.Unlock() + producedHeight := uint64(prom["evnode_sequencer_height"]) + daInclusionHeight := uint64(prom["evnode_sequencer_da_inclusion_height"]) + totalTxs := uint64(prom["evnode_sequencer_total_txs"]) + if totalTxs == 0 { + totalTxs = txs + } + + p.mu.Lock() + elapsed := time.Since(p.startedAt) + peakInj := p.peakInjRPS + peakTx := p.peakTxRPS + p.mu.Unlock() + + avgInj := 0.0 + if elapsed.Seconds() > 0 { + avgInj = float64(injected) / elapsed.Seconds() + } + avgTx := 0.0 + if elapsed.Seconds() > 0 { + avgTx = float64(totalTxs) / elapsed.Seconds() + } + txsPerBlock := 0.0 + if blocks > 0 { + txsPerBlock = float64(totalTxs) / float64(blocks) + } + txSize := float64(p.txSize) + mb := func(rps float64) float64 { return rps * txSize / (1024 * 1024) } + + p.mu.Lock() + peakDa := p.peakDaRPS + p.mu.Unlock() + + var avgDaSettled float64 + if daInclusionHeight > 0 && elapsed.Seconds() > 0 { + avgDaSettled = float64(daInclusionHeight) * txsPerBlock / elapsed.Seconds() + } + + fmt.Println() + fmt.Println(strings.Repeat("=", 70)) + fmt.Println(" BASELINE SUMMARY") + fmt.Println(strings.Repeat("=", 70)) + fmt.Printf("Duration: %s\n", elapsed.Truncate(time.Millisecond)) + fmt.Printf("Tx size: %d B\n", p.txSize) + fmt.Println() + fmt.Printf("Injection: avg %.0f tx/s (%.1f MB/s), peak %.0f tx/s (%.0f MB/s)\n", + avgInj, mb(avgInj), peakInj, mb(peakInj)) + fmt.Printf("Block production: avg %.0f tx/s (%.2f MB/s), peak %.0f tx/s (%.1f MB/s)\n", + avgTx, mb(avgTx), peakTx, mb(peakTx)) + fmt.Printf("DA-settled: avg %.0f tx/s (%.2f MB/s), peak %.0f tx/s (%.1f MB/s)\n", + avgDaSettled, mb(avgDaSettled), peakDa, mb(peakDa)) + fmt.Println() + fmt.Printf("Blocks produced: %d (prod_h=%d)\n", blocks, producedHeight) + fmt.Printf("DA-included height: %d (lag = %d blocks behind production)\n", + daInclusionHeight, producedHeight-daInclusionHeight) + fmt.Printf("Txs into blocks: %d (%.1f tx/blk)\n", totalTxs, txsPerBlock) + fmt.Printf("Dropped (mempool full): %d\n", dropped) + fmt.Printf("Mempool high-water: %d\n", mempoolHigh) + + upStats := p.adapter.uploadStats() + if upStats.Count > 0 { + fmt.Println() + fmt.Println("Fibre Upload latency (per call observed at the adapter):") + fmt.Printf(" count: %d (failures: %d)\n", upStats.Count, upStats.Failures) + fmt.Printf(" mean: %s\n", upStats.Mean.Truncate(time.Millisecond)) + fmt.Printf(" p50: %s\n", upStats.P50.Truncate(time.Millisecond)) + fmt.Printf(" p99: %s\n", upStats.P99.Truncate(time.Millisecond)) + fmt.Printf(" max: %s\n", upStats.Max.Truncate(time.Millisecond)) + // ev-node's submitter runs ONE header-Upload goroutine and + // ONE data-Upload goroutine concurrently (each TryLock'd via + // its own mutex in submitter.go). A block settles only when + // BOTH its header and data Uploads have returned, and each + // stream submits at most 1 Upload per mean_latency seconds — + // so the per-stream cap is 1/mean blocks/s, and the block + // settlement cap (min of the two) equals it. We print this + // so the operator can compare it to the observed da_inc_h + // rate and tell apart "Fibre Upload is slow" from "ev-node + // is leaving capacity on the table". + if upStats.Mean > 0 { + capBlocksPerSec := 1.0 / upStats.Mean.Seconds() + fmt.Printf(" implied cap (1/mean per stream): %.2f blocks/s ≈ %.0f tx/s (%.2f MB/s)\n", + capBlocksPerSec, + capBlocksPerSec*txsPerBlock, + capBlocksPerSec*txsPerBlock*txSize/(1024*1024), + ) + } + } + fmt.Println(strings.Repeat("=", 70)) +} diff --git a/tools/celestia-node-fiber/cmd/fiber-bench/util.go b/tools/celestia-node-fiber/cmd/fiber-bench/util.go new file mode 100644 index 0000000000..666d00f041 --- /dev/null +++ b/tools/celestia-node-fiber/cmd/fiber-bench/util.go @@ -0,0 +1,17 @@ +package main + +import ( + "os" + "path/filepath" +) + +// defaultKeyringDir is where we put the bench's cosmos keyring by default. +// Sibling of the ev-node home (~/.fiber-bench/node) so --keep-home=false +// runs cannot wipe it. +func defaultKeyringDir() string { + home, err := os.UserHomeDir() + if err != nil { + return ".fiber-bench-keyring" + } + return filepath.Join(home, ".fiber-bench", "keyring") +} diff --git a/tools/celestia-node-fiber/config.go b/tools/celestia-node-fiber/config.go new file mode 100644 index 0000000000..784d8a0faf --- /dev/null +++ b/tools/celestia-node-fiber/config.go @@ -0,0 +1,18 @@ +package celestianodefiber + +import ( + "github.com/celestiaorg/celestia-node/api/client" +) + +// Config configures the celestia-node-backed Fibre adapter. +type Config struct { + // Client is the full celestia-node api/client.Config. See that package + // for field semantics (ReadConfig.BridgeDAAddr, SubmitConfig.DefaultKeyName, + // SubmitConfig.CoreGRPCConfig, SubmitConfig.Fibre, etc.). + Client client.Config + + // ListenChannelSize bounds the buffered BlobEvent channel returned by + // Listen. 0 selects the default (16), matching the upstream + // blob.Subscribe buffer so backpressure behaves consistently. + ListenChannelSize int +} diff --git a/tools/celestia-node-fiber/doc.go b/tools/celestia-node-fiber/doc.go new file mode 100644 index 0000000000..1e1d22e394 --- /dev/null +++ b/tools/celestia-node-fiber/doc.go @@ -0,0 +1,14 @@ +// Package celestianodefiber implements the ev-node fiber.DA interface by +// delegating to a celestia-node api/client.Client. +// +// Upload and Download run locally against a Celestia consensus node (over +// gRPC) and Fibre Storage Providers (over Fibre gRPC), without a bridge-node +// hop, using the self-sufficient client introduced in celestia-node #4961. +// Listen subscribes to a bridge node's blob stream (JSON-RPC/HTTP) and +// forwards only share-version-2 blobs — the ones settled on-chain through +// MsgPayForFibre. +// +// This package is a separate Go sub-module from the parent ev-node repository +// so that ev-node core does not pick up the celestia-app / cosmos-sdk +// replace-directive soup that celestia-node requires. +package celestianodefiber diff --git a/tools/celestia-node-fiber/go.mod b/tools/celestia-node-fiber/go.mod new file mode 100644 index 0000000000..7054108df0 --- /dev/null +++ b/tools/celestia-node-fiber/go.mod @@ -0,0 +1,418 @@ +module github.com/evstack/ev-node/tools/celestia-node-fiber + +go 1.26.1 + +replace ( + // celestia-node replace directives (copied from its go.mod so we pull the + // same Celestia forks). Keep this block in sync with + // github.com/celestiaorg/celestia-node go.mod. + cosmossdk.io/api => github.com/celestiaorg/cosmos-sdk/api v0.7.6 + cosmossdk.io/log => github.com/celestiaorg/cosmos-sdk/log v1.1.1-0.20251116153902-f48fea92e627 + cosmossdk.io/x/upgrade => github.com/celestiaorg/cosmos-sdk/x/upgrade v0.2.0 + github.com/bytedance/sonic => github.com/bytedance/sonic v1.15.0 + github.com/bytedance/sonic/loader => github.com/bytedance/sonic/loader v0.5.0 + github.com/cloudwego/base64x => github.com/cloudwego/base64x v0.1.6 + github.com/cometbft/cometbft => github.com/celestiaorg/celestia-core v0.40.1 + github.com/consensys/gnark-crypto => github.com/consensys/gnark-crypto v0.18.0 + github.com/cosmos/cosmos-sdk => github.com/celestiaorg/cosmos-sdk v0.52.3 + github.com/cosmos/ibc-go/v8 => github.com/celestiaorg/ibc-go/v8 v8.7.2 + github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 + github.com/ipfs/boxo => github.com/celestiaorg/boxo v0.29.0-fork-4 + github.com/ipfs/go-datastore => github.com/celestiaorg/go-datastore v0.0.0-20250801131506-48a63ae531e4 + github.com/moby/term => github.com/moby/term v0.5.2 + github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + github.com/tendermint/tendermint => github.com/celestiaorg/celestia-core v1.55.0-tm-v0.34.35 + nhooyr.io/websocket => github.com/coder/websocket v1.8.6 +) + +// Use the parent ev-node module for block.* types re-exported from +// block/public.go. +replace github.com/evstack/ev-node => ../../. + +require ( + cosmossdk.io/math v1.5.3 + github.com/celestiaorg/celestia-app/v9 v9.0.0-20260427114616-9ff33a36eb19 + github.com/celestiaorg/celestia-node v0.29.3-mocha.0.20260427115656-23bec13f01a3 + github.com/celestiaorg/go-square/v4 v4.0.0-rc4.0.20260318002530-1ca8ff7b42ea + github.com/cometbft/cometbft v1.0.1 + github.com/cosmos/cosmos-sdk v0.50.13 + github.com/cristalhq/jwt/v5 v5.4.0 + github.com/evstack/ev-node v1.1.0 + github.com/evstack/ev-node/core v1.0.0 + github.com/ipfs/go-datastore v0.9.1 + github.com/libp2p/go-libp2p v0.48.0 + github.com/rs/zerolog v1.35.0 + github.com/stretchr/testify v1.11.1 + go.uber.org/fx v1.24.0 +) + +require ( + cel.dev/expr v0.25.1 // indirect + cloud.google.com/go v0.123.0 // indirect + cloud.google.com/go/auth v0.20.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/iam v1.7.0 // indirect + cloud.google.com/go/kms v1.29.0 // indirect + cloud.google.com/go/longrunning v0.9.0 // indirect + cloud.google.com/go/monitoring v1.24.3 // indirect + cloud.google.com/go/storage v1.61.3 // indirect + connectrpc.com/connect v1.19.2 // indirect + connectrpc.com/grpcreflect v1.3.0 // indirect + cosmossdk.io/api v1.0.0 // indirect + cosmossdk.io/client/v2 v2.0.0-beta.8 // indirect + cosmossdk.io/collections v0.4.0 // indirect + cosmossdk.io/core v1.1.0 // indirect + cosmossdk.io/depinject v1.2.1 // indirect + cosmossdk.io/errors v1.0.2 // indirect + cosmossdk.io/log v1.6.1 // indirect + cosmossdk.io/store v1.1.2 // indirect + cosmossdk.io/x/circuit v0.1.1 // indirect + cosmossdk.io/x/evidence v0.1.1 // indirect + cosmossdk.io/x/feegrant v0.1.1 // indirect + cosmossdk.io/x/tx v0.13.8 // indirect + cosmossdk.io/x/upgrade v0.1.4 // indirect + filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5 // indirect + filippo.io/edwards25519 v1.1.1 // indirect + filippo.io/keygen v0.0.0-20260114151900-8e2790ea4c5b // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.2 // indirect + github.com/BurntSushi/toml v1.6.0 // indirect + github.com/DataDog/datadog-go v4.8.3+incompatible // indirect + github.com/DataDog/zstd v1.5.7 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect + github.com/Jorropo/jsync v1.0.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect + github.com/RaduBerinde/axisds v0.1.0 // indirect + github.com/RaduBerinde/btreemap v0.0.0-20250419174037-3d62b7205d54 // indirect + github.com/armon/go-metrics v0.4.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.6 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.9 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.16 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.15 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.14 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.22 // indirect + github.com/aws/aws-sdk-go-v2/service/kms v1.50.5 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.99.1 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.16 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.42.0 // indirect + github.com/aws/smithy-go v1.25.0 // indirect + github.com/bcp-innovations/hyperlane-cosmos v1.1.0 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect + github.com/bgentry/speakeasy v0.2.0 // indirect + github.com/bits-and-blooms/bitset v1.24.0 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/boltdb/bolt v1.3.1 // indirect + github.com/celestiaorg/go-header v0.8.5 // indirect + github.com/celestiaorg/go-libp2p-messenger v0.2.2 // indirect + github.com/celestiaorg/go-square/merkle v0.0.0-20240627094109-7d01436067a3 // indirect + github.com/celestiaorg/go-square/v2 v2.3.3 // indirect + github.com/celestiaorg/go-square/v3 v3.0.2 // indirect + github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4 // indirect + github.com/celestiaorg/nmt v0.24.3 // indirect + github.com/celestiaorg/rsmt2d v0.15.2 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/chzyer/readline v1.5.1 // indirect + github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect + github.com/cockroachdb/crlib v0.0.0-20241112164430-1264a2edc35b // indirect + github.com/cockroachdb/errors v1.12.0 // indirect + github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 // indirect + github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect + github.com/cockroachdb/pebble v1.1.5 // indirect + github.com/cockroachdb/pebble/v2 v2.1.4 // indirect + github.com/cockroachdb/redact v1.1.6 // indirect + github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb // indirect + github.com/cometbft/cometbft-db v1.0.4 // indirect + github.com/consensys/gnark v0.14.0 // indirect + github.com/consensys/gnark-crypto v0.19.2 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-db v1.1.3 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/gogoproto v1.7.2 // indirect + github.com/cosmos/iavl v1.2.8 // indirect + github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8 v8.2.0 // indirect + github.com/cosmos/ibc-go/modules/capability v1.0.1 // indirect + github.com/cosmos/ibc-go/v8 v8.7.0 // indirect + github.com/cosmos/ics23/go v0.11.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.15.0 // indirect + github.com/cskr/pubsub v1.0.2 // indirect + github.com/danieljoos/wincred v1.2.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect + github.com/desertbit/timer v1.0.1 // indirect + github.com/dgraph-io/badger/v4 v4.9.1 // indirect + github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect + github.com/dunglas/httpsfv v1.1.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dvsekhvalnov/jose2go v1.7.0 // indirect + github.com/emicklei/dot v1.6.2 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect + github.com/ethereum/go-ethereum v1.17.0 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/filecoin-project/go-clock v0.1.0 // indirect + github.com/filecoin-project/go-jsonrpc v0.10.1 // indirect + github.com/flynn/noise v1.1.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/gammazero/chanqueue v1.1.1 // indirect + github.com/gammazero/deque v1.2.1 // indirect + github.com/gammazero/workerpool v1.2.1 // indirect + github.com/getsentry/sentry-go v0.42.0 // indirect + github.com/go-jose/go-jose/v4 v4.1.4 // 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.1 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect + github.com/gofrs/flock v0.13.0 // indirect + github.com/gogo/googleapis v1.4.1 // indirect + github.com/gogo/protobuf v1.3.3 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v1.0.0 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/flatbuffers v25.2.10+incompatible // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/gopacket v1.1.19 // indirect + github.com/google/orderedcode v0.0.1 // indirect + github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect + github.com/googleapis/gax-go/v2 v2.21.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/grafana/otel-profiling-go v0.5.1 // indirect + github.com/grafana/pyroscope-go v1.2.8 // indirect + github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect + github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-getter v1.8.6 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-metrics v0.5.4 // indirect + github.com/hashicorp/go-msgpack v0.5.5 // indirect + github.com/hashicorp/go-msgpack/v2 v2.1.2 // indirect + github.com/hashicorp/go-plugin v1.6.3 // indirect + github.com/hashicorp/go-version v1.8.0 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/arc/v2 v2.0.7 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/raft v1.7.3 // indirect + github.com/hashicorp/raft-boltdb v0.0.0-20251103221153-05f9dd7a5148 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect + github.com/hdevalence/ed25519consensus v0.2.0 // indirect + github.com/holiman/uint256 v1.3.2 // indirect + github.com/huandu/skiplist v1.2.1 // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/iancoleman/orderedmap v0.3.0 // indirect + github.com/iancoleman/strcase v0.3.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/improbable-eng/grpc-web v0.15.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/ingonyama-zk/icicle-gnark/v3 v3.2.2 // indirect + github.com/ipfs/bbloom v0.0.4 // indirect + github.com/ipfs/boxo v0.37.0 // indirect + github.com/ipfs/go-block-format v0.2.3 // indirect + github.com/ipfs/go-cid v0.6.0 // indirect + github.com/ipfs/go-ds-badger4 v0.1.8 // indirect + github.com/ipfs/go-ipfs-delay v0.0.1 // indirect + github.com/ipfs/go-ipfs-pq v0.0.3 // indirect + github.com/ipfs/go-ipld-format v0.6.3 // indirect + github.com/ipfs/go-ipld-legacy v0.2.2 // indirect + github.com/ipfs/go-log/v2 v2.9.1 // indirect + github.com/ipfs/go-metrics-interface v0.3.0 // indirect + github.com/ipfs/go-metrics-prometheus v0.1.0 // indirect + github.com/ipfs/go-peertaskqueue v0.8.2 // indirect + github.com/ipld/go-codec-dagpb v1.7.0 // indirect + github.com/ipld/go-ipld-prime v0.22.0 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect + github.com/klauspost/compress v1.18.5 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/klauspost/reedsolomon v1.13.4-0.20260420101718-f7e5efe6123a // indirect + github.com/koron/go-ssdp v0.0.6 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.12.3 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-cidranger v1.1.0 // indirect + github.com/libp2p/go-flow-metrics v0.3.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect + github.com/libp2p/go-libp2p-kad-dht v0.39.1 // indirect + github.com/libp2p/go-libp2p-kbucket v0.8.0 // indirect + github.com/libp2p/go-libp2p-pubsub v0.15.1-0.20260318184623-12372d99a0bc // indirect + github.com/libp2p/go-libp2p-record v0.3.1 // indirect + github.com/libp2p/go-libp2p-routing-helpers v0.7.5 // indirect + github.com/libp2p/go-msgio v0.3.0 // indirect + github.com/libp2p/go-netroute v0.4.0 // indirect + github.com/libp2p/go-reuseport v0.4.0 // indirect + github.com/libp2p/go-yamux/v5 v5.0.1 // indirect + github.com/linxGnu/grocksdb v1.9.8 // indirect + github.com/manifoldco/promptui v0.9.0 // indirect + github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect + github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect + github.com/minio/highwayhash v1.0.4 // indirect + github.com/minio/minlz v1.0.1-0.20250507153514-87eb42fe8882 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/mtibben/percent v0.2.1 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.16.1 // indirect + github.com/multiformats/go-multiaddr-dns v0.5.0 // indirect + github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.10.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-multistream v0.6.1 // indirect + github.com/multiformats/go-varint v0.1.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect + github.com/oklog/run v1.1.0 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect + github.com/pelletier/go-toml/v2 v2.3.0 // indirect + github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe // indirect + github.com/pion/datachannel v1.5.10 // indirect + github.com/pion/dtls/v3 v3.1.2 // indirect + github.com/pion/ice/v4 v4.0.10 // indirect + github.com/pion/interceptor v0.1.40 // indirect + github.com/pion/logging v0.2.4 // indirect + github.com/pion/mdns/v2 v2.0.7 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/rtcp v1.2.16 // indirect + github.com/pion/rtp v1.8.19 // indirect + github.com/pion/sctp v1.8.39 // indirect + github.com/pion/sdp/v3 v3.0.18 // indirect + github.com/pion/srtp/v3 v3.0.6 // indirect + github.com/pion/stun/v3 v3.1.1 // indirect + github.com/pion/transport/v3 v3.0.7 // indirect + github.com/pion/transport/v4 v4.0.1 // indirect + github.com/pion/turn/v4 v4.0.2 // indirect + github.com/pion/webrtc/v4 v4.1.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/polydawn/refmt v0.89.1-0.20231129105047-37766d95467a // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.20.1 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect + github.com/quic-go/webtransport-go v0.10.0 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/rollkit/go-da v0.9.0 // indirect + github.com/ronanh/intcomp v1.1.1 // indirect + github.com/rs/cors v1.11.1 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/sasha-s/go-deadlock v0.3.9 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/cobra v1.10.2 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/spf13/viper v1.21.0 // indirect + github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/subosito/gotenv v1.6.0 // 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/ulikunitz/xz v0.5.15 // indirect + github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect + github.com/wlynxg/anet v0.0.5 // indirect + github.com/x448/float16 v0.8.4 // indirect + github.com/zondax/golem v0.27.0 // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v1.0.1 // indirect + go.etcd.io/bbolt v1.4.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/bridges/prometheus v0.67.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect + go.opentelemetry.io/contrib/instrumentation/runtime v0.68.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/sdk v1.43.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/proto/otlp v1.10.0 // indirect + go.uber.org/dig v1.19.0 // indirect + go.uber.org/mock v0.5.2 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.1 // indirect + go.yaml.in/yaml/v2 v2.4.4 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.50.0 // indirect + golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/net v0.53.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.43.0 // indirect + golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c // indirect + golang.org/x/term v0.42.0 // indirect + golang.org/x/text v0.36.0 // indirect + golang.org/x/time v0.15.0 // indirect + golang.org/x/tools v0.43.0 // indirect + golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect + gonum.org/v1/gonum v0.17.0 // indirect + google.golang.org/api v0.276.0 // indirect + google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect + google.golang.org/grpc v1.80.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.5.2 // indirect + lukechampine.com/blake3 v1.4.1 // indirect + nhooyr.io/websocket v1.8.17 // indirect + pgregory.net/rapid v1.2.0 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect +) diff --git a/tools/celestia-node-fiber/go.sum b/tools/celestia-node-fiber/go.sum new file mode 100644 index 0000000000..c184ec3890 --- /dev/null +++ b/tools/celestia-node-fiber/go.sum @@ -0,0 +1,1899 @@ +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +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.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= +cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA= +cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/iam v1.7.0 h1:JD3zh0C6LHl16aCn5Akff0+GELdp1+4hmh6ndoFLl8U= +cloud.google.com/go/iam v1.7.0/go.mod h1:tetWZW1PD/m6vcuY2Zj/aU0eCHNPuxedbnbRTyKXvdY= +cloud.google.com/go/kms v1.29.0 h1:bAW1C5FQf+6GhPkywQzPlsULALCG7c16qpXLFGV9ivY= +cloud.google.com/go/kms v1.29.0/go.mod h1:YIyXZym11R5uovJJt4oN5eUL3oPmirF3yKeIh6QAf4U= +cloud.google.com/go/logging v1.13.2 h1:qqlHCBvieJT9Cdq4QqYx1KPadCQ2noD4FK02eNqHAjA= +cloud.google.com/go/logging v1.13.2/go.mod h1:zaybliM3yun1J8mU2dVQ1/qDzjbOqEijZCn6hSBtKak= +cloud.google.com/go/longrunning v0.9.0 h1:0EzbDEGsAvOZNbqXopgniY0w0a1phvu5IdUFq8grmqY= +cloud.google.com/go/longrunning v0.9.0/go.mod h1:pkTz846W7bF4o2SzdWJ40Hu0Re+UoNT6Q5t+igIcb8E= +cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE= +cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.61.3 h1:VS//ZfBuPGDvakfD9xyPW1RGF1Vy3BWUoVZXgW1KMOg= +cloud.google.com/go/storage v1.61.3/go.mod h1:JtqK8BBB7TWv0HVGHubtUdzYYrakOQIsMLffZ2Z/HWk= +cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U= +cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s= +connectrpc.com/connect v1.19.2 h1:McQ83FGdzL+t60peksi0gXC7MQ/iLKgLduAnThbM0mo= +connectrpc.com/connect v1.19.2/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= +connectrpc.com/grpcreflect v1.3.0 h1:Y4V+ACf8/vOb1XOc251Qun7jMB75gCUNw6llvB9csXc= +connectrpc.com/grpcreflect v1.3.0/go.mod h1:nfloOtCS8VUQOQ1+GTdFzVg2CJo4ZGaat8JIovCtDYs= +cosmossdk.io/client/v2 v2.0.0-beta.8 h1:RXMJdA4V9H1H3/3BfMD6dAW3lF8W9DpNPPYnKD+ArxY= +cosmossdk.io/client/v2 v2.0.0-beta.8/go.mod h1:x+E2eji+ToMtUIqKzoJ5mJIhat+Zak47xZ8jOYjJQBA= +cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= +cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= +cosmossdk.io/core v1.1.0 h1:iJ7j2DjNsFzg4/z4ImNQYzy2D4LfMCsaQ8Lrz1KCmxk= +cosmossdk.io/core v1.1.0/go.mod h1:qGmJxBFHobvG1k4bROQnueslotBU5MIKZLC57xVBYYI= +cosmossdk.io/depinject v1.2.1 h1:eD6FxkIjlVaNZT+dXTQuwQTKZrFZ4UrfCq1RKgzyhMw= +cosmossdk.io/depinject v1.2.1/go.mod h1:lqQEycz0H2JXqvOgVwTsjEdMI0plswI7p6KX+MVqFOM= +cosmossdk.io/errors v1.0.2 h1:wcYiJz08HThbWxd/L4jObeLaLySopyyuUFB5w4AGpCo= +cosmossdk.io/errors v1.0.2/go.mod h1:0rjgiHkftRYPj//3DrD6y8hcm40HcPv/dR4R/4efr0k= +cosmossdk.io/math v1.5.3 h1:WH6tu6Z3AUCeHbeOSHg2mt9rnoiUWVWaQ2t6Gkll96U= +cosmossdk.io/math v1.5.3/go.mod h1:uqcZv7vexnhMFJF+6zh9EWdm/+Ylyln34IvPnBauPCQ= +cosmossdk.io/store v1.1.2 h1:3HOZG8+CuThREKv6cn3WSohAc6yccxO3hLzwK6rBC7o= +cosmossdk.io/store v1.1.2/go.mod h1:60rAGzTHevGm592kFhiUVkNC9w7gooSEn5iUBPzHQ6A= +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/tx v0.13.8 h1:dQwC8jMe7awx/edi1HPPZ40AjHnsix6KSO/jbKMUYKk= +cosmossdk.io/x/tx v0.13.8/go.mod h1:V6DImnwJMTq5qFjeGWpXNiT/fjgE4HtmclRmTqRVM3w= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5 h1:JA0fFr+kxpqTdxR9LOBiTWpGNchqmkcsgmdeJZRclZ0= +filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5/go.mod h1:OjOXDNlClLblvXdwgFFOQFJEocLhhtai8vGLy0JCZlI= +filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw= +filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +filippo.io/keygen v0.0.0-20260114151900-8e2790ea4c5b h1:REI1FbdW71yO56Are4XAxD+OS/e+BQsB3gE4mZRQEXY= +filippo.io/keygen v0.0.0-20260114151900-8e2790ea4c5b/go.mod h1:9nnw1SlYHYuPSo/3wjQzNjSbeHlq2NsKo5iEtfJPWP0= +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.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= +github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= +github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE= +github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 h1:0s6TxfCu2KHkkZPnBfsQ2y5qia0jl3MMrmBhu3nCOYk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= +github.com/Jorropo/jsync v1.0.1 h1:6HgRolFZnsdfzRUj+ImB9og1JYOxQoReSywkHOGSaUU= +github.com/Jorropo/jsync v1.0.1/go.mod h1:jCOZj3vrBCri3bSU3ErUYvevKlnbssrXeCivybS5ABQ= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +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/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/RaduBerinde/axisds v0.1.0 h1:YItk/RmU5nvlsv/awo2Fjx97Mfpt4JfgtEVAGPrLdz8= +github.com/RaduBerinde/axisds v0.1.0/go.mod h1:UHGJonU9z4YYGKJxSaC6/TNcLOBptpmM5m2Cksbnw0Y= +github.com/RaduBerinde/btreemap v0.0.0-20250419174037-3d62b7205d54 h1:bsU8Tzxr/PNz75ayvCnxKZWEYdLMPDkUgticP4a4Bvk= +github.com/RaduBerinde/btreemap v0.0.0-20250419174037-3d62b7205d54/go.mod h1:0tr7FllbE9gJkHq7CVeeDDFAFKQVy5RnCSSNBOvdqbc= +github.com/Sereal/Sereal/Go/sereal v0.0.0-20231009093132-b9187f1a92c6/go.mod h1:JwrycNnC8+sZPDyzM3MQ86LvaGzSpfxg885KOOwFRW4= +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/aclements/go-perfevent v0.0.0-20240301234650-f7843625020f h1:JjxwchlOepwsUWcQwD2mLUAGE9aCp0/ehy6yCHFBOvo= +github.com/aclements/go-perfevent v0.0.0-20240301234650-f7843625020f/go.mod h1:tMDTce/yLLN/SK8gMOxQfnyeMeCg8KGzp0D1cbECEeo= +github.com/adlio/schema v1.4.0 h1:dekxG6P0my/bPvlyWzMULelR2Xej8RGErlnJcoY5ddw= +github.com/adlio/schema v1.4.0/go.mod h1:3/ojUldWBCWp4e+6VN9ets6unG5WdqbjF7vyzM0zTVQ= +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-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +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-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/aws/aws-sdk-go-v2 v1.41.6 h1:1AX0AthnBQzMx1vbmir3Y4WsnJgiydmnJjiLu+LvXOg= +github.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.9 h1:adBsCIIpLbLmYnkQU+nAChU5yhVTvu5PerROm+/Kq2A= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.9/go.mod h1:uOYhgfgThm/ZyAuJGNQ5YgNyOlYfqnGpTHXvk3cpykg= +github.com/aws/aws-sdk-go-v2/config v1.32.16 h1:Q0iQ7quUgJP0F/SCRTieScnaMdXr9h/2+wze1u3cNeM= +github.com/aws/aws-sdk-go-v2/config v1.32.16/go.mod h1:duCCnJEFqpt2RC6no1iK6q+8HpwOAkiUua0pY507dQc= +github.com/aws/aws-sdk-go-v2/credentials v1.19.15 h1:fyvgWTszojq8hEnMi8PPBTvZdTtEVmAVyo+NFLHBhH4= +github.com/aws/aws-sdk-go-v2/credentials v1.19.15/go.mod h1:gJiYyMOjNg8OEdRWOf3CrFQxM2a98qmrtjx1zuiQfB8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 h1:IOGsJ1xVWhsi+ZO7/NW8OuZZBtMJLZbk4P5HDjJO0jQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22/go.mod h1:b+hYdbU+jGKfXE8kKM6g1+h+L/Go3vMvzlxBsiuGsxg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 h1:GmLa5Kw1ESqtFpXsx5MmC84QWa/ZrLZvlJGa2y+4kcQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22/go.mod h1:6sW9iWm9DK9YRpRGga/qzrzNLgKpT2cIxb7Vo2eNOp0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 h1:dY4kWZiSaXIzxnKlj17nHnBcXXBfac6UlsAx2qL6XrU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22/go.mod h1:KIpEUx0JuRZLO7U6cbV204cWAEco2iC3l061IxlwLtI= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 h1:FPXsW9+gMuIeKmz7j6ENWcWtBGTe1kH8r9thNt5Uxx4= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23/go.mod h1:7J8iGMdRKk6lw2C+cMIphgAnT8uTwBwNOsGkyOCm80U= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 h1:HtOTYcbVcGABLOVuPYaIihj6IlkqubBwFj10K5fxRek= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8/go.mod h1:VsK9abqQeGlzPgUr+isNWzPlK2vKe9INMLWnY65f5Xs= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.14 h1:xnvDEnw+pnj5mctWiYuFbigrEzSm35x7k4KS/ZkCANg= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.14/go.mod h1:yS5rNogD8e0Wu9+l3MUwr6eENBzEeGejvINpN5PAYfY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 h1:PUmZeJU6Y1Lbvt9WFuJ0ugUK2xn6hIWUBBbKuOWF30s= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22/go.mod h1:nO6egFBoAaoXze24a2C0NjQCvdpk8OueRoYimvEB9jo= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.22 h1:SE+aQ4DEqG53RRCAIHlCf//B2ycxGH7jFkpnAh/kKPM= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.22/go.mod h1:ES3ynECd7fYeJIL6+oax+uIEljmfps0S70BaQzbMd/o= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.5 h1:nEzwx/ZlpUZ2Y6WztsgYmfBh5Ixd3QiECawXMzvTMeo= +github.com/aws/aws-sdk-go-v2/service/kms v1.50.5/go.mod h1:GBO/aaEi47QldDVoqw2CsM2UZQDoqDiFIMJD/ztHPs0= +github.com/aws/aws-sdk-go-v2/service/s3 v1.99.1 h1:kU/eBN5+MWNo/LcbNa4hWDdN76hdcd7hocU5kvu7IsU= +github.com/aws/aws-sdk-go-v2/service/s3 v1.99.1/go.mod h1:Fw9aqhJicIVee1VytBBjH+l+5ov6/PhbtIK/u3rt/ls= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.10 h1:a1Fq/KXn75wSzoJaPQTgZO0wHGqE9mjFnylnqEPTchA= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.10/go.mod h1:p6+MXNxW7IA6dMgHfTAzljuwSKD0NCm/4lbS4t6+7vI= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.16 h1:x6bKbmDhsgSZwv6q19wY/u3rLk/3FGjJWyqKcIRufpE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.16/go.mod h1:CudnEVKRtLn0+3uMV0yEXZ+YZOKnAtUJ5DmDhilVnIw= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 h1:oK/njaL8GtyEihkWMD4k3VgHCT64RQKkZwh0DG5j8ak= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20/go.mod h1:JHs8/y1f3zY7U5WcuzoJ/yAYGYtNIVPKLIbp61euvmg= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.0 h1:ks8KBcZPh3PYISr5dAiXCM5/Thcuxk8l+PG4+A0exds= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.0/go.mod h1:pFw33T0WLvXU3rw1WBkpMlkgIn54eCB5FYLhjDc9Foo= +github.com/aws/smithy-go v1.25.0 h1:Sz/XJ64rwuiKtB6j98nDIPyYrV1nVNJ4YU74gttcl5U= +github.com/aws/smithy-go v1.25.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/bcp-innovations/hyperlane-cosmos v1.1.0 h1:WXt+WrKv2DG/xVIkLvggDRbi/2law104Vj6AWZGxHNw= +github.com/bcp-innovations/hyperlane-cosmos v1.1.0/go.mod h1:NP59yKAk2qFaT7+FSCh7kkoKKLlTxXNdIlxMstAJ5no= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +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.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE51E= +github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.24.0 h1:H4x4TuulnokZKvHLfzVRTHJfFfnHEeSYJizujEZvmAM= +github.com/bits-and-blooms/bitset v1.24.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/btcsuite/btcd/btcec/v2 v2.3.5 h1:dpAlnAwmT1yIBm3exhT1/8iUSD98RDJM5vqJVQDQLiU= +github.com/btcsuite/btcd/btcec/v2 v2.3.5/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= +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/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3 h1:oe6fCvaEpkhyW3qAicT0TnGtyht/UrgvOwMcEgLb7Aw= +github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3/go.mod h1:qdP0gaj0QtgX2RUZhnlVrceJ+Qln8aSlDyJwelLLFeM= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/celestiaorg/boxo v0.29.0-fork-4 h1:A202u8w3Iqjw4ZlqSukfiMbefQEN+740GUj1Z3VI960= +github.com/celestiaorg/boxo v0.29.0-fork-4/go.mod h1:rXql6ncaLZZfLqDG3Cuw9ZYQKd3rMU5bk1TGXF0+ZL0= +github.com/celestiaorg/celestia-app/v9 v9.0.0-20260427114616-9ff33a36eb19 h1:7+h4twHnq0oQaj3N73VqBWtmcIMStXJk6Q5XqwiAmzg= +github.com/celestiaorg/celestia-app/v9 v9.0.0-20260427114616-9ff33a36eb19/go.mod h1:HKqFxEeuWopDU87dCOkLktn6P4N+wEeVm1FdSEESvSQ= +github.com/celestiaorg/celestia-core v0.40.1 h1:JF9gyLKLU5oCFIeTAQtiHBZeOhppozEToN+o8bgvxT8= +github.com/celestiaorg/celestia-core v0.40.1/go.mod h1:3Jhugz4ibMVEP2+7+FjLEDsV4TcU2tHgfGSm4zkSNv4= +github.com/celestiaorg/celestia-node v0.29.3-mocha.0.20260427115656-23bec13f01a3 h1:UrzojEvUNgOiCDZB+4ba7oqhx/FtreUE0JYQ5bMxNtY= +github.com/celestiaorg/celestia-node v0.29.3-mocha.0.20260427115656-23bec13f01a3/go.mod h1:LlQzPvGjZIQKJ9XXgR6yMeRIv+WBUDhlcva++NhnMNs= +github.com/celestiaorg/cosmos-sdk v0.52.3 h1:YPMFCycTw77P7tn+HQHTmmdBwXWNMDOrZ6/xVPK9nvM= +github.com/celestiaorg/cosmos-sdk v0.52.3/go.mod h1:2N4NRio08+WQsB7hsKo/ELXCQSWl78GiYdd9M1H6MpQ= +github.com/celestiaorg/cosmos-sdk/api v0.7.6 h1:81in9Zk+noz0ko+hZFSSK8L1aawFN8/CmdcQAUhbiUU= +github.com/celestiaorg/cosmos-sdk/api v0.7.6/go.mod h1:1BgQSufu6ZQkst3YBIHDCo/TPUrhfU4fV7tOI0ftql8= +github.com/celestiaorg/cosmos-sdk/log v1.1.1-0.20251116153902-f48fea92e627 h1:qYV81fA5E739ZtdMFCjChx0AMY+qBmMVPfRE3ol+VCE= +github.com/celestiaorg/cosmos-sdk/log v1.1.1-0.20251116153902-f48fea92e627/go.mod h1:lQTBplaW3HQLKQdPaQq+ElW6zASAoo9r3bJ7pOr8SWo= +github.com/celestiaorg/cosmos-sdk/x/upgrade v0.2.0 h1:GyDYfK8dLETlUI7F+w+3QYQgAszUegMXgB6cTbDm7CA= +github.com/celestiaorg/cosmos-sdk/x/upgrade v0.2.0/go.mod h1:T4K9O18zQNKNpt4YvTL3lcUt4aKOEU05ZIFWVdQi3Ak= +github.com/celestiaorg/go-datastore v0.0.0-20250801131506-48a63ae531e4 h1:udw77BU45zmvTV7798FhR1wHFmsFpu4GnA5mubtMcR0= +github.com/celestiaorg/go-datastore v0.0.0-20250801131506-48a63ae531e4/go.mod h1:W+pI1NsUsz3tcsAACMtfC+IZdnQTnC/7VfPoJBQuts0= +github.com/celestiaorg/go-header v0.8.5 h1:MkzlioiSeybKVNDa0805fS3mS3NG8ub93Gs2xaKwSZ4= +github.com/celestiaorg/go-header v0.8.5/go.mod h1:DKl6pcKCJ0ehGUgDmfxBNz6Lv0Ky4E1Oyrcx96eQm/4= +github.com/celestiaorg/go-libp2p-messenger v0.2.2 h1:osoUfqjss7vWTIZrrDSy953RjQz+ps/vBFE7bychLEc= +github.com/celestiaorg/go-libp2p-messenger v0.2.2/go.mod h1:oTCRV5TfdO7V/k6nkx7QjQzGrWuJbupv+0o1cgnY2i4= +github.com/celestiaorg/go-square/merkle v0.0.0-20240627094109-7d01436067a3 h1:wP84mtwOCVNOTfS3zErICjxKLnh74Z1uf+tdrlSFjVM= +github.com/celestiaorg/go-square/merkle v0.0.0-20240627094109-7d01436067a3/go.mod h1:86qIYnEhmn/hfW+xvw98NOI3zGaDEB3x8JGjYo2FqLs= +github.com/celestiaorg/go-square/v2 v2.3.3 h1:vhu6Lt39km19Q/Jk4nS3r2cuWJq6jFg+/1+iG8YGftY= +github.com/celestiaorg/go-square/v2 v2.3.3/go.mod h1:vY5RRv+qRmEVjPF6dAdr0dyLwKmTTDHHffENPQw8pUA= +github.com/celestiaorg/go-square/v3 v3.0.2 h1:eSQOgNII8inK9IhiBZ+6GADQeWbRq4HYY72BOgcduA4= +github.com/celestiaorg/go-square/v3 v3.0.2/go.mod h1:oFReMLsSDMRs82ICFEeFQFCqNvwdsbIM1BzCcb0f7dM= +github.com/celestiaorg/go-square/v4 v4.0.0-rc4.0.20260318002530-1ca8ff7b42ea h1:gpezhWD8ATU12vl79F0N9PHdZC6CW+5mxFn0gBlUf2Y= +github.com/celestiaorg/go-square/v4 v4.0.0-rc4.0.20260318002530-1ca8ff7b42ea/go.mod h1:UoBkF0lYuU51du9XN6oIJ7nM5Wun+mgbiItQCdxlNJU= +github.com/celestiaorg/ibc-go/v8 v8.7.2 h1:AWae851fdX7pJWlGnUBKlKJzpr4c2t5m4TLs6vDfmAY= +github.com/celestiaorg/ibc-go/v8 v8.7.2/go.mod h1:E3WTax+cfyDIehNRpwEI96/0E8GBtU1g9XWr18qUGZ8= +github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4 h1:CJdIpo8n5MFP2MwK0gSRcOVlDlFdQJO1p+FqdxYzmvc= +github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4/go.mod h1:fzuHnhzj1pUygGz+1ZkB3uQbEUL4htqCGJ4Qs2LwMZA= +github.com/celestiaorg/nmt v0.24.3 h1:ylQnRlXkVoTtq36CxtCyXYZX4JISBsHgKlAAUAnf7ig= +github.com/celestiaorg/nmt v0.24.3/go.mod h1:vgLBpWBi8F5KLxTdXSwb7AU4NhiIQ1AQRGa+PzdcLEA= +github.com/celestiaorg/rsmt2d v0.15.2 h1:wHqNqaBboSX5e8Czm4FnBnys4RPp5gSNm4CAcsXAyTU= +github.com/celestiaorg/rsmt2d v0.15.2/go.mod h1:1NyWG9hj7veHbLmpQUKg+77teLuVgq0kpv3FS9nEtL4= +github.com/celestiaorg/utils v0.1.0 h1:WsP3O8jF7jKRgLNFmlDCwdThwOFMFxg0MnqhkLFVxPo= +github.com/celestiaorg/utils v0.1.0/go.mod h1:vQTh7MHnvpIeCQZ2/Ph+w7K1R2UerDheZbgJEJD2hSU= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +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.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +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/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +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/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= +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-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= +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/crlib v0.0.0-20241112164430-1264a2edc35b h1:SHlYZ/bMx7frnmeqCu+xm0TCxXLzX3jQIVuFbnFGtFU= +github.com/cockroachdb/crlib v0.0.0-20241112164430-1264a2edc35b/go.mod h1:Gq51ZeKaFCXk6QwuGM0w1dnaOqc/F5zKT2zA9D6Xeac= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v1.0.3-0.20250407164829-2945557346d5 h1:UycK/E0TkisVrQbSoxvU827FwgBBcZ95nRRmpj/12QI= +github.com/cockroachdb/datadriven v1.0.3-0.20250407164829-2945557346d5/go.mod h1:jsaKMvD3RBCATk1/jbUZM8C9idWBJME9+VRZ5+Liq1g= +github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo= +github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g= +github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 h1:pU88SPhIFid6/k0egdR5V6eALQYq2qbSmukrkgIh/0A= +github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILMqgNeV5jiqR4j+sTuvQNHdf2chuKj1M5k= +github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506/go.mod h1:Mw7HqKr2kdtu6aYGn3tPmAftiP3QPX63LdK/zcariIo= +github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895 h1:XANOgPYtvELQ/h4IrmPAohXqe2pWA8Bwhejr3VQoZsA= +github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895/go.mod h1:aPd7gM9ov9M8v32Yy5NJrDyOcD8z642dqs+F0CeNXfA= +github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw= +github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo= +github.com/cockroachdb/pebble/v2 v2.1.4 h1:j9wPgMDbkErFdAKYFGhsoCcvzcjR+6zrJ4jhKtJ6bOk= +github.com/cockroachdb/pebble/v2 v2.1.4/go.mod h1:Reo1RTniv1UjVTAu/Fv74y5i3kJ5gmVrPhO9UtFiKn8= +github.com/cockroachdb/redact v1.1.6 h1:zXJBwDZ84xJNlHl1rMyCojqyIxv+7YUpQiJLQ7n4314= +github.com/cockroachdb/redact v1.1.6/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b h1:VXvSNzmr8hMj8XTuY0PT9Ane9qZGul/p67vGYwl9BFI= +github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b/go.mod h1:yBRu/cnL4ks9bgy4vAASdjIW+/xMlFwuHKqtmh3GZQg= +github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb h1:3bCgBvB8PbJVMX1ouCcSIxvsqKPYM7gs72o0zC76n9g= +github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coder/websocket v1.8.6 h1:OmNKdwUvLj7VvHnl5o8skaVghSPLjWdHGCnFbkWqs9w= +github.com/coder/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +github.com/cometbft/cometbft-db v1.0.4 h1:cezb8yx/ZWcF124wqUtAFjAuDksS1y1yXedvtprUFxs= +github.com/cometbft/cometbft-db v1.0.4/go.mod h1:M+BtHAGU2XLrpUxo3Nn1nOCcnVCiLM9yx5OuT0u5SCA= +github.com/consensys/gnark v0.14.0 h1:RG+8WxRanFSFBSlmCDRJnYMYYKpH3Ncs5SMzg24B5HQ= +github.com/consensys/gnark v0.14.0/go.mod h1:1IBpDPB/Rdyh55bQRR4b0z1WvfHQN1e0020jCvKP2Gk= +github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0= +github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= +github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= +github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +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.1.3 h1:7QNT77+vkefostcKkhrzDK9uoIEryzFrU9eoMeaQOPY= +github.com/cosmos/cosmos-db v1.1.3/go.mod h1:kN+wGsnwUJZYn8Sy5Q2O0vCYA99MJllkKASbs6Unb9U= +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/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.2 h1:5G25McIraOC0mRFv9TVO139Uh3OklV2hczr13KKVHCA= +github.com/cosmos/gogoproto v1.7.2/go.mod h1:8S7w53P1Y1cHwND64o0BnArT6RmdgIvsBuco6uTllsk= +github.com/cosmos/iavl v1.2.8 h1:55F96BGUJ7KT7h+Ky/cEqS+pEvhFqsU4O8Th3F0N1js= +github.com/cosmos/iavl v1.2.8/go.mod h1:FRHN4tO+6crf0p2zsqye+nAbsMgiwdkxpWm18DyP6+Y= +github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8 v8.2.0 h1:rM+S14DFiqmu6Rc3PuhvWqwywPsnt/CbIslSnBftPFs= +github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8 v8.2.0/go.mod h1:O5H9Ic3Pe6cmJn1eqlj5N48sLb8WQ1VWmDP4/11g/4E= +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/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU= +github.com/cosmos/ics23/go v0.11.0/go.mod h1:A8OjxPE67hHST4Icw94hOxxFEJMBG031xIGF/JHNIY0= +github.com/cosmos/ledger-cosmos-go v0.15.0 h1:xmizkkEX19tyFLVL6PPMQNg21Jc9W9/bpbwxMDdtxXg= +github.com/cosmos/ledger-cosmos-go v0.15.0/go.mod h1:KJqW5U4/MMl8ICPO4WPjIAyC4TfYRnr28d9N9bBUKWc= +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.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +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/cristalhq/jwt/v5 v5.4.0 h1:Wxi1TocFHaijyV608j7v7B9mPc4ZNjvWT3LKBO0d4QI= +github.com/cristalhq/jwt/v5 v5.4.0/go.mod h1:+b/BzaCWEpFDmXxspJ5h4SdJ1N/45KMjKOetWzmHvDA= +github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= +github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= +github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs= +github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= +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/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892/go.mod h1:CTDl0pzVzE5DEzZhPfvhY/9sPFMQIxaJ9VAMs9AagrE= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= +github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= +github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= +github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/desertbit/timer v1.0.1 h1:yRpYNn5Vaaj6QXecdLMPMJsW81JLiI1eokUft5nBmeo= +github.com/desertbit/timer v1.0.1/go.mod h1:htRrYeY5V/t4iu1xCJ5XsQvp4xve8QulXXctAzxqcwE= +github.com/dgraph-io/badger/v4 v4.9.1 h1:DocZXZkg5JJHJPtUErA0ibyHxOVUDVoXLSCV6t8NC8w= +github.com/dgraph-io/badger/v4 v4.9.1/go.mod h1:5/MEx97uzdPUHR4KtkNt8asfI2T4JiEiQlV7kWUo8c0= +github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= +github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-ddmin v0.0.0-20210904190556-96a6d69f1034/go.mod h1:zz4KxBkcXUWKjIcrc+uphJ1gPh/t18ymGm3PmQ+VGTk= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +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/dunglas/httpsfv v1.1.0 h1:Jw76nAyKWKZKFrpMMcL76y35tOpYHqQPzHQiwDvpe54= +github.com/dunglas/httpsfv v1.1.0/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg= +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.7.0 h1:bnQc8+GMnidJZA8zc6lLEAb4xNrIqHwO+9TzqvtQZPo= +github.com/dvsekhvalnov/jose2go v1.7.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.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/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.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= +github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= +github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= +github.com/evstack/ev-node/core v1.0.0 h1:s0Tx0uWHme7SJn/ZNEtee4qNM8UO6PIxXnHhPbbKTz8= +github.com/evstack/ev-node/core v1.0.0/go.mod h1:n2w/LhYQTPsi48m6lMj16YiIqsaQw6gxwjyJvR+B3sY= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9gd6MPfXbKVU= +github.com/filecoin-project/go-clock v0.1.0/go.mod h1:4uB/O4PvOjlx1VCMdZ9MyDZXRm//gkj1ELEbxfI1AZs= +github.com/filecoin-project/go-jsonrpc v0.10.1 h1:iEhgrjO0+rawwOZWRNgexLrWGLA+IEUyWiRRL134Ob8= +github.com/filecoin-project/go-jsonrpc v0.10.1/go.mod h1:OG7kVBVh/AbDFHIwx7Kw0l9ARmKOS6gGOr0LbdBpbLc= +github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= +github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= +github.com/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.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/gammazero/chanqueue v1.1.1 h1:n9Y+zbBxw2f7uUE9wpgs0rOSkP/I/yhDLiNuhyVjojQ= +github.com/gammazero/chanqueue v1.1.1/go.mod h1:fMwpwEiuUgpab0sH4VHiVcEoji1pSi+EIzeG4TPeKPc= +github.com/gammazero/deque v1.2.1 h1:9fnQVFCCZ9/NOc7ccTNqzoKd1tCWOqeI05/lPqFPMGQ= +github.com/gammazero/deque v1.2.1/go.mod h1:5nSFkzVm+afG9+gy0VIowlqVAW4N8zNcMne+CMQVD2g= +github.com/gammazero/workerpool v1.2.1 h1:MEDvUJsNYGuCvl1RwIXNKu2YtQtHqCSF9XWF04N7lqs= +github.com/gammazero/workerpool v1.2.1/go.mod h1:E32GVRUanF4d6QtRmdss3AScgaDkIyrvPtgRQUWgmx4= +github.com/getsentry/sentry-go v0.42.0 h1:eeFMACuZTbUQf90RE8dE4tXeSe4CZyfvR1MBL7RLEt8= +github.com/getsentry/sentry-go v0.42.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s= +github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9 h1:r5GgOLGbza2wVHRzK7aAj6lWZjfbAwiu/RDCVOKjRyM= +github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= +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 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +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 v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +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.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE= +github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= +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-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +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/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= +github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= +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/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-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 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.4/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/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.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 v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/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 v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= +github.com/google/flatbuffers v25.2.10+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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz 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/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= +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-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +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.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.21.0 h1:h45NjjzEO3faG9Lg/cFrBh2PgegVVgzqKzuZl/wMbiI= +github.com/googleapis/gax-go/v2 v2.21.0/go.mod h1:But/NJU6TnZsrLai/xBAQLLz+Hc7fHZJt/hsCz3Fih4= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +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.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= +github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= +github.com/grafana/pyroscope-go v1.2.8 h1:UvCwIhlx9DeV7F6TW/z8q1Mi4PIm3vuUJ2ZlCEvmA4M= +github.com/grafana/pyroscope-go v1.2.8/go.mod h1:SSi59eQ1/zmKoY/BKwa5rSFsJaq+242Bcrr4wPix1g8= +github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og= +github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= +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/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/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +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/aws-sdk-go-base/v2 v2.0.0-beta.72 h1:vTCWu1wbdYo7PEZFem/rlr01+Un+wwVmI7wiegFdRLk= +github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72/go.mod h1:Vn+BBgKQHVQYdVQ4NZDICE1Brb+JfaONyDHr3q07oQc= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +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/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.8.6 h1:9sQboWULaydVphxc4S64oAI4YqpuCk7nPmvbk131ebY= +github.com/hashicorp/go-getter v1.8.6/go.mod h1:nVH12eOV2P58dIiL3rsU6Fh3wLeJEKBOJzhMmzlSWoo= +github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/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.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY= +github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack/v2 v2.1.2 h1:4Ee8FTp834e+ewB71RDrQ0VKpyFdrKOjvYtnQ/ltVj0= +github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= +github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= +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-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.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.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/arc/v2 v2.0.7 h1:QxkVTxwColcduO+LP7eJO56r2hFiG8zEbfAAzRv52KQ= +github.com/hashicorp/golang-lru/arc/v2 v2.0.7/go.mod h1:Pe7gBlGdc8clY5LJ0LpJXMt5AmgmWNH1g+oFFVUHOEc= +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/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/raft v1.7.3 h1:DxpEqZJysHN0wK+fviai5mFcSYsCkNpFUl1xpAW8Rbo= +github.com/hashicorp/raft v1.7.3/go.mod h1:DfvCGFxpAUPE0L4Uc8JLlTPtc3GzSbdH0MTJCLgnmJQ= +github.com/hashicorp/raft-boltdb v0.0.0-20251103221153-05f9dd7a5148 h1:tjaIHlfKX22DCCPTx2mK+6N/kTP9DV7B3bxEUyQtjKA= +github.com/hashicorp/raft-boltdb v0.0.0-20251103221153-05f9dd7a5148/go.mod h1:sgCxzMuvQ3huVxgmeDdj73YIMmezWZ40HQu2IPmjJWk= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU= +github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= +github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= +github.com/huandu/skiplist v1.2.1 h1:dTi93MgjwErA/8idWTzIw4Y1kZsMWx35fmI2c8Rij7w= +github.com/huandu/skiplist v1.2.1/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= +github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= +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-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +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/ingonyama-zk/icicle-gnark/v3 v3.2.2 h1:B+aWVgAx+GlFLhtYjIaF0uGjU3rzpl99Wf9wZWt+Mq8= +github.com/ingonyama-zk/icicle-gnark/v3 v3.2.2/go.mod h1:CH/cwcr21pPWH+9GtK/PFaa4OGTv4CtfkCKro6GpbRE= +github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= +github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= +github.com/ipfs/go-block-format v0.2.3 h1:mpCuDaNXJ4wrBJLrtEaGFGXkferrw5eqVvzaHhtFKQk= +github.com/ipfs/go-block-format v0.2.3/go.mod h1:WJaQmPAKhD3LspLixqlqNFxiZ3BZ3xgqxxoSR/76pnA= +github.com/ipfs/go-cid v0.6.0 h1:DlOReBV1xhHBhhfy/gBNNTSyfOM6rLiIx9J7A4DGf30= +github.com/ipfs/go-cid v0.6.0/go.mod h1:NC4kS1LZjzfhK40UGmpXv5/qD2kcMzACYJNntCUiDhQ= +github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= +github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= +github.com/ipfs/go-ds-badger4 v0.1.8 h1:frNczf5CjCVm62RJ5mW5tD/oLQY/9IKAUpKviRV9QAI= +github.com/ipfs/go-ds-badger4 v0.1.8/go.mod h1:FdqSLA5TMsyqooENB/Hf4xzYE/iH0z/ErLD6ogtfMrA= +github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= +github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= +github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE= +github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4= +github.com/ipfs/go-ipld-format v0.6.3 h1:9/lurLDTotJpZSuL++gh3sTdmcFhVkCwsgx2+rAh4j8= +github.com/ipfs/go-ipld-format v0.6.3/go.mod h1:74ilVN12NXVMIV+SrBAyC05UJRk0jVvGqdmrcYZvCBk= +github.com/ipfs/go-ipld-legacy v0.2.2 h1:DThbqCPVLpWBcGtU23KDLiY2YRZZnTkXQyfz8aOfBkQ= +github.com/ipfs/go-ipld-legacy v0.2.2/go.mod h1:hhkj+b3kG9b2BcUNw8IFYAsfeNo8E3U7eYlWeAOPyDU= +github.com/ipfs/go-log/v2 v2.9.1 h1:3JXwHWU31dsCpvQ+7asz6/QsFJHqFr4gLgQ0FWteujk= +github.com/ipfs/go-log/v2 v2.9.1/go.mod h1:evFx7sBiohUN3AG12mXlZBw5hacBQld3ZPHrowlJYoo= +github.com/ipfs/go-metrics-interface v0.3.0 h1:YwG7/Cy4R94mYDUuwsBfeziJCVm9pBMJ6q/JR9V40TU= +github.com/ipfs/go-metrics-interface v0.3.0/go.mod h1:OxxQjZDGocXVdyTPocns6cOLwHieqej/jos7H4POwoY= +github.com/ipfs/go-metrics-prometheus v0.1.0 h1:bApWOHkrH3VTBHzTHrZSfq4n4weOZDzZFxUXv+HyKcA= +github.com/ipfs/go-metrics-prometheus v0.1.0/go.mod h1:2GtL525C/4yxtvSXpRJ4dnE45mCX9AS0XRa03vHx7G0= +github.com/ipfs/go-peertaskqueue v0.8.2 h1:PaHFRaVFdxQk1Qo3OKiHPYjmmusQy7gKQUaL8JDszAU= +github.com/ipfs/go-peertaskqueue v0.8.2/go.mod h1:L6QPvou0346c2qPJNiJa6BvOibxDfaiPlqHInmzg0FA= +github.com/ipfs/go-test v0.2.3 h1:Z/jXNAReQFtCYyn7bsv/ZqUwS6E7iIcSpJ2CuzCvnrc= +github.com/ipfs/go-test v0.2.3/go.mod h1:QW8vSKkwYvWFwIZQLGQXdkt9Ud76eQXRQ9Ao2H+cA1o= +github.com/ipld/go-codec-dagpb v1.7.0 h1:hpuvQjCSVSLnTnHXn+QAMR0mLmb1gA6wl10LExo2Ts0= +github.com/ipld/go-codec-dagpb v1.7.0/go.mod h1:rD3Zg+zub9ZnxcLwfol/OTQRVjaLzXypgy4UqHQvilM= +github.com/ipld/go-ipld-prime v0.22.0 h1:YJhDhjEOvOYaqshd3b4atIWUoRg/rKrgmwCyUHwlbuY= +github.com/ipld/go-ipld-prime v0.22.0/go.mod h1:ol7vKxOOVgEh0iAPuiDalM+0gScXVMA5ZZa4DVrTnEA= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= +github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= +github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= +github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +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.11/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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +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.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +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.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= +github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/reedsolomon v1.13.4-0.20260420101718-f7e5efe6123a h1:aP94idRf0yhG07gBSIyW3sy/cd+XNLWnghSp11y0oIc= +github.com/klauspost/reedsolomon v1.13.4-0.20260420101718-f7e5efe6123a/go.mod h1:yjqqjgMTQkBUHSG97/rm4zipffCNbCiZcB3kTqr++sQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/koron/go-ssdp v0.0.6 h1:Jb0h04599eq/CY7rB5YEqPS83HmRfHP2azkxMN2rFtU= +github.com/koron/go-ssdp v0.0.6/go.mod h1:0R9LfRJGek1zWTjN3JUNlm5INCDYGpRDfAptnct63fI= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +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/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/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= +github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= +github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= +github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= +github.com/libp2p/go-flow-metrics v0.3.0 h1:q31zcHUvHnwDO0SHaukewPYgwOBSxtt830uJtUx6784= +github.com/libp2p/go-flow-metrics v0.3.0/go.mod h1:nuhlreIwEguM1IvHAew3ij7A8BMlyHQJ279ao24eZZo= +github.com/libp2p/go-libp2p v0.48.0 h1:h2BrLAgrj7X8bEN05K7qmrjpNHYA+6tnsGRdprjTnvo= +github.com/libp2p/go-libp2p v0.48.0/go.mod h1:Q1fBZNdmC2Hf82husCTfkKJVfHm2we5zk+NWmOGEmWk= +github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= +github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= +github.com/libp2p/go-libp2p-kad-dht v0.39.1 h1:9RzUBc7zywT4ZNamRSgEvPZmVlK3Y6xdlCYfXXvlR/Q= +github.com/libp2p/go-libp2p-kad-dht v0.39.1/go.mod h1:Po2JugFEkDq9Vig/JXtc153ntOi0q58o4j7IuITCOVs= +github.com/libp2p/go-libp2p-kbucket v0.8.0 h1:QAK7RzKJpYe+EuSEATAaaHYMYLkPDGC18m9jxPLnU8s= +github.com/libp2p/go-libp2p-kbucket v0.8.0/go.mod h1:JMlxqcEyKwO6ox716eyC0hmiduSWZZl6JY93mGaaqc4= +github.com/libp2p/go-libp2p-pubsub v0.15.1-0.20260318184623-12372d99a0bc h1:HhJIXh0faDKheCiqQ4mqoySFWW9XEUW6Om1GtGRTYAM= +github.com/libp2p/go-libp2p-pubsub v0.15.1-0.20260318184623-12372d99a0bc/go.mod h1:lr4oE8bFgQaifRcoc2uWhWWiK6tPdOEKpUuR408GFN4= +github.com/libp2p/go-libp2p-record v0.3.1 h1:cly48Xi5GjNw5Wq+7gmjfBiG9HCzQVkiZOUZ8kUl+Fg= +github.com/libp2p/go-libp2p-record v0.3.1/go.mod h1:T8itUkLcWQLCYMqtX7Th6r7SexyUJpIyPgks757td/E= +github.com/libp2p/go-libp2p-routing-helpers v0.7.5 h1:HdwZj9NKovMx0vqq6YNPTh6aaNzey5zHD7HeLJtq6fI= +github.com/libp2p/go-libp2p-routing-helpers v0.7.5/go.mod h1:3YaxrwP0OBPDD7my3D0KxfR89FlcX/IEbxDEDfAmj98= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= +github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= +github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= +github.com/libp2p/go-netroute v0.4.0 h1:sZZx9hyANYUx9PZyqcgE/E1GUG3iEtTZHUEvdtXT7/Q= +github.com/libp2p/go-netroute v0.4.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= +github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= +github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= +github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfYRSg= +github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= +github.com/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.8 h1:vOIKv9/+HKiqJAElJIEYv3ZLcihRxyP7Suu/Mu8Dxjs= +github.com/linxGnu/grocksdb v1.9.8/go.mod h1:C3CNe9UYc9hlEM2pC82AqiGS3LRW537u9LFV4wIZuHk= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= +github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= +github.com/marcopolo/simnet v0.0.4 h1:50Kx4hS9kFGSRIbrt9xUS3NJX33EyPqHVmpXvaKLqrY= +github.com/marcopolo/simnet v0.0.4/go.mod h1:tfQF1u2DmaB6WHODMtQaLtClEf3a296CKQLq5gAsIS0= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +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/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/highwayhash v1.0.4 h1:asJizugGgchQod2ja9NJlGOWq4s7KsAWr5XUc9Clgl4= +github.com/minio/highwayhash v1.0.4/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/minio/minlz v1.0.1-0.20250507153514-87eb42fe8882 h1:0lgqHvJWHLGW5TuObJrfyEi6+ASTKDBWikGvPqy9Yiw= +github.com/minio/minlz v1.0.1-0.20250507153514-87eb42fe8882/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/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/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.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +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/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/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/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= +github.com/multiformats/go-multiaddr v0.16.1 h1:fgJ0Pitow+wWXzN9do+1b8Pyjmo8m5WhGfzpL82MpCw= +github.com/multiformats/go-multiaddr v0.16.1/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= +github.com/multiformats/go-multiaddr-dns v0.5.0 h1:p/FTyHKX0nl59f+S+dEUe8HRK+i5Ow/QHMw8Nh3gPCo= +github.com/multiformats/go-multiaddr-dns v0.5.0/go.mod h1:yJ349b8TPIAANUyuOzn1oz9o22tV9f+06L+cCeMxC14= +github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.10.0 h1:UpP223cig/Cx8J76jWt91njpK3GTAO1w02sdcjZDSuc= +github.com/multiformats/go-multicodec v0.10.0/go.mod h1:wg88pM+s2kZJEQfRCKBNU+g32F5aWBEjyFHXvZLTcLI= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-multistream v0.6.1 h1:4aoX5v6T+yWmc2raBHsTvzmFhOI8WVOer28DeBBEYdQ= +github.com/multiformats/go-multistream v0.6.1/go.mod h1:ksQf6kqHAb6zIsyw7Zm+gAuVo57Qbq84E27YlYqavqw= +github.com/multiformats/go-varint v0.1.0 h1:i2wqFp4sdl3IcIxfAonHQV9qU5OsZ4Ts9IOoETFs5dI= +github.com/multiformats/go-varint v0.1.0/go.mod h1:5KVAVXegtfmNQQm/lCY+ATvDzvJJhSkUlGQV9wgObdI= +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/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +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.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0= +github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= +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/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= +github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +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.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/opencontainers/runc v1.2.8 h1:RnEICeDReapbZ5lZEgHvj7E9Q3Eex9toYmaGBsbvU5Q= +github.com/opencontainers/runc v1.2.8/go.mod h1:cC0YkmZcuvr+rtBZ6T7NBoVbMGNAdLa/21vIElJDOzI= +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.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +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/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM= +github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe h1:vHpqOnPlnkba8iSxU4j/CvDSS9J4+F4473esQsYLGoE= +github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= +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/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= +github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= +github.com/pion/dtls/v3 v3.1.2 h1:gqEdOUXLtCGW+afsBLO0LtDD8GnuBBjEy6HRtyofZTc= +github.com/pion/dtls/v3 v3.1.2/go.mod h1:Hw/igcX4pdY69z1Hgv5x7wJFrUkdgHwAn/Q/uo7YHRo= +github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4= +github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= +github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4= +github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic= +github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= +github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so= +github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= +github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo= +github.com/pion/rtcp v1.2.16/go.mod h1:/as7VKfYbs5NIb4h6muQ35kQF/J0ZVNz2Z3xKoCBYOo= +github.com/pion/rtp v1.8.19 h1:jhdO/3XhL/aKm/wARFVmvTfq0lC/CvN1xwYKmduly3c= +github.com/pion/rtp v1.8.19/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= +github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE= +github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= +github.com/pion/sdp/v3 v3.0.18 h1:l0bAXazKHpepazVdp+tPYnrsy9dfh7ZbT8DxesH5ZnI= +github.com/pion/sdp/v3 v3.0.18/go.mod h1:ZREGo6A9ZygQ9XkqAj5xYCQtQpif0i6Pa81HOiAdqQ8= +github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4= +github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY= +github.com/pion/stun/v3 v3.1.1 h1:CkQxveJ4xGQjulGSROXbXq94TAWu8gIX2dT+ePhUkqw= +github.com/pion/stun/v3 v3.1.1/go.mod h1:qC1DfmcCTQjl9PBaMa5wSn3x9IPmKxSdcCsxBcDBndM= +github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= +github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= +github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k8o= +github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM= +github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= +github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= +github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54= +github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U= +github.com/pkg/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/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +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/polydawn/refmt v0.89.1-0.20231129105047-37766d95467a h1:cgqrm0F3zwf9IPzca7xN4w+Zy6MC9ZkPvAC8QEWa/iQ= +github.com/polydawn/refmt v0.89.1-0.20231129105047-37766d95467a/go.mod h1:ocZfO/tLSHqfScRDNTJbAJR1by4D1lewauX9OwTaPuY= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= +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.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +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.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +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.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +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.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= +github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/quic-go/webtransport-go v0.10.0 h1:LqXXPOXuETY5Xe8ITdGisBzTYmUOy5eSj+9n4hLTjHI= +github.com/quic-go/webtransport-go v0.10.0/go.mod h1:LeGIXr5BQKE3UsynwVBeQrU1TPrbh73MGoC6jd+V7ow= +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/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= +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.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rollkit/go-da v0.9.0 h1:ECpS7RSLE84w61Y5a93RfaZrAZKeiyfjVwGxzjsW2cU= +github.com/rollkit/go-da v0.9.0/go.mod h1:LQImomegjZ/dEQLKDJA7RdUnOLINSZJwI7q1CvFPWJw= +github.com/ronanh/intcomp v1.1.1 h1:+1bGV/wEBiHI0FvzS7RHgzqOpfbBJzLIxkqMJ9e6yxY= +github.com/ronanh/intcomp v1.1.1/go.mod h1:7FOLy3P3Zj3er/kVrU/pl+Ql7JFZj7bwliMGketo0IU= +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/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI= +github.com/rs/zerolog v1.35.0/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= +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.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sasha-s/go-deadlock v0.3.9 h1:fiaT9rB7g5sr5ddNZvlwheclN9IP86eFW9WgqlEQV+w= +github.com/sasha-s/go-deadlock v0.3.9/go.mod h1:KuZj51ZFmx42q/mPaYbRk0P1xcwe697zsJKE03vD4/Y= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= +github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= +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/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= +github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +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.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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/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/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/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 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +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.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= +github.com/ulikunitz/xz v0.5.15/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/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/warpfork/go-testmark v0.12.1 h1:rMgCpJfwy1sJ50x0M0NgyphxYYPMOODIJHhsXyEHU0s= +github.com/warpfork/go-testmark v0.12.1/go.mod h1:kHwy7wfvGSPh1rQJYKayD4AbtNaeyZdcGi9tNJTaa5Y= +github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= +github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= +github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= +github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= +github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zondax/golem v0.27.0 h1:IbBjGIXF3SoGOZHsILJvIM/F/ylwJzMcHAcggiqniPw= +github.com/zondax/golem v0.27.0/go.mod h1:AmorCgJPt00L8xN1VrMBe13PSifoZksnQ1Ge906bu4A= +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 v1.0.1 h1:Ks/2tz/dOF+dbRynfZ0dEhcdL1lqw43Sa0zMXHpQ3aQ= +github.com/zondax/ledger-go v1.0.1/go.mod h1:j7IgMY39f30apthJYMd1YsHZRqdyu4KbVmUp0nU78X0= +gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8/go.mod h1:ZkMZ0dpQyWwlENaeZVBiQRjhMEZvk6VTXquzl3FOFP8= +gitlab.com/NebulousLabs/errors v0.0.0-20200929122200-06c536cf6975 h1:L/ENs/Ar1bFzUeKx6m3XjlmBgIUlykX9dzvp5k9NGxc= +gitlab.com/NebulousLabs/errors v0.0.0-20200929122200-06c536cf6975/go.mod h1:ZkMZ0dpQyWwlENaeZVBiQRjhMEZvk6VTXquzl3FOFP8= +gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 h1:dizWJqTWjwyD8KGcMOwgrkqu1JIkofYgKkmDeNE7oAs= +gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40/go.mod h1:rOnSnoRyxMI3fe/7KIbVcsHRGxe30OONv8dEgo+vCfA= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= +go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/bridges/prometheus v0.67.0 h1:dkBzNEAIKADEaFnuESzcXvpd09vxvDZsOjx11gjUqLk= +go.opentelemetry.io/contrib/bridges/prometheus v0.67.0/go.mod h1:Z5RIwRkZgauOIfnG5IpidvLpERjhTninpP1dTG2jTl4= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= +go.opentelemetry.io/contrib/instrumentation/runtime v0.68.0 h1:jhVIQEprwUTV+KfzzliLidclhoTOoHTgdz96kAyR8mU= +go.opentelemetry.io/contrib/instrumentation/runtime v0.68.0/go.mod h1:4HsdbLUbernaTnA8CNaNE+1g026SciXb3juRYe3l8EY= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0 h1:ZrPRak/kS4xI3AVXy8F7pipuDXmDsrO8Lg+yQjBLjw0= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0/go.mod h1:3y6kQCWztq6hyW8Z9YxQDDm0Je9AJoFar2G0yDcmhRk= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= +go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= +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.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= +go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= +go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.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.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/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-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +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.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= +golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= +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-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/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-20200520182314-0ba52f642ac2/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-20200707034311-ab3426394381/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-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-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-20190312061237-fead79001313/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-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/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-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/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-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210603081109-ebe580a85c40/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-20210630005230-0f9fa26af87c/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-20210927094055-39ccf1dd6fa6/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-20220310020820-b874c991c1a5/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-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c h1:6a8FdnNk6bTXBjR4AGKFgUKuo+7GnR3FX5L7CbveeZc= +golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw= +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.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= +golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +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-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-20190312151545-0bb0c0a6e846/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-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/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-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/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-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +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-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.276.0 h1:nVArUtfLEihtW+b0DdcqRGK1xoEm2+ltAihyztq7MKY= +google.golang.org/api v0.276.0/go.mod h1:Fnag/EWUPIcJXuIkP1pjoTgS5vdxlk3eeemL7Do6bvw= +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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-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-20190418145605-e7d98fc518a7/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-20190502173448-54afdca5d873/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-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= +google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= +google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +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.21.1/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.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.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.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +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.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +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.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.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +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/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +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/vmihailenco/msgpack.v2 v2.9.2/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8= +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.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/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= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= +lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= +pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= +pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/tools/celestia-node-fiber/listen.go b/tools/celestia-node-fiber/listen.go new file mode 100644 index 0000000000..9415a9c87a --- /dev/null +++ b/tools/celestia-node-fiber/listen.go @@ -0,0 +1,144 @@ +package celestianodefiber + +import ( + "context" + "errors" + "fmt" + + appfibre "github.com/celestiaorg/celestia-app/v9/fibre" + libshare "github.com/celestiaorg/go-square/v4/share" + + "github.com/celestiaorg/celestia-node/blob" + + "github.com/evstack/ev-node/block" +) + +// Listen implements fiber.DA.Listen. It subscribes to blob.Subscribe on the +// bridge node starting at fromHeight and forwards only share-version-2 +// (Fibre) blobs as BlobEvents. PFB blobs (v0/v1) sharing the namespace are +// dropped so consumers see a pure Fibre event stream. +// +// fromHeight == 0 starts the stream at the chain head (live follow). +// fromHeight > 0 replays from that block forward via the node's +// WaitForHeight loop so a subscriber can resume after a restart without +// missing blobs. +// +// DataSize on emitted events is the original payload byte length — matching +// the fibermock contract ev-node consumers code against. The v2 share only +// carries (fibre_blob_version + commitment), so the real size isn't derivable +// from the subscription alone; Listen therefore performs a Download per event +// to recover the size before forwarding. This adds one FSP round-trip per +// blob. If that cost becomes material we can expose an opt-out mode, but for +// now correctness over latency. +func (a *Adapter) Listen(ctx context.Context, namespace []byte, fromHeight uint64) (<-chan block.FiberBlobEvent, error) { + ns, err := toV0Namespace(namespace) + if err != nil { + return nil, fmt.Errorf("namespace: %w", err) + } + sub, err := a.blob.Subscribe(ctx, ns, fromHeight) + if err != nil { + return nil, fmt.Errorf("subscribing to blob stream: %w", err) + } + out := make(chan block.FiberBlobEvent, a.listenChannelSz) + go a.forwardFibreBlobs(ctx, sub, out) + return out, nil +} + +// forwardFibreBlobs drains a blob.SubscriptionResponse stream and emits a +// BlobEvent per share-version-2 blob. The output channel is closed when the +// subscription closes or ctx is cancelled. +func (a *Adapter) forwardFibreBlobs( + ctx context.Context, + sub <-chan *blob.SubscriptionResponse, + out chan<- block.FiberBlobEvent, +) { + defer close(out) + for { + select { + case resp, ok := <-sub: + if !ok { + return + } + if resp == nil { + continue + } + height := resolveHeight(resp) + for _, b := range resp.Blobs { + if b == nil || !b.IsFibreBlob() { + continue + } + event, err := a.fibreBlobToEvent(ctx, b.Blob, height) + if err != nil { + // Skip a malformed or un-fetchable v2 blob rather than + // kill the subscription. Most likely causes: the v2 + // payload was garbage-collected from FSPs, or the + // download was cancelled. Either way the consumer has + // no actionable signal for this single blob. + continue + } + select { + case out <- event: + case <-ctx.Done(): + return + } + } + case <-ctx.Done(): + return + } + } +} + +// resolveHeight picks the authoritative height from the subscription +// response. celestia-node flagged resp.Height as deprecated in favour of +// resp.Header.Height(); use the header when present, fall back otherwise. +func resolveHeight(resp *blob.SubscriptionResponse) uint64 { + if resp.Header != nil { + return uint64(resp.Header.Height) + } + return resp.Height +} + +// fibreBlobToEvent reconstructs the Fibre BlobID (version byte + 32-byte +// commitment) from a share-version-2 libshare.Blob, downloads the blob to +// determine the original payload size, and wraps everything as a BlobEvent. +// +// The Download is what makes DataSize accurate. Without it we would have to +// either report the v2 share size (wrong — misleads consumers) or zero +// (lossy). See the Listen doc for the cost / correctness rationale. +func (a *Adapter) fibreBlobToEvent( + ctx context.Context, + b *libshare.Blob, + height uint64, +) (block.FiberBlobEvent, error) { + version, err := b.FibreBlobVersion() + if err != nil { + return block.FiberBlobEvent{}, err + } + commit, err := b.FibreCommitment() + if err != nil { + return block.FiberBlobEvent{}, err + } + if len(commit) != appfibre.CommitmentSize { + return block.FiberBlobEvent{}, fmt.Errorf( + "fibre commitment must be %d bytes, got %d", + appfibre.CommitmentSize, len(commit), + ) + } + var c appfibre.Commitment + copy(c[:], commit) + id := appfibre.NewBlobID(uint8(version), c) + + res, err := a.fibre.Download(ctx, id) + if err != nil { + return block.FiberBlobEvent{}, fmt.Errorf("resolving payload size via Download: %w", err) + } + if res == nil { + return block.FiberBlobEvent{}, errors.New("fibre.Download returned nil result while resolving payload size") + } + + return block.FiberBlobEvent{ + BlobID: block.FiberBlobID(id), + Height: height, + DataSize: uint64(len(res.Data)), + }, nil +} diff --git a/tools/celestia-node-fiber/testing/bridge.go b/tools/celestia-node-fiber/testing/bridge.go new file mode 100644 index 0000000000..a6dcc2ce7e --- /dev/null +++ b/tools/celestia-node-fiber/testing/bridge.go @@ -0,0 +1,135 @@ +//go:build fibre + +package cnfibertest + +import ( + "context" + "crypto/rand" + "net" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cristalhq/jwt/v5" + "github.com/stretchr/testify/require" + "go.uber.org/fx" + + appfibre "github.com/celestiaorg/celestia-app/v9/fibre" + + "github.com/celestiaorg/celestia-node/api/client" + "github.com/celestiaorg/celestia-node/api/rpc/perms" + "github.com/celestiaorg/celestia-node/nodebuilder" + "github.com/celestiaorg/celestia-node/nodebuilder/node" + "github.com/celestiaorg/celestia-node/nodebuilder/p2p" + stateapi "github.com/celestiaorg/celestia-node/nodebuilder/state" +) + +// Bridge bundles an in-process celestia-node bridge node and the admin +// JWT that grants it authenticated RPC access. The adapter's ReadConfig +// needs both the address and the token for Blob.Subscribe to work. +type Bridge struct { + Node *nodebuilder.Node + AdminToken string +} + +// RPCAddr returns a WebSocket URL the adapter uses in +// Config.ReadConfig.BridgeDAAddr. WebSocket (not HTTP) is required +// because Blob.Subscribe returns a channel; go-jsonrpc only supports +// channel-returning methods over a streaming transport. +func (b *Bridge) RPCAddr() string { + return "ws://" + b.Node.RPCServer.ListenAddr() +} + +// StartBridge brings up an in-process celestia-node bridge connected to +// the Network's consensus gRPC endpoint. Mirrors celestia-node's +// api/client test helpers so TestShowcase has a real JSON-RPC server for +// Blob.Subscribe. +func StartBridge(t *testing.T, ctx context.Context, network *Network) *Bridge { + t.Helper() + + cfg := nodebuilder.DefaultConfig(node.Bridge) + + ip, port, err := net.SplitHostPort(network.ConsensusGRPCAddr()) + require.NoError(t, err, "splitting consensus gRPC addr") + cfg.Core.IP = ip + cfg.Core.Port = port + // Pin the bridge RPC to an ephemeral port; the test discovers it via + // Node.RPCServer.ListenAddr() after Start. + cfg.RPC.Port = "0" + + tempDir := t.TempDir() + store := nodebuilder.MockStore(t, cfg) + + auth, adminToken := bridgeAuth(t) + kr := bridgeKeyring(t, tempDir) + + bn, err := nodebuilder.New(node.Bridge, p2p.Private, store, + auth, + stateapi.WithKeyring(kr), + stateapi.WithKeyName(stateapi.AccountName(bridgeSigningKey)), + fx.Replace(node.StorePath(tempDir)), + ) + require.NoError(t, err, "constructing bridge node") + + require.NoError(t, bn.Start(ctx), "starting bridge node") + t.Cleanup(func() { + stopCtx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + _ = bn.Stop(stopCtx) + }) + + return &Bridge{ + Node: bn, + AdminToken: adminToken, + } +} + +// bridgeSigningKey is the keyring account the bridge uses for its own +// tx submissions. Distinct from the client's account so the two keyrings +// don't collide. +const bridgeSigningKey = "bridge-signer" + +func bridgeKeyring(t *testing.T, tempDir string) keyring.Keyring { + t.Helper() + + kr, err := client.KeyringWithNewKey(client.KeyringConfig{ + KeyName: bridgeSigningKey, + BackendName: keyring.BackendTest, + }, tempDir) + require.NoError(t, err, "creating bridge keyring") + + // The Fibre module on the bridge expects a key under + // appfibre.DefaultKeyName to exist, even though our client never uses + // the bridge's Fibre module for Upload/Download. Without it, + // fx.Start fails during fibre module wiring. + _, _, err = kr.NewMnemonic( + appfibre.DefaultKeyName, + keyring.English, "", "", hd.Secp256k1, + ) + require.NoError(t, err, "provisioning bridge fibre key") + return kr +} + +// bridgeAuth creates an HS256 JWT signer pair and an admin token. The +// returned fx option injects the signer/verifier into the node; the +// token is what the adapter puts in ReadConfig.DAAuthToken. +func bridgeAuth(t *testing.T) (fx.Option, string) { + t.Helper() + + key := make([]byte, 32) + _, err := rand.Read(key) + require.NoError(t, err, "rand.Read jwt key") + + signer, err := jwt.NewSignerHS(jwt.HS256, key) + require.NoError(t, err) + verifier, err := jwt.NewVerifierHS(jwt.HS256, key) + require.NoError(t, err) + + token, err := perms.NewTokenWithPerms(signer, perms.AllPerms) + require.NoError(t, err) + + return fx.Decorate(func() (jwt.Signer, jwt.Verifier, error) { + return signer, verifier, nil + }), string(token) +} diff --git a/tools/celestia-node-fiber/testing/doc.go b/tools/celestia-node-fiber/testing/doc.go new file mode 100644 index 0000000000..639adf723c --- /dev/null +++ b/tools/celestia-node-fiber/testing/doc.go @@ -0,0 +1,16 @@ +//go:build fibre + +// Package cnfibertest wires a single-validator Celestia chain, an in-process +// Fibre server, a celestia-node bridge and the celestia-node-fiber adapter +// together so Upload → Listen → Download can be exercised end-to-end in a +// Go test. +// +// The chain is a celestia-app testnode built with -tags fibre. The Fibre +// server runs in the same process and its FSP endpoint is registered with +// the valaddr module so the client's host registry can find it. The +// underlying adapter talks directly to consensus gRPC and the Fibre server; +// only Listen goes through the bridge node's blob subscription. +// +// This is the "fast sanity" variant. A multi-validator showcase is planned +// as a Docker Compose follow-up that exercises real quorum collection. +package cnfibertest diff --git a/tools/celestia-node-fiber/testing/evnode_fiber_test.go b/tools/celestia-node-fiber/testing/evnode_fiber_test.go new file mode 100644 index 0000000000..2220f42751 --- /dev/null +++ b/tools/celestia-node-fiber/testing/evnode_fiber_test.go @@ -0,0 +1,309 @@ +//go:build fibre + +package cnfibertest_test + +import ( + "context" + "crypto/rand" + "fmt" + "sync" + "testing" + "time" + + "github.com/ipfs/go-datastore" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + + "github.com/evstack/ev-node/block" + coreexecution "github.com/evstack/ev-node/core/execution" + "github.com/evstack/ev-node/node" + "github.com/evstack/ev-node/pkg/config" + datypes "github.com/evstack/ev-node/pkg/da/types" + genesispkg "github.com/evstack/ev-node/pkg/genesis" + "github.com/evstack/ev-node/pkg/p2p" + "github.com/evstack/ev-node/pkg/p2p/key" + "github.com/evstack/ev-node/pkg/sequencers/solo" + pkgsigner "github.com/evstack/ev-node/pkg/signer" + "github.com/evstack/ev-node/pkg/signer/file" + "github.com/evstack/ev-node/pkg/store" + + "github.com/celestiaorg/celestia-node/api/client" + + cnfiber "github.com/evstack/ev-node/tools/celestia-node-fiber" + cnfibertest "github.com/evstack/ev-node/tools/celestia-node-fiber/testing" +) + +const ( + evnodeBlockTime = 200 * time.Millisecond + evnodeDABlockTime = 1 * time.Second + evnodeHeaderNS = "ev-fib-ht" + evnodeDataNS = "ev-fib-da" + evnodeChainID = "ev-fiber-test" + evnodeBlockTimeout = 30 * time.Second + evnodePassphrase = "test-passphrase-evnode" +) + +// TestEvNode_FiberDA_Posting wires a full ev-node in-memory to the +// celestia-node-fiber adapter and verifies that block data is posted +// to the Fibre DA layer. The test: +// - Starts a single-validator Celestia chain + Fibre server + bridge +// - Creates a celestia-node-fiber adapter (block.FiberClient) +// - Constructs an ev-node aggregator node that uses the adapter as DA +// - Subscribes to the data namespace via adapter.Listen before uploading +// - Injects a transaction and waits for block production +// - Confirms the DA submitter pushed blobs to Fiber by receiving events +// on the subscription and round-tripping each through Download +func TestEvNode_FiberDA_Posting(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + t.Cleanup(cancel) + + network := cnfibertest.StartNetwork(t, ctx) + bridge := cnfibertest.StartBridge(t, ctx, network) + + adapter, err := cnfiber.New(ctx, cnfiber.Config{ + Client: client.Config{ + ReadConfig: client.ReadConfig{ + BridgeDAAddr: bridge.RPCAddr(), + DAAuthToken: bridge.AdminToken, + EnableDATLS: false, + }, + SubmitConfig: client.SubmitConfig{ + DefaultKeyName: network.ClientAccount, + Network: "private", + CoreGRPCConfig: client.CoreGRPCConfig{ + Addr: network.ConsensusGRPCAddr(), + }, + }, + }, + }, network.Consensus.Keyring) + require.NoError(t, err, "constructing adapter") + t.Cleanup(func() { _ = adapter.Close() }) + + // Subscribe to the header namespace BEFORE starting the node so we + // don't race against the first DA submission. fromHeight=0 follows + // the live tip. The adapter expects the 10-byte v0 namespace ID + // (the last 10 bytes of the full 29-byte namespace), matching what + // fiberDAClient.Submit extracts before calling fiber.Upload. + fullHeaderNS := datypes.NamespaceFromString(evnodeHeaderNS).Bytes() + headerNSID := fullHeaderNS[len(fullHeaderNS)-10:] + events, err := adapter.Listen(ctx, headerNSID, 0) + require.NoError(t, err, "starting fiber Listen on header namespace") + + rollnode, exec, nodeCleanup := newFiberEvNode(t, ctx, adapter) + t.Cleanup(nodeCleanup) + + nodeErrCh := make(chan error, 1) + go func() { + defer func() { + if r := recover(); r != nil { + nodeErrCh <- fmt.Errorf("node panicked: %v", r) + } + }() + nodeErrCh <- rollnode.Run(ctx) + }() + + txPayload := fmt.Sprintf("fiber-key=fiber-value-%d", time.Now().UnixNano()) + exec.InjectTx([]byte(txPayload)) + + require.Eventually(t, func() bool { + stats := exec.Stats() + t.Logf("blocks=%d txs=%d", stats.BlocksProduced, stats.TotalExecutedTxs) + return stats.BlocksProduced >= 1 && stats.TotalExecutedTxs >= 1 + }, evnodeBlockTimeout, 200*time.Millisecond, "ev-node should produce at least one block with the transaction") + + // Drain at least one Fiber BlobEvent from the subscription to prove + // the DA submitter pushed data through the fiber adapter's Upload + // path and the settlement landed on-chain. + var seen []block.FiberBlobEvent + require.Eventually(t, func() bool { + select { + case ev, ok := <-events: + if !ok { + return false + } + seen = append(seen, ev) + t.Logf("fiber event: blob_id=%x height=%d data_size=%d", + ev.BlobID, ev.Height, ev.DataSize) + return true + default: + return false + } + }, evnodeBlockTimeout, 500*time.Millisecond, "expected at least one Fiber BlobEvent from DA submission") + + for _, ev := range seen { + got, err := adapter.Download(ctx, ev.BlobID) + require.NoError(t, err, "adapter.Download blob_id=%x", ev.BlobID) + require.NotEmpty(t, got, "downloaded blob must not be empty") + t.Logf("download ok: blob_id=%x bytes=%d", ev.BlobID, len(got)) + } + + select { + case err := <-nodeErrCh: + t.Fatalf("node exited unexpectedly: %v", err) + default: + } +} + +type inMemExecutor struct { + mu sync.Mutex + data map[string]string + + txChan chan []byte + blocksProduced uint64 + totalExecutedTxs uint64 +} + +func newInMemExecutor() *inMemExecutor { + return &inMemExecutor{ + data: make(map[string]string), + txChan: make(chan []byte, 10000), + } +} + +func (e *inMemExecutor) InjectTx(tx []byte) { + select { + case e.txChan <- tx: + default: + } +} + +type execStats struct { + BlocksProduced uint64 + TotalExecutedTxs uint64 +} + +func (e *inMemExecutor) Stats() execStats { + e.mu.Lock() + defer e.mu.Unlock() + return execStats{BlocksProduced: e.blocksProduced, TotalExecutedTxs: e.totalExecutedTxs} +} + +func (e *inMemExecutor) InitChain(_ context.Context, _ time.Time, _ uint64, _ string) ([]byte, error) { + return []byte("inmem-genesis-root"), nil +} + +func (e *inMemExecutor) GetTxs(_ context.Context) ([][]byte, error) { + var txs [][]byte + for { + select { + case tx := <-e.txChan: + txs = append(txs, tx) + default: + return txs, nil + } + } +} + +func (e *inMemExecutor) ExecuteTxs(_ context.Context, txs [][]byte, _ uint64, _ time.Time, _ []byte) ([]byte, error) { + e.mu.Lock() + defer e.mu.Unlock() + for _, tx := range txs { + k, v, ok := parseKV(tx) + if ok { + e.data[k] = v + } + } + e.blocksProduced++ + e.totalExecutedTxs += uint64(len(txs)) + return []byte(fmt.Sprintf("root-%d", e.blocksProduced)), nil +} + +func (e *inMemExecutor) SetFinal(_ context.Context, _ uint64) error { return nil } +func (e *inMemExecutor) Rollback(_ context.Context, _ uint64) error { return nil } +func (e *inMemExecutor) GetExecutionInfo(_ context.Context) (coreexecution.ExecutionInfo, error) { + return coreexecution.ExecutionInfo{MaxGas: 0}, nil +} +func (e *inMemExecutor) FilterTxs(_ context.Context, txs [][]byte, _, _ uint64, _ bool) ([]coreexecution.FilterStatus, error) { + st := make([]coreexecution.FilterStatus, len(txs)) + for i := range st { + st[i] = coreexecution.FilterOK + } + return st, nil +} + +func parseKV(tx []byte) (string, string, bool) { + s := string(tx) + for i := 0; i < len(s); i++ { + if s[i] == '=' { + return s[:i], s[i+1:], true + } + } + return "", "", false +} + +func newFiberEvNode(t *testing.T, ctx context.Context, fiberClient block.FiberClient) (node.Node, *inMemExecutor, func()) { + t.Helper() + + tmpDir := t.TempDir() + logger := zerolog.New(zerolog.NewTestWriter(t)).With().Timestamp().Logger() + + // Create a file-backed signer so the executor can sign blocks. + signerDir := tmpDir + fs, err := file.CreateFileSystemSigner(signerDir, []byte(evnodePassphrase)) + require.NoError(t, err, "creating file signer") + signerAddr, err := fs.GetAddress() + require.NoError(t, err, "getting signer address") + + // Generate a separate libp2p node key for P2P networking. + nodePrivKey, _, err := crypto.GenerateEd25519Key(rand.Reader) + require.NoError(t, err, "generating node key") + nodeKey := &key.NodeKey{PrivKey: nodePrivKey} + + genesis := genesispkg.NewGenesis(evnodeChainID, 1, time.Now(), signerAddr) + require.NoError(t, genesis.Validate(), "validating genesis") + + cfg := config.DefaultConfig() + cfg.RootDir = tmpDir + cfg.DBPath = "data" + cfg.Node.Aggregator = true + cfg.Node.BlockTime = config.DurationWrapper{Duration: evnodeBlockTime} + cfg.Node.LazyMode = false + cfg.DA.BlockTime = config.DurationWrapper{Duration: evnodeDABlockTime} + cfg.DA.Namespace = evnodeHeaderNS + cfg.DA.DataNamespace = evnodeDataNS + cfg.DA.BatchingStrategy = "immediate" + cfg.DA.Fiber.Enabled = true + cfg.DA.RequestTimeout = config.DurationWrapper{Duration: 60 * time.Second} + cfg.P2P.ListenAddress = "/ip4/0.0.0.0/tcp/0" + cfg.P2P.DisableConnectionGater = true + cfg.Instrumentation.Prometheus = false + cfg.Instrumentation.Pprof = false + cfg.RPC.Address = "127.0.0.1:0" + cfg.Log.Level = "debug" + cfg.Signer.SignerType = "file" + cfg.Signer.SignerPath = signerDir + + // Build the full signer via the factory (needed for consistency with + // how the real node boots). + signer, err := pkgsigner.NewSigner(ctx, &cfg, evnodePassphrase) + require.NoError(t, err, "creating signer via factory") + + ds, err := store.NewDefaultKVStore(tmpDir, cfg.DBPath, "testdb") + require.NoError(t, err, "creating datastore") + + executor := newInMemExecutor() + sequencer := solo.NewSoloSequencer(logger, []byte(genesis.ChainID), executor) + daClient := block.NewFiberDAClient(fiberClient, cfg, logger, 0) + p2pClient, err := p2p.NewClient(cfg.P2P, nodeKey.PrivKey, datastore.NewMapDatastore(), genesis.ChainID, logger, nil) + require.NoError(t, err, "creating p2p client") + + rollnode, err := node.NewNode( + cfg, + executor, + sequencer, + daClient, + signer, + p2pClient, + genesis, + ds, + node.DefaultMetricsProvider(cfg.Instrumentation), + logger, + node.NodeOptions{}, + ) + require.NoError(t, err, "creating node") + + return rollnode, executor, func() {} +} + +var _ coreexecution.Executor = (*inMemExecutor)(nil) diff --git a/tools/celestia-node-fiber/testing/network.go b/tools/celestia-node-fiber/testing/network.go new file mode 100644 index 0000000000..7d9d2660aa --- /dev/null +++ b/tools/celestia-node-fiber/testing/network.go @@ -0,0 +1,222 @@ +//go:build fibre + +package cnfibertest + +import ( + "context" + "path/filepath" + "testing" + "time" + + sdkmath "cosmossdk.io/math" + "github.com/cometbft/cometbft/privval" + core "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/require" + + "github.com/celestiaorg/celestia-app/v9/app" + "github.com/celestiaorg/celestia-app/v9/app/encoding" + appfibre "github.com/celestiaorg/celestia-app/v9/fibre" + "github.com/celestiaorg/celestia-app/v9/pkg/appconsts" + "github.com/celestiaorg/celestia-app/v9/pkg/user" + "github.com/celestiaorg/celestia-app/v9/test/util/testnode" + fibretypes "github.com/celestiaorg/celestia-app/v9/x/fibre/types" + valtypes "github.com/celestiaorg/celestia-app/v9/x/valaddr/types" +) + +const ( + // defaultChainID matches the celestia-node bridge test helper so the + // bridge and consensus node agree on network identity. + defaultChainID = "private" + + // escrowDeposit is a generous initial escrow. Uploads consume from + // escrow for gas + blob fees, so leave headroom for multiple runs. + escrowDeposit = 50_000_000 // 50 TIA in utia + + // clientAccount is the keyring account the adapter uses to sign + // payment promises and MsgPayForFibre. Pre-funded in genesis; also + // has a funded escrow after StartNetwork returns. + clientAccount = appfibre.DefaultKeyName +) + +// Network bundles a single-validator celestia-app chain, an in-process +// Fibre server registered via valaddr, and a funded escrow account. The +// caller's *testing.T cleanup stops everything. +type Network struct { + // Consensus is the celestia-app testnode context (keyring, gRPC + // client, home dir). Use Consensus.GRPCClient for consensus gRPC + // access and Consensus.Keyring for signing. + Consensus testnode.Context + + // FibreServer is the in-process Fibre gRPC server registered with + // the chain's valaddr module. The adapter (via appfibre.Client's + // default host registry) discovers it through valaddr. + FibreServer *appfibre.Server + + // ChainID matches what the bridge node needs to be configured with. + ChainID string + + // ClientAccount is the keyring name for the pre-funded + pre-escrowed + // account the showcase uses. Expose it so the test wires SubmitConfig. + ClientAccount string +} + +// StartNetwork boots a single-validator Fibre chain + server and returns +// a ready-to-use Network. Registration + escrow funding is complete when +// this returns. +func StartNetwork(t *testing.T, ctx context.Context) *Network { + t.Helper() + + cfg := testnode.DefaultConfig(). + WithChainID(defaultChainID). + WithFundedAccounts(clientAccount). + WithDelayedPrecommitTimeout(50 * time.Millisecond) + + cctx, _, grpcAddr := testnode.NewNetwork(t, cfg) + _, err := cctx.WaitForHeight(1) + require.NoError(t, err, "waiting for first block") + + server := startFibreServer(t, ctx, cctx, grpcAddr) + // Register the fibre server's address in plain `host:port` form — + // celestia-app's x/valaddr now requires it (no scheme prefix), and + // the gRPC dialer accepts bare `host:port` directly via the + // passthrough resolver. + registerValidator(t, ctx, cctx, server.ListenAddress()) + fundEscrow(t, ctx, cctx) + + return &Network{ + Consensus: cctx, + FibreServer: server, + ChainID: defaultChainID, + ClientAccount: clientAccount, + } +} + +// ConsensusGRPCAddr returns the host:port of the chain's gRPC endpoint +// that the adapter's SubmitConfig.CoreGRPCConfig should point at. +func (n *Network) ConsensusGRPCAddr() string { + return n.Consensus.GRPCClient.Target() +} + +// startFibreServer spins up an in-process Fibre gRPC server bound to an +// ephemeral localhost port. The server uses the testnode's private +// validator key for BLS signing, and keeps blob data in memory. +func startFibreServer( + t *testing.T, + ctx context.Context, + cctx testnode.Context, + appGRPCAddr string, +) *appfibre.Server { + t.Helper() + + pvKey := filepath.Join(cctx.HomeDir, "config", "priv_validator_key.json") + pvState := filepath.Join(cctx.HomeDir, "data", "priv_validator_state.json") + filePV := privval.LoadFilePV(pvKey, pvState) + + serverCfg := appfibre.DefaultServerConfig() + serverCfg.AppGRPCAddress = appGRPCAddr + serverCfg.ServerListenAddress = "127.0.0.1:0" + serverCfg.SignerFn = func(string) (core.PrivValidator, error) { + return filePV, nil + } + serverCfg.StoreFn = func(sc appfibre.StoreConfig) (*appfibre.Store, error) { + return appfibre.NewMemoryStore(sc), nil + } + + server, err := appfibre.NewServer(serverCfg) + require.NoError(t, err, "creating fibre server") + require.NoError(t, server.Start(ctx), "starting fibre server") + t.Cleanup(func() { + stopCtx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + _ = server.Stop(stopCtx) + }) + return server +} + +// registerValidator submits MsgSetFibreProviderInfo so the chain's +// valaddr module maps the validator's consensus address to the Fibre +// server's listen address. Without this the client's host registry +// cannot locate any FSPs. +func registerValidator( + t *testing.T, + ctx context.Context, + cctx testnode.Context, + fibreAddr string, +) { + t.Helper() + + stakingClient := stakingtypes.NewQueryClient(cctx.GRPCClient) + validators, err := stakingClient.Validators(ctx, &stakingtypes.QueryValidatorsRequest{}) + require.NoError(t, err) + require.Len(t, validators.Validators, 1, "single-validator testnode expected") + valOperator := validators.Validators[0].OperatorAddress + + txClient, err := testnode.NewTxClientFromContext(cctx) + require.NoError(t, err) + + msg := &valtypes.MsgSetFibreProviderInfo{ + Signer: valOperator, + Host: fibreAddr, + } + resp, err := txClient.SubmitTx(ctx, []sdk.Msg{msg}, user.SetGasLimit(200_000), user.SetFee(5_000)) + require.NoError(t, err, "registering validator fibre host") + require.Equal(t, uint32(0), resp.Code, "register validator tx failed") + require.NoError(t, cctx.WaitForNextBlock()) + + // Sanity-check the registration landed. + tmClient := cmtservice.NewServiceClient(cctx.GRPCClient) + valSet, err := tmClient.GetLatestValidatorSet(ctx, &cmtservice.GetLatestValidatorSetRequest{}) + require.NoError(t, err) + require.Len(t, valSet.Validators, 1) + consAddr, err := sdk.ConsAddressFromBech32(valSet.Validators[0].Address) + require.NoError(t, err) + + valAddrClient := valtypes.NewQueryClient(cctx.GRPCClient) + info, err := valAddrClient.FibreProviderInfo(ctx, &valtypes.QueryFibreProviderInfoRequest{ + ValidatorConsensusAddress: consAddr.String(), + }) + require.NoError(t, err) + require.True(t, info.Found, "fibre provider info not registered") + require.Equal(t, fibreAddr, info.Info.Host) +} + +// fundEscrow deposits enough utia into the client's escrow account to +// cover payment promises for several blob uploads. The async PFF +// settlement kicked off by adapter.Upload debits this account. +func fundEscrow(t *testing.T, ctx context.Context, cctx testnode.Context) { + t.Helper() + + info, err := cctx.Keyring.Key(clientAccount) + require.NoError(t, err, "loading client keyring entry") + addr, err := info.GetAddress() + require.NoError(t, err) + + ecfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) + txClient, err := user.SetupTxClient( + ctx, cctx.Keyring, cctx.GRPCClient, ecfg, + user.WithDefaultAccount(clientAccount), + ) + require.NoError(t, err, "setting up funded-account tx client") + + amount := sdk.NewCoin(appconsts.BondDenom, sdkmath.NewInt(escrowDeposit)) + msg := &fibretypes.MsgDepositToEscrow{ + Signer: addr.String(), + Amount: amount, + } + resp, err := txClient.SubmitTx(ctx, []sdk.Msg{msg}, user.SetGasLimit(200_000), user.SetFee(5_000)) + require.NoError(t, err, "depositing to escrow") + require.Equal(t, uint32(0), resp.Code, "deposit tx failed") + require.NoError(t, cctx.WaitForNextBlock()) + + // Sanity: escrow is now visible. + queryClient := fibretypes.NewQueryClient(cctx.GRPCClient) + escrow, err := queryClient.EscrowAccount(ctx, &fibretypes.QueryEscrowAccountRequest{ + Signer: addr.String(), + }) + require.NoError(t, err) + require.True(t, escrow.Found, "escrow account not found after deposit") + require.Equal(t, amount, escrow.EscrowAccount.Balance, "escrow balance mismatch") +} diff --git a/tools/celestia-node-fiber/testing/showcase_test.go b/tools/celestia-node-fiber/testing/showcase_test.go new file mode 100644 index 0000000000..f55b0a0c2b --- /dev/null +++ b/tools/celestia-node-fiber/testing/showcase_test.go @@ -0,0 +1,284 @@ +//go:build fibre + +package cnfibertest_test + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/celestiaorg/celestia-node/api/client" + + "github.com/evstack/ev-node/block" + cnfiber "github.com/evstack/ev-node/tools/celestia-node-fiber" + cnfibertest "github.com/evstack/ev-node/tools/celestia-node-fiber/testing" +) + +const ( + // showcaseBlobs is how many distinct-payload blobs the test pushes + // through the adapter. Large enough to surface ordering and + // duplicate-handling bugs, small enough to keep wall time reasonable. + showcaseBlobs = 10 + + // listenEventsTimeout bounds the collection window for N BlobEvents. + // The async MsgPayForFibre broadcasts serialize on the TxClient + // mutex, so the dominant cost is block_time_per_tx × N. 60s gives + // ~6s per blob which is generous for a 50ms-precommit testnode. + listenEventsTimeout = 60 * time.Second +) + +// TestShowcase spins up a single-validator Celestia chain with an +// in-process Fibre server, a celestia-node bridge, and drives the full +// adapter surface: Listen subscribes first, Upload pushes N distinct +// blobs, the async MsgPayForFibre settlements commit on-chain, the +// subscription delivers an event per blob, and Download round-trips each +// payload byte-for-byte. +func TestShowcase(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + t.Cleanup(cancel) + + network := cnfibertest.StartNetwork(t, ctx) + bridge := cnfibertest.StartBridge(t, ctx, network) + + adapter, err := cnfiber.New(ctx, cnfiber.Config{ + Client: client.Config{ + ReadConfig: client.ReadConfig{ + BridgeDAAddr: bridge.RPCAddr(), + DAAuthToken: bridge.AdminToken, + EnableDATLS: false, + }, + SubmitConfig: client.SubmitConfig{ + DefaultKeyName: network.ClientAccount, + Network: "private", + CoreGRPCConfig: client.CoreGRPCConfig{ + Addr: network.ConsensusGRPCAddr(), + }, + }, + }, + }, network.Consensus.Keyring) + require.NoError(t, err, "constructing adapter") + t.Cleanup(func() { _ = adapter.Close() }) + + // Namespace: 10 bytes of v0 ID. Uploads share a namespace so Listen + // sees every settlement event in one stream. + namespace := bytes.Repeat([]byte{0xfe}, 10) + + // Subscribe BEFORE uploading so we don't race against settlements. + // fromHeight=0 → follow from the live tip. TestShowcaseResume below + // exercises the non-zero path. + events, err := adapter.Listen(ctx, namespace, 0) + require.NoError(t, err, "starting Listen subscription") + + // Build N distinctive payloads so byte-swapping or off-by-one BlobID + // reconstruction would be caught by the download diff below. + payloads := make([][]byte, showcaseBlobs) + for i := range payloads { + payloads[i] = []byte(fmt.Sprintf( + "showcase blob %02d — payload=%s", + i, bytes.Repeat([]byte{'a' + byte(i)}, 8+i), + )) + } + + // expected maps hex(BlobID) → original payload; populated by Upload + // and consulted by Listen + Download to catch misrouted bytes. + expected := make(map[string][]byte, showcaseBlobs) + ids := make([]block.FiberBlobID, showcaseBlobs) + for i, payload := range payloads { + res, err := adapter.Upload(ctx, namespace, payload) + require.NoError(t, err, "adapter.Upload #%d", i) + require.NotEmpty(t, res.BlobID, "upload #%d returned empty BlobID", i) + key := hex.EncodeToString(res.BlobID) + _, dup := expected[key] + require.False(t, dup, "adapter.Upload #%d returned a duplicate BlobID %s", i, key) + expected[key] = payload + ids[i] = res.BlobID + t.Logf("upload[%02d] ok: blob_id=%s size=%d", i, key, len(payload)) + } + + // Drain events until every Upload has a matching BlobEvent. Order is + // not guaranteed — multiple settlements can land in the same block. + seen := make(map[string]block.FiberBlobEvent, showcaseBlobs) + deadline := time.After(listenEventsTimeout) + for len(seen) < showcaseBlobs { + select { + case ev, ok := <-events: + require.True(t, ok, + "Listen channel closed with only %d/%d events", len(seen), showcaseBlobs) + key := hex.EncodeToString(ev.BlobID) + if _, want := expected[key]; !want { + t.Logf("listen: ignoring unexpected BlobID %s", key) + continue + } + if prev, dup := seen[key]; dup { + t.Fatalf("listen: duplicate event for BlobID %s (prev height=%d new height=%d)", + key, prev.Height, ev.Height) + } + seen[key] = ev + t.Logf("listen[%02d/%02d] ok: blob_id=%s height=%d data_size=%d", + len(seen), showcaseBlobs, key, ev.Height, ev.DataSize) + case <-deadline: + missing := make([]string, 0, showcaseBlobs-len(seen)) + for k := range expected { + if _, got := seen[k]; !got { + missing = append(missing, k) + } + } + t.Fatalf("timed out after %s: got %d/%d events; missing=%v", + listenEventsTimeout, len(seen), showcaseBlobs, missing) + } + } + + // Every event must carry the right DataSize and a non-zero block + // height. DataSize matches the original payload length because the + // adapter's Listen issues a Download per event to recover it (see + // listen.go). A silent byte truncation anywhere upstream would + // surface here before we even get to the Download round-trip. + for key, ev := range seen { + require.Greater(t, ev.Height, uint64(0), + "BlobEvent %s must carry a real block height", key) + require.Equal(t, uint64(len(expected[key])), ev.DataSize, + "BlobEvent %s DataSize must match original payload length", key) + } + + // Round-trip every blob through Download and diff bytes. Walking + // ids (upload order) rather than seen (map iteration order) keeps + // log output deterministic. + for i, id := range ids { + key := hex.EncodeToString(id) + got, err := adapter.Download(ctx, id) + require.NoError(t, err, "adapter.Download #%d (%s)", i, key) + require.Equal(t, expected[key], got, + "Download #%d (%s) bytes mismatch", i, key) + t.Logf("download[%02d] ok: blob_id=%s bytes=%d", i, key, len(got)) + } +} + +// TestShowcaseResume verifies that a subscriber can rejoin the stream +// from a historical block height and receive every matching blob that +// was settled since. This is the fromHeight > 0 path added by +// celestia-node#4962: Listen opens a WaitForHeight loop starting at the +// requested height, so callers can resume after a restart without +// missing blobs. +func TestShowcaseResume(t *testing.T) { + const resumeBlobs = 3 + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + t.Cleanup(cancel) + + network := cnfibertest.StartNetwork(t, ctx) + bridge := cnfibertest.StartBridge(t, ctx, network) + + adapter, err := cnfiber.New(ctx, cnfiber.Config{ + Client: client.Config{ + ReadConfig: client.ReadConfig{ + BridgeDAAddr: bridge.RPCAddr(), + DAAuthToken: bridge.AdminToken, + EnableDATLS: false, + }, + SubmitConfig: client.SubmitConfig{ + DefaultKeyName: network.ClientAccount, + Network: "private", + CoreGRPCConfig: client.CoreGRPCConfig{ + Addr: network.ConsensusGRPCAddr(), + }, + }, + }, + }, network.Consensus.Keyring) + require.NoError(t, err, "constructing adapter") + t.Cleanup(func() { _ = adapter.Close() }) + + namespace := bytes.Repeat([]byte{0xfd}, 10) + + // Phase 1: open a live subscription, upload N blobs, and harvest + // each blob's settlement height as reported by Listen. These + // heights are the ground truth for the resume test below. + liveCtx, liveCancel := context.WithCancel(ctx) + liveEvents, err := adapter.Listen(liveCtx, namespace, 0) + require.NoError(t, err, "starting live Listen for height discovery") + + payloads := make([][]byte, resumeBlobs) + ids := make([]block.FiberBlobID, resumeBlobs) + expected := make(map[string][]byte, resumeBlobs) + for i := range payloads { + payloads[i] = []byte(fmt.Sprintf("resume blob %d", i)) + res, err := adapter.Upload(ctx, namespace, payloads[i]) + require.NoError(t, err, "upload #%d", i) + ids[i] = res.BlobID + expected[hex.EncodeToString(res.BlobID)] = payloads[i] + t.Logf("phase1 upload[%d] blob_id=%s", i, hex.EncodeToString(res.BlobID)) + } + + heights := make(map[string]uint64, resumeBlobs) + for len(heights) < resumeBlobs { + select { + case ev, ok := <-liveEvents: + require.True(t, ok, "live Listen channel closed early") + key := hex.EncodeToString(ev.BlobID) + if _, want := expected[key]; !want { + continue + } + heights[key] = ev.Height + t.Logf("phase1 listen blob_id=%s height=%d", key, ev.Height) + case <-time.After(listenEventsTimeout): + t.Fatalf("timed out collecting heights: got %d/%d", len(heights), resumeBlobs) + } + } + liveCancel() + + // Pick the smallest height across all uploads. Resuming from this + // height must replay every blob we uploaded, regardless of whether + // multiple settlements landed in the same block. + var fromHeight uint64 + for _, h := range heights { + if fromHeight == 0 || h < fromHeight { + fromHeight = h + } + } + t.Logf("resume fromHeight=%d", fromHeight) + + // Phase 2: fresh Listen starting at fromHeight. Expect every blob + // we uploaded in phase 1 to be replayed. + resumeEvents, err := adapter.Listen(ctx, namespace, fromHeight) + require.NoError(t, err, "starting resume Listen") + + seen := make(map[string]block.FiberBlobEvent, resumeBlobs) + for len(seen) < resumeBlobs { + select { + case ev, ok := <-resumeEvents: + require.True(t, ok, "resume Listen channel closed early") + key := hex.EncodeToString(ev.BlobID) + if _, want := expected[key]; !want { + continue + } + if _, dup := seen[key]; dup { + t.Fatalf("resume Listen emitted duplicate for BlobID %s", key) + } + seen[key] = ev + t.Logf("phase2 listen[%d/%d] blob_id=%s height=%d data_size=%d", + len(seen), resumeBlobs, key, ev.Height, ev.DataSize) + case <-time.After(listenEventsTimeout): + missing := make([]string, 0, resumeBlobs-len(seen)) + for k := range expected { + if _, got := seen[k]; !got { + missing = append(missing, k) + } + } + t.Fatalf("timed out on resume Listen: got %d/%d; missing=%v", + len(seen), resumeBlobs, missing) + } + } + + // Every resume event must carry the correct DataSize (Download- + // resolved, same as the live Listen path) and the right height. + for key, ev := range seen { + require.Equal(t, uint64(len(expected[key])), ev.DataSize, + "resume BlobEvent %s DataSize must match original payload length", key) + require.Equal(t, heights[key], ev.Height, + "resume BlobEvent %s Height must match the block it settled in", key) + } +}