diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 976fd83821..ace7a971fb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -76,6 +76,17 @@ jobs: uses: actions/setup-go@v6 with: go-version-file: ./go.mod + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build evstack:local-dev (cached) + uses: docker/build-push-action@v6 + with: + context: . + file: apps/testapp/Dockerfile + load: true + tags: evstack:local-dev + cache-from: type=gha + cache-to: type=gha,mode=max - name: E2E Tests run: make test-e2e diff --git a/scripts/build.mk b/scripts/build.mk index d191c9d2ed..67bfe95dd9 100644 --- a/scripts/build.mk +++ b/scripts/build.mk @@ -60,7 +60,7 @@ build-da: ## docker-build: Build Docker image for local testing docker-build: @echo "--> Building Docker image for local testing" - @docker build -t evstack:local-dev . + @docker build -t evstack:local-dev -f apps/testapp/Dockerfile . @echo "--> Docker image built: evstack:local-dev" @echo "--> Checking if image exists locally..." @docker images evstack:local-dev diff --git a/scripts/test.mk b/scripts/test.mk index 27a0be7865..e461e33cac 100644 --- a/scripts/test.mk +++ b/scripts/test.mk @@ -22,7 +22,7 @@ test-integration: .PHONY: test-integration ## test-e2e: Running e2e tests -test-e2e: build build-da build-evm +test-e2e: build build-da build-evm docker-build-if-local @echo "--> Running e2e tests" @cd test/e2e && go test -mod=readonly -failfast -timeout=15m -tags='e2e evm' ./... --binary=../../build/testapp --evm-binary=../../build/evm .PHONY: test-e2e @@ -69,8 +69,12 @@ test-docker-upgrade-e2e: ## docker-build-if-local: Build Docker image if using local repository docker-build-if-local: @if [ -z "$(EV_NODE_IMAGE_REPO)" ] || [ "$(EV_NODE_IMAGE_REPO)" = "evstack" ]; then \ - echo "--> Local repository detected, building Docker image..."; \ - $(MAKE) docker-build; \ + if docker image inspect evstack:local-dev >/dev/null 2>&1; then \ + echo "--> Found local Docker image: evstack:local-dev (skipping build)"; \ + else \ + echo "--> Local repository detected, building Docker image..."; \ + $(MAKE) docker-build; \ + fi; \ else \ echo "--> Using remote repository: $(EV_NODE_IMAGE_REPO)"; \ fi diff --git a/test/e2e/da_client_integration_test.go b/test/e2e/da_client_integration_test.go index 408541dfe7..125aa0c93d 100644 --- a/test/e2e/da_client_integration_test.go +++ b/test/e2e/da_client_integration_test.go @@ -1,3 +1,5 @@ +//go:build e2e + package e2e import ( diff --git a/test/e2e/da_posting_integration_test.go b/test/e2e/da_posting_integration_test.go new file mode 100644 index 0000000000..bdaaae8b5e --- /dev/null +++ b/test/e2e/da_posting_integration_test.go @@ -0,0 +1,359 @@ +//go:build e2e + +package e2e + +import ( + "context" + "encoding/base64" + "fmt" + "io" + "net/http" + "os" + "os/exec" + "strings" + "testing" + "time" + + tastoradocker "github.com/celestiaorg/tastora/framework/docker" + tastoraconsts "github.com/celestiaorg/tastora/framework/docker/consts" + "github.com/celestiaorg/tastora/framework/docker/container" + tastoracosmos "github.com/celestiaorg/tastora/framework/docker/cosmos" + tastorada "github.com/celestiaorg/tastora/framework/docker/dataavailability" + "github.com/celestiaorg/tastora/framework/docker/evstack" + "github.com/celestiaorg/tastora/framework/testutil/query" + "github.com/celestiaorg/tastora/framework/testutil/wait" + tastoratypes "github.com/celestiaorg/tastora/framework/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/ibc-go/v8/modules/apps/transfer" + coreda "github.com/evstack/ev-node/core/da" + "github.com/evstack/ev-node/da/jsonrpc" + seqcommon "github.com/evstack/ev-node/sequencers/common" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" +) + +// TestEvNode_PostsToDA spins up celestia-app, a celestia bridge node and an +// EV Node (aggregator) via tastora, then verifies the EV Node actually posts +// data to DA by confirming blobs exist in the ev-data namespace via the DA +// JSON-RPC client. +func TestEvNode_PostsToDA(t *testing.T) { + if testing.Short() { + t.Skip("skip integration in short mode") + } + + configurePrefixOnce.Do(configureCelestiaBech32Prefix) + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + uniqueTestName := fmt.Sprintf("%s-%d", t.Name(), time.Now().UnixNano()) + + dockerClient, networkID := tastoradocker.Setup(t) + t.Cleanup(tastoradocker.Cleanup(t, dockerClient)) + + encCfg := testutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, bank.AppModuleBasic{}, transfer.AppModuleBasic{}, gov.AppModuleBasic{}) + + // 1) Start celestia-app chain + chainImage := container.Image{ + Repository: "ghcr.io/celestiaorg/celestia-app", + Version: "v5.0.10", + UIDGID: "10001:10001", + } + + chainBuilder := tastoracosmos.NewChainBuilderWithTestName(t, uniqueTestName). + WithDockerClient(dockerClient). + WithDockerNetworkID(networkID). + WithImage(chainImage). + WithEncodingConfig(&encCfg). + WithAdditionalStartArgs( + "--force-no-bbr", + "--grpc.enable", + "--grpc.address", "0.0.0.0:9090", + "--rpc.grpc_laddr=tcp://0.0.0.0:9098", + "--rpc.laddr=tcp://0.0.0.0:26657", + "--timeout-commit", "1s", + "--minimum-gas-prices", "0utia", + ). + WithNode(tastoracosmos.NewChainNodeConfigBuilder().Build()) + + chain, err := chainBuilder.Build(ctx) + require.NoError(t, err, "build celestia-app chain") + require.NoError(t, chain.Start(ctx), "start celestia-app chain") + + chainID := chain.GetChainID() + genesisHash, err := fetchGenesisHash(ctx, chain) + require.NoError(t, err, "genesis hash") + + chainNetInfo, err := chain.GetNodes()[0].GetNetworkInfo(ctx) + require.NoError(t, err, "chain network info") + coreHost := chainNetInfo.Internal.Hostname + + // 2) Start celestia-node (bridge) + daImage := container.Image{ + Repository: "ghcr.io/celestiaorg/celestia-node", + Version: "v0.28.4-mocha", + UIDGID: "10001:10001", + } + + daNetwork, err := tastorada.NewNetworkBuilderWithTestName(t, uniqueTestName). + WithDockerClient(dockerClient). + WithDockerNetworkID(networkID). + WithImage(daImage). + WithNodes(tastorada.NewNodeBuilder().WithNodeType(tastoratypes.BridgeNode).Build()). + Build(ctx) + require.NoError(t, err, "build da network") + + bridge := daNetwork.GetBridgeNodes()[0] + err = bridge.Start(ctx, + tastorada.WithChainID(chainID), + tastorada.WithAdditionalStartArguments( + "--p2p.network", chainID, + "--core.ip", coreHost, + "--rpc.addr", "0.0.0.0", + ), + tastorada.WithEnvironmentVariables(map[string]string{ + "CELESTIA_CUSTOM": tastoratypes.BuildCelestiaCustomEnvVar(chainID, genesisHash, ""), + "P2P_NETWORK": chainID, + }), + ) + require.NoError(t, err, "start bridge node") + + bridgeWallet, err := bridge.GetWallet() + require.NoError(t, err, "bridge wallet") + + faucet := chain.GetFaucetWallet() + require.NotNil(t, faucet, "faucet wallet") + + validatorNode := chain.GetNodes()[0].(*tastoracosmos.ChainNode) + + // Wait for the node to serve RPC before funding. + err = wait.ForCondition(ctx, 2*time.Minute, time.Second, func() (bool, error) { + c, err := validatorNode.GetRPCClient() + if err != nil { + return false, nil + } + if _, err := c.Status(ctx); err != nil { + return false, nil + } + h, err := validatorNode.Height(ctx) + if err != nil { + return false, nil + } + return h >= 3, nil + }) + require.NoError(t, err, "validator RPC ready") + + // Fund the bridge wallet via CLI to avoid `BroadcastMessages` JSON-RPC decoding issues. + faucetKey := tastoraconsts.FaucetAccountKeyName + sendAmt := sdk.NewInt64Coin(chain.Config.Denom, 5_000_000_000) + rpcNode := fmt.Sprintf("tcp://%s:26657", coreHost) + cmd := []string{ + validatorNode.BinaryName, + "tx", "bank", "send", + faucetKey, + bridgeWallet.FormattedAddress, + sendAmt.String(), + "--chain-id", chainID, + "--home", validatorNode.HomeDir(), + "--keyring-backend", "test", + "--node", rpcNode, + "--fees", fmt.Sprintf("1000%s", chain.Config.Denom), + "--broadcast-mode", "sync", + "--yes", + } + stdout, stderr, err := validatorNode.Exec(ctx, cmd, nil) + require.NoErrorf(t, err, "fund bridge wallet via CLI: %s", string(stderr)) + require.Contains(t, string(stdout), "code: 0", "bank send succeeded") + + err = wait.ForCondition(ctx, 2*time.Minute, time.Second, func() (bool, error) { + amnt, err := query.Balance(ctx, chain.GetNode().GrpcConn, bridgeWallet.FormattedAddress, chain.Config.Denom) + if err != nil { + return false, nil + } + return amnt.Int64() > 0, nil + }) + require.NoError(t, err, "bridge wallet should have balance") + + bridgeNetInfo, err := bridge.GetNetworkInfo(ctx) + require.NoError(t, err, "bridge network info") + + // 4) Start EV Node (aggregator) pointing at DA + evNodeChain, err := evstack.NewChainBuilderWithTestName(t, uniqueTestName). + WithChainID("evchain-test"). + WithBinaryName("testapp"). + WithAggregatorPassphrase("12345678"). + WithImage(getEvNodeImage(t)). + WithDockerClient(dockerClient). + WithDockerNetworkID(networkID). + WithNode(evstack.NewNodeBuilder().WithAggregator(true).Build()). + Build(ctx) + require.NoError(t, err, "build ev node chain") + + evNode := evNodeChain.GetNodes()[0] + require.NoError(t, evNode.Init(ctx), "ev node init") + + authToken, err := bridge.GetAuthToken() + require.NoError(t, err, "bridge auth token") + + daAddress := fmt.Sprintf("http://%s", bridgeNetInfo.Internal.RPCAddress()) + headerNamespaceStr := "ev-header" + dataNamespaceStr := "ev-data" + dataNamespace := coreda.NamespaceFromString(dataNamespaceStr) + + require.NoError(t, evNode.Start(ctx, + "--evnode.da.address", daAddress, + "--evnode.da.auth_token", authToken, + "--evnode.rpc.address", "0.0.0.0:7331", + "--evnode.da.namespace", headerNamespaceStr, + "--evnode.da.data_namespace", dataNamespaceStr, + "--kv-endpoint", "0.0.0.0:8080", + ), "start ev node") + + evNetInfo, err := evNode.GetNetworkInfo(ctx) + require.NoError(t, err, "ev node network info") + httpAddr := evNetInfo.External.HTTPAddress() + require.NotEmpty(t, httpAddr) + parts := strings.Split(httpAddr, ":") + require.Len(t, parts, 2) + host, port := parts[0], parts[1] + if host == "0.0.0.0" { + host = "localhost" + } + cli, err := newHTTPClient(host, port) + require.NoError(t, err) + + // 5) Submit a tx to ev-node to trigger block production + DA posting + key, value := "da-key", "da-value" + _, err = cli.Post(ctx, "/tx", key, value) + require.NoError(t, err) + + wait.ForCondition(ctx, 30*time.Second, 2*time.Second, func() (bool, error) { + res, err := cli.Get(ctx, "/kv?key="+key) + if err != nil { + return false, nil + } + return string(res) == value, nil + }) + + // 6) Assert data landed on DA via celestia-node blob RPC (namespace ev-data) + daRPCAddr := fmt.Sprintf("http://%s", bridgeNetInfo.Internal.RPCAddress()) + daClient, err := jsonrpc.NewClient(ctx, zerolog.Nop(), daRPCAddr, authToken, seqcommon.AbsoluteMaxBlobSize) + require.NoError(t, err, "new da client") + defer daClient.Close() + + validator := chain.GetNodes()[0].(*tastoracosmos.ChainNode) + tmRPC, err := validator.GetRPCClient() + require.NoError(t, err, "tm rpc client") + + var pfbHeight int64 + wait.ForCondition(ctx, time.Minute, 5*time.Second, func() (bool, error) { + res, err := tmRPC.TxSearch(ctx, "message.action='/celestia.blob.v1.MsgPayForBlobs'", false, nil, nil, "desc") + if err != nil || len(res.Txs) == 0 { + return false, nil + } + dataNSB64 := base64.StdEncoding.EncodeToString(dataNamespace.Bytes()) + for _, tx := range res.Txs { + if tx.TxResult.Code != 0 { + continue + } + for _, ev := range tx.TxResult.Events { + if ev.Type != "celestia.blob.v1.EventPayForBlobs" { + continue + } + for _, attr := range ev.Attributes { + if string(attr.Key) == "namespaces" && strings.Contains(string(attr.Value), dataNSB64) { + pfbHeight = tx.Height + return true, nil + } + } + } + } + return false, nil + }) + + wait.ForCondition(ctx, time.Minute, 5*time.Second, func() (bool, error) { + if pfbHeight == 0 { + return false, nil + } + for h := pfbHeight; h <= pfbHeight+10; h++ { + ids, err := daClient.DA.GetIDs(ctx, uint64(h), dataNamespace.Bytes()) + if err != nil { + t.Logf("GetIDs data height=%d err=%v", h, err) + continue + } + if ids != nil && len(ids.IDs) > 0 { + return true, nil + } + } + return false, nil + }) +} + +// newHTTPClient is a small helper to avoid importing the docker_e2e client. +func newHTTPClient(host, port string) (*httpClient, error) { + return &httpClient{baseURL: fmt.Sprintf("http://%s:%s", host, port)}, nil +} + +type httpClient struct { + baseURL string +} + +func (c *httpClient) Get(ctx context.Context, path string) ([]byte, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+path, nil) + if err != nil { + return nil, err + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, resp.Status) + } + return io.ReadAll(resp.Body) +} + +func (c *httpClient) Post(ctx context.Context, path, key, value string) ([]byte, error) { + body := strings.NewReader(fmt.Sprintf("%s=%s", key, value)) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+path, body) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "text/plain") + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + return io.ReadAll(resp.Body) +} + +// getEvNodeImage resolves the EV Node image to use for the test. +// Falls back to EV_NODE_IMAGE_REPO:EV_NODE_IMAGE_TAG or evstack:local-dev. +func getEvNodeImage(t *testing.T) container.Image { + t.Helper() + + repo := strings.TrimSpace(getEnvDefault("EV_NODE_IMAGE_REPO", "evstack")) + tag := strings.TrimSpace(getEnvDefault("EV_NODE_IMAGE_TAG", "local-dev")) + + // When using the default local image, fail fast with a clear message if the image is missing. + if repo == "evstack" && tag == "local-dev" { + if err := exec.Command("docker", "image", "inspect", "evstack:local-dev").Run(); err != nil { + t.Fatalf("missing docker image evstack:local-dev; run `make docker-build` (or set `EV_NODE_IMAGE_REPO`/`EV_NODE_IMAGE_TAG` to a pullable image): %v", err) + } + } + + return container.NewImage(repo, tag, "10001:10001") +} + +func getEnvDefault(key, def string) string { + if v := strings.TrimSpace(os.Getenv(key)); v != "" { + return v + } + return def +} diff --git a/test/e2e/go.mod b/test/e2e/go.mod index 5011f11387..2baff6dd01 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -12,9 +12,12 @@ require ( github.com/cosmos/ibc-go/v8 v8.7.0 github.com/ethereum/go-ethereum v1.16.7 github.com/evstack/ev-node v1.0.0-beta.10 + github.com/evstack/ev-node/core v1.0.0-beta.5 + github.com/evstack/ev-node/da v0.0.0-00010101000000-000000000000 github.com/evstack/ev-node/execution/evm v0.0.0-20250602130019-2a732cf903a5 github.com/evstack/ev-node/execution/evm/test v0.0.0-00010101000000-000000000000 github.com/libp2p/go-libp2p v0.45.0 + github.com/rs/zerolog v1.34.0 github.com/stretchr/testify v1.11.1 ) @@ -51,15 +54,16 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.2.0 // indirect github.com/bits-and-blooms/bitset v1.22.0 // indirect - github.com/bytedance/sonic v1.13.2 // indirect - github.com/bytedance/sonic/loader v0.2.4 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.14.2 // indirect + github.com/bytedance/sonic/loader v0.4.0 // indirect github.com/celestiaorg/go-header v0.7.4 // indirect github.com/celestiaorg/go-square/merkle v0.0.0-20240627094109-7d01436067a3 // indirect github.com/celestiaorg/nmt v0.24.2 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chzyer/readline v1.5.1 // indirect - github.com/cloudwego/base64x v0.1.5 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect github.com/cockroachdb/errors v1.12.0 // indirect github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect @@ -98,7 +102,6 @@ require ( github.com/emicklei/dot v1.6.2 // indirect github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect github.com/ethereum/go-verkle v0.2.2 // indirect - github.com/evstack/ev-node/core v1.0.0-beta.5 // indirect github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/ferranbt/fastssz v0.1.4 // indirect @@ -198,7 +201,6 @@ require ( github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/cors v1.11.1 // indirect - github.com/rs/zerolog v1.34.0 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/sasha-s/go-deadlock v0.3.5 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect diff --git a/test/e2e/go.sum b/test/e2e/go.sum index f291834660..03603f3c36 100644 --- a/test/e2e/go.sum +++ b/test/e2e/go.sum @@ -122,11 +122,12 @@ github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/ github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE= github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= -github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= -github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= -github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= -github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= +github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= +github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= +github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/celestiaorg/go-header v0.7.4 h1:kQx3bVvKV+H2etxRi4IUuby5VQydBONx3giHFXDcZ/o= github.com/celestiaorg/go-header v0.7.4/go.mod h1:eX9iTSPthVEAlEDLux40ZT/olXPGhpxHd+mEzJeDhd0= @@ -160,9 +161,8 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D 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/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= -github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= @@ -606,10 +606,8 @@ github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 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/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 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= @@ -1011,6 +1009,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -1413,7 +1413,6 @@ lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=